# 기여하기

Transformers는 QLoRA, GPTQ, LLM.int8, AWQ 등 다양한 양자화 방법을 지원합니다. 그러나 아직 통합되지 않은 양자화 접근법이 더 많이 있습니다. Transformers에서 이러한 양자화 방법을 추가하고 사용하는 것을 더 쉽게 하려면 [HfQuantizer](/docs/transformers/main/ko/main_classes/quantization#transformers.quantizers.HfQuantizer) 클래스를 사용하세요. [HfQuantizer](/docs/transformers/main/ko/main_classes/quantization#transformers.quantizers.HfQuantizer)는 모든 PyTorch 모듈에 적용되는 것이 아니라 양자화 방법을 추가하기 위한 내부 헬퍼 클래스로 설계되었습니다.

이 가이드에서는 [HfQuantizer](/docs/transformers/main/ko/main_classes/quantization#transformers.quantizers.HfQuantizer)에 새로운 양자화 방법을 통합하는 방법을 보여 드립니다.

## 요구 사항

Transformers에 새로운 양자화 방법을 통합하기 전에, 해당 방법이 다음 요구 사항을 충족하는지 확인하세요. PyTorch 모듈로 실행할 수 있는 양자화 방법만 지원됩니다.

- 양자화 방법은 pip으로 설치할 수 있는 Python 패키지를 통해 사용할 수 있습니다(소스에서 패키지만 설치할 수 있는 경우에도 문제없습니다). 이상적으로는 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)
  ```

  이 방식은 [nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html) 인스턴스를 대상 클래스로 교체하여 Transformers 모델을 쉽게 양자화할 수 있게 합니다.

- 양자화 방법은 직렬화 가능해야 합니다. 양자화된 가중치를 로컬에 저장하거나 Hub에 push할 수 있습니다.
- 양자화 커널/프라이미티브를 포함하는 패키지가 안정적(빈번한 깨지는 변경 없음)인지 확인하세요.

일부 양자화 방법은 데이터 보정을 통해 모델을 "사전 양자화"해야 할 수 있습니다(AWQ). 이 경우, Transformers에서는 추론만 지원하고, 모델 양자화 자체는 ML 커뮤니티에서 유지 관리하는 서드파티 라이브러리에 맡기는 것을 권장합니다.

## 새로운 HFQuantizer 클래스 생성

0. 가장 좋은 시작점은 Finegrained Fp8과 같은 다른 양자화 방법을 살펴보는 것입니다. 총 3개의 파일을 업데이트하거나 새로 만들어야 합니다: [config 파일](https://github.com/huggingface/transformers/blob/main/src/transformers/utils/quantization_config.py), [integration 파일](https://github.com/huggingface/transformers/blob/main/src/transformers/integrations/finegrained_fp8.py), [quantizer 파일](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) 안에 새로운 양자화 설정 클래스를 생성합니다. Transformers의 [src/transformers/\_\_init\_\_.py](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/__init__.py) 파일 내부의 [\_import_structure](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/__init__.py#L1088)에 새로운 양자화 설정을 추가합니다.

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. quantization method에 대해 다음 클래스 속성과 프로퍼티 메서드를 정의하세요:

   - `requires_calibration`: 양자화 방식에 데이터 보정 과정이 필요한지 여부. `True`로 설정하면 양자화된 가중치를 사용한 추론만 지원되며, 추론과 양자화를 동시에 수행할 수 없습니다.
   - `is_serializable`: 해당 메서드가 직렬화 가능한지 여부를 결정하는 프로퍼티 메서드입니다.
   - `is_trainable`: 양자화 방식(PEFT 방식 사용 여부와 관계없이) 위에 모델을 fine-tune할 수 있는지 여부를 결정하는 property method입니다.

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에서는 각 텐서를 물리적으로 생성한 후 일련의 다양한 연산을 적용합니다. 이 경우 양자화 연산은 마지막에 수행됩니다. `ConversionOps`의 하위 클래스인 `XXXQuantize`를 생성하고 `convert` 메서드를 추가해야 합니다. `convert` 메서드에서는 가중치를 양자화하고 양자화된 매개변수의 딕셔너리를 반환해야 합니다.

7. 양자화가 사전 양자화 가중치 로딩을 지원하는 경우, 양자화기 클래스에 `get_weight_conversions` 메서드를 추가합니다. transformers에서는 여러 텐서를 수집하고 그 위에 연산을 적용할 수 있습니다. 이는 체크포인트에 있는 텐서를 다시 그룹화하여 양자화 텐서를 재구성해야 하는 경우에 특히 유용합니다.

8. 필요한 경우 `_process_model_after_weight_loading` 메서드를 작성하세요. 이 메서드는 가중치를 로드한 후 모델을 조작해야 하는 추가 기능을 구현할 수 있게 합니다.

9. 모든 것을 문서화하세요! `docs/source/en/quantization` 아래에 새 파일을 추가하여 양자화 방법이 문서화되었는지 확인하세요.

10. `docker/transformers-quantization-latest-gpu` 안의 nightly Dockerfile에 패키지를 추가하고, `tests/quantization/xxx`에 새로운 테스트 파일을 추가하여 테스트를 추가해야 합니다. 구현 방식을 확인하려면 기존 양자화 메서드를 참고해 보세요.

## 파일 개요

| 파일                                         | 목적                                                                                          |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `utils/quantization_config.py`               | `QuantizationConfigMixin`을 상속받는 `YourMethodConfig`를 정의합니다                              |
| `quantizers/quantizer_your_method.py`        | `HfQuantizer`를 상속받아 `YourMethodHfQuantizer`를 구현합니다                                  |
| `integrations/your_method.py`                | `ConversionOps` 하위 클래스 및 헬퍼 함수 구현                                        |
| `quantizers/auto.py`                         | `AUTO_QUANTIZER_MAPPING`과 `AUTO_QUANTIZATION_CONFIG_MAPPING`에 quantizer와 config를 등록합니다 |
| `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` 메서드는 한 번에 하나의 텐서를 받아서 양자화하고, 양자화된 매개변수의 사전(dict)을 반환할 수 있습니다. 예를 들어:

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

