Как работает QLoRA (Quantized LoRA) для training?

Краткий тезис

QLoRA (Quantized Low-Rank Adaptation) — это метод эффективного fine-tuning больших языковых моделей, который сочетает 4‑битную квантизацию весов модели с LoRA-адаптерами в половинной точности (FP16). Благодаря этому можно дообучать модели с 70 миллиардами параметров на одной видеокарте с 48 ГБ памяти (например, A6000) с потерей качества всего 1–2% по сравнению с обычным LoRA. QLoRA использует три ключевые техники: NormalFloat4 (NF4) — оптимальный 4‑битный формат, Double Quantization для дополнительной экономии памяти и Paged Optimizers для управления памятью при больших градиентах.


1. Проблема: fine-tuning больших моделей требует огромной памяти

Полный fine-tuning модели с 70B параметров в FP16 требует около 140 ГБ только для весов (70B × 2 байта). Плюс градиенты, состояния оптимизатора (Adam хранит два дополнительных значения на параметр) — итого более 300 ГБ. Это недоступно даже для топовых GPU (A100 80 ГБ). Даже LoRA (Low-Rank Adaptation), который обучает только маленькие адаптеры, требует загрузки всей модели в память, что для 70B в FP16 — 140 ГБ. QLoRA решает эту проблему, загружая модель в 4‑битном формате, что сокращает память для весов в 4 раза.


2. LoRA (Low-Rank Adaptation) — база для QLoRA

LoRA — это метод параметр-эффективного fine-tuning. Вместо обновления всей матрицы весов (W \in \mathbb{R}^{d \times k}) он добавляет две низкоранговые матрицы (B \in \mathbb{R}^{d \times r}) и (A \in \mathbb{R}^{r \times k}) (где (r \ll \min(d,k))). Прямой проход: (h = Wx + BAx). Обучаются только (B) и (A) (обычно в FP16), а исходные веса (W) заморожены. Это резко сокращает число обучаемых параметров и память для градиентов/оптимизатора, но веса модели всё равно должны быть загружены в память.


3. Квантизация (Quantization) — снижение точности весов

Квантизация — это процесс преобразования чисел с плавающей точкой (например, FP16) в целые числа с меньшей разрядностью (например, INT4). В QLoRA используется 4‑битная квантизация, которая уменьшает размер весов в 4 раза по сравнению с FP16. Однако простое округление до 4 бит сильно ухудшает качество. QLoRA вводит специальный формат — NormalFloat4 (NF4).


4. NormalFloat4 (NF4) — оптимальный 4‑битный формат

NF4 — это тип квантизации, который предполагает, что веса модели имеют нормальное распределение. Он использует нелинейное квантование: больше уровней квантования выделяется для значений около нуля (где плотность распределения выше), и меньше — для хвостов. Это минимизирует ошибку квантизации. Формат NF4 даёт почти такую же точность, как 8‑битная квантизация, но занимает вдвое меньше памяти.


5. Double Quantization — двойная квантизация

Даже после квантизации весов в NF4 остаются константы квантизации (scaling factors) для каждого блока (обычно 64 веса). Эти константы хранятся в FP32 и занимают дополнительную память. Double Quantization решает эту проблему: константы квантизации сами квантуются в 8‑битный формат. Это добавляет ещё ~0.5 бита на параметр, но экономит память, занимаемую константами.


6. Paged Optimizers — управление памятью

Во время обучения оптимизатор (Adam) хранит два состояния на каждый обучаемый параметр (моменты первого и второго порядка). Для LoRA-адаптеров это немного, но для больших моделей может возникнуть нехватка памяти при обработке длинных последовательностей. Paged Optimizers используют механизм page swapping: когда GPU-память заканчивается, состояния оптимизатора выгружаются на CPU и подгружаются обратно по мере необходимости. Это позволяет обучать модели с контекстом до 4096 токенов на одной GPU.


7. Архитектура QLoRA: как всё соединяется

Процесс обучения с QLoRA:

  1. Загрузка модели в 4‑битном формате NF4 с Double Quantization (используется библиотека bitsandbytes).
  2. Добавление LoRA-адаптеров в FP16 (или BF16) к выбранным слоям (обычно к attention проекциям Q, K, V, O).
  3. Заморозка всех исходных 4‑битных весов.
  4. Обучение только LoRA-адаптеров с помощью Paged Optimizer.
  5. Инференс: можно объединить LoRA-адаптеры с 4‑битной моделью (через дельта-веса) или оставить отдельно.

Важно: градиенты считаются относительно 4‑битных весов, но они не обновляются — обновляются только LoRA-адаптеры. При обратном проходе 4‑битные веса временно деквантуются в FP16 для вычисления градиентов (это происходит на лету).


8. Сравнение памяти: Full Fine-tuning vs LoRA vs QLoRA

МетодРазмер весов (70B)Память для градиентов/оптимизатораИтого (приблизительно)
Full FP16140 ГБ~200 ГБ (градиенты + Adam)>340 ГБ
LoRA (модель в FP16)140 ГБ~2 ГБ (только адаптеры)~142 ГБ
QLoRA (модель в NF4)35 ГБ~2 ГБ (адаптеры)~37 ГБ

QLoRA позволяет уместить 70B модель на одной GPU с 48 ГБ (A6000) с запасом.


9. Качество: насколько QLoRA хуже LoRA?

В оригинальной статье QLoRA (Dettmers et al., 2023) показано, что QLoRA с NF4 достигает качества, сопоставимого с LoRA (FP16). Разница составляет 1–2% по метрикам (например, MMLU, GSM8K). При использовании 4‑битной квантизации без NF4 (например, обычный INT4) падение качества может быть 5–10%. Таким образом, NF4 — ключевой компонент для сохранения точности.


10. Практические рекомендации

  • Когда использовать QLoRA: когда у вас ограниченная GPU-память (одна карта 24–48 ГБ) и нужно дообучить модель 7B–70B. Для моделей до 7B можно обойтись LoRA, но QLoRA даёт запас.
  • Выбор ранга LoRA: r=8–64. Для QLoRA ранг можно брать таким же, как для LoRA.
  • Слои для адаптеров: обычно все линейные слои attention (q_proj, k_proj, v_proj, o_proj). Иногда добавляют MLP.
  • Библиотеки: Hugging Face Transformers + bitsandbytes + PEFT (Parameter-Efficient Fine-Tuning).
  • Ограничения: QLoRA медленнее LoRA из-за деквантизации на каждом шаге (примерно на 20–30%). Также не подходит для обучения с нуля — только для fine-tuning.

11. Пет-проект для закрепления

Задача: Дообучить модель LLaMA-2 7B на датасете инструкций (например, Alpaca) с помощью QLoRA на одной GPU с 8 ГБ (например, RTX 3070).

Инструменты: Python, PyTorch, Transformers, bitsandbytes, PEFT, Datasets.

Шаги:

  1. Установить библиотеки: pip install transformers bitsandbytes peft datasets accelerate.
  2. Загрузить модель и токенизатор с квантизацией NF4:
    from transformers import AutoModelForCausalLM, BitsAndBytesConfig
    bnb_config = BitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_use_double_quant=True,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_compute_dtype=torch.bfloat16
    )
    model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", quantization_config=bnb_config)
    
  3. Добавить LoRA-адаптеры через PEFT:
    from peft import LoraConfig, get_peft_model
    lora_config = LoraConfig(r=8, lora_alpha=32, target_modules=["q_proj","v_proj"], lora_dropout=0.05)
    model = get_peft_model(model, lora_config)
    
  4. Загрузить датасет Alpaca, подготовить промпты.
  5. Обучить с помощью Trainer или собственного цикла (используя Paged AdamW).
  6. Сохранить адаптеры: model.save_pretrained("llama2-qlora-alpaca").
  7. Протестировать генерацию: загрузить базовую 4‑битную модель, затем загрузить адаптеры.

Ожидаемый результат: Модель научится отвечать на инструкции, при этом пиковое использование GPU-памяти не превысит 6–7 ГБ.


12. Связь с другими вопросами

ВопросТема
481Как работает LoRA?
480Что такое fine-tuning и PEFT?
483Какие методы квантизации моделей вы знаете?
484Как работает 4‑битная квантизация (GPTQ, AWQ)?
485Как объединить LoRA-адаптеры с квантизованной моделью?

Навигация