# Внести вклад

Transformers поддерживает множество методов квантования, таких как QLoRA, GPTQ, LLM.int8 и AWQ. Однако существует ещё много подходов к квантованию, которые пока не интегрированы. Чтобы упростить добавление и использование этих методов квантования в Transformers, используйте класс [HfQuantizer](/docs/transformers/v5.23.0/ru/main_classes/quantization#transformers.quantizers.HfQuantizer). [HfQuantizer](/docs/transformers/v5.23.0/ru/main_classes/quantization#transformers.quantizers.HfQuantizer) спроектирован как внутренний вспомогательный класс для добавления метода квантования, а не как объект, применяемый к каждому модулю PyTorch.

В этом руководстве показано, как интегрировать новый метод квантования с [HfQuantizer](/docs/transformers/v5.23.0/ru/main_classes/quantization#transformers.quantizers.HfQuantizer).

## Требования

Прежде чем интегрировать новый метод квантования в Transformers, убедитесь, что он соответствует следующим требованиям. Поддерживаются только методы квантования, которые могут выполняться с модулями PyTorch.

- Метод квантования доступен через пакет Python, который можно установить с помощью pip (также допустимо, если пакет можно установить только из исходного кода). В идеале в pip-пакет должны входить предварительно скомпилированные ядра.
- Метод может выполняться на общепринятом оборудовании (CPU, GPU и т. д.).
- Метод обернут в [nn.Module](https://pytorch.org/docs/stable/generated/torch.nn.Module.html) (`~bitsandbytes.nn.Linear8bitLt`, `~bitsandbytes.nn.Linear4bit`), и квантованный линейный слой должен иметь следующее определение.

  ```py
  class Linear4bit(nn.Module):
      def __init__(self, ...):
          ...

      def forward(self, x):
          return my_4bit_kernel(x, self.weight, self.bias)
  ```

  Таким образом, модели Transformers легко квантуются путём замены экземпляров [nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html) на целевой класс.

- Метод квантования должен быть сериализуемым. Вы можете сохранить квантованные веса локально или загрузить их в Hub.
- Убедитесь, что пакет, содержащий ядра/примитивы квантования, стабилен (без частых несовместимых изменений).

Некоторые методы квантования могут требовать «предварительного квантования» модели с помощью калибровки данных (AWQ). В этом случае мы предпочитаем поддерживать в Transformers только инференс, а само квантование модели передаём на обработку сторонней библиотеке, поддерживаемой сообществом ML.

## Создание нового класса HFQuantizer

0. Лучшей отправной точкой будет изучение другого метода квантования, например Finegrained Fp8. В общей сложности вам потребуется обновить или создать три файла: [файл конфигурации](https://github.com/huggingface/transformers/blob/main/src/transformers/utils/quantization_config.py), [файл интеграции](https://github.com/huggingface/transformers/blob/main/src/transformers/integrations/finegrained_fp8.py) и [файл квантизатора](https://github.com/huggingface/transformers/blob/main/src/transformers/quantizers/quantizer_finegrained_fp8.py).

1. Создайте новый класс конфигурации квантования внутри [src/transformers/utils/quantization_config.py](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/utils/quantization_config.py). Добавьте новую конфигурацию квантования в [\_import_structure](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/__init__.py#L1088) в файле [src/transformers/\_\_init\_\_.py](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/__init__.py) библиотеки Transformers.

2. Создайте новый файл `quantizer_your_method.py` в каталоге [src/transformers/quantizers/](https://github.com/huggingface/transformers/tree/abbffc4525566a48a9733639797c812301218b83/src/transformers/quantizers) и унаследуйте его от [`~quantizers.HfQuantizer`]. Не забудьте добавить новый квантизатор и конфигурацию квантизации в автоматическое сопоставление квантизации в [src/transformers/quantizers/auto.py](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/quantizers/auto.py).

3. Определите следующие атрибуты класса и методы-свойства для вашего метода квантования:

   - `requires_calibration`: требуется ли методу квантования процесс калибровки данных. Если установлено значение `True`, поддерживается только инференс (с квантованными весами), а не инференс и квантование.
   - `is_serializable`: Метод-свойство, определяющий, сериализуем ли данный метод.
   - `is_trainable`: метод-свойство, определяющий, можно ли дообучать модели поверх метода квантования (с использованием подходов PEFT или без них).

4. Напишите методы `validate_environment` и `update_dtype`. Эти методы вызываются перед созданием квантованной модели, чтобы убедиться, что пользователь использует правильную конфигурацию. Пример реализации можно найти в других квантовщиках.

5. Напишите метод `_process_model_before_weight_loading`. В Transformers квантованные модели сначала инициализируются на устройстве `"meta"` перед загрузкой весов. Это означает, что метод `_process_model_before_weight_loading` отвечает за модификацию каркаса модели для замены некоторых модулей ([nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html)) целевыми модулями (модулями квантования).

Вы можете определить логику замены модулей или любой другой вспомогательный метод, создав новый файл в [transformers/src/integrations/](https://github.com/huggingface/transformers/tree/abbffc4525566a48a9733639797c812301218b83/src/transformers/integrations) и экспортировав соответствующие методы в файле `__init__.py` этой папки.

6. Добавьте метод `get_quantize_ops` в класс квантизатора, если квантизация поддерживает квантизацию «на лету». В transformers каждый тензор материализуется, и к нему применяется последовательность различных операций. В нашем случае операция квантизации выполняется в конце. Необходимо создать `XXXQuantize`, подкласс `ConversionOps`, и добавить метод `convert`. В методе `convert` нужно заквантовать веса и вернуть словарь заквантованных параметров.

7. Добавьте метод `get_weight_conversions` в класс квантизатора, если квантование поддерживает загрузку предварительно квантованных весов. В transformers можно собрать несколько тензоров и применить к ним операции. Это особенно полезно, когда в чекпоинте есть тензоры, которые необходимо перегруппировать, чтобы воссоздать квантованные тензоры.

8. При необходимости реализуйте метод `_process_model_after_weight_loading`. Этот метод позволяет добавлять дополнительные функции, требующие манипуляций с моделью после загрузки весов.

9. Документируйте всё! Убедитесь, что ваш метод квантования задокументирован, добавив новый файл в `docs/source/en/quantization`.

10. Вы должны добавить тесты, включив пакет в наш ночной 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) и квантовании во время загрузки.

```text
Checkpoint: model.safetensors (float16 weights for example)
     ↓
get_quantize_ops → YourQuantize.convert()
     ↓
Result: Quantized weights in memory
```

Метод `convert` принимает по одному тензору за раз, квантует его и может возвращать словарь с квантованными параметрами, например:

```py
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, действительно требуют сборки квантованного тензора из нескольких сохранённых компонентов.

```text
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`:

```py
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}
```

