Как обучить LoRA-адаптер для своей задачи

Как обучить LoRA-адаптер для языковой модели на одном GPU — от подготовки данных до оценки результата.

Как обучить LoRA-адаптер для своей задачи

LoRA (Low-Rank Adaptation) — метод дообучения языковых моделей, который обучает только 0.1–1% параметров вместо всех миллиардов. Это сокращает требования к GPU с кластера до одной видеокарты: адаптер для Llama 3.1 8B обучается на RTX 3090 (24 ГБ VRAM) за 1–4 часа. Принцип работы fine-tuning мы разбирали ранее — здесь переходим к практике.

Когда нужен LoRA

LoRA оправдан в трёх сценариях. Первый — адаптация стиля: модель должна отвечать в тоне компании, использовать специфическую терминологию. Второй — обучение на закрытых данных: модель должна знать внутреннюю документацию, продуктовые спецификации. Третий — специализация: модель для узкой задачи (классификация обращений, генерация SQL, медицинские ответы) работает точнее универсальной.

Если задача решается промпт-инжинирингом или RAG, LoRA не нужен. Дообучение — тяжёлая артиллерия для случаев, когда промпт не справляется.

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

Качество данных определяет качество адаптера. Минимальный датасет — 100–500 примеров для простых задач, 1000–5000 для сложных. Формат — JSONL с парами инструкция-ответ:

{"instruction": "Классифицируй обращение", "input": "Не могу оплатить заказ картой", "output": "Категория: оплата. Приоритет: высокий."}
{"instruction": "Классифицируй обращение", "input": "Когда приедет курьер?", "output": "Категория: доставка. Приоритет: средний."}

Правила подготовки данных:

Разнообразие. Покрывайте все типы запросов, которые модель будет получать. 100 разнообразных примеров лучше 1000 однотипных.

Качество. Каждый пример — эталонный ответ. Ошибки в данных учатся моделью буквально.

Формат. Используйте тот же шаблон (chat template), что и базовая модель. Для Llama 3 — формат с тегами <|begin_of_text|>, для Mistral — формат [INST].

Установка инструментов

pip install torch transformers peft trl datasets bitsandbytes accelerate

Ключевые библиотеки: peft (Parameter-Efficient Fine-Tuning, реализация LoRA), trl (Transformer Reinforcement Learning, удобные тренеры), bitsandbytes (квантизация для экономии памяти).

Код обучения

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

# Загрузка модели в 4-bit для экономии памяти
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)

model_name = "meta-llama/Llama-3.1-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    model_name, quantization_config=bnb_config, device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token

# Настройка LoRA
lora_config = LoraConfig(
    r=16,              # Ранг адаптера (8-64, выше = больше параметров)
    lora_alpha=32,     # Масштабирующий коэффициент
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

model = prepare_model_for_kbit_training(model)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()  # ~0.5% от всех параметров

# Загрузка данных
dataset = load_dataset("json", data_files="training_data.jsonl", split="train")

# Обучение
training_config = SFTConfig(
    output_dir="./lora-adapter",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    warmup_steps=10,
    logging_steps=10,
    save_strategy="epoch",
    bf16=True
)

trainer = SFTTrainer(
    model=model,
    train_dataset=dataset,
    args=training_config,
    tokenizer=tokenizer
)

trainer.train()
trainer.save_model("./lora-adapter")

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

Rank (r) — размерность адаптера. r=8 — минимум, r=16 — стандарт, r=64 — для сложных задач. Больший ранг = больше обучаемых параметров = лучше результат, но медленнее обучение.

Alpha — масштабирующий коэффициент. Правило: alpha = 2×r. Alpha/r определяет «силу» адаптера.

Target modules — какие слои модели адаптировать. Минимум: q_proj и v_proj (attention). Максимум: все линейные слои. Больше слоёв = точнее адаптация, но дороже обучение.

Learning rate. 1e-4 — 3e-4 для большинства задач. Слишком высокий — модель «забывает» базовые навыки. Слишком низкий — адаптер не обучается.

Использование адаптера

from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
model = PeftModel.from_pretrained(base_model, "./lora-adapter")

# Инференс как обычно
inputs = tokenizer("Классифицируй: не работает оплата", return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

Адаптер занимает 10–50 МБ — в тысячи раз меньше базовой модели. Можно хранить десятки адаптеров для разных задач и переключаться между ними на лету. Для продакшен-деплоя используйте vLLM с поддержкой LoRA.

Оценка результата

Сравните ответы модели до и после адаптации на тестовой выборке (10–20% от данных, не использованных в обучении). Метрики зависят от задачи: accuracy для классификации, BLEU/ROUGE для генерации, экспертная оценка для стилистических задач.

Если адаптер ухудшил общие навыки модели (отвечает хуже на вопросы не по теме обучения), снизьте learning rate или уменьшите количество эпох. Это называется «catastrophic forgetting» — модель забывает старое, обучаясь новому.

Итог

LoRA — доступный способ специализировать LLM под конкретную задачу. На одном GPU (24 ГБ VRAM) за несколько часов можно обучить адаптер, который превратит универсальную модель в специалиста по вашей предметной области. Начните с 200–500 качественных примеров, стандартных гиперпараметров (r=16, alpha=32) и трёх эпох — и итерируйте от результата.

LoRA: принцип работы и преимущества

LoRA (Low-Rank Adaptation) — метод дообучения больших языковых моделей с минимальным числом обучаемых параметров. Вместо изменения всех весов модели, LoRA добавляет пары малых матриц к существующим весам. При rank=8 обучается в 1000 раз меньше параметров, чем при полном fine-tuning.

LoRA vs полное дообучение

МетодОбучаемые параметрыVRAM (7B)Время (1K примеров)Качество
Full Fine-tuning100% (~7B)80GB+8+ ч на A100★★★★★
LoRA (rank=8)~0.1% (~7M)12–16GB1–2 ч на RTX 3090★★★★☆
QLoRA (4bit + LoRA)~0.1%6–8GB2–3 ч на RTX 3090★★★★☆
Prompt Tuning<0.01%8GB30 мин★★★☆☆

Ключевые гиперпараметры LoRA

rank (r) — размерность адаптера. Чем выше — больше параметров и лучше качество, но медленнее обучение. Типичные значения: 4, 8, 16, 64. Начинайте с r=8.

lora_alpha — коэффициент масштабирования. Обычно устанавливают равным 2×rank (alpha=16 при r=8).

target_modules — какие слои адаптировать. Стандарт для Llama: q_proj, v_proj, k_proj, o_proj. Добавление gate_proj, up_proj, down_proj улучшает качество за счёт большего числа параметров.

Формат датасета

Стандартный формат — JSONL с полем text в ChatML-формате:

{"text": "<|system|>Ты помощник.<|user|>Что такое оферта?<|assistant|>Оферта это..."}

Минимальный датасет для заметного эффекта: 200–500 примеров для специализации. Для полной смены поведения: 2 000–10 000 примеров. Качество данных важнее количества — 200 чистых примеров лучше 2 000 с шумом.

Где запускать дообучение бесплатно

Google Colab Pro (~$10/мес) — A100 40GB, достаточно для QLoRA на Llama 3.1 8B.
Kaggle Notebooks — бесплатно, 2×T4 GPU, 30 часов/неделю.
Modal.com — serverless GPU, платите только за вычисления (~$1.50/час A100).
RunPod / Vast.ai — аренда GPU по требованию, A100 от $1.50/час.