QLoRA
QLoRA выполняет тонкую настройку LLM, обучая адаптеры LoRA на замороженной базовой модели, хранящейся в формате 4-битного NF4. Это позволяет разместить модель с 65 млрд параметров на одном GPU с 48 ГБ памяти.
QLoRA (квантованная низкоранговая адаптация) — метод дообучения, при котором предварительно обученная крупная языковая модель (LLM) фиксируется с точностью 4 бита, а поверх неё обучаются небольшие адаптеры LoRA. Градиенты распространяются через квантованные веса к адаптерам. Впервые метод описали Деттмерс и соавторы в статье о QLoRA 2023 года. Он позволил сократить объём памяти GPU, необходимый для дообучения модели с 65 миллиардами параметров, с более чем 780 ГБ до менее чем 48 ГБ — достаточно для одного графического процессора рабочей станции, — при этом качество решения задачи соответствовало дообучению с точностью 16 бит. Модели Guanaco, дообученные этим методом, через 24 часа обучения на одном GPU набрали 99.3% от результата ChatGPT в бенчмарке Vicuna.
Как работает QLoRA#
QLoRA сочетает замороженную сжатую базовую модель с тремя методами экономии памяти:
- 4-битный NormalFloat (NF4): каждый вес хранится в виде 4-битного кода, указывающего на один из 16 уровней, соответствующих квантилям нормального распределения. Для каждого блока из 64 весов используется один масштабный коэффициент по абсолютному максимуму. Предварительно обученные веса модели имеют приблизительно нормальное распределение, поэтому уровни-квантили обеспечивают более высокое разрешение вблизи нуля, где расположена большая часть весов.
- Двойное квантование: масштабы для каждого блока сами квантуются до 8 бит блоками по 256 значений. Это сокращает их накладные расходы с 0.5 примерно до 0.127 бита на параметр, что позволяет сэкономить около 3 ГБ для модели с 65 млрд параметров.
- Пейджинговые оптимизаторы: состояния оптимизатора хранятся в унифицированной памяти NVIDIA и во время пикового расхода памяти переносятся в оперативную память CPU, например при обработке длинных последовательностей с контрольными точками градиента. Благодаря этому обучение не прерывается из-за нехватки памяти.
При прямом проходе каждый замороженный слой непосредственно перед умножением матриц преобразует веса NF4 обратно в bfloat16 и добавляет выход адаптера LoRA. При обратном распространении градиенты вычисляются через деквантованные веса, но обновляются только две низкоранговые матрицы LoRA. Авторы статьи обнаружили, что для соответствия полному дообучению нужны адаптеры во всех линейных слоях блока трансформера:
Приведённый ниже фрагмент создаёт уровни NF4 так же, как это делает библиотека bitsandbytes, и сравнивает их с 16 равномерно распределёнными уровнями на блоках нормально распределённых весов:
import torch
torch.manual_seed(0)
normal = torch.distributions.Normal(0.0, 1.0)
# NF4: 16 levels at quantiles of a normal distribution, with an exact zero (as built in bitsandbytes)
positive = normal.icdf(torch.linspace(0.9677083, 0.5, 9)[:-1])
negative = -normal.icdf(torch.linspace(0.9677083, 0.5, 8)[:-1])
nf4 = torch.cat([negative, torch.zeros(1), positive]).sort().values
nf4 = nf4 / nf4.max()
int4 = torch.linspace(-1, 1, 16) # evenly spaced 4-bit levels for comparison
weights = torch.randn(4096, 64) * 0.02 # pretrained weights are roughly normal, in blocks of 64
absmax = weights.abs().amax(dim=1, keepdim=True) # one scale per block
for name, levels in (("NF4", nf4), ("INT4", int4)):
codes = ((weights / absmax)[..., None] - levels).abs().argmin(dim=-1) # 4-bit index per weight
restored = levels[codes] * absmax
print(f"{name} mean squared error: {(restored - weights).pow(2).mean():.3e}")Фрагмент выводит среднеквадратичную ошибку 3.395e-06 для NF4 и 4.053e-06 для равномерно распределённых уровней: при тех же 16 уровнях и тех же масштабах блоков расположение уровней NF4 по квантилям снижает ошибку реконструкции нормально распределённых весов примерно на 16%. На практике Hugging Face Transformers загружает модель в NF4 с помощью bitsandbytes и BitsAndBytesConfig, задающего load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_use_double_quant=True и bnb_4bit_compute_dtype=torch.bfloat16, а библиотека PEFT подключает адаптеры LoRA.
QLoRA и смежные методы#
- LoRA: обучает такие же адаптеры на базовой модели, сохраняемой с точностью 16 бит. Только 16-битные веса модели с 65 млрд параметров занимают около 130 ГБ, а NF4 сокращает этот объём примерно до четверти.
- Параметрически эффективное дообучение (PEFT): семейство методов, которые обучают небольшое число параметров, включая LoRA, QLoRA, адаптеры и настройку промпта.
- Квантование модели после обучения: сжимает готовую модель для вывода. Модель, настроенную с помощью QLoRA, часто объединяют с 16-битными весами, а затем ещё раз квантуют для эксплуатации.
- Обучение с учётом квантования (QAT): имитирует округление до низкой точности при прямом проходе, пока обучаются все веса. Благодаря этому развёрнутая модель сохраняет точность при низкой разрядности. Ultralytics YOLO26 выполняет INT8 QAT при обучении с
quantize=8, добавляя фиктивное квантование в свёрточные и линейные слои с помощью NVIDIA ModelOpt, чтобы обученные веса сохранялись при экспорте в INT8.
Компромиссы#
QLoRA экономит память за счёт скорости: преобразование каждого замороженного веса при каждом прямом и обратном проходе замедляет шаг обучения по сравнению с LoRA с точностью 16 бит. Адаптеры достаточно малы, чтобы хранить их и менять для каждой задачи, но перед объединением с базовой моделью её нужно сначала деквантовать до 16 бит. У модели с 65 млрд параметров 4-битные веса и дважды квантованные масштабы занимают около 34 ГБ, оставляя остальную часть GPU с 48 ГБ памяти для адаптеров, активаций и состояний пейджингового оптимизатора.










