Transformers documentation

Quantizing exports

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Quantizing exports

모든 export 설정은 quantizer를 받을 수 있으며, 이는 export 과정에서 모델을 양자화합니다. 양자화기는 transformers.exporters.quantizers에 위치하며, 각각 export의 고정된 시점에 실행됩니다. torch.export FX 그래프에서, 어떤 변환도 수행하기 전에, 또는 백엔드가 변환한 모델에서 실행됩니다.

Quantizer양자화DynamoONNXExecuTorchOpenVINO
PT2EQuantizerPyTorch 2 Export(PT2E) 양자화를 사용한 FX 그래프🟢🟢🟢🟢
NNCFTorchFXQuantizerNNCF를 사용한 FX 그래프🟢🟢🟢 (가중치 전용)🟢
OnnxRuntimeQuantizeronnxruntime.quantization를 사용한 ONNX 모델🔴🟢🔴🔴
NNCFOnnxQuantizerNNCF가 적용된 ONNX 모델🔴🟢🔴🔴
NNCFOpenVINOQuantizerNNCF가 적용된 OpenVINO 모델🔴🔴🔴🟢

export는 tracing 전에 quantizer를 해당 backend 및 설치된 패키지와 대조하여 확인하므로, 불일치가 발생하면 초기 단계에서 실패합니다. exporter와 마찬가지로 각 quantizer는 테스트에 사용된 패키지 버전을 나열하며, 사용 중인 버전이 다르면 경고를 표시합니다.

아래 예시에서는 ONNX Runtime을 사용하여 감정 분류기를 int8로 양자화한 후 실행합니다.

from transformers import AutoModelForSequenceClassification, AutoTokenizer
from transformers.exporters import OnnxConfig, OnnxExporter, OnnxRuntimeQuantizer

model_id = "distilbert/distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)

texts = [
    "A wonderful, heartfelt film.",
    "The plot was dull and the acting worse.",
    "I would watch it again tomorrow.",
    "Two hours I will never get back.",
]
calibration_dataset = [tokenizer(text, return_tensors="pt") for text in texts]
inputs = tokenizer("An absolute delight from start to finish.", return_tensors="pt")

config = OnnxConfig(dynamic=True, quantizer=OnnxRuntimeQuantizer(calibration_dataset=calibration_dataset))
exported = OnnxExporter().export(model, inputs, config)

logits = exported(**inputs).logits
print(model.config.id2label[int(logits.argmax())])  # POSITIVE

PT2E

PT2EQuantizer는 torchao 또는 대상 런타임에서 가져온 PT2E quantizer를 래핑하여 FX 그래프(prepare_pt2e → calibrate → convert_pt2e)에서 실행하므로 모든 백엔드와 호환됩니다. 각 PT2E quantizer는 자체적인 quantize/dequantize 연산을 주입하므로, 해당 런타임이 지원하는 quantizer를 선택하세요:

실행 위치PT2E 양자화기다음에서 가져오기
PyTorch 인덕터, ONNX Runtime (QDQ), OpenVINOX86InductorQuantizertorchao.quantization.pt2e.quantizer.x86_inductor_quantizer
ExecuTorch XNNPACK 백엔드XNNPACKQuantizerexecutorch.backends.xnnpack.quantizer.xnnpack_quantizer

ExecuTorch의 경우, ExecuTorch 자체의 XNNPACKQuantizer를 래핑합니다:

from executorch.backends.xnnpack.quantizer.xnnpack_quantizer import (
    XNNPACKQuantizer,
    get_symmetric_quantization_config,
)
from transformers.exporters import ExecutorchConfig, PT2EQuantizer

xnnpack = XNNPACKQuantizer().set_global(get_symmetric_quantization_config())
config = ExecutorchConfig(backend="xnnpack", quantizer=PT2EQuantizer(xnnpack, calibration_dataset=calibration_dataset))

~HfExporter.export_for_generation를 통해 생성형 모델을 양자화합니다. 여기서 calibration_dataset에는 inputs와 같은 generate kwargs가 포함되며, 각 구성 요소는 해당 구성 요소에 대해 캡처된 입력으로 보정됩니다. 텍스트 전용 모델에는 prefill 및 decode 구성 요소가 있고, 멀티모달 모델은 prefill을 모달리티 인코더(image_encoder 등), 프로젝터, language_model, lm_head로 더 세분화합니다(export_for_generation 작동 방식 참조).

from torchao.quantization.pt2e.quantizer.x86_inductor_quantizer import (
    X86InductorQuantizer,
    get_default_x86_inductor_quantization_config,
)
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.exporters import DynamoConfig, DynamoExporter, PT2EQuantizer

model_id = "meta-llama/Llama-3.2-1B"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id)

prompts = ["The capital of France is", "Photosynthesis turns sunlight into", "The first moon landing was in"]
calibration_dataset = [tokenizer(prompt, return_tensors="pt") for prompt in prompts]
inputs = tokenizer("Once upon a time", return_tensors="pt")

x86 = X86InductorQuantizer().set_global(get_default_x86_inductor_quantization_config())
config = DynamoConfig(dynamic=True, quantizer=PT2EQuantizer(x86, calibration_dataset=calibration_dataset))
exported = DynamoExporter().export_for_generation(model, inputs, config)  # "prefill" and "decode" components

OpenVINO에서는 PT2E 가중치가 양자화/역양자화 쌍 뒤에 전체 정밀도로 유지되며, OpenVINO는 이를 FakeQuantize로 변환하고 자체적으로 int8로 압축합니다.

ONNX Runtime

OnnxRuntimeQuantizer는 기본적으로 quantize_static를 실행합니다(int8 QDQ activations 및 weights, calibration_dataset로 보정). 이는 위 예시와 같으며 OnnxRuntimeStaticConfig의 인수를 사용합니다. OnnxRuntimeDynamicConfig를 사용하면 quantize_dynamic를 실행합니다(int8 weights, 런타임에 양자화되는 activations, 보정 없음).

from transformers.exporters import OnnxRuntimeDynamicConfig, OnnxRuntimeStaticConfig

config = OnnxConfig(
    quantizer=OnnxRuntimeQuantizer(OnnxRuntimeStaticConfig(per_channel=True), calibration_dataset=calibration_dataset)
)
config = OnnxConfig(quantizer=OnnxRuntimeQuantizer(OnnxRuntimeDynamicConfig()))

NNCF

위 표의 세 가지 NNCF 양자화기는 기본적으로 nncf.quantize를 실행합니다(int8 활성화 및 가중치, calibration_dataset로 보정, model_type=nncf.ModelType.TRANSFORMER를 전달하지 않는 한). 인수는 NNCFQuantizationConfig의 것입니다. NNCFWeightCompressionConfig와 함께 사용하면 nncf.compress_weights를 실행합니다(int8 또는 int4의 가중치 전용, int4의 경우 AWQ 및 스케일 추정, OpenVINO의 경우 GPTQ 및 LoRA 보정). ExecuTorch는 NNCFTorchFXQuantizer의 가중치 전용 결과만 낮춥니다.

import nncf
from transformers.exporters import (
    DynamoConfig,
    NNCFOnnxQuantizer,
    NNCFOpenVINOQuantizer,
    NNCFTorchFXQuantizer,
    NNCFWeightCompressionConfig,
    OnnxConfig,
    OpenVINOConfig,
    OpenVINOExporter,
)

int4 = NNCFWeightCompressionConfig(mode=nncf.CompressWeightsMode.INT4_SYM, group_size=128)

# OpenVINO: int8 activations and weights, or weight-only int4
openvino_int8 = OpenVINOConfig(quantizer=NNCFOpenVINOQuantizer(calibration_dataset=calibration_dataset))
openvino_int4 = OpenVINOConfig(quantizer=NNCFOpenVINOQuantizer(int4))
# ONNX: weight-only int4, as ONNX Runtime's `MatMulNBits`
onnx_int4 = OnnxConfig(quantizer=NNCFOnnxQuantizer(int4))
# Dynamo: int8 on the FX graph
dynamo_int8 = DynamoConfig(quantizer=NNCFTorchFXQuantizer(calibration_dataset=calibration_dataset))

exported = OpenVINOExporter().export(model, inputs, openvino_int4)

Int8 activations는 int8 matmul을 수행하며, VNNI가 없는 CPU(AVX2 전용 Intel 및 AMD Zen 2 이전)에서는 포화(saturation)가 발생하므로 해당 환경에서는 정확도가 낮아질 수 있습니다. Weight-only compression은 matmul을 부동소수점으로 유지합니다.

교정

Quantizer의 calibration_dataset은 모델 입력의 반복 가능한 객체(iterable)로, 토크나이저 출력 목록이나 모델 입력으로 collate되는 배치의 DataLoader 등이 해당됩니다. 이는 일반 속성이므로, quantizer 생성 후에도 설정할 수 있습니다(quantizer.calibration_dataset = samples). 이 값을 생략하면, 데이터가 필요한 quantizer는 경고와 함께 export 자체의 샘플 입력으로 보정(calibration)을 수행합니다. 이는 샘플 하나만으로는 관찰된 범위가 왜곡될 수 있기 때문입니다. 데이터가 필요하지 않은 quantizer(동적 ONNX Runtime 양자화, int8 가중치 압축)는 이 값을 읽지 않습니다.

컴포넌트별 다른 레시피

{component: config} 딕셔너리를 ~HfExporter.export_for_generation에 전달하여 각 구성 요소를 개별적으로 양자화할 수 있습니다. 위의 모델의 경우, prefill에는 정적 int8을, decode에는 더 가벼운 동적 int8을 사용합니다:

dynamic_x86 = X86InductorQuantizer().set_global(get_default_x86_inductor_quantization_config(is_dynamic=True))

config = {
    "prefill": DynamoConfig(dynamic=True, quantizer=PT2EQuantizer(x86)),
    "decode": DynamoConfig(dynamic=True, quantizer=PT2EQuantizer(dynamic_x86)),
}
exported = DynamoExporter().export_for_generation(model, inputs, config)

dict에는 decompose_for_generation()이 생성하는 모든 컴포넌트를 명시해야 합니다(멀티모달 모델은 인코더를 추가하고, language_model과 lm_head를 분리할 수 있습니다). config에 quantizer가 설정되지 않은 컴포넌트는 전체 정밀도로 유지됩니다. 이 dict에서 quantizer의 calibration_dataset에는 generate kwargs가 아닌 해당 컴포넌트의 forward kwargs가 포함됩니다.

양자화기 추가

Quantizer는 transformers.exporters.quantizers에 있으며, 도구 체인 및 표현 방식별로 하나의 클래스씩 정의되어 있습니다. ExportQuantizer를 서브클래스로 선언하고 다음을 명시하세요:

  • quantizes_fx_graph: torch.export FX 그래프에 대해 True (변환 전, 즉 모든 백엔드가 결과를 받음) 또는 백엔드가 변환하는 모델(onnx_ir.Model, openvino.Model)에 대해 False(기본값).
  • supported_formats: 양자화할 내보내기 형식입니다. 트레이싱 전에 다른 형식은 모두 거부됩니다.
  • required_packages, 그리고 선택적으로 min_versions와 tested_versions: 이 모듈이 가져오는 패키지입니다. 내보내기 시 트레이싱 전에 설치 여부를 확인하고, 최소 버전 미만이면 예외를 발생시키며, 테스트된 버전과 다르면 경고를 표시합니다.

이어서 quantize(model, sample_inputs, export_format)를 구현합니다. model은 FX 그래프 또는 백엔드 모델이고, sample_inputs는 export 자체의 입력입니다. CalibrationSet로 보정(calibrate)합니다. 이는 quantizer의 calibration_dataset(없을 경우 sample_inputs)를 읽고, feed를 통해 각 샘플을 모델 자체의 입력으로 매핑합니다. FX 그래프의 경우 transformers.exporters.quantizers.utils에서 traced_inputs를, ONNX 또는 OpenVINO의 경우 transformers.exporters.utils에서 onnx_feed 또는 openvino_feed를 사용합니다.

from transformers.exporters import ExportFormat
from transformers.exporters.quantizers import CalibrationSet, ExportQuantizer
from transformers.exporters.utils import onnx_feed


class MyOnnxQuantizer(ExportQuantizer):
    supported_formats = (ExportFormat.ONNX,)
    required_packages = ("my_toolkit",)
    tested_versions = {"my_toolkit": "1.0.0"}

    def quantize(self, model, sample_inputs, export_format):
        import my_toolkit

        names = [graph_input.name for graph_input in model.graph.inputs]
        calibration = CalibrationSet(self.calibration_dataset, sample_inputs, lambda sample: onnx_feed(names, sample))
        return my_toolkit.quantize(model, calibration_feeds=list(calibration))
Update on GitHub