Что такое QLoRA? Как 4-bit NormalFloat (NF4) quantization и Double Quantization позволяют fine-tune 70B модель на одной 24GB GPU?
Краткий тезис
QLoRA (Quantized Low-Rank Adaptation) — это метод эффективного fine-tuning больших языковых моделей, который комбинирует 4-битное квантование весов базовой модели с низкоранговой адаптацией (LoRA), работающей в 16-битной точности. Благодаря двум ключевым инновациям — 4-bit NormalFloat (NF4) и Double Quantization — QLoRA позволяет выполнять fine-tuning модели на 70 миллиардов параметров на одном GPU с 24 ГБ памяти без заметной потери качества. Метод был представлен в статье QLoRA: Efficient Finetuning of Quantized Language Models (Dettmers et al., 2023).
--------|---------------------|-------------------------| | Базовая модель | ~35 ГБ (4 бита) | ~140 ГБ (float16) | | LoRA-адаптеры | ~2-4 ГБ (float16) | те же | | Градиенты / Optimizer | ~4-8 ГБ | ~140 ГБ | | Итого (пример) | ~46 ГБ | ~284 ГБ |
Фактически QLoRA умещается в 24 ГБ благодаря градиентной чекпоинтингу, перераспределению памяти (сброс градиентов после шага) и тому, что память для активаций — временная.
2. NF4: распределение весов нормальное, 4 бита
NormalFloat (NF4) — это 4-битный тип данных, специально разработанный для квантования весов, имеющих приблизительно нормальное распределение (как после обучения). Он построен на основе метода Quantile Quantization: уровни квантования выбираются не равномерно, а так, чтобы в каждом квантиле оказывалось одинаковое количество значений.
Принцип построения NF4:
- Предполагаем, что веса модели распределены по нормальному закону ( w \sim \mathcal{N}(0, \sigma^2) ).
- Вычисляем 16 квантилей (для 4 бит — 16 возможных значений) стандартного нормального распределения.
- Эти квантили становятся уровнями квантования. Например, первый квантиль (0.0625) соответствует ( -\infty ) — среднему первых 6.25% самых отрицательных весов. Последний — среднему самых положительных.
- Каждый вес проецируется на ближайший квантиль, и его индекс (0–15) сохраняется.
Почему это лучше равномерного квантования (uniform 4-bit)?
- Веса больших моделей действительно близки к нормальному распределению с толстыми хвостами. Равномерное квантование тратит много уровней на редкие экстремальные значения, а на плотную центральную часть остаётся мало уровней — возрастает ошибка.
- NF4, наоборот, плотно упаковывает центральную область, где находятся 90% весов, и лишь грубо описывает хвосты (что допустимо, так как там мало значений).
- В экспериментах NF4 показывает качество, близкое к float16 (loss +0.1–0.3% для LLM на перплексии), тогда как равномерное 4-битное даёт существенную деградацию.
Техническая деталь: При деквантовании в прямой проходе вес восстанавливается как scale * q_val, где scale (фактор масштаба) хранится отдельно для каждого блока (например, 64 веса объединены в блок с общим масштабом). Это снижает перерасход памяти на хранение масштабов.
3. Double Quantization: квантуем константы квантования
Даже при блочном квантовании с блоками по 64 веса мы вынуждены хранить scale-константы для каждого блока. Если блоков много (например, 70B / 64 ≈ 1.1 млрд блоков), а масштаб хранится в float32 (4 байта), то «накладные расходы» составят около 4.4 ГБ — почти 10% от самой модели.
Double Quantization (DQ) решает эту проблему, применяя вторичное квантование к самим масштабам:
- Каждый масштабный коэффициент (float32) квантируется в 8-битное представление (uint8).
- Для этого мы берём второй уровень блоков: например, 256 соседних масштабов группируются в супер-блок, и для этого супер-блока хранится общий масштаб второго уровня (также float32).
- Таким образом, каждый масштаб первого уровня занимает уже 1 байт, а не 4. При размере супер-блока 256 накладные расходы становятся примерно ( 4 + \frac{256 \cdot 1}{256} \approx 5) байт на супер-блок, что при 70B даёт всего ~0.11 ГБ дополнительно.
Формально:
- Пусть блок первого уровня — $B_1$ весов, их масштаб $s_1$ (float32).
- Блок второго уровня — $B_2$ масштабов $s_1$. Для $B_2$ масштабов считаем общий масштаб $s_2$ (float32) и квантируем каждый $s_1$ в 8-битное представление: $\hat{s}_1 = [text](/wiki/text){Round}(s_1 / s_2 \cdot 127) / 127 \cdot s_2$.
В итоге Double Quantisation снижает overhead с 4.4 ГБ до <0.2 ГБ. В комбинации с NF4 это позволяет уместить 7B–70B модели в память потребительских GPU.
4. Итог: 70B → память как у 12B
QLoRA достигает памяти 24 ГБ для fine-tuning 70B модели за счёт синергии трёх техник:
- NF4 квантование — сжимает веса в 4 раза (с 140→35 ГБ) без потери качества благодаря нормальноподобной сетке уровней.
- Double Quantization — почти полностью устраняет накладные расходы на хранение масштабных коэффициентов (дополнительная экономия ~4 ГБ).
- LoRA — оставляет только малую долю параметров в 16-битной точности для тренировки, что даёт экономию памяти для градиентов и состояний оптимизатора.
Сравнительная таблица для 70B модели (ориентировочно):
| Метод | Память (GPU) | Качество (perplexity) | Скорость (tok/s) |
|---|---|---|---|
| Full fine-tune (float16) | 4 × 80 ГБ | baseline | 1.0× (reference) |
| LoRA (float16 base) | 2 × 80 ГБ | ≈ baseline | 0.9× |
| QLoRA (4-bit NF4 + DQ) | 1 × 24 ГБ | ≈ baseline (loss <0.5%) | 0.5–0.7× |
| QLoRA (4-bit uniform) | 1 × 24 ГБ | хуже на 1–2% | 0.5–0.7× |
Таким образом, QLoRA делает fine-tuning 70B моделей доступным для исследователей с одним GPU (например, RTX 4090 или A6000), сохраняя качество на уровне полного fine-tuning. Этот подход стал стандартом индустрии для дообучения под доменные задачи (instruction tuning, RLHF).
5. Пет-проект для закрепления
Задача: Обучить 7B-модель (например, Mistral 7B или Llama 2-7B) на датасете медицинских инструкций с помощью QLoRA и сравнить качество с обычным LoRA (float16) на одной GPU (например, RTX 3060 12GB).
Инструменты:
- Python 3.10+, PyTorch 2.0+, transformers >= 4.30
- bitsandbytes (реализация NF4 и Double Quantization)
- PEFT библиотека (с поддержкой QLoRA)
- TRL (для SFTTrainer)
- datasets (медицинский датасет, например
medalpaca/medical_meadow_qa) - WandB для логирования
Шаги:
-
Загрузите модель в 4-битном режиме:
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( "mistralai/Mistral-7B-v0.1", quantization_config=bnb_config, device_map="auto" ) -
Сконфигурируйте LoRA:
from peft import LoraConfig, get_peft_model lora_config = LoraConfig( r=8, lora_alpha=16, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config) -
Загрузите датасет и обучите:
from datasets import load_dataset from trl import SFTTrainer dataset = load_dataset("medalpaca/medical_meadow_qa", split="train") trainer = SFTTrainer( model=model, train_dataset=dataset, args=TrainingArguments( per_device_train_batch_size=1, gradient_accumulation_steps=4, num_train_epochs=1, logging_steps=10, save_strategy="epoch", ) ) trainer.train() -
Сравнение с обычным LoRA (float16): загрузите ту же модель без quantization_config, но с LoRA (займёт ~14 ГБ вместо 6 ГБ). Оцените перплексию на тестовом наборе медицинских вопросов.
-
Эксперимент: отключите
bnb_4bit_use_double_quantили заменитеquant_typeна"fp4"и сравните качество.
Ожидаемый результат: Вы убедитесь, что QLoRA (NF4 + Double Quant) позволяет fine-tune 7B модель на GPU с 12 ГБ, а качество (перплексия, ответы) отличается от float16-LoRA не более чем на 0.3–0.5%. При отключении Double Quant память возрастёт незначительно (на 0.2–0.5 ГБ), но при отключении NF4 (fp4) качество может заметно упасть.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 482 | LoRA — основная техника PEFT, лежащая в основе QLoRA |
| 952 | Квантование моделей — общие принципы и методы снижения точности весов |
Навигация
- Предыдущий: 954
- Следующий: 956
- Индекс: 00. Индекс разборов