Как дообучить нейросеть под свои задачи: от подготовки датасета в JSONL до экспорта модели в GGUF и деплоя через vLLM. Реальный код на Unsloth + TRL (LoRA/QLoRA), SVG-схема пайплайна, сравнение с RAG, расчёт стоимости обучения на арендованной видеокарте и разбор типичных ошибок, из-за которых модель «тупеет» вместо того, чтобы умнеть.
Файнтюнинг — это дообучение готовой модели на ваших данных, чтобы она писала в нужном стиле, следовала формату ответа или разбиралась в узкой предметной области. В 2026 году это уже не «роскошь для исследовательских лабораторий»: с появлением LoRA и его 4-битной версии QLoRA дообучить модель на 7–8 млрд параметров можно на одной арендованной видеокарте за несколько часов и за деньги, сопоставимые с ценой пары чашек кофе. Библиотека Unsloth ускорила обучение в 2–3 раза, а фреймворк TRL дал готовый пайплайн с минимальным количеством кода. В этом гайде — полный путь от «у меня есть 200 примеров диалогов» до работающей модели, которую можно запустить через llama.cpp или vLLM. Весь код рабочий, его можно запустить на своём компьютере или в Google Colab.
Файнтюнинг — это продолжение обучения уже готовой модели на дополнительных примерах. В отличие от RAG, где вы просто «подкладываете» модели нужные документы в промпт, файнтюнинг меняет сами веса модели. Правило выбора простое: RAG — для знаний, файнтюнинг — для поведения и стиля.
Если ваша задача — «отвечать по актуальной базе документов, которая меняется каждый день» — берите RAG. Если задача — «писать ответы в строгом корпоративном тоне», «всегда возвращать строго валидный JSON», «отвечать на медицинские вопросы как врач с 20-летним стажем» или «понимать жаргон конкретной отрасли» — это территория файнтюнинга. Идеальная комбинация для продакшена — дообученная модель плюс RAG поверх неё: модель знает, как отвечать, а RAG даёт что отвечать.
Полное дообучение всех весов (full fine-tuning) модели на 70 млрд параметров требует кластер GPU и стоит сотни долларов. Поэтому в 2026 году стандарт де-факто — LoRA: вместо изменения всех миллиардов параметров вы обучаете маленькую надстройку из нескольких миллионов (обычно 0.1–1% от модели). QLoRA дополнительно сжимает базовую модель до 4 бит, что позволяет дообучать 7B-модель на одной видеокарте с 16–24 ГБ VRAM. Результат — файл-адаптер размером в десятки мегабайт, который можно мержить в модель или подгружать отдельно.
Качество датасета решает 80% результата. Самый распространённый формат для SFT (supervised fine-tuning) — JSONL: каждая строка — один пример. Стандарт де-факто — формат Alpaca с полями instruction (задача), input (контекст, может быть пустым) и output (эталонный ответ).
# train.jsonl — по одному примеру на строку {"instruction": "Классифицируй обращение клиента в одну из категорий: возврат, доставка, гарантия", "input": "Товар пришёл с браком, хочу вернуть деньги", "output": "возврат"} {"instruction": "Классифицируй обращение клиента в одну из категорий: возврат, доставка, гарантия", "input": "Где мой заказ, уже три дня нет трек-номера", "output": "доставка"} {"instruction": "Ответь на вопрос клиента вежливо и кратко, строго по регламенту", "input": "Можно ли вернуть товар без упаковки?", "output": "Да, возврат возможен без оригинальной упаковки в течение 14 дней при сохранении товарного вида."}
Сколько примеров нужно? Для смены стиля и формата хватает 100–1000 качественных примеров. Для глубокого погружения в предметную область — от 5 000 до 50 000. Больше — не всегда лучше: «мусорный» датасет на миллион строк хуже, чем чистая сотня, вручную проверенная экспертом. Ключевые правила очистки: уберите дубликаты, примеры с противоречивыми ответами, слишком короткие (менее 10 символов) и слишком длинные ответы, а также любые PII и внутренние секреты, если модель потом уйдёт в открытый доступ.
Обязательно выделите held-out тестовую выборку — 5–10% примеров, которые не участвуют в обучении, но по которым вы потом проверите, не «зазубрила» ли модель данные вместо того, чтобы обобщать.
Unsloth — оптимизированная библиотека поверх Transformers, которая ускоряет QLoRA-обучение в 2–3 раза и экономит до 60% VRAM за счёт кастомных CUDA-ядер. Ставится за одну команду и работает прямо в Colab (там уже предустановлены нужные версии PyTorch). Базовая модель — открытая Meta Llama 3.1 8B (подробнее о линейке Llama — на странице Meta Llama), для китайского/многоязычного стека часто берут Qwen, для скорости — Mistral.
# Установка (на Google Colab всё уже есть — этот шаг можно пропустить) pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git" pip install --no-deps trl peft accelerate bitsandbytes from unsloth import FastLanguageModel import torch max_seq_length = 2048 # максимум токенов на пример dtype = None # авто-определение fp16/bf16 load_in_4bit = True # QLoRA: модель грузим в 4 бита model, tokenizer = FastLanguageModel.from_pretrained( model_name="unsloth/Meta-Llama-3.1-8B-bnb-4bit", max_seq_length=max_seq_length, dtype=dtype, load_in_4bit=load_in_4bit, )
Префикс unsloth/ в имени модели означает, что Unsloth подтянет свою оптимизированную версию весов. Если видеокарта слабая и 8B не влезает — возьмите unsloth/Llama-3.2-3B-bnb-4bit или unsloth/Qwen2.5-3B-bnb-4bit.
Теперь добавляем LoRA-адаптер. Ключевые параметры: r (ранг матриц — сколько новых параметров обучаем), lora_alpha (масштаб обновлений), target_modules (какие слои дообучать — для LLM это проекции внимания q/k/v/o и MLP gate/up/down). Значения по умолчанию r=16, alpha=16 — хорошая отправная точка почти для всех задач.
model = FastLanguageModel.get_peft_model( model, r=16, # ранг LoRA: больше — мощнее, но тяжелее target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], lora_alpha=16, # масштаб обновлений lora_dropout=0, # 0 для переобучения, иначе 0.05 bias="none", use_gradient_checkpointing="unsloth", # экономия VRAM random_state=3407, use_rslora=False, loftq_config=None, )
Хорошая эвристика: alpha = r — самый стабильный режим для старта. Если модель «недоучивается» (ответы почти не изменились) — поднимите r до 32–64. Если переобучается (отвечает только заученными фразами) — уменьшите r или добавьте lora_dropout=0.05. Число обучаемых параметров при r=16 на 8B-модели — около 40 миллионов, то есть менее 0.5% от всех весов.
Для SFT в связке с Unsloth используют SFTTrainer из TRL. Сначала превратим датасет в промпты формата Alpaca и добавим EOS-токен, затем запустим обучение. Стартовые гиперпараметры ниже подходят для 500–5000 примеров на одной видеокарте.
from datasets import load_dataset alpaca_prompt = """Ниже инструкция с контекстом. Напиши ответ строго по инструкции. ### Инструкция: {} ### Контекст: {} ### Ответ: {}""" def formatting(examples): texts = [] for instr, inp, out in zip(examples["instruction"], examples["input"], examples["output"]): texts.append(alpaca_prompt.format(instr, inp, out) + tokenizer.eos_token) return {"text": texts} dataset = load_dataset("json", data_files="train.jsonl", split="train") dataset = dataset.map(formatting, batched=True)
from trl import SFTTrainer from transformers import TrainingArguments from unsloth import is_bfloat16_supported trainer = SFTTrainer( model=model, tokenizer=tokenizer, train_dataset=dataset, dataset_text_field="text", max_seq_length=max_seq_length, dataset_num_proc=2, packing=False, # True экономит VRAM на коротких примерах args=TrainingArguments( per_device_train_batch_size=2, gradient_accumulation_steps=4, # эффективный батч = 2*4 = 8 warmup_steps=5, max_steps=60, # 60 шагов — для пробы, поднимите для реального прогона learning_rate=2e-4, fp16=not is_bfloat16_supported(), bf16=is_bfloat16_supported(), logging_steps=1, optim="adamw_8bit", weight_decay=0.01, lr_scheduler_type="linear", seed=3407, output_dir="outputs", ), ) stats = trainer.train()
Не гонитесь за большим числом эпох: для SFT на малых датасетах чаще всего достаточно 1–3 эпох. Следите за loss в логах — если он падает почти до нуля, модель переобучается, останавливайтесь раньше. Полезный трюк — early stopping: сохраняйте чекпоинты каждый N шагов и проверяйте качество на тестовой выборке.
Обязательно сравните ответы модели до и после обучения на одних и тех же примерах — иначе непонятно, дал ли файнтюнинг хоть что-то. Прогоните 10–20 промптов из тестовой выборки через базовую модель и через адаптер, и сверьте с эталонными ответами.
FastLanguageModel.for_inference(model) # перевод в режим инференса test_prompt = alpaca_prompt.format( "Классифицируй обращение клиента: возврат, доставка, гарантия", "Курьер привёз чужой заказ, что делать?", "", # пустой ответ — модель допишет сама ) inputs = tokenizer([test_prompt], return_tensors="pt").to("cuda") outputs = model.generate(**inputs, max_new_tokens=64, use_cache=True) print(tokenizer.batch_decode(outputs)[0])
Для объективной оценки используйте LLM-as-a-judge: попросите более сильную модель выставить балл 1–5 за точность, следование формату и отсутствие выдумок, сравнивая ответ с эталоном. Если средний балл по 30–50 тестовым примерам не вырос по сравнению с базовой моделью — файнтюнинг не удался: проблема либо в датасете, либо в гиперпараметрах, либо в неверном ожидании (возможно, вашу задачу решает RAG).
После обучения адаптер нужно превратить в то, что можно запустить. Два основных пути. GGUF + llama.cpp — для запуска на CPU или на локальной машине: модель квантуют в 4–8 бит, и она едет на Mac, ноутбук или слабый сервер. vLLM — для продакшена с GPU: даёт OpenAI-совместимый API и высокую пропускную способность.
# 1) Сохранить LoRA-адаптер отдельно model.save_pretrained("lora_model") # 2) Или смержить адаптер в полную модель (для vLLM) model.save_pretrained_merged("model_merged", tokenizer, save_method="merged_16bit") # 3) Экспорт в GGUF (для llama.cpp) model.save_pretrained_gguf("model_gguf", tokenizer, quantization_method="q4_k_m")
# Запуск в llama.cpp (CPU / локально) llama-cli -m model_gguf/unsloth.Q4_K_M.gguf -p "Классифицируй обращение: ..." # Запуск в vLLM (GPU, OpenAI-совместимый API) pip install vllm vllm serve model_merged --served-model-name my-finetuned --port 8000 # Теперь модель доступна как обычный OpenAI-эндпоинт: curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"my-finetuned","messages":[{"role":"user","content":"привет"}]}'
Для России отдельный вопрос — аренда GPU и оплата облачных сервисов. Зарубежные провайдеры (RunPod, Lambda, Vast.ai) принимают оплату картами зарубежных банков; из РФ удобнее платить через посредников или виртуальные карты — готовый разбор способов есть на странице оплаты AI-сервисов из РФ. Альтернатива — отечественные облака (Yandex Cloud, Selectel, VK Cloud) с GPU в каталоге.
Стоимость файнтюнинга складывается из аренды GPU и времени. Приблизительные цены на рынке аренды в 2026 году: RTX 4090 (24 ГБ) — от $0.35/час, A100 80 ГБ — $1.5–2.5/час, L4 — около $0.7/час. Дообучение 7–8B-модели QLoRA на 1000 примеров занимает на 4090 примерно 1–2 часа, то есть весь прогон обходится в $1–5. Полный файнтюнинг 70B-модели — уже $50–200 за прогон, и без LoRA тут не обойтись. Подробнее про тарифы и оптимизацию бюджета — в разделе цены на AI.
Типичные ошибки, из-за которых модель «тупеет» после дообучения:
01. Катастрофическое забывание — слишком высокий learning rate или слишком много эпох, модель теряет базовые навыки. Лечится низким LR (1e-4–2e-4) и малым числом шагов.
02. Грязный датасет — дубликаты и противоречивые ответы. Один противоречивый пример способен перебить десяток корректных.
03. Утечка теста в обучение — тестовая выборка попала в train, метрики врут. Всегда отделяйте held-out перед обучением.
04. Файнтюнинг вместо RAG — попытка «зашить» актуальные факты в веса. Факты устаревают, веса — нет; для знаний используйте RAG.
05. Неверный формат промпта — при обучении использовали один шаблон, при инференсе — другой. Модель «сходит с ума» на ровном месте. Храните шаблон промпта вместе с моделью.
06. Слишком маленький датасет с высоким r — 50 примеров и r=64 гарантированно переобучат модель. Малый датасет — малый ранг (r=8–16).
Файнтюнинг LLM в 2026 году — доступная процедура, а не исследовательский проект: базовая модель (Llama/Qwen/Mistral) + чистый датасет в JSONL + QLoRA-адаптер через Unsloth и TRL = дообученная модель за $1–5 и пару часов. Запомните главное правило: RAG — для знаний, файнтюнинг — для поведения и формата, а в продакшене они работают в паре. Начните с малого датасета в 100–200 качественных примеров, обязательно сравните ответы до и после, и только затем масштабируйте данные и ранг. Готовую модель экспортируйте в GGUF для локального запуска или разверните через vLLM как OpenAI-совместимый API. Подписывайтесь на Telegram-канал qantcore — там разбираем инструменты и практики работы с LLM каждую неделю.