Большие языковые модели
~20 мин

Дообучение LLM

LoRA, QLoRA, PEFT, инструкционное обучение, RLHF/DPO.

Fine-tuning LLM — как превратить общую модель в специалиста

Pre-trained LLM — это эрудит, который знает обо всём понемногу. Он может написать стихотворение, объяснить квантовую механику и сгенерировать код. Но попроси его отвечать на вопросы клиентов банка в строгом формате, со ссылками на внутренние документы — и начнутся проблемы. Промпт-инжиниринг помогает до определённого предела: few-shot примеры, system prompt, chain-of-thought. Но когда нужна стабильная работа в узком домене, точный формат ответов, следование корпоративным политикам — промпта недостаточно.

Fine-tuning — это дообучение модели на твоих данных. После него модель «из коробки» отвечает в нужном стиле, знает доменную терминологию и следует инструкциям без километровых промптов. Проблема в том, что полное дообучение 7B-модели требует 60+ ГБ видеопамяти и кластер GPU. Но за последние два года появились методы, которые позволяют делать fine-tuning на одной потребительской видеокарте — и это изменило всё.

Большая картина: от pretrained LLM до продакшена

Весь процесс адаптации LLM можно разбить на четыре этапа — каждый решает свою задачу: Этап 1. Pre-training. Модель обучается на триллионах токенов из интернета. Результат: «сырая» модель, которая умеет продолжать текст, но не умеет отвечать на вопросы. Этот этап стоит миллионы долларов и делается один раз (Meta, Google, Mistral). Этап 2. Supervised Fine-Tuning (SFT). Модель учится следовать инструкциям — отвечать на вопросы, писать код, вести диалог. Данные: тысячи пар (инструкция, ответ). Это первый этап, который можно делать самому. Этап 3. Alignment (опционально). Модель учится отвечать так, как предпочитают люди: полезно, безопасно, честно. Методы: RLHF, DPO, GRPO. Именно alignment превращает «умную, но неуправляемую» модель в ChatGPT. Этап 4. Evaluation → Deploy. Оцениваем на бенчмарках и вручную, затем деплоим. Если качество не устраивает — возвращаемся к этапу 2 или 3 с лучшими данными.

Pipeline: Pre-training → SFT → Alignment (RLHF/DPO) → Evaluation → Deploy
Полный pipeline дообучения LLM. Pre-training — фундамент (делают большие лаборатории). SFT и Alignment — этапы, которые можно делать самостоятельно

Когда fine-tuning НЕ нужен

• Задачу можно решить промптом — few-shot примеры + system prompt дают нужное качество • Нужен доступ к свежим данным — тогда лучше RAG (retrieval-augmented generation) • Мало данных (< 100 примеров) — fine-tuning может переобучиться, лучше in-context learning Fine-tuning нужен, когда: требуется стабильный формат ответов, доменная адаптация, снижение latency (не нужен длинный промпт), или конфиденциальность (модель работает локально).

Full fine-tuning: максимум качества, максимум затрат

Full fine-tuning — это обновление всех параметров модели. Для 7B-модели это 7 миллиардов весов, и для каждого нужно хранить: сам вес (fp16 = 2 байта), градиент (2 байта), optimizer states для Adam (2 момента × 4 байта = 8 байт). Итого: ~12 байт на параметр, или 84 ГБ только для обучения 7B модели. Плюс активации, плюс данные — реально нужно 4-8 A100 по 80 ГБ.

Главный риск — catastrophic forgetting: модель «забывает» знания из pre-training, пока учится на новых данных. Представь, что ты учишь эрудита отвечать на вопросы о банковских продуктах, а он в процессе забывает математику и английский. На практике это проявляется так: модель отлично решает целевую задачу, но деградирует на общих бенчмарках.

Когда full fine-tuning оправдан? Когда задача сильно отличается от pre-training данных (например, модель для медицинской диагностики), когда нужно максимальное качество и есть бюджет на GPU, или когда data mix большой и разнообразный (тысячи задач, как при обучении instruction-following моделей).

LoRA — Low-Rank Adaptation: главная идея

LoRA (2021) — метод, который сделал fine-tuning доступным. Ключевое наблюдение: при fine-tuning обновление весов ΔW имеет низкий ранг. Модель не переучивается кардинально — она лишь «подкручивает» определённые направления в пространстве весов. LoRA формализует это: вместо полной матрицы обновлений ΔW размера d×d обучаем две маленькие матрицы B (d×r) и A (r×d), где r — ранг, обычно 8-64.

Аналогия: представь, что ты настраиваешь эквалайзер на колонках. Ты не переписываешь прошивку колонки (full fine-tuning), а крутишь несколько ручек — bass, treble, mid. Ручек мало (ранг r = несколько), но они позволяют кардинально изменить звучание. LoRA — это те самые «ручки» для нейросети.

W — оригинальные веса (заморожены), B и A — обучаемые матрицы. Произведение BA имеет ранг ≤ r, что и даёт экономию

Посчитаем экономию на конкретных числах. Матрица Q-проекции в LLaMA-7B: d = 4096. Полная матрица ΔW — это 4096 × 4096 = 16.7 млн параметров. С LoRA при r = 16: B — 4096 × 16 = 65K, A — 16 × 4096 = 65K, итого 131K параметров — в 128 раз меньше. А таких матриц в модели десятки. Суммарно LoRA обучает 0.1-1% от всех параметров.

LoRA: параллельная ветка low-rank матриц BA добавляется к замороженным весам W
Вход x проходит через оригинальный W (заморожен) и через BA (обучается) параллельно. Результаты складываются. При инференсе можно слить: W' = W + BA — нет оверхеда

Гиперпараметры LoRA

Rank (r) — главный гиперпараметр. Определяет «выразительность» адаптации. r = 8 — минимум, хватает для простых задач (классификация, форматирование). r = 32-64 — для сложных задач (генерация кода, математика). r = 128+ — почти не уступает full fine-tuning, но теряется смысл LoRA. Alpha (α) — scaling factor. Адаптация масштабируется как (α/r) · BA. Обычно α = 2 × r. Контролирует «силу» адаптации: слишком маленький α — модель почти не меняется, слишком большой — нестабильность. Target modules — к каким слоям применять LoRA. Минимум: q_proj и v_proj (attention). Практика показывает, что добавление k_proj, o_proj, gate_proj, up_proj, down_proj даёт +2-5% качества при незначительном росте параметров.

Killer feature: merge при инференсе

После обучения LoRA-веса можно слить с оригинальными: W' = W + BA. Получаем обычную модель без какого-либо оверхеда по скорости или памяти. А можно не сливать — тогда можно быстро переключаться между разными LoRA-адаптерами (разные задачи) на одной базовой модели.

QLoRA — fine-tuning на одной потребительской GPU

LoRA уже экономит память на обучаемых параметрах, но базовая модель всё ещё занимает много места: 7B в fp16 = 14 ГБ. QLoRA (2023) идёт дальше: квантизует базовую модель в 4-bit (NF4, нормализованный 4-bit формат), а LoRA-адаптеры обучает в fp16/bf16.

Результат: базовая 7B модель = ~3.5 ГБ (вместо 14). Плюс LoRA-адаптеры — несколько десятков МБ. Плюс optimizer states и активации. Итого: fine-tuning 7B модели на одной GPU с 24 ГБ (RTX 4090, A5000). Fine-tuning 70B — на одной A100 80 ГБ. Статья QLoRA показала, что потеря качества от 4-bit квантизации минимальна — модели не уступают full fine-tuning на большинстве бенчмарков.

Три ключевые техники QLoRA: • NF4 (4-bit NormalFloat) — специальный формат квантизации, оптимальный для нормально распределённых весов нейросетей. Каждый вес хранится в 4 битах вместо 16. • Double Quantization — квантизует не только веса, но и сами квантизационные константы. Экономит ещё ~3 ГБ для 65B модели. • Paged Optimizers — использует CPU RAM для хранения optimizer states, когда GPU памяти не хватает (аналог подкачки).

PEFT: другие методы parameter-efficient fine-tuning

LoRA — самый популярный, но не единственный PEFT-метод. Вот основные альтернативы и когда они полезны:

Prefix Tuning (2021) — добавляет обучаемые «виртуальные токены» в начало каждого слоя. Модель видит их как часть контекста, но эти токены не соответствуют реальным словам — они обучаются напрямую. Количество параметров ещё меньше, чем у LoRA, но и качество обычно ниже. Prompt Tuning (2021) — упрощённая версия Prefix Tuning: обучаемые эмбеддинги добавляются только на входе (а не на каждом слое). Очень мало параметров (тысячи), работает для крупных моделей (10B+), но для моделей меньше 10B существенно уступает LoRA. Adapters (2019) — маленькие обучаемые сети (bottleneck: down-project → nonlinearity → up-project), вставленные между замороженными слоями. Исторически первый PEFT-метод. Добавляет latency при инференсе (в отличие от LoRA, которую можно слить).

Сравнение методов: Full FT, LoRA, QLoRA, Prefix Tuning, Adapters — параметры, память, качество
Сравнение PEFT-методов. LoRA/QLoRA — лучший баланс качества и эффективности. Prompt Tuning — минимум затрат, но хуже качество

Данные для fine-tuning: качество важнее количества

Данные — главный фактор успеха fine-tuning. Можно взять лучшую архитектуру с идеальными гиперпараметрами, но мусорные данные дадут мусорную модель. Ключевое правило: 1 000 отличных примеров > 100 000 шумных. Статья LIMA (2023) показала, что SFT на всего 1 000 тщательно подобранных примеров может конкурировать с моделями, обученными на десятках тысяч.

Формат данных

Стандартный формат для SFT — instruction tuning: каждый пример содержит инструкцию (что делать), опциональный input (контекст/данные) и output (желаемый ответ). Для диалогов — массив сообщений с ролями (system, user, assistant).

//  instruction tuning (Alpaca-style)
{
  "instruction": "Классифицируй отзыв клиента по тональности",
  "input": "Доставка задержалась на 3 дня, но менеджер помог решить проблему быстро",
  "output": "Смешанная (негативная: задержка доставки, позитивная: качественная поддержка)"
}

//  multi-turn dialogue (ShareGPT-style)
{
  "conversations": [
    {"role": "system", "content": "Ты ассистент банка..."},
    {"role": "user", "content": "Какой процент по вкладу?"},
    {"role": "assistant", "content": "Актуальные ставки по вкладам..."}
  ]
}

Критично: используй chat template конкретной модели. У LLaMA, Mistral, Qwen — разные спецтокены и форматы. Перепутаешь — модель будет генерировать мусор. Библиотека transformers поддерживает apply_chat_template(), которая делает это автоматически.

Чеклист качества данных

  • Разнообразие — покрой все сценарии и edge cases. Не дублируй типовые примеры
  • Консистентность — единый стиль ответов, формат, уровень детализации
  • Валидация — 5-10% на eval-сет. Следи за training loss + ручная проверка ответов
  • Decontamination — убери из трейна примеры, похожие на бенчмарки. Иначе метрики врут
  • Loss masking — loss считается только по ответам assistant, не по промптам user

Synthetic data — когда своих данных мало

Нет 10 000 размеченных примеров? Можно попросить более сильную модель (GPT-4, Claude) сгенерировать данные. Это synthetic data — и это работает. Alpaca, Vicuna, WizardLM — все обучены на синтетических данных от GPT-4. Подход: напиши несколько seed-примеров → сгенерируй тысячи вариаций → отфильтруй низкокачественные → обучи на остатке. Ключевой риск: модель может выучить артефакты генератора (стилистические привычки GPT-4), а не решение задачи.

RLHF: обучение с подкреплением на предпочтениях людей

SFT учит модель формату ответов — «как выглядит хороший ответ». Но что значит «хороший»? Для одного запроса может быть десять правильных ответов разного качества. SFT не различает их: loss одинаковый, если формат правильный. RLHF (Reinforcement Learning from Human Feedback) решает эту проблему: модель учится не просто генерировать корректные ответы, а генерировать предпочитаемые ответы.

Аналогия: SFT — это как учить повара рецептам. Он научится готовить по книге. RLHF — это как дать повару дегустатора, который пробует каждое блюдо и говорит: «это лучше, это хуже». Повар постепенно учится готовить не просто «по рецепту», а вкусно.

RLHF-pipeline состоит из трёх этапов: Этап 1. Сбор предпочтений. Для каждого промпта модель генерирует два (или больше) ответа. Человек-асессор выбирает, какой лучше: (prompt, chosen, rejected). Нужно 10K-100K пар. Этап 2. Reward Model. Обучается отдельная модель (обычно SFT-модель с линейной головой), которая по паре (prompt, response) выдаёт скалярный скор — «насколько ответ хорош». Обучается на парах предпочтений: reward(chosen) > reward(rejected). Этап 3. RL-оптимизация (PPO). LLM генерирует ответы, reward model их оценивает, и PPO (Proximal Policy Optimization) обновляет веса LLM в сторону более высокого reward. Добавляется KL-штраф — чтобы модель не уходила слишком далеко от SFT-чекпоинта.

π_θ — текущая политика (LLM), π_ref — референсная (SFT-модель), R_φ — reward model, β — коэффициент KL-штрафа. Максимизируем reward, но не уходим далеко от SFT

Проблемы RLHF: сложность (три модели одновременно: LLM + reward model + reference model), нестабильность PPO (чувствителен к гиперпараметрам, reward hacking — модель находит «лазейки» в reward model), дороговизна (асессоры стоят денег). Именно эти проблемы мотивировали поиск более простых альтернатив.

DPO: alignment без reward model

DPO (Direct Preference Optimization, 2023) — элегантная альтернатива RLHF. Ключевая идея: авторы показали, что оптимальное решение RLHF-задачи можно выразить аналитически через логарифм отношения вероятностей. Это значит, что reward model и PPO не нужны — можно обучать LLM напрямую на парах предпочтений.

Аналогия: вместо того чтобы нанимать дегустатора (reward model), который оценивает каждое блюдо, а потом методом проб и ошибок (PPO) подбирать рецепт — ты просто показываешь повару два блюда и говоришь: «вот это лучше вот этого». Повар напрямую учится из сравнений.

y_w — preferred (winning) ответ, y_l — rejected (losing), σ — сигмоида, β — температура. Loss уменьшается, когда модель присваивает больше вероятности preferred ответу

Что формула говорит на пальцах: берём пару ответов (хороший y_w и плохой y_l). Считаем, насколько текущая модель «отклонилась» от референсной в сторону каждого ответа. Если модель стала больше «любить» хороший ответ (относительно плохого) — loss снижается. Сигмоида превращает разницу в вероятность, и мы максимизируем эту вероятность.

Преимущества DPO: не нужна reward model (экономия GPU и сложности), стабильнее PPO (обычный supervised loss, нет RL-нестабильности), проще имплементация (десятки строк кода вместо тысяч). Ограничения: нет явного reward signal (сложнее дебажить), чувствителен к качеству preference data, может быть хуже RLHF на очень сложных задачах alignment.

GRPO — новый подход (DeepSeek-R1)

Group Relative Policy Optimization (GRPO): для каждого промпта генерируем группу ответов, оцениваем их (reward model или rule-based), и учимся на контрасте лучших vs худших внутри группы. Не нужна ни отдельная reward model, ни critic. Используется в DeepSeek-R1 для обучения reasoning.

Практика: fine-tuning с HuggingFace TRL

Вот минимальный, но рабочий pipeline SFT + LoRA с библиотекой TRL (Transformer Reinforcement Learning). Всё, что нужно — одна GPU с 24 ГБ и датасет в правильном формате.

from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig
from trl import SFTTrainer, SFTConfig
from datasets import load_dataset

# 1. Загружаем модель в 4-bit (QLoRA)
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",           # NormalFloat4
    bnb_4bit_compute_dtype="bfloat16",    # вычисления в bf16
)
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B-Instruct",
    quantization_config=bnb_config,
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct")

# 2. LoRA-конфиг
lora_config = LoraConfig(
    r=16,                                  # ранг
    lora_alpha=32,                         # scaling = alpha/r = 2
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                     "gate_proj", "up_proj", "down_proj"],
    lora_dropout=0.05,
    task_type="CAUSAL_LM",
)

# 3. Датасет (instruction format)
dataset = load_dataset("json", data_files="train.jsonl", split="train")

# 4. Обучение
trainer = SFTTrainer(
    model=model,
    train_dataset=dataset,
    peft_config=lora_config,
    args=SFTConfig(
        output_dir="./checkpoints",
        num_train_epochs=3,
        per_device_train_batch_size=4,
        gradient_accumulation_steps=4,     # effective batch = 16
        learning_rate=2e-4,
        warmup_ratio=0.03,
        logging_steps=10,
        bf16=True,
    ),
)
trainer.train()
trainer.save_model("./final-adapter")      # сохраняем только LoRA-веса (~50MB)

Типичные ошибки

  • Неправильный chat template — модель генерирует мусор. Решение: tokenizer.apply_chat_template(), не форматируй промпты вручную
  • Слишком высокий learning rate — loss скачет, модель деградирует. Для LoRA: 1e-4 — 3e-4. Для full FT: 1e-5 — 5e-5
  • Мало данных + много эпох — переобучение. Мониторь eval loss, остановись когда он перестаёт падать
  • Забыл loss masking — считаешь loss и по промпту, и по ответу. Модель учится повторять инструкции вместо решения задач
  • Rank слишком маленький — r=4 на сложной задаче. Начни с r=16, увеличивай если качество не устраивает
  • Нет eval set — не видишь переобучение. Всегда выделяй 5-10% данных на валидацию

🎯 На собеседовании

Junior

Зачем fine-tuning, если есть промпт? Fine-tuning даёт стабильность, доменную адаптацию, экономию на длинных промптах. Промпт — быстро, но хрупко. • Что такое LoRA? Замораживаем оригинальные веса, добавляем маленькие обучаемые матрицы A и B (low-rank). Обучаем 0.1-1% параметров вместо 100%. • Full fine-tuning vs LoRA — когда что? Full: максимум качества, нужно 4-8 GPU. LoRA: 95%+ качества, 1 GPU. Для большинства задач LoRA достаточно.

Middle

Как LoRA экономит память? Посчитай. Для d=4096, r=16: full ΔW = 16.7M параметров, LoRA BA = 131K — в 128× меньше. Плюс не нужны optimizer states для замороженных весов. • QLoRA vs LoRA — в чём разница? QLoRA квантизует базу в 4-bit (NF4), а LoRA-адаптеры в fp16. Экономия: 7B модель = 3.5 ГБ вместо 14 ГБ. Потеря качества минимальна. • Что такое RLHF и зачем он нужен после SFT? SFT учит формату, RLHF учит предпочтениям. Reward model + PPO. KL-штраф не даёт уйти далеко от SFT. • DPO vs RLHF? DPO — без reward model. Оптимизирует preference напрямую через log-ratio trick. Проще, стабильнее, но сложнее дебажить без явного reward.

Senior

Почему обновление весов при fine-tuning имеет низкий ранг? Pre-trained модель уже в «хорошей» области пространства параметров. Fine-tuning подстраивает модель под задачу — это смещение по нескольким ключевым направлениям, а не полная перестройка. Эмпирически rank(ΔW) << d. • Как бороться с reward hacking в RLHF? KL-штраф (β), ensemble reward models, регулярный аудит reward model на adversarial inputs, constrained optimization. • Когда DPO хуже RLHF? На задачах с complex preferences (safety, многокритериальное alignment), когда нужен итеративный сбор данных (online DPO vs offline), когда preference data шумная. • Спроектируй pipeline fine-tuning для production. Сбор данных (seed → synthetic → human filter) → QLoRA SFT → eval (auto metrics + human) → DPO на preference pairs → A/B тест → merge LoRA → deploy с vLLM.

Собираем всё вместе

Fine-tuning LLM — это спектр подходов от «дорого, но максимум контроля» (full fine-tuning) до «дёшево и почти так же хорошо» (QLoRA). LoRA — главный метод: замораживаем базу, учим маленькие матрицы BA. QLoRA добавляет 4-bit квантизацию и делает fine-tuning доступным на одной GPU. Данные решают: 1000 качественных примеров > 100K шумных. SFT учит формату, alignment (RLHF/DPO) учит предпочтениям.

Если запомнить одну вещь из этой ноды: LoRA позволяет адаптировать любую LLM под свою задачу на одной GPU за несколько часов, обучая менее 1% параметров. Это демократизировало fine-tuning и превратило его из привилегии больших лабораторий в инструмент каждого ML-инженера.

Дальше на роадмапе: подробности SFT с chat templates и loss masking — в «SFT и дообучение». Формулы и pipeline alignment — в «RLHF и DPO». Продвинутые техники (GRPO, iterative DPO) — в «Advanced Post-Training».