Конспект 8: Fine-tuning и адаптация моделей

Pavel 13.12.2025 22:11 19 просмотров

Оглавление

  1. Введение в Fine-tuning
  2. Когда нужен fine-tuning
  3. Типы fine-tuning
  4. PEFT - Parameter-Efficient Fine-Tuning
  5. LoRA и QLoRA
  6. Подготовка данных
  7. Процесс fine-tuning
  8. Валидация и оценка
  9. Развертывание fine-tuned моделей
  10. Практические примеры
  11. Лучшие практики

Введение в Fine-tuning

Что такое Fine-tuning?

Определение: Fine-tuning — это процесс адаптации предварительно обученной модели путем дополнительного обучения на специфических данных.

Аналогия:

Base Model (Foundation Model)
    ↓ (обучена на 2 триллионах текстов)

Fine-tuning (на 10,000 ваших примеров)
    ↓
Specialized Model
    ↓
Работает лучше всего на вашей задаче

Жизненный цикл модели

Pre-training (Общее обучение)
    ├─ Обучена на 2-3 триллионах токенов
    ├─ Знает основные паттерны языка
    └─ Может решать много задач
    ↓
Fine-tuning (Специализация)
    ├─ Дополнительное обучение на 1K-100K примеров
    ├─ Адаптация к вашему домену
    └─ Улучшение качества на 20-50%
    ↓
Deployment (Развертывание)
    ├─ Использование в production
    ├─ Мониторинг качества
    └─ Периодическое переобучение

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

Матрица решений

                        Качество
                          ↑
                          |
    Fine-tuning ●         |
    (дорого)              |
                          |
    Prompting ●────────────┼─────────→ Стоимость
    (дешево)              |
                          |

Сценарии для fine-tuning

Сценарий Fine-tuning? Причина
Стандартная классификация ❌ Нет Zero-shot работает хорошо
Специфичный домен (медицина) ✅ Да Нужны специальные знания
Очень большой объем запросов ✅ Да Дешевле обучить, чем платить API
Требуется специфичный стиль ✅ Да Модель должна писать в вашем стиле
Требуется приватность ✅ Да Нельзя отправлять данные в облако
Редкий язык ✅ Да Base model плохо его знает
Уникальный формат вывода ⚠️ Иногда Prompting часто достаточно

Анализ ROI

Затраты на fine-tuning:
- Data preparation: $5K-50K
- Training GPU time: $1K-10K
- Инженеры (2-4 недели): $10K-40K
- Total: $16K-100K

Экономия:
- Сравним fine-tuned vs API

Пример:
10 миллионов запросов/год

API (GPT-4): 10M × 500 tokens × $0.00003 = $150K/год
Fine-tuned: $50K (one-time) + $10K (maintenance) = $60K/year

ROI: (150K - 60K) / 50K = 1.8x в первый год

Типы fine-tuning

1. Full Fine-tuning (Complete Retraining)

Что обновляется: Все параметры модели

Исходная модель: 7 миллиардов параметров
Fine-tuning: Обновляются все 7B параметров

Требования:
- GPU память: 80GB+ (A100)
- Время обучения: 2-7 дней
- Стоимость: $5K-20K
- Требуемые данные: 10K+ примеров

Плюсы: - ✅ Максимальное улучшение качества - ✅ Может переучить поведение модели - ✅ Лучше всего подходит для специфичных доменов

Минусы: - ❌ Очень дорого ($5K+) - ❌ Требует много данных (10K+) - ❌ Долгое обучение (дни) - ❌ Нужна мощная инфраструктура

Когда использовать: - Специфичная медицинская диагностика - Очень большой домен-специфичный датасет - Требуется полная переработка поведения модели

2. PEFT Fine-tuning (Parameter-Efficient)

Что обновляется: Только малая часть параметров

Исходная модель: 7 миллиардов параметров
PEFT update: 0.1-1% параметров (~7-70 миллионов)

Требования:
- GPU память: 8-16GB (RTX4090)
- Время обучения: 2-8 часов
- Стоимость: $100-500
- Требуемые данные: 1K-5K примеров

Плюсы: - ✅ Дешево ($100-500) - ✅ Быстро (часы, не дни) - ✅ Можно обучить на одном GPU - ✅ Нужно меньше данных

Минусы: - ❌ Небольшое улучшение качества (+10-20%) - ❌ Не может переучить основное поведение

Когда использовать: - Большинство реальных задач - Когда бюджет ограничен - Когда нужна быстрая итерация

3. Instruction Tuning

Что обновляется: Способность следовать инструкциям

Dataset:
[
  {"instruction": "Классифицируй эмейл", "input": "...", "output": "SPAM"},
  {"instruction": "Резюмируй текст", "input": "...", "output": "..."},
  ...
]

Результат:
- Модель лучше следует инструкциям
- Улучшение на 15-30%

Примеры моделей: - Alpaca (fine-tuned Llama с instruction tuning) - Vicuna - OpenAssistant

4. RLHF (Reinforcement Learning from Human Feedback)

Процесс:

1. Fine-tune на instruction tuning данных
2. Обучить reward model (предсказывает предпочтение человека)
3. Обучить LLM максимизировать reward (RL)
4. Повторить с новыми примерами

Результат: Модель, которая производит "лучшие" ответы по мнению людей

Примеры: - ChatGPT (построен с RLHF) - Claude (обучен с Constitutional AI, вариант RLHF)

Требования: - Очень сложно реализовать - Нужны аннотаторы - Требует много вычислений - Стоимость: $50K-1M+


PEFT - Parameter-Efficient Fine-Tuning

Главные техники PEFT

1. LoRA (Low-Rank Adaptation)

Идея: Вместо обновления всей матрицы весов, добавляем две маленькие матрицы

Original weight matrix W: (d × d)
    ↓
LoRA adaptation:
    W' = W + ΔW
где ΔW = A × B
A: (d × r)
B: (r × d)
r (rank): обычно 8-64 (маленький)

Экономия параметров:
Полный fine-tuning: d² параметров
LoRA: 2 × d × r параметров

Пример (d=4096, r=16):
Полный: 16M параметров
LoRA: 131K параметров
Экономия: 99.2%

Как это работает:

Input x
    ↓
┌─────────────────┐
│  Original W     │ ← Не обновляется
└─────────────────┘
    ↓ y_original
    ├─────────────────┐
    │       +         │
    │  ┌─────────────┐│
    │  │  LoRA (AB)  ││ ← Обновляется (0.1% параметров)
    │  └─────────────┘│
    └─────────────────┘
        ↓ y_final
    Output

Где добавляется LoRA:

Обычно в:
- Query и Value проекции в attention
- Feed-forward слоях
- Может быть везде

Параметры LoRA:

rank (r): 8, 16, 32, 64 (меньше = дешевле, но худше)
alpha: масштабирующий коэффициент (обычно = r)
target_modules: где применять (attention, mlp, all)
lora_dropout: dropout для регуляризации

2. QLoRA (Quantized LoRA)

Улучшение LoRA с quantization:

Исходная модель 7B (float32): 28GB VRAM
    ↓
Quantized to 4-bit: 2GB VRAM
    ↓
LoRA adapters: 0.1GB VRAM
    ↓
Total: 2.1GB VRAM (вместо 28GB!)

Процесс:

1. Загрузить базовую модель в 4-bit
2. Добавить LoRA слои
3. Обучать только LoRA (базовая модель frozen)
4. Во время inference: 4-bit модель + LoRA адаптеры

Требования: - GPU: RTX 3090 (24GB) или даже RTX 4090 - VRAM: 8-16GB (очень экономно) - Скорость обучения: -30-40% медленнее

Когда использовать: - У вас ограниченный GPU - Нужна экономия памяти - Готовы потерять 10-20% скорости обучения

3. Prefix Tuning

Идея: Добавить обучаемый префикс в начало последовательности

Обычный prompt:
[User: "Классифицируй эмейл"]

С prefix tuning:
[Learnable prefix tokens (100 tokens)] + [User: "..."]

Обучаемые параметры: только префикс (очень мало)

Плюсы: - Очень мало параметров - Разные задачи = разные префиксы

Минусы: - Качество может быть хуже чем LoRA - Занимает место в контексте

4. Adapter Tuning

Идея: Добавить маленькие адаптер слои между слоями

Original Layer
    
[Adapter Down (compress)]
    
[ReLU activation]
    
[Adapter Up (expand)]
    
Output

Размер адаптера: ~3-5% параметров модели

LoRA и QLoRA

LoRA архитектура в деталях

Query matrix W_q (4096 × 4096)
    ↓
W_q' = W_q + A @ B
где A (4096 × 8), B (8 × 4096)
    ↓
Forward pass:
y = x @ W_q' = x @ W_q + x @ A @ B

Практическая настройка LoRA параметров

Parameter | Мало | Среднее | Много |
|----------|------|---------|-------|
| rank (r) | 8 | 32 | 64 |
| alpha | 16 | 64 | 128 |
| dropout | 0.1 | 0.05 | 0.01 |
| training time | 1h | 4h | 8h |
| memory | 8GB | 12GB | 16GB |
| quality gain | +10% | +20% | +25% |

QLoRA пример конфигурации

{
  "model": "meta-llama/Llama-2-7b-hf",
  "quantization_config": {
    "load_in_4bit": true,
    "bnb_4bit_compute_dtype": "float16",
    "bnb_4bit_quant_type": "nf4",
    "bnb_4bit_use_double_quant": true
  },
  "lora_config": {
    "r": 16,
    "lora_alpha": 32,
    "lora_dropout": 0.05,
    "bias": "none",
    "task_type": "CAUSAL_LM",
    "target_modules": ["q_proj", "v_proj"]
  },
  "training_args": {
    "num_train_epochs": 3,
    "per_device_train_batch_size": 4,
    "gradient_accumulation_steps": 4,
    "learning_rate": 2e-4,
    "warmup_steps": 100,
    "logging_steps": 10,
    "save_steps": 100
  }
}

Подготовка данных

Структура датасета

Для instruction tuning:

[
  {
    "instruction": "Классифицируй эмейл как SPAM или HAM",
    "input": "You have won $1,000,000! Click here to claim.",
    "output": "SPAM"
  },
  {
    "instruction": "Резюмируй текст в одно предложение",
    "input": "N8N это платформа для автоматизации...",
    "output": "N8N автоматизирует рабочие процессы."
  }
]

Для chat fine-tuning:

[
  {
    "messages": [
      {"role": "system", "content": "Ты Python разработчик"},
      {"role": "user", "content": "Как работает @property?"},
      {"role": "assistant", "content": "@property позволяет..."}
    ]
  }
]

Размер датасета

Правило: 100 примеров на 1B параметров модели

7B модель: 700 примеров (минимум)
13B модель: 1,300 примеров
70B модель: 7,000 примеров

Но лучше:
- 1K примеров для базового fine-tuning
- 5K+ для хорошего качества
- 10K+ для очень хорошего качества

Качество данных > Количество

Сценарий 1: 1000 хороших примеров
→ Fine-tuning качество: 85%

Сценарий 2: 5000 плохих примеров
→ Fine-tuning качество: 60%

Вывод: 1000 clean examples > 5000 noisy examples

Подготовка данных - чек-лист

☑️ Данные репрезентативны (представляют задачу)
☑️ Баланс классов (если классификация)
☑️ Нет дублей или близких дублей
☑️ Оформатированы правильно (JSON, CSV)
☑️ Разделены на train/val/test (80/10/10)
☑️ Примеры проверены вручную (sampling)
☑️ Размер достаточен (100+ минимум)
☑️ Нет PII данных (приватность)
☑️ Лицензия позволяет использовать
☑️ Версионировано (git, S3)

Процесс fine-tuning

Шаг 1: Подготовка окружения

Локально (с GPU):

# Установка зависимостей
pip install transformers datasets peft bitsandbytes

# Для QLoRA потребуется:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

В облаке:

Google Colab Pro (бесплатно, 40GB GPU)
Lambda Labs (дешево, $0.6-1.5/час GPU)
Replicate (управляемый сервис)
Together AI (специально для fine-tuning)

Шаг 2: Загрузка базовой модели

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import get_peft_model, LoraConfig, TaskType

model_id = "meta-llama/Llama-2-7b-hf"

# Для обычного fine-tuning
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    device_map="auto",
    torch_dtype=torch.float16
)

# ДЛЯ QLoRA (4-bit quantization)
from transformers import BitsAndBytesConfig

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_use_double_quant=True
)

model = AutoModelForCausalLM.from_pretrained(
    model_id,
    quantization_config=bnb_config,
    device_map="auto"
)

tokenizer = AutoTokenizer.from_pretrained(model_id)

Шаг 3: Конфигурация LoRA

peft_config = LoraConfig(
    r=8,
    lora_alpha=16,
    lora_dropout=0.1,
    bias="none",
    task_type=TaskType.CAUSAL_LM,
    target_modules=["q_proj", "v_proj"],  # Attention layers
    modules_to_save=["lm_head"]  # Сохранить также финальный слой
)

model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
# Output: trainable params: 4194304 || all params: 6738415616 || trainable: 0.06%

Шаг 4: Подготовка данных

from datasets import load_dataset

dataset = load_dataset("json", data_files="data.json")

def formatting_func(examples):
    outputs = []
    for i in range(len(examples["instruction"])):
        text = f"""Instruction: {examples["instruction"][i]}
Input: {examples["input"][i]}
Output: {examples["output"][i]}"""
        outputs.append(text)
    return {"text": outputs}

dataset = dataset.map(
    formatting_func,
    batched=True,
    remove_columns=dataset.column_names
)

# Токенизация
def tokenize_function(examples):
    return tokenizer(
        examples["text"],
        truncation=True,
        max_length=512,
        padding="max_length"
    )

tokenized_datasets = dataset.map(tokenize_function, batched=True)

train_dataset = tokenized_datasets["train"]
eval_dataset = tokenized_datasets["test"]

Шаг 5: Обучение

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir="./lora-finetuned-model",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    warmup_steps=100,
    weight_decay=0.001,
    logging_steps=10,
    save_steps=100,
    evaluation_strategy="steps",
    eval_steps=50,
    save_total_limit=3,
    load_best_model_at_end=True,
    metric_for_best_model="eval_loss",
    greater_is_better=False,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)
)

trainer.train()

Шаг 6: Сохранение

# Сохранить LoRA адаптеры
model.save_pretrained("./lora-model")

# Сохранить полную модель (если хочешь)
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./merged-model")

# Загрузить позже
from peft import AutoPeftModelForCausalLM

loaded_model = AutoPeftModelForCausalLM.from_pretrained(
    "./lora-model",
    device_map="auto"
)

Валидация и оценка

Метрики оценки

Перплексия (Perplexity):
- Как "удивлена" модель предсказаниями
- Ниже = лучше
- Обычно: 10-50 для хорошо обученной модели

BLEU Score:
- Для генерации текста
- 0-100 (выше = лучше)
- >30 считается хорошим

ROUGE Score:
- Для summarization
- >0.3 считается хорошим

Exact Match:
- Для QA и классификации
- % ответов которые совпадают на 100%

F1 Score:
- Для классификации
- >0.8 считается хорошим

Валидация на тестовом сете

eval_results = trainer.evaluate()
print(f"Perplexity: {eval_results['eval_loss']:.4f}")
print(f"Eval loss: {eval_results['eval_loss']:.4f}")

# Manual evaluation
model.eval()
test_input = "Classify: This is great!"
inputs = tokenizer(test_input, return_tensors="pt")
outputs = model.generate(**inputs, max_length=100)
print(tokenizer.decode(outputs[0]))

A/B тестирование fine-tuned vs Base

# Тестовый датасет
test_examples = [
    "Classify: You won $1000!",
    "Classify: How are you doing?",
    ...
]

# Оценка base модели
base_accuracy = evaluate_model(base_model, test_examples)

# Оценка fine-tuned
finetuned_accuracy = evaluate_model(finetuned_model, test_examples)

improvement = (finetuned_accuracy - base_accuracy) / base_accuracy * 100
print(f"Improvement: {improvement:.1f}%")

# Если < 10% - может быть не стоит fine-tuning
# Если > 20% - отличный результат

Развертывание fine-tuned моделей

Локальное развертывание

from peft import AutoPeftModelForCausalLM
from transformers import AutoTokenizer
import torch

# Загрузить fine-tuned модель
model = AutoPeftModelForCausalLM.from_pretrained(
    "path/to/lora-model",
    device_map="auto",
    torch_dtype=torch.float16
)

tokenizer = AutoTokenizer.from_pretrained("base_model_id")

# Инферен
input_text = "Classify: This is spam"
inputs = tokenizer(input_text, return_tensors="pt")
outputs = model.generate(**inputs, max_length=50)
result = tokenizer.decode(outputs[0])
print(result)

Облачное развертывание

HuggingFace Hub:

# Загрузить в HuggingFace
model.push_to_hub("username/my-finetuned-llama")

# Использовать через API
from transformers import pipeline

pipe = pipeline(
    "text-generation",
    model="username/my-finetuned-llama"
)
result = pipe("Classify: This is great!")

Replicate или Together AI:

import replicate

output = replicate.run(
    "username/my-finetuned-llama:version-id",
    input={"prompt": "Classify: ..."}
)

Merge и оптимизация

# Merge base модель и LoRA
merged = model.merge_and_unload()

# Quantize для инферена
from transformers import AutoModelForCausalLM
from bitsandbytes.nn import Linear4bit

# Можно quantize до 8-bit или 4-bit для deployment

Практические примеры

Пример 1: Fine-tune для classification

Задача: Классифицировать Support tickets

Dataset:

[
  {
    "instruction": "Classify support ticket",
    "input": "Cannot login to my account",
    "output": "TECHNICAL"
  },
  {
    "instruction": "Classify support ticket",
    "input": "How much does pro plan cost?",
    "output": "BILLING"
  }
]

Процесс:

1. Подготовить 500+ примеров
2. LoRA config: r=8, target_modules=["q_proj", "v_proj"]
3. Обучить 2-3 эпохи
4. Оценить на test сете
5. Развернуть локально или в облаке

Результат: - Base модель: 65% accuracy - Fine-tuned: 92% accuracy - Improvement: +27% ✅

Пример 2: Fine-tune для generation

Задача: Генерация SQL запросов

Dataset:

[
  {
    "instruction": "Generate SQL query",
    "input": "Get users older than 30",
    "output": "SELECT * FROM users WHERE age > 30;"
  }
]

Процесс: 1. 1000+ примеров SQL запросов 2. LoRA config: r=16 (SQL сложнее) 3. Обучить 3-5 эпох 4. Оценить синтаксис + semantic correctness


Лучшие практики

Do's ✅

  1. Начните с малого Сначала: 100 примеров + LoRA r=8 Потом: 1000 примеров + r=16 Полный fine-tuning: если нужно

  2. Используйте QLoRA для больших моделей 70B модель + QLoRA = 16GB GPU Вместо: 280GB GPU

  3. Валидируйте на реальных примерах Не только метрики, но и manual testing

  4. Версионируйте данные и модели git для кода W&B для экспериментов S3 для весов

  5. Мониторьте degradation в production Иногда fine-tuned модель худше на edge cases Установите алерты на качество

  6. Переиспользуйте LoRA адаптеры Можно обучить несколько адаптеров для разных задач

Don'ts ❌

  1. Не переучивайте на маленьком датасете <50 примеров = переобучение (overfitting)

  2. Не игнорируйте базовое качество Если base модель плохая, fine-tuning не спасет

  3. Не используйте очень большой learning rate Может полностью испортить модель LR: 1e-4 до 5e-4 обычно хорошо

  4. Не забывайте eval во время обучения Без eval нельзя знать когда остановиться

  5. Не деплойте без тестирования Протестируйте на real data перед production

Чек-лист fine-tuning

Подготовка:
☑️ Данные очищены и отформатированы
☑️ Размер датасета достаточен (100+)
☑️ Train/val/test split сделан
☑️ GPU/вычисления готовы

Обучение:
☑️ LoRA конфигурация выбрана
☑️ Learning rate оптимален
☑️ Обучение мониторится (логи)
☑️ Early stopping настроено

Валидация:
☑️ Метрики считаются
☑️ Тестирование на новых примерах
☑️ Сравнение с base моделью
☑️ Edge cases проверены

Развертывание:
☑️ Модель сохранена
☑️ Инферен код готов
☑️ API/интерфейс работает
☑️ Мониторинг включен

Дата создания: December 2025
Версия: 1.0
Автор: Pavel
Применение: Model Adaptation, Domain-Specific Fine-tuning, Production ML

Комментарии (0)

Для добавления комментария необходимо войти в аккаунт

Войти / Зарегистрироваться