Transformers documentation

Quantizing exports

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
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))
onnx_program = OnnxExporter().export(model, inputs, config)

logits = onnx_program(**inputs)[0]
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))
components = DynamoExporter().export_for_generation(model, inputs, config)  # {"prefill": ..., "decode": ...}

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))

ov_model = 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)),
}
components = 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)를 읽고, transformers.exporters.quantizers.utils의 피드(feed)를 사용하여 각 샘플을 모델 자체의 입력으로 매핑합니다(FX 그래프의 경우 traced_inputs, ONNX의 경우 onnx_feed, OpenVINO의 경우 openvino_feed).

from transformers.exporters import ExportFormat
from transformers.exporters.quantizers import CalibrationSet, ExportQuantizer
from transformers.exporters.quantizers.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