Transformers documentation
Quantizing exports
Quantizing exports
모든 export 설정은 quantizer를 받을 수 있으며, 이는 export 과정에서 모델을 양자화합니다. 양자화기는 transformers.exporters.quantizers에 위치하며, 각각 export의 고정된 시점에 실행됩니다. torch.export FX 그래프에서, 어떤 변환도 수행하기 전에, 또는 백엔드가 변환한 모델에서 실행됩니다.
| Quantizer | 양자화 | Dynamo | ONNX | ExecuTorch | OpenVINO |
|---|---|---|---|---|---|
| PT2EQuantizer | PyTorch 2 Export(PT2E) 양자화를 사용한 FX 그래프 | 🟢 | 🟢 | 🟢 | 🟢 |
| NNCFTorchFXQuantizer | NNCF를 사용한 FX 그래프 | 🟢 | 🟢 | 🟢 (가중치 전용) | 🟢 |
| OnnxRuntimeQuantizer | onnxruntime.quantization를 사용한 ONNX 모델 | 🔴 | 🟢 | 🔴 | 🔴 |
| NNCFOnnxQuantizer | NNCF가 적용된 ONNX 모델 | 🔴 | 🟢 | 🔴 | 🔴 |
| NNCFOpenVINOQuantizer | NNCF가 적용된 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())]) # POSITIVEPT2E
PT2EQuantizer는 torchao 또는 대상 런타임에서 가져온 PT2E quantizer를 래핑하여 FX 그래프(prepare_pt2e → calibrate → convert_pt2e)에서 실행하므로 모든 백엔드와 호환됩니다. 각 PT2E quantizer는 자체적인 quantize/dequantize 연산을 주입하므로, 해당 런타임이 지원하는 quantizer를 선택하세요:
| 실행 위치 | PT2E 양자화기 | 다음에서 가져오기 |
|---|---|---|
| PyTorch 인덕터, ONNX Runtime (QDQ), OpenVINO | X86InductorQuantizer | torchao.quantization.pt2e.quantizer.x86_inductor_quantizer |
| ExecuTorch XNNPACK 백엔드 | XNNPACKQuantizer | executorch.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.exportFX 그래프에 대해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))