Дообучение LLM методом LoRA (Low-Rank Adaptation)
LoRA — метод параметрически эффективного дообучения, при котором исходные веса модели замораживаются, а рядом с ними обучаются небольшие матрицы низкого ранга. Метод предложен в 2021 году (Hu et al., Microsoft Research) и стал стандартом de facto для fine-tuning LLM. LoRA позволяет дообучить 7B модель на одном GPU A100 40GB вместо нескольких, при этом потеря качества по сравнению с Full Fine-Tuning минимальна для большинства задач.
Математика LoRA
Для весовой матрицы W ∈ R^(d×k) LoRA добавляет произведение двух матриц:
W' = W + ΔW = W + BA
где B ∈ R^(d×r), A ∈ R^(r×k), r << min(d, k)
Ранг r — ключевой гиперпараметр. При r=16 и d=k=4096 (типичные размеры attention проекций в 7B модели) количество обучаемых параметров в одном слое: 16×4096 + 4096×16 = 131 072 вместо 4096×4096 = 16 777 216. Это сжатие в 128×.
При инициализации A — случайная гауссова матрица, B — нулевая. Это обеспечивает ΔW=0 в начале — модель начинает с оригинального поведения.
Конфигурация LoRA: ключевые гиперпараметры
from peft import LoraConfig
config = LoraConfig(
r=16, # Ранг: 4, 8, 16, 32, 64, 128
lora_alpha=32, # Масштаб: обычно = 2*r
target_modules=[ # Какие слои адаптируем
"q_proj", "v_proj", # Минимум
"k_proj", "o_proj", # Расширенный вариант
"gate_proj", "up_proj", "down_proj" # MLP включительно
],
lora_dropout=0.05, # Регуляризация адаптера
bias="none", # "none", "all", "lora_only"
task_type="CAUSAL_LM",
modules_to_save=["embed_tokens", "lm_head"], # Обучаем полностью
)
Выбор r: чем сложнее задача и чем сильнее домен отличается от предобучения — тем выше r. Для классификации и форматирования: r=4–8. Для генерации в специфическом стиле: r=16–32. Для сложного domain adaptation: r=64–128.
lora_alpha: контролирует масштаб адаптера. Эффективный lr адаптера = lr × (alpha/r). Стандартная практика: alpha = 2r.
DoRA: улучшение LoRA
DoRA (Weight-Decomposed Low-Rank Adaptation) разделяет обновление весов на magnitude и direction компоненты:
config = LoraConfig(
r=16,
use_dora=True, # Включает DoRA вместо обычной LoRA
...
)
DoRA улучшает качество на 1–3% по сравнению со стандартной LoRA без увеличения инференс-затрат.
Практический кейс: LoRA для классификации NER
Задача: извлечение именованных сущностей из медицинских записей (4 класса: MEDICATION, DOSAGE, CONDITION, PROCEDURE).
Базовая модель: Llama 3.1 8B Instruct.
Конфигурация: r=16, alpha=32, target_modules=["q_proj","v_proj"], 3 эпохи.
Датасет: 2200 примеров, A100 40GB, QLoRA 4-bit, время обучения 2.5 часа.
| Метрика | Базовая модель (5-shot) | LoRA r=8 | LoRA r=16 | LoRA r=32 |
|---|---|---|---|---|
| F1 MEDICATION | 0.71 | 0.88 | 0.91 | 0.92 |
| F1 DOSAGE | 0.64 | 0.83 | 0.87 | 0.88 |
| F1 CONDITION | 0.79 | 0.91 | 0.94 | 0.94 |
| F1 PROCEDURE | 0.68 | 0.85 | 0.89 | 0.90 |
Разрыв между r=16 и r=32 незначителен — r=16 оптимален.
Слияние адаптера для деплоя
LoRA-адаптер можно слить с базовой моделью для упрощения инференса:
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Meta-Llama-3.1-8B-Instruct")
model = PeftModel.from_pretrained(base_model, "./lora-adapter")
# Слияние: результат — обычная модель без PEFT overhead
merged = model.merge_and_unload()
merged.save_pretrained("./merged-model")
После слияния модель идентична по скорости инференса полно обученной — overhead LoRA на inference исчезает.
Сроки
- Подготовка данных: 2–4 недели
- Обучение (7B, LoRA, A100 40GB): 2–8 часов
- Итерации гиперпараметров: 3–5 дней
- Итого: 3–6 недель







