Transformers documentation
Внести вклад
Внести вклад
Transformers поддерживает множество методов квантования, таких как QLoRA, GPTQ, LLM.int8 и AWQ. Однако существует ещё много подходов к квантованию, которые пока не интегрированы. Чтобы упростить добавление и использование этих методов квантования в Transformers, используйте класс HfQuantizer. HfQuantizer предназначен для того, чтобы выступать в роли внутреннего вспомогательного класса для добавления метода квантования, а не применяться к каждому модулю PyTorch.
В этом руководстве показано, как интегрировать новый метод квантования с HfQuantizer.
Требования
Прежде чем интегрировать новый метод квантования в Transformers, убедитесь, что он соответствует следующим требованиям. Поддерживаются только методы квантования, которые могут выполняться с модулями PyTorch.
Метод квантования доступен через пакет Python, который можно установить с помощью pip (также допустимо, если пакет можно установить только из исходного кода). В идеале в pip-пакет должны входить предварительно скомпилированные ядра.
Метод может выполняться на общепринятом оборудовании (CPU, GPU и т. д.).
Метод обернут в nn.Module (
~bitsandbytes.nn.Linear8bitLt,~bitsandbytes.nn.Linear4bit), и квантованный линейный слой должен иметь следующее определение.class Linear4bit(nn.Module): def __init__(self, ...): ... def forward(self, x): return my_4bit_kernel(x, self.weight, self.bias)Таким образом, модели Transformers легко квантуются путём замены экземпляров nn.Linear на целевой класс.
Метод квантования должен быть сериализуемым. Вы можете сохранить квантованные веса локально или загрузить их в Hub.
Убедитесь, что пакет, содержащий ядра/примитивы квантования, стабилен (без частых несовместимых изменений).
Некоторые методы квантования могут требовать «предварительного квантования» модели с помощью калибровки данных (AWQ). В этом случае мы предпочитаем поддерживать в Transformers только инференс, а само квантование модели делегировать сторонней библиотеке, поддерживаемой сообществом ML.
Создание нового класса HFQuantizer
Лучшей отправной точкой будет изучение другого метода квантования, например Finegrained Fp8. В общей сложности вам потребуется обновить или создать три файла: файл конфигурации, файл интеграции и файл квантизатора.
Создайте новый класс конфигурации квантования в файле src/transformers/utils/quantization_config.py. Добавьте новую конфигурацию квантования в _import_structure в файле src/transformers/__init__.py библиотеки Transformers.
Создайте новый файл
quantizer_your_method.pyв каталоге src/transformers/quantizers/ и унаследуйте его от [`~quantizers.HfQuantizer`]. Не забудьте добавить новый квантизатор и конфигурацию квантизации в автоматическое сопоставление квантизации в src/transformers/quantizers/auto.py.Определите следующие атрибуты класса и методы-свойства для вашего метода квантования:
requires_calibration: требуется ли методу квантования процесс калибровки данных. Если установлено значениеTrue, поддерживается только инференс (с квантованными весами), а не инференс и квантование.is_serializable: Метод-свойство, определяющий, сериализуем ли данный метод.is_trainable: метод-свойство, определяющий, можно ли дообучать модели поверх метода квантования (с использованием подходов PEFT или без них).
Напишите методы
validate_environmentиupdate_dtype. Эти методы вызываются перед созданием квантованной модели, чтобы убедиться, что пользователь использует правильную конфигурацию. Пример реализации можно найти в других квантовщиках.Напишите метод
_process_model_before_weight_loading. В Transformers квантованные модели сначала инициализируются на устройстве"meta"перед загрузкой весов. Это означает, что метод_process_model_before_weight_loadingотвечает за модификацию каркаса модели для замены некоторых модулей (nn.Linear) целевыми модулями (модулями квантования).
Вы можете определить логику замены модулей или любой другой вспомогательный метод, создав новый файл в transformers/src/integrations/ и экспортировав соответствующие методы в файле __init__.py этой папки.
Добавьте метод
get_quantize_opsв класс квантизатора, если квантизация поддерживает квантизацию «на лету». В transformers каждый тензор материализуется, и к нему применяется последовательность различных операций. В нашем случае операция квантизации выполняется в конце. Необходимо создатьXXXQuantize, подклассConversionOps, и добавить методconvert. В методеconvertнужно заквантовать веса и вернуть словарь заквантованных параметров.Добавьте метод
get_weight_conversionsв класс квантизатора, если квантование поддерживает загрузку предварительно квантованных весов. В transformers можно собрать несколько тензоров и применить к ним операции. Это особенно полезно, когда в чекпоинте есть тензоры, которые необходимо перегруппировать, чтобы воссоздать квантованные тензоры.При необходимости реализуйте метод
_process_model_after_weight_loading. Этот метод позволяет добавлять дополнительные функции, требующие манипуляций с моделью после загрузки весов.Документируйте всё! Убедитесь, что ваш метод квантования задокументирован, добавив новый файл в
docs/source/en/quantization.Вы должны добавить тесты, включив пакет в наш ночной Dockerfile внутри
docker/transformers-quantization-latest-gpu, а затем создав новый файл тестов вtests/quantization/xxx. Вы можете обратиться к существующим методам квантования, чтобы посмотреть, как это реализовано.
Обзор файлов
| Файл | Назначение |
|---|---|
utils/quantization_config.py | Определите YourMethodConfig, наследующийся от QuantizationConfigMixin |
quantizers/quantizer_your_method.py | Реализуйте YourMethodHfQuantizer, наследующий от HfQuantizer |
integrations/your_method.py | Реализуйте подклассы ConversionOps и вспомогательные функции |
quantizers/auto.py | Зарегистрируйте квантизатор и конфигурацию в AUTO_QUANTIZER_MAPPING и AUTO_QUANTIZATION_CONFIG_MAPPING |
docs/source/en/quantization/your_method.md | Использование документации пользователями |
tests/quantization/your_method/ | Добавить интеграционные тесты |
Понимание различий между get_quantize_ops и get_weight_conversions
Эти два метода предназначены для различных сценариев загрузки весов. Понимание того, когда следует использовать каждый из них, имеет решающее значение.
get_quantize_ops — Квантование на лету
Используйте это при загрузке неквантованного чекпоинта (например, весов float16/bfloat16) и квантовании во время загрузки.
Checkpoint: model.safetensors (float16 weights for example)
↓
get_quantize_ops → YourQuantize.convert()
↓
Result: Quantized weights in memoryМетод convert принимает по одному тензору за раз, квантует его и может возвращать словарь с квантованными параметрами, например:
class YourQuantize(ConversionOps):
def convert(self, input_dict, model, full_layer_name, missing_keys, **kwargs):
# input_dict = {"layer.weight": <float16 tensor>}
value = list(input_dict.values())[0]
module, tensor_name = get_module_from_name(model, full_layer_name)
# Quantize and assign
quantized, scale, zero_point = your_quantize_fn(value)
return {full_layer_name: quantized, full_layer_name + ".scale": scale, full_layer_name + ".zero_point": zero_point}get_weight_conversions — Загрузка предварительно квантованных контрольных точек
Используйте этот параметр при загрузке предварительно квантованной контрольной точки, в которой квантованные веса сохранены в виде нескольких отдельных компонентов (например, data, scale и zero point), которые необходимо объединить в один тензор при загрузке. Не все методы квантования требуют этого шага реконструкции: например, некоторые методы, такие как FP8, просто загружают веса и масштабы как есть, без их объединения. Другие, такие как torchao, действительно требуют сборки квантованного тензора из нескольких сохранённых компонентов.
Checkpoint: model.safetensors (quantized components)
- layer._weight_qdata
- layer._weight_scale
- layer._weight_zero_point
↓
get_weight_conversions → WeightConverter + YourDeserialize.convert()
↓
Result: Reconstructed quantized tensor → layer.weightКласс WeightConverter собирает связанные тензоры на основе source_patterns, а затем передаёт их в ваш метод convert:
def get_weight_conversions(self):
if self.pre_quantized:
return [
WeightConverter(
source_patterns=["_weight_qdata", "_weight_scale", "_weight_zero_point"],
target_patterns="weight",
operations=[YourDeserialize(self)],
),
]
return []
class YourDeserialize(ConversionOps):
def convert(self, input_dict, model, full_layer_name, **kwargs):
# input_dict contains all collected tensors
# Reconstruct the quantized tensor from components
reconstructed_tensor = reconstruct_from_components(input_dict)
return {full_layer_name: reconstructed_tensor}