Transformers documentation
エクスポートの量子化
エクスポートの量子化
すべての export 設定は quantizer を受け取ることができ、エクスポート中にモデルを量子化します。量子化器は transformers.exporters.quantizers に配置されており、それぞれがエクスポートの固定ポイントで実行されます。具体的には、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 モデル | 🔴 | 🔴 | 🔴 | 🟢 |
エクスポートでは、トレースの実行前に、クオンタイザーがバックエンドおよびインストール済みパッケージと整合しているかをチェックするため、不一致の場合に早期に失敗します。 エクスポートと同様に、各クオンタイザーはテスト済みのパッケージバージョンを列挙しており、ユーザーの環境が異なる場合は警告を表示します。
以下の例では、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 クアンタイザーをラップし、FX グラフ上で実行します(prepare_pt2e → カリブレーション → convert_pt2e)。そのため、すべてのバックエンドで動作します。各 PT2E クアンタイザーは独自のクアンタイズ/デクアンタイズ演算を挿入するため、お使いのランタイムがサポートしているものを選択してください。
| 実行場所 | PT2E量子化器 | インポート元 |
|---|---|---|
| PyTorch inductor、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 の引数を持ち、各コンポーネントは、そのコンポーネント用にキャプチャされた入力でキャリブレーションを行います。テキスト専用のモデルには 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 の重みは quantize/dequantize のペアの背後でフル精度のまま保持され、OpenVINO がこれを FakeQuantize に変換し、独自に int8 に圧縮します。
ONNX Runtime
OnnxRuntimeQuantizer は、デフォルトで quantize_static を実行します(int8 QDQ アクティベーションと重み、calibration_dataset によるキャリブレーション)。上記の例と同様に、OnnxRuntimeStaticConfig の引数を使用します。OnnxRuntimeDynamicConfig を使用すると、quantize_dynamic が実行されます(int8 重み、アクティベーションはランタイムで量子化、キャリブレーションなし)。
from transformers.exporters import OnnxRuntimeDynamicConfig, OnnxRuntimeStaticConfig
config = OnnxConfig(
quantizer=OnnxRuntimeQuantizer(OnnxRuntimeStaticConfig(per_channel=True), calibration_dataset=calibration_dataset)
)
config = OnnxConfig(quantizer=OnnxRuntimeQuantizer(OnnxRuntimeDynamicConfig()))NNCF
上記の表にある3つの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 以前)では飽和が発生するため、これらの環境では精度が低下する可能性があります。Weight-only compression は matmul を浮動小数点のまま維持します。
キャリブレーション
量子化器のcalibration_datasetは、モデルの入力(トークナイザーの出力のリストや、バッチがモデルの入力にコラートされるDataLoaderなど)のイテラブルです。これは通常の属性であるため、量子化器の作成後に設定することもできます(quantizer.calibration_dataset = samples)。省略した場合、データが必要とされる量子化器は、警告を出力しながら、エクスポート自身のサンプル入力でキャリブレーションを行います。これは、1つのサンプルだけで観測される範囲が偏る可能性があるためです。データが不要な量子化器(動的 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 でクオンタイザーが設定されていないコンポーネントは、フル精度のまま保持されます。この dict において、クオンタイザーの calibration_dataset には、generate の kwargs ではなく、コンポーネントの forward の kwargs が格納されます。
量子化器を追加
Quantizer は transformers.exporters.quantizers に存在し、ツールチェーンと表現ごとに1クラスずつ用意されています。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 はエクスポート自身の入力です。CalibrationSet でキャリブレーションを実行します。これは、クオンタイザーの calibration_dataset(存在しない場合は sample_inputs)を読み取り、transformers.exporters.quantizers.utils のフィード(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))