量子化とは?LLMを軽くする省メモリ技術

FP16のLLM weightをINT8とINT4へ圧縮する量子化のアイキャッチ

量子化(Quantization、連続的な数値を少ない表現段階へ写す処理)は、LLMのweightやactivationを低bitで表し、保存容量とメモリ転送量を減らす技術です。FP16のweightをINT8へ変えれば生のweight payloadは概ね半分、INT4なら概ね4分の1になります。

ただし、モデル全体のVRAMが同じ比率で減るとは限りません。scaleなどのmetadata、量子化しない層、KV Cache、一時bufferが残るためです。速度もhardwareと推論runtimeが対象formatのkernelを持つ場合に限って改善しやすくなります。

FP16のLLM weightをINT8とINT4へ圧縮する量子化のアイキャッチ

3文要約

  1. 量子化ではweightやactivationの実数値をscaleとzero pointで有限個の値へ写し、丸めとclippingによる誤差と引き換えに容量・転送量を減らします。
  2. INT8とINT4の違いはbit数だけではなく、weight-onlyかweight-activationか、scaleの粒度、calibration、packing、計算dtypeまで確認する必要があります。
  3. 採用判断ではmodel fileの大きさだけでなく、実測VRAM、prefill・decode速度、対象taskの品質を同じ条件で比較します。

代表的な論文

LLM量子化には一つの標準手法があるわけではありません。代表例は、それぞれ異なる問題を扱います。

手法 主な対象 bit構成の代表例 中心となる考え方
LLM.int8() weightとactivation 8-bit中心のmixed precision activationの外れ値次元を高精度計算へ分離
SmoothQuant weightとactivation W8A8 activationの量子化困難性をweight側へ移す
GPTQ weight W4A16など 近似的な2次情報でweight量子化誤差を補償
AWQ weight W4A16など activation統計から重要なweight channelを保護

個人ブログのシリーズ計画ではQLoRA論文を量子化の代表資料として挙げています。QLoRAのNF4は重要な4-bit表現ですが、主題は凍結した量子化base modelを通してLoRAを学習することです。本記事では推論時の量子化全体を扱い、QLoRAによる追加学習はQLoRAとは?量子化で省メモリFine-tuningする方法へ分けます。

量子化する対象を先に分ける

LLM推論でメモリを使う数値はweightだけではありません。

対象 役割 生存期間 量子化したときの主効果
Weight 学習済みparameter modelを載せている間 model fileとweight読出し量を減らす
Activation 層の入出力・中間値 forward中 演算入力と中間転送を減らす
KV Cache 過去tokenのK/V requestの生成中 長いcontextや多数requestのcache量を減らす
Optimizer state 学習時の更新状態 training中 推論には通常存在しない

「4-bit model」と書かれていても、すべての値が4-bitとは限りません。weightだけ4-bitで、activationと計算はFP16/BF16という構成も一般的です。LayerNormや出力headなど、一部moduleを高精度のまま残す実装もあります。

KV Cacheの量は系列長とrequest数で増えます。weight量子化後にKV Cacheが支配的になることもあるため、KV Cacheとは?LLMの生成を高速化する仕組みと分けて見積もります。

LLM推論のweight、activation、KV Cacheを別の量子化対象として分けた図

実数を整数へ写す基本式

affine quantization(scaleとzero pointを使う線形な量子化)では、実数 \(x\) を整数 \(q\) へ次のように写します。

\[q=\mathrm{clip}\left(\mathrm{round}\left(\frac{x}{S}\right)+Z, q_{min},q_{max}\right)\]
  • \(S\): scale。整数1段階が実数のどれだけに相当するか
  • \(Z\): zero point。実数0に対応する整数値
  • \(q_{min},q_{max}\): 量子化後の表現範囲

利用時には、整数から近似的な実数へ戻します。

\[\hat{x}=S(q-Z)\]

\(\hat{x}\) は元の \(x\) と完全には一致しません。有限個の格子点へ丸めるquantization error(量子化誤差)と、範囲外を端へ寄せるclipping errorが生じます。

具体値を代入する

値域 \([-1.0,1.0]\) をsigned INT4のうち対称な \([-7,7]\) へ写す例を考えます。zero pointは0、scaleは次です。

\[S=\frac{1.0}{7}\approx0.142857\]

\(x=0.68\)を量子化すると、

\[q=\mathrm{round}\left(\frac{0.68}{1/7}\right)=\mathrm{round}(4.76)=5\]

逆量子化値は、

\[\hat{x}=5\times\frac{1}{7}\approx0.714\]

元の値との差は約0.034です。同じscaleを使う値が外れ値を含み、たとえば最大絶対値が7へ広がれば、scaleは1になります。すると0.2、0.4、0.68のような値を細かく区別できず、量子化後の解像度が粗くなります。これがscaleを決める範囲と粒度が重要な理由です。

実数0.68をscale 1/7でINT4値5へ量子化し0.714へ逆量子化する計算

Hugging Face TransformersのQuantization conceptsでは、affine/symmetric quantization、scale、zero point、粒度、PTQ/QATが整理されています。

symmetricとasymmetricの違い

比較軸 Symmetric Asymmetric
実数範囲 0を中心に対称な範囲として扱う 観測した最小・最大へ合わせる
zero point 通常0へ固定 計算して保持
長所 表現と演算を単純化しやすい 0を中心としない分布の範囲を使いやすい
弱点 非対称な分布では段階を無駄にする metadataと補正処理が増える

どちらが常に高精度という関係ではありません。weightやactivationの分布、kernelが対応するscheme、量子化単位から決めます。

scaleを共有する粒度

同じINT4でも、どの範囲でscaleを共有するかにより誤差と容量が変わります。

粒度 scaleを共有する範囲 長所 代償
Per-tensor tensor全体 metadataが少なく単純 外れ値の影響が全体へ広がる
Per-channel 出力channelなど channelごとの値域へ合わせやすい scale数と処理が増える
Group-wise 連続するweightのgroup 誤差とmetadataの折衷 group sizeとpackingがformat依存

group sizeを小さくすると局所的な分布へ合わせやすい一方、scaleやzero pointの数は増えます。そのため「4-bitだから0.5 byte/parameter」と計算した値は、生weight payloadの下限に近い概算です。

per-tensor、per-channel、group-wiseでscale共有範囲が異なる比較

INT8とINT4を比較する

signed integerの代表的な表現段階は、INT8が256、INT4が16です。INT4は多くの環境で2値を1 byteへpackして保存します。

比較軸 FP16/BF16 INT8 INT4
1要素の生bit数 16 8 4
生weight payload 基準 約1/2 約1/4
表現段階 浮動小数点形式に依存 256 16
量子化誤差 量子化なし 比較的小さい傾向 大きくなりやすい
追加処理 不要 scale適用など packing/unpackingとscale適用など
実測速度 基準 対応kernel次第 対応kernelとmemory-bound度次第

FP16とBF16も同じ16-bitですが、指数部と仮数部の割り当てが異なります。INT8・INT4は整数の固定格子へ写す方式であり、「bit数が同じなら数値特性も同じ」とは限りません。

W4A16とW8A8は何を表すのか

量子化構成は、W(weight)とA(activation)を分けると読みやすくなります。

表記 Weight Activation 典型的な狙い
W8A16 8-bit 16-bit weight容量と転送量を抑える
W4A16 4-bit 16-bit weight-onlyでさらに圧縮する
W8A8 8-bit 8-bit weightとactivationの転送・演算を低精度化する

表記だけでは不十分です。たとえばW4A16でも、weightを4-bitで保存してkernel内で16-bitへdequantizeするのか、専用の低bit matrix kernelを使うのかで速度は変わります。積のaccumulationをINT32やFP32で行う場合もあり、保存dtype、入力dtype、計算dtype、accumulation dtypeを分けて確認します。

PTQとQATの違い

比較軸 PTQ QAT
実施時期 学習済みmodelへ適用 学習中に量子化誤差を模擬
parameter更新 通常は追加trainingなし 量子化を考慮して更新
必要データ calibration dataを使う場合がある training dataと学習環境
導入cost 小さい 大きい
低bitでの適応 限定的 modelが誤差へ適応できる

PTQ(Post-Training Quantization、学習後量子化)は、既存modelを短時間で圧縮したい場合に向きます。calibration(代表入力で値の分布や重要度を測る処理)を必要とする方式では、dataの選び方が結果へ影響します。

QAT(Quantization-Aware Training、量子化を考慮した学習)は、forward中に丸めやclippingを模擬し、modelを誤差へ適応させます。精度を保ちやすい可能性がある一方、学習cost、data、実装検証が必要です。

代表的なLLM量子化手法

LLM.int8()は外れ値次元を分離する

LLM.int8()論文は、大規模Transformerのactivationに現れる外れ値featureを扱います。大部分の値を8-bit matrix multiplicationへ送り、外れ値次元を16-bitで計算するmixed-precision decompositionを採用しました。

単純に全要素を同じINT8 scaleへ押し込むのではなく、量子化しにくい部分だけ高精度へ逃がす設計です。したがって「LLM.int8()では全演算が純粋なINT8になる」と理解するとずれます。

SmoothQuantはactivationの難しさをweight側へ移す

SmoothQuant論文は、activation outlierのためにW8A8が難しい問題へ取り組みます。channelごとの等価なscale変換により、activationの大きさを抑える代わりにweight側を調整し、量子化の難しさを移します。

これはactivationを削除する方法ではありません。weightとactivationの両方をINT8化しやすい分布へ変換するPTQです。論文の速度・精度結果は対象modelとhardware、実装条件に基づくため、任意の環境へ固定倍率として当てはめません。

GPTQは量子化誤差を後続weightで補償する

GPTQ論文は、approximate second-order information(入力に対する誤差感度を近似する2次情報)を使うone-shot weight quantizationです。weightを順に量子化し、そこで生じた誤差をまだ量子化していないweightへ反映します。

単純なround-to-nearestより出力誤差を抑えることを狙い、3-bit・4-bitのweight-only量子化を大規模modelへ適用しました。実行にはcalibration input、group size、dampeningなどの設定が関わります。

AWQはactivationから重要なweightを見つける

AWQ論文は、すべてのweightが同じ重要度ではない点に着目します。activation統計を使ってsalient weight channelを特定し、等価なscalingにより重要channelの量子化誤差を抑えます。

Activation-awareという名前ですが、AWQはlow-bit weight-only quantizationです。activationをINT4で保存する方式という意味ではありません。

4方式を同じ軸で比べる

手法 Weight Activation calibration 主に扱う問題
LLM.int8() INT8中心 INT8+外れ値を高精度 実行時の外れ値処理 大規模LLMのactivation outlier
SmoothQuant INT8 INT8 activation統計 W8A8でのactivation量子化
GPTQ 低bit 高精度 layer input weight量子化の出力誤差
AWQ 低bit 高精度 activation統計 salient weight channelの保護

この表はalgorithmの役割を比較したものです。実際に利用できるformat、GPU、CPU、kernel、model architectureは各runtimeの対応表で確認します。

LLM.int8、SmoothQuant、GPTQ、AWQを量子化対象と誤差対策で整理した図

7B modelのweight容量を計算する

parameter数を \(N\)、weightの平均bit数を \(b\) とすると、生のweight payloadは次の概算です。

\[M_{weight}=N\times\frac{b}{8}\ \mathrm{bytes}\]

7B parameterを単純計算すると次の値になります。

形式 計算 生weight payload
FP16/BF16 \(7\times10^9\times2\) bytes 約14 GB
INT8 \(7\times10^9\times1\) byte 約7 GB
INT4 \(7\times10^9\times0.5\) byte 約3.5 GB

これは全parameterを一度ずつ数えた10進GBの理論値です。実際のmodel fileにはscale、zero point、header、alignmentが入り、embeddingやnormalizationなど一部tensorを16-bitや32-bitで保持する場合もあります。

実行時VRAMはさらに次のように考えます。

\[M_{runtime}\approx M_{weight}+M_{KV\ Cache}+M_{activation}+M_{workspace}+M_{allocator}\]

そのため、14 GBのweightが3.5 GB相当になっても、総VRAMが14 GBから3.5 GBへ下がるとは限りません。contextを長くしたときにKV Cacheが増える点も別に見積もります。

7B modelの生weightがFP16で14 GB、INT8で7 GB、INT4で3.5 GBになる比較

量子化しても速くならない理由

weightを低bit化すると、GPU memoryやRAMから読むbyte数を減らせます。decodeのようにweight読出しがbottleneckになりやすい場面では、これは速度向上につながる可能性があります。

一方、次のcostが加わります。

  • packed INT4をunpackする
  • scaleとzero pointを読み、dequantizeする
  • 対応しない演算を高精度へfallbackする
  • 量子化できないmoduleとの間でdtypeを変換する
  • 小さいbatchではkernel launchや変換のoverheadが相対的に大きくなる
条件 速度が上がりやすい 上がりにくい・下がり得る
Hardware 対象bit幅の命令・Tensor Core等へ対応 native対応がない
Kernel format専用のfused kernelがある 汎用演算へ毎回展開する
Bottleneck memory bandwidthが支配的 別の演算やCPU転送が支配的
Workload 大きいmodelを繰り返し読む modelが小さくoverheadが目立つ
Format runtimeの期待するpackingと一致 変換やfallbackが必要

INT4自体は互換format名ではありません。NVIDIA TensorRTのQuantization Schemesでも、INT4の対象、block size、scale dtypeにはbackend固有の条件があります。使用するruntimeのversionとhardwareで実測します。

低bit化によるメモリ転送削減とdequantizeなどの追加costを比較した図

品質はperplexityだけで決めない

量子化誤差の影響は、model size、layer、task、prompt、生成設定で変わります。perplexity(次token予測の不確実さを表す指標)が元modelに近くても、利用taskで同じ出力品質になる保証はありません。

評価では少なくとも次を固定します。

  1. 同じbase model revisionとtokenizerを使う
  2. 同じprompt setとchat templateを使う
  3. temperature、top-p、seedなど生成条件を揃える
  4. perplexityやbenchmarkに加え、実際のtask成功率を見る
  5. code、math、長文、instruction followingなど重要なsliceを分ける
  6. 失敗例を保存し、丸め誤差以外の原因と切り分ける

PTQでcalibration dataを使う場合は、dataset名、件数、sequence length、前処理も記録します。評価dataと同じものを調整へ使えば、汎化性能を過大評価する可能性があります。

方式を選ぶ手順

1. 何が収まらないかを測る

model weight、KV Cache、activation、runtime予約領域を分けます。weightが支配的ならweight-only量子化が直接効きます。長いcontextのKV Cacheが支配的なら、weightをさらに下げるだけでは解決しません。

2. hardwareとruntimeの対応formatを絞る

同じ量子化modelでも、対応kernelがない環境では読み込めないか、高精度へ展開されます。GPU architecture、CPU instruction、runtime version、推奨packingを先に確認します。

3. INT8を基準にINT4を比較する

容量に余裕があり品質を優先するなら、まず8-bitまたは高精度をbaselineにします。4-bitが必要なら、GPTQやAWQなどmodelとruntimeが対応する方式を候補にし、group sizeとcalibration条件も固定します。

4. 容量・速度・品質を同時に記録する

記録する例
容量 model file、peak allocated VRAM、peak reserved VRAM、RAM
速度 prefill tokens/s、decode tokens/s、time to first token、inter-token latency
品質 task成功率、perplexity、長文・code・mathなどのslice

一つの軸だけで結論を出しません。たとえば容量が半分でもdecodeが遅くなるformatは、単一GPUへ載せる目的には有効でもlatency要件には合わない場合があります。

よくある誤解

INT4なら必ずFP16の4分の1か

生weight payloadは概ね4分の1ですが、scale、zero point、alignment、非量子化tensorが加わります。実行時にはKV Cacheやworkspaceも必要です。

AWQはactivationも量子化するのか

AWQはactivation統計を使って重要なweight channelを見つけるweight-only方式です。名称のActivation-awareは、activation自体を低bit化するという意味ではありません。

量子化modelを読み込めれば専用kernelで動いているか

読み込み成功だけでは判断できません。実際のmodule型、kernel、profiler、throughputを確認します。backendが一部演算を高精度へfallbackする場合があります。

INT4と4-bit NF4は同じか

同じではありません。INT4は整数格子、NF4はQLoRAで導入されたnormally distributed weight向けの4-bit data typeです。bit数が同じでも表現値と用途が異なります。

量子化はtrainingでもそのまま使えるか

推論専用の量子化weightを通常の方法で全parameter更新できるとは限りません。QLoRAは4-bit base weightを凍結し、別のLoRA parameterを学習します。推論量子化と学習方式を分けて確認します。

まとめ

LLMの量子化は、weightやactivationを少ないbit数で表し、容量とメモリ転送量を減らす技術です。scale、zero point、粒度、calibrationにより量子化誤差を制御し、GPTQやAWQなどは単純な丸めよりweight-only低bit化の誤差を抑えることを狙います。

重要なのは、bit幅だけで判断しないことです。何を量子化するか、計算時に何bitへ戻すか、対応kernelがあるか、どのtaskで品質を測るかまでが量子化構成です。model file、実測VRAM、prefill・decode速度、task品質を同じ条件で比較してください。

次回は、数値のbit幅ではなくGPUメモリ階層間の読み書きを減らしてAttentionを高速化するFlashAttentionを扱います。

関連記事

参考資料

コメント

タイトルとURLをコピーしました