Безплатне донавчання медичної 7B-моделі: QLoRA, FAISS і захисні обмеження
Конвеєр із нульовим бюджетом на Kaggle T4: синтетичні дані, QLoRA, RAG за класичними текстами, перевірений шар знань, жорстко задані екстрені обмеження, реліз у GGUF і баги, на які ми натрапили.
Медичні чат-боти зазвичай помиляються одним із двох способів. Або вигадують лікування, або змішують традиційну й доказову медицину в одну небезпечну мішанину. Завдання полягало в тому, щоб зробити модель, яка суворо розділяє ці два світи, показує, звідки бере інформацію, і відступає, коли людина описує екстрену ситуацію. Вона називається Expert Vaidya, відокремлює аюрведу від сучасних клінічних рекомендацій, і вся вона зроблена безплатно.
Залізо — дві відеокарти Tesla T4 на безплатному тарифі Kaggle. Модель — 7B з відкритими вагами, а знання взято з класичних текстів у суспільному надбанні. Результат працює офлайн на ноутбуці з 16 ГБ і ставиться однією командою. Нижче весь конвеєр.
З чого все складається
- Базова модель: Qwen2.5-7B-Instruct, донавчена з Unsloth і QLoRA
- Навчальні дані: 52 структуровані приклади, 2 написано вручну та 50 синтетичних, що пройшли фільтр якості
- Пошук: індекс FAISS по 6 765 чанках із 1 492 сторінок «Сушрута-самхіти» (суспільне надбання на Archive.org) плюс перевірена база знань із 22 записів на основі WHO, ADA, NICE і PubMed Central
- Безпека: код обмежень на звичайному Python, без жодної моделі, який перевіряє кожне запитання до того, як воно потрапить до LLM
- Постачання: файл GGUF Q4_K_M (4,68 ГБ), опублікований на Hugging Face і в реєстрі Ollama
Крок 1: дешеві синтетичні дані
Розмічені медичні дані коштують більше, ніж дозволяє бюджет хобі-проєкту, тому приклади з 50 медичних тем згенерувала менша модель, Qwen2.5-3B у 4 бітах, за суворим контрактом на формат виводу. У кожній відповіді мали бути аюрведичний розділ, сучасний розділ та історичний розділ, а все неповне відкидалося автоматично:
python has_all_3 = all(x in response for x in ["Ayurvedic", "Modern", "Historical"])
if has_all_3 and len(response) > 200:
keep(example)
Усі 50 згенерованих прикладів пройшли перевірку. Разом із 2 написаними вручну затравками вийшло 52 навчальні рядки. Набір крихітний, але мета була не навчити фактів. Мета була навчити формату й дисципліни. Факти надходять із пошуку.
Крок 2: QLoRA на безплатних T4
Unsloth на двох T4 у Kaggle навчав лише адаптери LoRA:
model = FastLanguageModel.get_peft_model(
model, r=16,
target_modules=["q_proj","k_proj","v_proj","o_proj",
"gate_proj","up_proj","down_proj"],
lora_alpha=16, lora_dropout=0,
use_gradient_checkpointing="unsloth",
)
Змінилися лише 0,53% ваг: 40 370 176 параметрів, що навчаються, із 7 655 986 688, 5 епох, 35 кроків, ефективний розмір батча 8 і підсумковий loss на навчанні 1,0493. За часом вийшло менше 10 хвилин. Медицини модель тут не вчилася. Вона навчилася завжди відповідати в п'ятичастинній структурі й ніколи не змішувати галузі.
Крок 3: 1 492 сторінки у FAISS
Формат сам по собі не робить відповіді обґрунтованими. Два англійські переклади «Сушрута-самхіти» в суспільному надбанні (812 і 680 сторінок) узято з Archive.org. PyMuPDF видобув текст, рекурсивний сплітер LangChain нарізав його на чанки по 500 символів із перекриттям 100, а all-MiniLM-L6-v2 побудував ембединги. Вийшло 6 765 чанків, нормалізованих і завантажених у FAISS IndexFlatIP.
Під час запиту система дістає top-k чанків, але з порогом релевантності, оцінкою щонайменше 0,3–0,4, тож нерелевантні сторінки у вікно контексту не потрапляють.
Крок 4: перевірений шар
Старі переклади, відскановані з друкованих видань, — це шумний OCR, тому поруч із першим індексом стоїть другий, ретельно дібраний. У ньому 22 записи, перевірені вручну, у кожного вказано джерело й оцінку впевненості. Серед джерел «Чарака-самхіта», «Сушрута-самхіта» й «Аштанга-хрідая», рекомендації WHO 2023 і 2024 років, ADA Standards of Care 2025, рекомендації NICE щодо тривожних ознак (NG185 і NG173) та три дослідження з PubMed Central: Gymnema PMC2915138, Boswellia PMC3251738 і Ashwagandha PMC3573577. Доповнює все таблиця взаємодій трав і ліків для варфарину та препаратів від діабету. Пошук залишає лише записи з verified == True і confidence >= 0.8.
Крок 5: обмеження спрацьовують першими
Ключове проєктне рішення: екстрені випадки ніколи не доходять до моделі. Звичайний Python перевіряє введення раніше за все інше:
warnings, disclaimer = safety_guard.generate_safety_response(user_question)
if warnings and "EMERGENCY" in warnings[0]:
return warnings[0] + disclaimer # LLM never runs
Він відстежує 14 ознак екстреної ситуації, як-от біль у грудях, ознаки інсульту, анафілаксію та суїцидальні висловлювання, плюс 7 небезпечних патернів самолікування на кшталт "stop taking", "replace insulin" чи "double the dose". На повідомлення про біль у грудях повертається попередження для сортування з екстреними номерами (112, 911, 999), щоразу те саме.
Крок 6: квантування, публікація, розгортання
Після злиття ваг LoRA Unsloth записав GGUF на 4,68 ГБ у форматі Q4_K_M, який вирушив на Hugging Face і до реєстру Ollama. Тепер запустити модель може будь-хто:
bash ollama run tejaas6/expert-vaidya
Системний промпт, температура й поведінка обмежень постачаються всередині моделі. Жодного налаштування й жодного API-ключа не потрібно, і все працює цілком офлайн.
Що пішло не так
Ось частина, про яку мало хто пише:
- Нескінченний цикл переліку. За низької температури модель якось видала сотні варіантів "hyaluronan polymer...". Зупинило це налаштування
no_repeat_ngram_size=3. - Падіння під час завантаження на Hugging Face. Файл на 4,4 ГБ спричиняв ImportError з
XetProgressReporter. Допомогло задатиHF_HUB_DISABLE_XET=1, щоб оминути передавання через xet, і очистити закешовані модулі перед повторним імпортом. - Зникомий /tmp у Kaggle. Unsloth надіслав експорт GGUF до
/tmp, бо в робочій теці бракувало місця, а/tmpне зберігається як вивід ядра. Розв'язав проблему явнийshutil.copyдо/kaggle/workingперед архівуванням. - Витік мови. Qwen іноді вставляв у відповіді китайські ієрогліфи. Це прибрали постобробка регулярним виразом і правило «лише англійська» в промпті.
Результати
- Запитання про діабет посилається на класифікацію Прамеха з «Чараки» та на діагностичні пороги WHO і ADA, причому кожне окремо.
- На запитання, чи можна приймати Gymnema разом із препаратами від діабету, модель вказує на сумарний ризик гіпоглікемії та на протокол контролю.
- Опис стискального болю в грудях повністю оминає LLM і повертає лише рекомендації щодо сортування.
- На вигадану хворобу модель відповідає відмовою, а не вигаданим лікуванням.
Де знайти
Модель можна завантажити з реєстру Ollama, ваги GGUF лежать на Hugging Face під назвою Expert-Vaidya-Qwen7B-GGUF. Це освітній дослідницький проєкт. Це не медична консультація й не інструмент діагностики.