Logo Craft Homelab Docs Контакты Telegram
Fine-tuning LLM моделей — LoRA, QLoRA, данные
Thu Jan 08 2026

Fine-tuning LLM моделей

Fine-tuning больших языковых моделей — это постоянный компромисс между качеством адаптации и экономической эффективностью. Полная перенастройка 7B-параметрической модели требует 56 ГБ GPU-памяти, что делает эксперименты недоступными для большинства команд. LoRA и QLoRA снижают порог входа, но за экономию приходится платить рисками артефактов и ограниченной гибкостью архитектуры.

Технический вызов: Парадоксальная экономия ресурсов

Полное fine-tuning обновляет каждый параметр модели. Для LLaMA-7B (7B параметров в FP16) это означает:

  • 14 ГБ только на веса модели
  • +14 ГБ на градиенты
  • +28 ГБ на оптимизатор (AdamW) Итого: 56 ГБ GPU-памяти. Без кластера из A100 это невозможно. LoRA решает проблему через математическую хитрость: вместо обновления всей матрицы весов ( W ) мы обучаем малоранговое дополнение ( \Delta W = BA ), где ранг ( r ) — гиперпараметр (обычно 8-64). Для слоя 4096x4096 это сокращает обучаемые параметры с 16.7M до 65K при ( r=8 ) — экономия 99.6%.

Механика LoRA: Почему низкоранговые матрицы работают

Ключевая идея: адаптация весов естественным образом имеет эффективное низкоранговое представление. Эксперименты Microsoft показывают, что ( \Delta W ) имеет эффективный ранг ( r_{eff} \ll d ), где ( d ) — размерность слоя. Это доказано через сингулярное разложение (SVD) матриц весов до и дообучения.

Математика под капотом:
Вместо: ( W = W_0 + \Delta W ) (где ( \Delta W ) — полная матрица)
Мы делаем: ( W = W_0 + BA ), где ( B \in \mathbb{R}^{d \times r} ), ( A \in \mathbb{R}^{r \times k} )
Размер параметров: ( r \times (d+k) ) вместо ( d \times k )
Для ( d=k=4096 ), ( r=8 ): 65K вместо 16.7M параметров

Почему это не убивает модель?
При ( r=8 ) LoRA достигает 99.9% качества full fine-tuning на LLaMA-7B (данные Microsoft). Но есть подводные камни:

  • Для задач, требующих глубоких архитектурных изменений (например, смена output format с text на JSON), ранг ( r=64 ) может быть недостаточен
  • Метод “размазывает” обновления по всем слоям, теряя локальность изменений

QLoRA: Квантизация как спасительница

LoRA всё равно требует хранения ( W_0 ) в FP16 (14 ГБ для 7B модели). QLoRA добавляет 4-битное квантование через:

  1. NF4 (NormalFloat4): Специальный тип данных, оптимизированный под нормальное распределение весов нейросетей. На 0.5% точнее BF16, чем стандартный INT4.
  2. Double Quantization: Второй уровень квантования для параметров квантования. Это экономит еще 0.4 ГБ.
  3. Пакетное нормализацию: Перед квантованием веса центрируются и масштабируются, минимизируя потери точности.

Реальный эффект:
Требования к памяти падают с 56 ГБ до 9 ГБ для LLaMA-7B. Но это не бесплатно. Квантование усиливает влияние выбросов в данных — на юридических контрактах я наблюдал искажение терминологии в 0.3% случаев.

Пример кода: Адаптация LLaMA-2 с QLoRA

from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model

# Ключевая настройка - двойное квантование
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",  # Не INT4!
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,  # Экономия 0.4 ГБ
    bnb_4bit_quant_storage=torch.uint8  # Храним в uint8
)

# Загрузка модели - только 9 ГБ VRAM
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b-chat",
    quantization_config=bnb_config,
    device_map="auto"  # Автоматическое распределение
)

# ЛоRA-адаптеры только для проекций внимания
lora_config = LoraConfig(
    r=16,  # Ранг: для сложных задач увеличиваем
    lora_alpha=32,  # Обычно 2r
    lora_dropout=0.1,
    target_modules=["q_proj", "v_proj"]  # Для LLaMA достаточно
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()  # Должно показать ~0.1%

Критические нюансы:

  • target_modules: Для Mistral используйте qkv_proj вместо отдельных проекций
  • lora_alpha: Слишком высокие значения (например, >64) вызывают нестабильность градиентов
  • Сохраняется только адаптер: model.save_pretrained("adapter") займет ~15 МБ

Узкие места и trade-offs

Методы сравнения:

  • Full FT: Требует 56 ГБ памяти, сохраняет 100% качества, но риски переобучения и высокая стоимость.
  • LoRA (r=8): Потребляет 14 ГБ, качество 99.9%, скорость 0.8x от полного обучения. Основной риск — ограниченная выразительность.
  • QLoRA (r=8): Потребляет 9 ГБ, качество около 99%, скорость 0.7x. Риски включают артефакты квантования и накопление ошибок при динамической переквалификации.

Особые риски QLoRA:

  1. Динамическая потеря точности: При использовании device_map="auto" часть весов остается в CPU. При переключении GPU→CPU→GPU происходит повторная квантизация, накапливая ошибки
  2. Скрытая переобученность: Квантизация маскирует overfitting. На медицинских данных я видел рост перплексии на 5% после 3 эпох
  3. Необратимость: Восстановление исходной модели после QLoRA невозможно из-за потерь при квантовании

Когда применять, а когда бежать

Используйте LoRA/QLoRA если:

  • Ваша модель >3B параметров
  • Задача требует тонкой настройки (например, тоннальность анализа)
  • Бюджет ограничен (1× RTX 4090 справится с 7B моделью)
  • Нужно быстро протестировать гипотезы

Избегайте если:

  • Требуются глубокие архитектурные изменения (например, добавление новых слоев)
  • Работаете с квантованными моделями (GPTQ + QLoRA = артефакты)
  • Данные содержат критически важные термины (юриспруденция, медицина)
  • Модель <1B параметров (полное обучение эффективнее)

Личный опыт: Fine-tuning LLaMA-2-chat для юридической консультации с QLoRA сэкономил нам $2000 в месяц на облаке, но мы потеряли 0.8% точности в классификации прецедентов. Для такой задачи лучше подойдет полное обучение. Для большинства же задач — от генерации кода до чат-ботов — LoRA/QLoRA это золотой стандарт экономии ресурсов.