Model Quantization
モデル量子化は、重みと活性化をINT8などの低精度で保存し、モデルを小型化して推論を高速化します。仕組みとPTQとQATの違いを解説します。
モデル量子化では、ニューラルネットワークの重みと、多くの場合は活性化を、32ビット浮動小数点数(FP32)から、16ビット浮動小数点数(FP16)、8ビット整数(INT8)、4ビット整数など、より低精度の形式に変換します。各値の保存や転送に必要なビット数が減り、ほとんどのプロセッサでは整数演算の高速化と省電力化が可能です。INT8モデルのサイズは元のFP32モデルの4分の1です。これにより、物体検出モデルをスマートフォン、カメラなどのエッジAIデバイスでリアルタイムに実行できるようになり、大規模言語モデル(LLM)を単一のGPUに収められます。
量子化の仕組み#
量子化では、実数の範囲を少数の整数レベルに対応付けます。一般的なアフィン方式では、各浮動小数点値xを整数値q = round(x / scale) + zero_pointとして保存し、x ≈ (q - zero_point) × scaleとして復元します。スケールはレベル間の刻み幅を定め、ゼロ点は浮動小数点値の0が正確な整数に対応するようにします。INT8には256段階があるため、範囲を慎重に選ぶ必要があります。範囲が広すぎると、実際には現れない値にレベルを割り当てて無駄になります。狭すぎると外れ値がクリップされます。重みは事前にわかっているため、その範囲は学習済みモデルから直接取得でき、多くの場合、出力チャネルごとに個別に計算されます。活性化の範囲は入力に依存するため、代表的な画像やテキストのサンプルを少量モデルに入力してキャリブレーションを行い、計測します。
import numpy as np
rng = np.random.default_rng(0)
weights = rng.normal(0, 0.05, size=1000).astype(np.float32)
# Affine INT8 quantization maps the observed float range onto 256 integer levels
scale = (weights.max() - weights.min()) / 255
zero_point = np.round(-128 - weights.min() / scale)
q = np.clip(np.round(weights / scale + zero_point), -128, 127).astype(np.int8)
# Dequantize to measure the rounding error the model has to tolerate
restored = (q.astype(np.float32) - zero_point) * scale
print(f"Size: {weights.nbytes} -> {q.nbytes} bytes")
print(f"Max error: {np.abs(weights - restored).max():.5f} (half a step: {scale / 2:.5f})")Size: 4000 -> 1000 bytesと、最大誤差0.00068が出力されます。この誤差は量子化ステップ1つ分の半分に相当します。重みのサイズは4分の1になり、元の値からの変化はどれもステップ幅の半分以内です。
学習後量子化と量子化対応トレーニング#
- 学習後量子化(PTQ): 学習済みモデルを変換し、少量のキャリブレーションセットを使って活性化の範囲を設定します。数分で完了し、これらのサンプル以外の学習データは必要ありません。動的量子化はPTQの一種であり、推論時に活性化の範囲を随時計算します。重みのみの量子化では、活性化を浮動小数点のままにします。
- 量子化対応トレーニング(QAT): 順伝播で値を丸めることにより、トレーニングまたはファインチューニング中に量子化をシミュレートし、重みが丸め誤差に耐えられるよう学習させます。追加のトレーニングが必要になりますが、PTQで失われた精度の大半を取り戻せます。
チームは通常、まずPTQを試し、精度の低下が大きすぎる場合にのみQATへ切り替えます。
LLMには独自の手法もあります。GPTQやAWQなどの重みのみの4ビット方式は、活性化を16ビットのままにして数十億のパラメーターを圧縮します。GGUF形式はローカル推論向けに量子化モデルを保存し、最近のGPUはFP8などの低精度浮動小数点形式でネイティブに計算します。
量子化と関連技術の比較#
- 混合精度: 学習を高速化する手法で、重みのマスターコピーはFP32で保持しながら、ほとんどの演算をFP16またはBF16で実行します。量子化は推論を対象とし、多くの場合はさらに低精度の整数まで使用します。
- モデルプルーニング: 重みやチャネル全体を削除して演算数を減らします。残った演算のコストを下げる量子化と組み合わせて使われることもよくあります。
- 知識蒸留: 大規模な教師モデルを模倣する小型の生徒モデルを学習します。デプロイ前に生徒モデルを量子化することがよくあります。
量子化の実践#
高速化の効果はハードウェアのサポート状況によって異なります。INT8モデルが高速に動作するのは、TensorRTを搭載したNVIDIA GPU、OpenVINOを搭載したIntel CPU、スマートフォン内のニューラルプロセッシングユニット(NPU)など、INT8カーネルを備えたプロセッサに限られます。Ultralytics YOLO26はエクスポート時にquantize=8を使ってPTQを適用し、dataデータセットの画像でキャリブレーションします。一方、model.train(quantize=8)はQATを使ってチェックポイントをファインチューニングします。次の例では、Androidや組み込みデバイス向けのGoogleのランタイムであるLiteRT用に、INT8モデルをエクスポートします。
from ultralytics import YOLO
# 事前学習済みYOLO26 nanoモデルを読み込みます
model = YOLO("yolo26n.pt")
# INT8の学習後量子化では、coco8.yamlの画像を使って活性化の範囲をキャリブレーションします
model.export(format="litert", quantize=8, data="coco8.yaml")COCOでTensorRTを使って計測すると、INT8 PTQによってYOLO26nのmAP50-95は0.403から0.393に低下します。一方、YOLO26sでは0.479から0.441へと低下幅が大きくなりますが、QATによって0.471まで回復します。










