Зачем вообще нужен fine-tuning? (И почему 90% людей делают его неправильно)
Тонкая настройка LLM — это не магия, а инженерная работа. Скажу сразу: если вы думаете, что загрузите модель, скормите ей 100 примеров и получите гения в узкой области, вас ждет разочарование. Fine-tuning работает только тогда, когда вы понимаете, что происходит на каждом этапе.
Проблема в том, что большинство руководств показывают идеальный путь. В реальности всё иначе: данные грязные, память заканчивается, модели переобучаются. Сегодня я покажу не только как делать, но и как не надо делать. И да, мы пройдем весь путь — от сбора данных до запуска обученной модели в продакшн.
1 Сбор данных: что пойдет в топку?
Первая и главная ошибка — начать с модели. Нет, начинаем с данных. Всегда. В моей предыдущей статье "Где брать данные для обучения и fine-tuning" я подробно разбирал источники. Напомню кратко:
- Синтетические данные — генерируете с помощью более мощной модели (GPT-4, Claude). Дешево, но есть риск наследовать ошибки.
- Скрапинг — собираете с сайтов. Трудоемко, юридически рискованно, зато уникально.
- Готовые датасеты — берете с Hugging Face. Быстро, но качество — лотерея.
Сколько данных нужно? Забудьте про "чем больше, тем лучше". Для качественного fine-tuning'а достаточно 1000-5000 примеров. Но! Эти примеры должны быть идеально релевантными вашей задаче.
Предупреждение: Не смешивайте задачи! Если учите модель писать SQL-запросы, не добавляйте примеры генерации Python-кода. Модель начнет путаться и делать глупые ошибки.
2 Подготовка данных: рецепт идеального датасета
Собрали сырые данные? Теперь самое скучное — чистка. Вот как выглядит типичный плохой пример:
# КАК НЕ НАДО ДЕЛАТЬ
{
"instruction": "Напиши SQL запрос",
"input": "получить пользователей",
"output": "SELECT * FROM users;"
}
Что не так? Инструкция расплывчатая, входные данные неконкретные, результат слишком общий. Исправляем:
# КАК НАДО ДЕЛАТЬ
{
"instruction": "Сгенерируй SQL запрос для получения активных пользователей, зарегистрированных в 2023 году",
"input": "Таблица 'users' имеет колонки: id, email, registration_date, is_active",
"output": "SELECT id, email FROM users WHERE is_active = TRUE AND EXTRACT(YEAR FROM registration_date) = 2023;"
}
Формат данных зависит от фреймворка. Для большинства библиотек fine-tuning'а подходит JSONL (каждая строка — JSON объект).
datasets от Hugging Face позволяет быстро просматривать статистики, находить дубликаты и аномалии.3 Выбор стратегии: полный fine-tuning vs LoRA
Здесь решается судьба вашего железа. Полный fine-tuning обновляет все веса модели — нужно много GPU памяти, времени и данных. LoRA (Low-Rank Adaptation) обучает только небольшие адаптеры, которые потом "встраиваются" в модель.
| Метод | Память | Время обучения | Качество | Когда использовать |
|---|---|---|---|---|
| Полный fine-tuning | Очень много (24GB+ для 7B) | Часы-дни | Высокое | Очень большой датасет, задача сильно отличается от предобучения |
| LoRA | Мало (8-12GB для 7B) | Минуты-часы | Достаточно высокое | 90% случаев, особенно на потребительском железе |
| QLoRA | Очень мало (6-8GB для 7B) | Минуты-часы | Немного ниже LoRA | Очень ограниченные ресурсы, эксперименты |
Личный совет: начинайте с LoRA. Если не хватит качества — переходите к полному fine-tuning'у. Кстати, если у вас слабое железо, посмотрите мой гайд "Как собрать мощную станцию для локальных LLM".
4 Обучение: код, который работает с первого раза
Теоретически всё просто: загружаем модель, загружаем данные, запускаем обучение. На практике — десятки параметров, которые нужно настроить. Вот минимальный рабочий пример с использованием библиотеки TRL и PEFT (LoRA):
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from trl import SFTTrainer
from peft import LoraConfig
import torch
# Загружаем модель и токенизатор
model_name = "mistralai/Mistral-7B-v0.1"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto"
)
# Конфигурация LoRA
peft_config = LoraConfig(
r=16, # ранг адаптера
lora_alpha=32,
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM",
target_modules=["q_proj", "v_proj"] # какие слои обучать
)
# Аргументы обучения
training_args = TrainingArguments(
output_dir="./results",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
warmup_steps=100,
logging_steps=10,
save_steps=500,
learning_rate=2e-4,
fp16=True,
optim="paged_adamw_8bit",
report_to="none"
)
# Создаем тренера
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset, # ваш датасет
peft_config=peft_config,
tokenizer=tokenizer,
formatting_func=formatting_func # функция для форматирования примеров
)
# Запускаем обучение
trainer.train()
Что здесь важно:
r=16— размерность адаптера. Чем больше, тем больше параметров учится, но и больше памяти нужно.target_modules— слои для обучения. Для разных моделей разные слои. Mistral —["q_proj", "v_proj"], LLaMA —["q_proj", "k_proj", "v_proj", "o_proj"].learning_rate— для LoRA обычно 1e-4 до 5e-4. Для полного fine-tuning'а — 1e-5 до 5e-5.
Ловушка: Не используйте слишком большой batch size. Если не хватает памяти — уменьшайте batch size и увеличивайте gradient_accumulation_steps. Например, batch size 2 и accumulation steps 8 дадут эффективный batch size 16.
5 Оценка: как понять, что модель не стала хуже?
Самая болезненная часть. Вы потратили часы на обучение, а модель забыла, как говорить по-английски (реальный случай!). Чтобы этого избежать:
- Валидационный набор — выделите 10-20% данных из обучающего набора для валидации. Следите за loss на валидации — если он растет, это переобучение.
- Тестовые промпты — создайте 20-30 промптов, которые охватывают разные аспекты вашей задачи. После обучения запустите их через модель и оцените качество вручную.
- Базовые способности — проверьте, не забыла ли модель базовые вещи. Попросите решить простую математическую задачу, написать абзац текста на общую тему.
Если модель переобучилась (стала хуже на общих задачах), уменьшите количество эпох или увеличьте размер датасета. Иногда помогает добавление общих примеров в датасет (10% общих промптов).
6 Запуск: из лаборатории в продакшн
Модель обучена, качество устраивает. Что дальше? Для LoRA есть два пути:
# 1. Объединить адаптеры с моделью (получаем обычную модель)
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
model = PeftModel.from_pretrained(base_model, "./lora-adapters")
model = model.merge_and_unload() # сливаем адаптеры
model.save_pretrained("./merged-model")
# 2. Использовать адаптеры отдельно (экономит место)
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
model = PeftModel.from_pretrained(base_model, "./lora-adapters")
# При инференсе модель автоматически использует адаптеры
Для запуска в продакшн рекомендую использовать специализированные серверы:
- vLLM — для максимальной скорости инференса
- llama.cpp — для запуска на CPU или ограниченных GPU
- TensorRT-LLM — для NVIDIA GPU с максимальной оптимизацией
Если планируете запускать локально, посмотрите сравнение LM Studio vs llama.cpp.
Частые ошибки (и как их избежать)
Ошибка 1: Обучение на слишком маленьком датасете (менее 100 примеров). Результат — модель запоминает примеры, но не учится обобщать.
Решение: Используйте аугментацию данных или генерацию синтетических данных.
Ошибка 2: Слишком высокий learning rate. Модель "перепрыгивает" оптимальные веса.
Решение: Начинайте с 1e-5 для полного fine-tuning'а, 2e-4 для LoRA. Используйте learning rate scheduler.
Ошибка 3: Несбалансированные данные. 90% примеров одного типа, 10% — другого.
Решение: Анализируйте распределение данных перед обучением. Балансируйте датасет.
Ошибка 4: Отсутствие валидации во время обучения.
Решение: Всегда выделяйте валидационный набор. Сохраняйте чекпоинты и выбирайте лучший.
Что дальше? (Когда fine-tuning не помогает)
Бывает, что fine-tuning не дает нужного качества. Возможные причины:
- Задача слишком сложная для выбранной модели — попробуйте более крупную модель
- Данные действительно плохие — пересмотрите процесс сбора и очистки
- Нужен другой подход — возможно, RAG (Retrieval-Augmented Generation) подойдет лучше
Помните: fine-tuning — это инструмент, а не серебряная пуля. Иногда достаточно хорошего промптинга. Иногда нужно полностью обучать модель с нуля. Главное — понимать, что вы делаете и зачем.
И последний совет: не бойтесь экспериментировать. Запустите обучение с разными параметрами, сравните результаты. Только так вы найдете оптимальную конфигурацию для вашей задачи. Удачи!