🧠

Файнтюнинг LLM в 2026: полный гайд по дообучению нейросетей

Как дообучить нейросеть под свои задачи: от подготовки датасета в JSONL до экспорта модели в GGUF и деплоя через vLLM. Реальный код на Unsloth + TRL (LoRA/QLoRA), SVG-схема пайплайна, сравнение с RAG, расчёт стоимости обучения на арендованной видеокарте и разбор типичных ошибок, из-за которых модель «тупеет» вместо того, чтобы умнеть.

FINETUNING ⏱ 25 мин

Файнтюнинг — это дообучение готовой модели на ваших данных, чтобы она писала в нужном стиле, следовала формату ответа или разбиралась в узкой предметной области. В 2026 году это уже не «роскошь для исследовательских лабораторий»: с появлением LoRA и его 4-битной версии QLoRA дообучить модель на 7–8 млрд параметров можно на одной арендованной видеокарте за несколько часов и за деньги, сопоставимые с ценой пары чашек кофе. Библиотека Unsloth ускорила обучение в 2–3 раза, а фреймворк TRL дал готовый пайплайн с минимальным количеством кода. В этом гайде — полный путь от «у меня есть 200 примеров диалогов» до работающей модели, которую можно запустить через llama.cpp или vLLM. Весь код рабочий, его можно запустить на своём компьютере или в Google Colab.

ПАЙПЛАЙН ФАЙНТЮНИНГА [ 6 этапов от базовой модели до продакшена ] 1. БАЗОВАЯ МОДЕЛЬ Llama 3.1 / Qwen / Mistral 2. ДАТАСЕТ JSONL + инструкции 3. LoRA-АДАПТЕР r, alpha, target_modules 4. ОБУЧЕНИЕ SFTTrainer + QLoRA 5. ОЦЕНКА тест до / после 6. ДЕПЛОЙ GGUF / vLLM адаптер весит десятки МБ → деплой через llama.cpp (GGUF) или vLLM (OpenAI-совместимый API)

# 1. Что такое файнтюнинг и зачем он нужен

Файнтюнинг — это продолжение обучения уже готовой модели на дополнительных примерах. В отличие от RAG, где вы просто «подкладываете» модели нужные документы в промпт, файнтюнинг меняет сами веса модели. Правило выбора простое: RAG — для знаний, файнтюнинг — для поведения и стиля.

Если ваша задача — «отвечать по актуальной базе документов, которая меняется каждый день» — берите RAG. Если задача — «писать ответы в строгом корпоративном тоне», «всегда возвращать строго валидный JSON», «отвечать на медицинские вопросы как врач с 20-летним стажем» или «понимать жаргон конкретной отрасли» — это территория файнтюнинга. Идеальная комбинация для продакшена — дообученная модель плюс RAG поверх неё: модель знает, как отвечать, а RAG даёт что отвечать.

Полное дообучение всех весов (full fine-tuning) модели на 70 млрд параметров требует кластер GPU и стоит сотни долларов. Поэтому в 2026 году стандарт де-факто — LoRA: вместо изменения всех миллиардов параметров вы обучаете маленькую надстройку из нескольких миллионов (обычно 0.1–1% от модели). QLoRA дополнительно сжимает базовую модель до 4 бит, что позволяет дообучать 7B-модель на одной видеокарте с 16–24 ГБ VRAM. Результат — файл-адаптер размером в десятки мегабайт, который можно мержить в модель или подгружать отдельно.

# 2. Подготовка датасета

Качество датасета решает 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% примеров, которые не участвуют в обучении, но по которым вы потом проверите, не «зазубрила» ли модель данные вместо того, чтобы обобщать.

# 3. Установка Unsloth и загрузка модели

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.

# 4. Настройка LoRA

Теперь добавляем 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% от всех весов.

# 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 шагов и проверяйте качество на тестовой выборке.

# 6. Оценка и тест до/после

Обязательно сравните ответы модели до и после обучения на одних и тех же примерах — иначе непонятно, дал ли файнтюнинг хоть что-то. Прогоните 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).

# 7. Экспорт в GGUF и деплой через vLLM

После обучения адаптер нужно превратить в то, что можно запустить. Два основных пути. 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 в каталоге.

# 8. Стоимость и типичные ошибки

Стоимость файнтюнинга складывается из аренды 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).

🔗 ПОЛЕЗНЫЕ ССЫЛКИ

🦙 Meta Llama — база для файнтюнинга 💳 Оплата GPU из РФ 💰 Цены на AI 📢 Telegram
✅ Итог

Файнтюнинг LLM в 2026 году — доступная процедура, а не исследовательский проект: базовая модель (Llama/Qwen/Mistral) + чистый датасет в JSONL + QLoRA-адаптер через Unsloth и TRL = дообученная модель за $1–5 и пару часов. Запомните главное правило: RAG — для знаний, файнтюнинг — для поведения и формата, а в продакшене они работают в паре. Начните с малого датасета в 100–200 качественных примеров, обязательно сравните ответы до и после, и только затем масштабируйте данные и ранг. Готовую модель экспортируйте в GGUF для локального запуска или разверните через vLLM как OpenAI-совместимый API. Подписывайтесь на Telegram-канал qantcore — там разбираем инструменты и практики работы с LLM каждую неделю.