PEFT documentation

Квантование

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.21.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Квантование

Квантование представляет данные с меньшим количеством бит, что делает его полезной техникой для снижения потребления памяти и ускорения вывода, особенно в случае больших языковых моделей (LLM). Существует несколько способов квантования модели, в том числе:

  • оптимизация выбора весов модели, квантизуемых алгоритмом AWQ
  • независимое квантование каждой строки матрицы весов с помощью алгоритма GPTQ
  • квантование до 8-битной и 4-битной точности с помощью библиотеки bitsandbytes
  • квантование до точности всего 2 бита с помощью алгоритма AQLM

Однако после квантования модель обычно не подвергается дальнейшему обучению для задач нижнего уровня, поскольку обучение может быть нестабильным из-за пониженной точности весов и активаций. Но поскольку методы PEFT добавляют только дополнительные обучаемые параметры, это позволяет обучать квантованную модель с адаптером PEFT поверх неё! Сочетание квантования с PEFT может стать хорошей стратегией для обучения даже самых больших моделей на одном GPU. Например, QLoRA — это метод, который квантует модель до 4 бит, а затем обучает её с помощью LoRA. Этот метод позволяет дообучать модель с 65 млрд параметров на одном GPU с 48 ГБ памяти!

В этом руководстве вы узнаете, как квантовать модель до 4 бит и обучить её с помощью LoRA.

Квантование модели

bitsandbytes — это библиотека квантования с интеграцией в Transformers. Благодаря этой интеграции вы можете квантовать модель до 8 или 4 бит и включить множество других опций, настроив класс BitsAndBytesConfig. Например, вы можете:

  • Установите load_in_4bit=True, чтобы квантовать модель до 4 бит при её загрузке
  • установите bnb_4bit_quant_type="nf4", чтобы использовать специальный 4-битный тип данных для весов, инициализированных из нормального распределения
  • Установите bnb_4bit_use_double_quant=True, чтобы использовать схему вложенной квантизации для квантизации уже квантованных весов
  • установите bnb_4bit_compute_dtype=torch.bfloat16, чтобы использовать bfloat16 для более быстрого вычисления
import torch
from transformers import BitsAndBytesConfig

config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.bfloat16,
)

Передайте config в метод from_pretrained.

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)

Далее вам следует вызвать функцию prepare_model_for_kbit_training(), чтобы подготовить квантованную модель для обучения.

from peft import prepare_model_for_kbit_training

model = prepare_model_for_kbit_training(model)

Теперь, когда квантованная модель готова, давайте настроим конфигурацию.

LoraConfig

Создайте LoraConfig со следующими параметрами (или выберите свои):

from peft import LoraConfig

config = LoraConfig(
    r=16,
    lora_alpha=8,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

Затем используйте функцию get_peft_model(), чтобы создать PeftModel из квантованной модели и конфигурации.

from peft import get_peft_model

model = get_peft_model(model, config)

Вы готовы к обучению с использованием любого удобного вам метода!

Инициализация LoftQ

LoftQ инициализирует веса LoRA таким образом, чтобы минимизировать ошибку квантования, и может улучшить производительность при обучении квантованных моделей. Для начала следуйте этим инструкциям.

LoftQ применяется только к линейным слоям, включая те, которые указаны через target_parameters. Слои встраиваний и свёрточные слои не поддерживаются и используют стандартную инициализацию LoRA. Слои nn.MultiheadAttention не поддерживаются и вызывают ошибку.

В целом, для наилучшей работы LoftQ рекомендуется применять LoRA к как можно большему количеству слоёв, поскольку к слоям, не включённым в целевой список, LoftQ применить невозможно. Это означает, что передача LoraConfig(..., target_modules="all-linear") с наибольшей вероятностью даст лучшие результаты. Кроме того, при использовании 4-битного квантования в конфигурации квантования следует указывать nf4 в качестве типа квантования, то есть BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4").

В целом, для наилучшей работы LoftQ рекомендуется применять LoRA к как можно большему количеству слоёв, поскольку к слоям, не включённым в целевой список, LoftQ применить невозможно. Это означает, что передача LoraConfig(..., target_modules="all-linear") с наибольшей вероятностью даст лучшие результаты. Кроме того, при использовании 4-битного квантования в конфигурации квантования следует указывать nf4 в качестве типа квантования, то есть BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4").

В настоящее время существует два предусмотренных способа применения LoftQ:

  1. загрузите базовую модель без квантования, примените LoftQ с вашим целевым уровнем квантования (например, bits=4 для nf4) и сохраните полученный адаптер. Затем выполните квантование базовой модели, например, с помощью BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4") и загрузите инициализированный с помощью LoftQ адаптер поверх неё
  2. загрузите квантованную базовую модель, инициализируйте PEFT-модель с LoRA (без loftq) и используйте replace_lora_weights_loftq, чтобы применить инициализацию LoftQ, стримингуя эталонные веса неквантованной базовой модели из файла весов (это подробнее описано в следующем разделе)

Использование replace_lora_weights_loftq для применения LoftQ на лету

Более простой, но менее гибкий способ применить инициализацию LoftQ — использовать вспомогательную функцию replace_lora_weights_loftq. Она принимает квантованную модель PEFT на вход и заменяет веса LoRA на месте их аналогами, инициализированными с помощью LoftQ.

from peft import replace_lora_weights_loftq
from transformers import BitsAndBytesConfig

bnb_config = BitsAndBytesConfig(load_in_4bit=True, ...)
base_model = AutoModelForCausalLM.from_pretrained(..., quantization_config=bnb_config)
# note: don't pass init_lora_weights="loftq" or loftq_config!
lora_config = LoraConfig(task_type="CAUSAL_LM")
peft_model = get_peft_model(base_model, lora_config)
replace_lora_weights_loftq(peft_model)

replace_lora_weights_loftq также позволяет передать аргумент callback, чтобы получить больший контроль над тем, какие слои следует изменять, а какие — нет; эмпирически это может значительно улучшить результаты. Более подробный пример можно найти в этом ноутбуке.

replace_lora_weights_loftq реализует только один итерационный шаг LoftQ. Это означает, что обновляются только веса LoRA, а не веса LoRA и квантованной базовой модели поочерёдно. Это может привести к снижению производительности, но имеет преимущество: можно использовать исходные квантованные веса, полученные из базовой модели, вместо необходимости хранить дополнительную копию изменённых квантованных весов. Стоит ли этот компромисс того, зависит от конкретного случая использования.

В настоящее время replace_lora_weights_loftq имеет следующие дополнительные ограничения:

  • Файлы модели должны храниться в формате safetensors.
  • Поддерживается только 4-битное квантование bitsandbytes.

Инициализация PiSSA

PiSSA нельзя применять к квантованным весам. Обычно его применяют к неквантованной базовой модели, после чего сохраняют и загружают с квантованием полученный остаток, что требует, чтобы вся неквантованная модель помещалась в память.

В качестве альтернативы replace_lora_weights_pissa принимает квантованную модель PEFT и загружает веса неквантованной модели из файла весов послойно. LoRA-веса каждого слоя инициализируются с помощью PiSSA, а его базовый вес заменяется пере-квантованным остатком.

from peft import replace_lora_weights_pissa
from transformers import BitsAndBytesConfig

bnb_config = BitsAndBytesConfig(load_in_4bit=True, ...)
base_model = AutoModelForCausalLM.from_pretrained(..., quantization_config=bnb_config)
# note: don't pass init_lora_weights="pissa" here!
lora_config = LoraConfig(task_type="CAUSAL_LM")
peft_model = get_peft_model(base_model, lora_config)
replace_lora_weights_pissa(peft_model, init_lora_weights="pissa_niter_4")

Поддерживаются как "pissa", так и "pissa_niter_[number of iters]".

Поскольку базовые веса изменяются, сохранения только адаптера недостаточно для восстановления модели. Как и в случае с другими инициализациями PiSSA, необходимо либо сохранить базовую модель, либо преобразовать обученный адаптер с помощью аргумента path_initial_model_for_weight_conversion в save_pretrained().

В настоящее время replace_lora_weights_pissa имеет следующие дополнительные ограничения:

  • Файлы модели должны храниться в формате safetensors.
  • Поддерживается только 4-битное квантование bitsandbytes.
  • Варианты LoRA, такие как DoRA, не поддерживаются.

Обучение в стиле QLoRA

QLoRA добавляет обучаемые веса во все линейные слои в архитектуре трансформера. Поскольку имена атрибутов для этих линейных слоев могут различаться в зависимости от архитектуры, установите target_modules в "all-linear", чтобы добавить LoRA во все линейные слои:

config = LoraConfig(target_modules="all-linear", ...)

Квантование GPTQ

Подробнее о квантовании на [2, 3, 4, 8] бит на основе GPTQ можно узнать в GPT-QModel и в документации Transformers по GPTQ. PEFT поддерживает пост-обучение GPTQ через GPT-QModel.

# GPT-QModel install
pip install "gptqmodel>=7.0.0"
from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig

model_id = "facebook/opt-125m"
tokenizer = AutoTokenizer.from_pretrained(model_id)

gptq_config = GPTQConfig(bits=4, group_size=128, dataset="wikitext2", tokenizer=tokenizer)

quantized_model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", quantization_config=gptq_config)

# save quantized model
quantized_model.save_pretrained("./opt-125m-gptq")
tokenizer.save_pretrained("./opt-125m-gptq")

После квантования вы можете выполнять пост-обучение моделей GPTQ с помощью API PEFT.

Квантование AQLM

Аддитивное квантование языковых моделей (AQLM) — это метод сжатия больших языковых моделей. Он квантует несколько весов совместно, используя взаимозависимости между ними. AQLM представляет группы из 8–16 весов в виде суммы нескольких векторных кодов. Это позволяет сжимать модели до 2 бит с незначительной потерей точности.

Поскольку процесс квантования AQLM требует значительных вычислительных ресурсов, рекомендуется использовать предварительно квантованные модели. Частичный список доступных моделей можно найти в официальном репозитории aqlm repository.

Модели поддерживают настройку адаптеров LoRA. Для настройки квантованной модели необходимо установить библиотеку вывода aqlm: pip install aqlm>=1.0.2. Дообученные адаптеры LoRA следует сохранять отдельно, так как их объединение с квантованными весами AQLM невозможно.

quantized_model = AutoModelForCausalLM.from_pretrained(
    "BlackSamorez/Mixtral-8x7b-AQLM-2Bit-1x16-hf-test-dispatch",
    dtype="auto", device_map="auto", low_cpu_mem_usage=True,
)

peft_config = LoraConfig(...)

quantized_model = get_peft_model(quantized_model, peft_config)

С примером Google Colab можно ознакомиться, чтобы получить общее представление о дообучении AQLM+LoRA.

Квантование EETQ

Вы также можете выполнять дообучение с использованием LoRA для квантованных моделей EETQ. Пакет EETQ предлагает простой и эффективный способ выполнения 8-битного квантования, который, по утверждениям, работает быстрее алгоритма LLM.int8(). Сначала убедитесь, что у вас установлена версия transformers, совместимая с EETQ (например, установив её из последней версии pypi или из исходного кода).

import torch
from transformers import EetqConfig

config = EetqConfig("int8")

Передайте config в метод from_pretrained.

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)

и создайте LoraConfig и передайте его в get_peft_model:

from peft import LoraConfig, get_peft_model

config = LoraConfig(
    r=16,
    lora_alpha=8,
    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, config)

Квантование HQQ

Модели, квантованные с помощью Half-Quadratic Quantization of Large Machine Learning Models (HQQ), поддерживают настройку адаптеров LoRA. Для настройки квантованной модели необходимо установить библиотеку hqq с помощью команды: pip install hqq.

from hqq.engine.hf import HQQModelForCausalLM

device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"

quantized_model = HQQModelForCausalLM.from_quantized(save_dir_or_hfhub, device=device)
peft_config = LoraConfig(...)
quantized_model = get_peft_model(quantized_model, peft_config)

Или используйте версию transformers, совместимую с HQQ (например, установив её из последней версии pypi или из исходного кода).

from transformers import HqqConfig, AutoModelForCausalLM

quant_config = HqqConfig(nbits=4, group_size=64)
quantized_model = AutoModelForCausalLM.from_pretrained(save_dir_or_hfhub, device_map=device_map, quantization_config=quant_config)
peft_config = LoraConfig(...)
quantized_model = get_peft_model(quantized_model, peft_config)

torchao (PyTorch Architecture Optimization)

PEFT поддерживает модели, квантованные с помощью torchao (“ao”) для int8-квантования.

from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, TorchAoConfig
from torchao.quantization import Int8WeightOnlyConfig

model_id = ...
quantization_config = TorchAoConfig(quant_type=Int8WeightOnlyConfig())
base_model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=quantization_config)
peft_config = LoraConfig(...)
model = get_peft_model(base_model, peft_config)

Ограничения:

  • Используйте самые последние версии torchao (>= v0.4.0) и transformers (> 4.42).
  • В настоящее время поддерживаются только линейные слои.
  • quant_type = "int4_weight_only" в настоящее время не поддерживается.
  • NF4 пока не реализован в transformers, поэтому не поддерживается.
  • В настоящее время DoRA работает только с quant_type = "int8_weight_only".
  • Для использования torchao в сочетании с LoRA предусмотрена явная поддержка. Однако, когда torchao квантует слой, его класс не меняется, изменяется только тип базового тензора. По этой причине методы PEFT, отличные от LoRA, как правило, также работают с torchao, даже если они не поддерживаются явно. Обратите внимание, однако, что слияние работает корректно только с LoRA и при quant_type = "int8_weight_only". Если вы используете другой метод PEFT или тип данных, слияние, скорее всего, приведёт к ошибке, а даже если ошибки не будет, результаты всё равно будут неверными.

Квантование INC

Intel Neural Compressor (INC) позволяет выполнять квантование моделей для различных устройств, включая ускорители Intel Gaudi (также известные как HPU-устройства). Вы можете выполнять дообучение с использованием LoRA для моделей, квантованных с помощью INC. Чтобы использовать INC с моделями PyTorch, установите библиотеку командой: pip install neural-compressor[pt]. Квантование модели до точности FP8 для HPU-устройств можно выполнить с помощью следующего одношагового процесса квантования:

import torch
from neural_compressor.torch.quantization import FP8Config, convert, finalize_calibration, prepare
quant_configs = {
    ...
}
config = FP8Config(**quant_configs)

Передайте конфигурацию в метод prepare, выполните инференс для сбора статистики калибровки, а затем вызовите методы finalize_calibration и convert, чтобы квантовать модель до точности FP8:

model = prepare(model, config)
# Run inference to collect calibration statistics
...
# Finalize calibration and convert the model to FP8 precision
finalize_calibration(model)
model = convert(model)
# Load PEFT LoRA adapter as usual
...

Пример, демонстрирующий, как загрузить адаптер PEFT LoRA в модель FLUX для генерации изображений по тексту с квантованием INC для устройств HPU, приведён здесь.

Ограничения:

  • Методы merge() и unmerge() в настоящее время не поддерживаются для моделей с квантованием INC.
  • В настоящее время при загрузке адаптеров PEFT поддерживаются только слои Linear с квантованием INC.

Другие поддерживаемые методы PEFT

Помимо LoRA, квантование поддерживают следующие методы PEFT:

  • VeRA (поддерживает квантование bitsandbytes)
  • AdaLoRA (поддерживает квантование как с помощью bitsandbytes, так и с помощью GPTQ)
  • (IA)³ (поддерживает квантование bitsandbytes)

LoRA для Transformer Engine (TE)

PEFT поддерживает адаптеры LoRA поверх слоёв NVIDIA Transformer Engine (te.pytorch.Linear, te.pytorch.LayerNormLinear и te.pytorch.LayerNormMLP). Слои TE используют FP8 и объединённые ядра для ускорения обучения трансформеров, а подключение адаптеров LoRA позволяет выполнять параметрически эффективную дообучение моделей, ускоренных TE.

from peft import LoraConfig, get_peft_model

config = LoraConfig(
    r=8,
    lora_alpha=16,
    target_modules=["q_proj", "v_proj"],
    task_type="TOKEN_CLS",
)
model = get_peft_model(te_model, config)

Если Transformer Engine установлен, PEFT автоматически направляет соответствующие слои в адаптер TeLinear — дополнительная настройка не требуется.

Ограничения

  • merge() и unmerge() пока не поддерживаются для слоёв TE.
  • DoRA не поддерживается с слоями TE.

Полный пример классификации токенов ESM2 доступен в examples/lora_finetuning_transformer_engine/.

Объединение адаптеров с квантованными весами

Для любого метода квантования, который это поддерживает, merge_adapter() деквантует базовые веса, добавляет веса адаптера и снова квантует результат. unmerge_adapter() выполняет те же действия в обратном порядке, поэтому не восстанавливает исходные квантованные веса, а многократные циклы слияния и разделения могут накапливать ошибку округления. Чтобы вернуть исходные веса, перезагрузите базовую модель. Чтобы переключаться между базовой моделью и адаптером без слияния, используйте контекстный менеджер disable_adapter().

Дальнейшие шаги

Если вы хотите узнать больше о квантовании, вам могут помочь следующие материалы:

Update on GitHub