- RTN / GPTQ / AWQ / LLM.int8() / NF4 / HQQ / AutoRound / SmoothQuant が、すべて「丸め誤差をどう埋め合わせるか」の別解であること
- キャリブレーションデータが必要な手法と不要な手法の境目、そして必要になる理由
group_size/act_order/ double quantization といったつまみが実際に何をしているか- 「難しさは消えず、移動するだけ」という、外れ値対策に共通する構図
- 自分の使い方(手元で対話するのか、GPU サーバで多並列に流すのか)から手法を絞る道筋
同じ 4bit でも、中身は同じではない
第 3 章で、量子化は「目盛り(scale)を決めて、そこへ丸める」作業だと分かりました。第 4 章では、重みの分布がゼロ中心の釣鐘型で、しかも一部の重みだけが出力に強く効くことを見ました。ここまでを踏まえると、「4bit にする」と言った時点でまだ決まっていないことが 2 つ残っています。
- 目盛りをどう並べるか — 等間隔にするのか、値が混んでいるところを細かくするのか
- 丸めた瞬間に出た誤差をどうするか — 放置するのか、どこかで埋め合わせるのか
本章に出てくる手法は、すべてこの 2 つへの答えの組み合わせです。だから「同じ 4bit」でも品質が変わります。どれが偉いという話ではなく、誤差の埋め合わせ方が違うだけです。
手法を大きく二分する境目がもう 1 つあります。キャリブレーション(= 数百件のテキストをモデルに流し、各層にどんな数値が流れ込むかを観測する工程。重みを学習し直すわけではない)を要求するかどうかです。Hugging Face の公式ドキュメントもこの軸で節を分けており、キャリブレーション不要な系統(bitsandbytes・HQQ・torchao など)を「オンザフライ量子化」としてまとめ、キャリブレーション型については「GPTQModel または AWQ」を「事前キャリブレーションありで良好な 4bit 精度」の用途に挙げています。
さらに 何を量子化するか の軸があります。ここでは第 3 章で定義した W<n>A<m> の表記(W が重み、A が活性化のビット数)をそのまま使います。本章で扱う手法のうち RTN・GPTQ・AWQ・NF4・HQQ・AutoRound はすべて weight-only、つまり W4A16 の側で、活性化は 16bit のまま流れます。活性化にも手を入れるのは LLM.int8() と SmoothQuant の 2 つだけです。
RTN — 素朴に丸めるだけ、そしてどこで折れるか
RTN(round-to-nearest、最近傍丸め)は一番素朴な方法です。ブロックごとに絶対最大値から scale を決め、各重みを最も近い格子点に丸める。それで終わりです。キャリブレーションは要らず、重みファイルだけあれば済みます。llama.cpp の基本形(Q4_0 系)や、bitsandbytes に load_in_4bit=True を渡したときのロード時変換はこの系統です。
GPTQ 論文は RTN の限界を「8bit のような緩い圧縮ではうまく働くが、圧縮率を上げると精度を保てない」と書いています。その実測が下の表です(OPT、WikiText2 perplexity。低いほど良い)。
| 手法 | bits | 1.3B | 6.7B | 13B | 30B | 175B |
|---|---|---|---|---|---|---|
| FP16(基準) | 16 | 14.63 | 10.86 | 10.13 | 9.56 | 8.34 |
| RTN | 4 | 48.17 | 12.10 | 11.32 | 10.98 | 10.54 |
| GPTQ | 4 | 15.47 | 11.39 | 10.31 | 9.63 | 8.37 |
| RTN | 3 | 1.3e4 | 5.8e3 | 3.4e3 | 1.6e3 | 7.3e3 |
| GPTQ | 3 | 20.97 | 14.86 | 11.61 | 10.27 | 8.68 |
4bit でも 175B の RTN は 10.54 で、FP16 の 8.34 から 2.2 ポイント悪化します。論文はこれを「10 倍小さい FP16 の 13B(10.13)より悪い」と表現しています。3bit では 1,000〜10,000 台に吹き飛び、文章になりません。同じ 3bit の GPTQ は 8.68 に留まります。
不思議に思うかもしれません。重み 1 個の丸め誤差は目盛り幅の半分以下しかないのに、なぜ perplexity が 3 桁も悪化するのか。理由は 2 つあります。第 4 章で見た層ごとの感度差 — 小さな誤差でも、出力に強く効く重みで起きれば効いてしまう。そして誤差が層をまたいで伝播すること — ある層の出力のズレは次の層の入力のズレになり、何十層も積み重なれば増幅されます。
ここから、以降のすべての手法に共通する発想が出てきます。重み 1 個 1 個を元の値に近づけるのをやめて、層の出力が元と合うようにする。
GPTQ — 丸めた誤差を、まだ丸めていない重みに配り直す
GPTQ が最小化するのは、重みの誤差ではなく層の出力の誤差です。
最小化する量 = ( W·X − Ŵ·X ) の二乗和
W は元の重み、Ŵ は量子化後の重み、X はその層に実際に流れ込む入力です。ここに X が出てくることが、GPTQ がキャリブレーションデータを要求する理由そのものです。X が無ければこの式は書けません。
言葉にすると「重み 1 個を丸めて出力がズレたら、まだ丸めていない残りの重みを少し動かしてズレを打ち消す」。数値で追ってみます(計算例)。重み 2 個の層で w1 = 0.30、w2 = 0.42、入力は両方 1.0、目盛りは 0.125 刻み(0、0.125、0.25、0.375、0.5 …)とします。元の出力は 0.72 です。
- RTN: w1 → 0.25、w2 → 0.375。出力 0.625、ズレは −0.095
- GPTQ: w1 → 0.25(ここで −0.05 のズレが出る)。これを打ち消すため w2 を 0.42 + 0.05 = 0.47 に動かしてから丸める → 0.5。出力 0.75、ズレは +0.03
注目してほしいのは、GPTQ 側の w2 が元の 0.42 から 0.5 へ遠ざかっているのに、層の出力は元に近づいていることです。重みを元に似せるのが目的ではない、という原則がここに出ています。
では「どの重みをどれだけ動かせば効くのか」はどう決まるのか。ここで Hessian(= 目的関数の曲がり具合を表す行列。ここでは「どの入力チャネルがよく使われ、どのチャネル同士が連動するか」の統計)が使われます。GPTQ の Hessian は H = 2·X·Xᵀ で、キャリブレーション入力 X だけから決まり、重みには依存しません。これがあれば動かす量が閉じた式で一発で出ます。学習も勾配降下も要りません。
論文はこの計算を実用速度に載せるため、全行を同じ列順で処理する・128 列ずつまとめて更新する・逆 Hessian を Cholesky 分解で前計算する(数十億パラメータ級では逆行列の反復更新が数値的に壊れるため)という 3 つの工夫を入れています。結果、OPT-13B が A100 1 枚で 20.9 分、175B で 4.2 時間。Hugging Face のドキュメントは 8B なら A100 1 枚で約 20 分としています。
つまみの意味
GPTQConfig(bits=4, group_size=128, dataset="c4", tokenizer=tokenizer)
group_size(既定 128)— 何個の重みで 1 組の scale を共有するか。公式 docstring も「推奨値は 128」、-1で列ごとです。細かくすれば精度は上がりますが、組ごとの scale と zero-point でビットが太ります。1 組につき FP16 の scale が 16 ビット、zero-point がbitsビット余分に付くので、実効 bpw(= 重み 1 個あたりの平均ビット数)は「bits +(16 + bits)÷ group_size」です。4bit・group 128・非対称なら 4 + 20 ÷ 128 = 4.156 ビット/重み(計算例)。GPTQ 論文が 2bit を「約 2.2 ビット」と書くのと同じ算術です。メモリ見積りは第 8 章で扱います。desc_act(別名 act_order、既定 False)— 活性化の大きい列から順に量子化するか。docstring は「False にすると推論が大幅に速くなるが perplexity はやや悪化しうる」と書いています。既定が精度の悪い側なのは速度を優先しているからです。折衷としてact_group_aware(GAR、既定 True。desc_actが False のときだけ効き、True にすると強制的に無効になる)があり、並べ替えをグループ内に限ることで推論時のオーバーヘッドをゼロにします。damp_percent(既定 0.1)— Hessian の対角に足す安定化項の割合。論文本文は対角平均の 1% を使っており、実装既定の 0.1 とは別値です。vLLM のドキュメントは同じ役割のつまみ(llm-compressor のdampening_frac)について「小さくすると精度が上がりうるが、数値不安定でアルゴリズムが失敗することがある」と注意しています。
弱点も明確です。重みの値を実際に動かすので、キャリブレーションデータの分布に引っ張られます。AWQ 論文の交差検証では、キャリブレーションと評価の分布が違うとき GPTQ の perplexity は 2.3〜4.9 悪化しました(同条件で AWQ は 0.5〜0.6)。Hugging Face も GPTQ の欠点に「キャリブレーションデータに過学習する可能性」を挙げています。なお AutoGPTQ は Transformers ではサポート終了で、現在は gptqmodel に移っています(2026-08 時点)。古い記事のコードをそのまま写すと動きません。
AWQ — 重要なチャネルを、膨らませて守る
AWQ の出発点は別の観察です。論文の主張を順に並べると、①重みはすべて同じ重要度ではない、②顕著な(salient)重みの 1% だけを守れば量子化誤差を大きく減らせる、③その 1% を見つけるには重みの分布ではなく 活性化の分布を見るべき、④一部だけ FP16 で持つ混合精度はハードウェア効率が悪いので、スケールアップで守る。
③の根拠が実測です。INT3・group 128 で一部の重みチャネルだけを FP16 に残したときの WikiText perplexity(OPT-1.3B、FP16 は 14.62)。何もしない RTN が 119.00 のところ、活性化の振幅が大きい 1% を残すと 16.91 まで戻ります。ところが重みが大きい 1% を残しても 98.55、ランダムに 1% でも 109.38 で、ほとんど効きません。「重要さ」は重みの側には書かれておらず、その重みに掛かってくる入力の側にあるのです。
④のスケールアップがなぜ守ることになるのか。ある重み w を s 倍し、対応する入力 x を 1/s 倍すれば、掛け算の結果は変わりません(数学的に等価な変換)。このとき誤差は次のようになります。
スケール後の誤差 ÷ 元の誤差 = (Δ' ÷ Δ) × (1 ÷ s) ≈ 1 ÷ s
Δ は目盛りの幅で、グループ内の絶対最大値から決まります。1 チャネルだけ大きくしてもグループの最大値は普通変わらないので Δ' ≈ Δ。丸め誤差の大きさ自体は変わらないのに、値が s 倍になった分だけ相対的な誤差が 1/s になるのです。
計算例。Δ = 0.125 なら丸め誤差の上限は 0.0625 です。値が 0.30 なら相対誤差は 0.0625 ÷ 0.30 = 21%。2 倍して 0.60 にすると、丸め誤差の上限は 0.0625 のままなので 0.0625 ÷ 0.60 = 10%。ちょうど半分になります。
では s は大きいほど良いのか。実測は違います(OPT-6.7B、salient 1% を s 倍、WikiText-2 perplexity)。s = 1(= RTN)で 23.54、s = 1.5 で 12.48、s = 2 で 11.92(最良)、s = 4 で 12.36 と悪化。s = 4 では 21.2% のチャネルで Δ が実際に増えてしまい、守っていない側の誤差が増幅されるためです。難しさは消えず、移動するだけという構図がここで顔を出します。
s の決め方も実用的です。探索空間を 1 パラメータに縮約し、s =(活性化のチャネルごとの平均振幅)の α 乗と置いて、α を 0〜1 の間で 20 分割してグリッドサーチします。逆スケール 1/s は多くの場合前段の LayerNorm や Linear に吸収できるので、推論時の追加コストになりません。
GPTQ との違いをまとめると、使う統計が二次(Hessian)か一次(平均振幅)か、そして重みの値を動かすか動かさないか、です。AWQ は重みを動かさないぶんキャリブレーションに引っ張られにくく、GPTQ の 10 分の 1 の量(16 系列 対 192 系列)でより良い perplexity に到達したと報告されています。精度は INT4・group 128 の Llama-2-7B で FP16 の 5.47 に対し 5.60、70B で 3.32 に対し 3.41 です。
実務上の制約が 2 つ。Hugging Face の対応表で AWQ は 4bit のみです(GPTQ と AutoRound は 2/3/4/8bit)。また AutoAWQ は非推奨になり、今から自分で AWQ 量子化するなら vLLM プロジェクトの llm-compressor(AWQModifier)を使います。Transformers 側は量子化済み AWQ モデルの読み込み専用です。
外れ値への二つの構え — LLM.int8() と SmoothQuant
ここまでは重みだけの話でした。活性化にも手を出すと難しくなります。活性化には外れ値(= 他の次元より桁違いに大きい値)が出るためです。
LLM.int8() — 難しいところを切り出して 16bit で計算する
8bit の代表が LLM.int8() です。この論文がまず示したのは、素朴な int8 が大きいモデルでだけ壊れるという事実でした。C4 の validation perplexity で見ると、13B の 32bit が 12.45 に対して int8 の absmax 量子化は 19.08。しかも int8 の 13B(19.08)は int8 の 6.7B(14.59)より悪い。モデルを大きくしたのに悪化するという、はっきりした異常です。
原因は約 6.7B パラメータ付近で起きる相転移(= 水が 0 度で凍るように、ある規模を超えた途端に性質が切り替わること)です。他の次元の最大 20 倍という外れ値が現れ、6.7B では長さ 2048 のシーケンスあたり 150,000 個も出ます。ところが、それが集中している特徴量次元はわずか 6 個(13B までのモデルでも最大 7 個)。この次元をゼロにすると perplexity が 600〜1000% 劣化します。全入力特徴量の 0.1% しか占めないのに、です(同数の次元をランダムに消した場合の劣化は約 0.1%)。
なぜ外れ値 1 つでテンソル全体が壊れるのか。absmax 量子化は絶対最大値で scale を決めるので、外れ値があると scale がそれに引っ張られ、残りのほとんどの値が int8 の 0 付近の狭い範囲に押し込まれて目盛りが無駄になります。LLM.int8() の答えは「分ける」でした。
出力 ≈ Σ(外れ値の列) X_fp16 × W_fp16 + S × Σ(残りの列) X_int8 × W_int8
閾値 6.0 を超える値を含む列だけを fp16 の行列積へ回し、残り(99.9% 超)を int8 の行列積で計算して足し合わせます。外れ値の次元が 7 個以下なので、この分解の追加メモリは約 0.1% だけ。結果、13B の perplexity は 12.45 — 32bit と同値に戻ります。実装上の注意として、bitsandbytes の閾値は 活性化に対して効き、重みの値に対してではありません。0.0 にすると混合精度分解が無効になります。
SmoothQuant — 難しさを weight 側へ移してしまう
SmoothQuant は逆の構えです。論文の一文がすべてを言っています。「重みは量子化しやすく、活性化はしにくい」。ならば難しさを weight 側へ移せばよい。
Y = ( X ÷ diag(s) ) × ( diag(s) × W )
チャネルごとの係数 s で活性化を割り、同じ s を重みに掛けます。結果 Y は変わらないので、これも数学的に等価な変換です。AWQ のスケール移動と同じ形をしていることに気づくと思います。違いは目的で、AWQ は W4A16 のために特定チャネルを守り、SmoothQuant は W8A8 を成立させるために山ごと移します。s の決め方には移動の強さ α が入ります。
s_j = max|X_j| の α 乗 ÷ max|W_j| の (1 − α) 乗
論文は多くのモデル(OPT・BLOOM の全系列)で α = 0.5 が均衡点だとしています。0.4 未満だと活性化が量子化しにくいまま、0.6 を超えると重みが量子化しにくくなる。外れ値が約 30% と多い GLM-130B のようなモデルでは 0.75 まで上げます。ここでも AWQ の s = 4 と同じ構図です。
この変換が実用になるのは、s の逆数を前段の層のパラメータへオフラインで融合できるからです。推論時に余分なカーネル呼び出しが増えません。成果は W8A8 で最大 1.56 倍の高速化・2 倍のメモリ削減で、530B のモデルを単一ノードで動かせるようになったと報告されています。
目盛りを作り替える、キャリブレーションを捨てる
NF4 と double quantization — 分布に合わせた 16 個の値
ここまでの手法は「等間隔の目盛りに、どう丸めるか」を工夫してきました。NF4(NormalFloat4)は目盛りそのものを作り替えます。まず押さえるべきは、NF4 は浮動小数点フォーマットではないことです。bitsandbytes のソースコメントがはっきり書いています。NF4 の 16 個の値は標準正規分布の分位点(= 分布を等しい確率の区間に切り分ける境目の値)から導いたルックアップテーブルで、4bit はその表の中の位置を指すインデックスにすぎない。符号・指数・仮数への分解を持ちません。第 2 章で見た FP4 とは根本的に別物です。
なぜ正規分布を前提にしていいのか。根拠は第 4 章の理由 1 で見たとおり、事前学習済みの重みがほぼすべてゼロ中心の正規分布に従うことを QLoRA 論文が検定で裏取りしている点にあります。分布が固定なら分位点も固定なので、「各目盛りの区間に同じ個数の値が入る」型を先に作れます。これが分位点量子化で、QLoRA 論文はこれをゼロ中心の正規分布データに対して情報理論的に最適だと主張しました(この最適性が厳密には成り立たないという後続論文の指摘と、それでも実務上は差が出ない理由は第 2 章で扱いました)。実際の 16 値は、ゼロ付近が 0 → 0.0796 → 0.1609 → 0.2461 と細かく、端は 0.7230 → 1.0 と粗い。値が集中する場所に表現力を寄せているわけです。
効果は QLoRA 論文の実測に出ています(Pile Common Crawl での OPT / BLOOM / LLaMA / Pythia 125M〜13B の平均 perplexity、低いほど良い)。Int4 が 34.34、FP4(指数 2bit)が 31.07、FP4(指数 3bit)が 29.48、NF4 + double quantization が 27.41。
その double quantization(入れ子量子化)が 2 つ目の工夫です。ブロックごとに scale を持つと、その scale 自体がメモリを食います。QLoRA は blocksize 64 で scale を FP32 で持つので:
定数のコスト = 32 ビット ÷ 64 個 = 0.5 ビット/重み
4bit の重みに対して 0.5 ビットは無視できません。そこで scale の並びをもう一度量子化します。2 段目は 8bit float、blocksize 256(8bit なら劣化が観測されないため)。
8 ÷ 64 + 32 ÷ (64 × 256) = 0.125 + 0.00195 = 0.127 ビット/重み
差は 0.373 ビット/重み。論文は「平均で約 0.37 ビット/パラメータ、65B モデルで約 3 GB」と書いています(Hugging Face のドキュメントは 0.4 ビットと丸めていますが、原論文の 0.373 が正)。公式の表現では「追加の性能コストなしで」メモリが節約できます。使い分けも明確で、「NF4 は 4bit ベースモデルの学習に使うべき」だが「推論では bnb_4bit_quant_type の性能への影響は大きくない」。QLoRA で学習するなら NF4 を選ぶ理由が強い、推論だけなら fp4 との差は小さい、ということです。
HQQ — キャリブレーションを使わずに最適化する
HQQ(Half-Quadratic Quantization)は、最適化はするがキャリブレーションデータは使わない立ち位置です。公式ブログの表現では「層の活性化ではなく、重みの誤差を最小化することに特化する」。誤差の測り方に二乗誤差ではなく p 乗ノルム(= 誤差を p 乗して足し上げる測り方。既定は p = 0.7)を使う点が特徴です。二乗(p = 2)は大きな誤差を強く罰するので、最適化が少数の外れ値に引っ張られます。p を 1 より小さくすると大きな誤差の重みが相対的に下がるため、外れ値に振り回されずに大多数の重みを合わせられます。解き方も、問題を 2 つの部分問題に分けてどちらも閉じた式で解きます(scale は固定し zero-point だけ最適化、既定の反復 20 回)。「autograd で勾配降下するのとは違い、閉形式解に依存する」ため非常に速く、公式ブログは Llama-2-70B を 5 分未満、GPTQ の 50 倍以上速いと報告しています。
品質は 4bit なら GPTQ / AWQ と同程度です(Llama-2-7B の WikiText2 perplexity で AWQ group128 が 5.32、HQQ group128 が 5.35、HQQ group64 が 5.30)。差が出るのは 3bit 以下で、70B の 3bit は HQQ が 3.58、GPTQ が 3.85。さらに 2bit の Llama-2-70B が、FP16 の Llama-2-13B より低い perplexity を同程度のメモリで達成しています(4.12 / 30.27 GB 対 4.63 / 25.6 GB)。ただし同じ 2bit でも 7B は 7.3〜15.61 と崩れています。モデルが大きいほど低ビットの崩れ方が緩やかになる、という第 4 章で見た傾向どおりです(逆に、サイズが大きいからといって 2bit まで削って良い、という話ではありません)。
HqqConfig(nbits=4, group_size=64, axis=1)
公式 FAQ は「まず nbits=4, group_size=64, axis=1 から始めるべき」としています。axis(グループ化する軸)は 0 の方が精度が良いのに、最適化された推論バックエンド(torchao / gemlite / Marlin)は 1 しか対応していません。精度の良い設定と速い設定が一致していない、という現実的な制約です。
AutoRound — 丸め方そのものを学習する
最後の系統は、丸める向きを学習で決めます。1 行で並べると分かりやすい。RTN は最近傍に丸める。GPTQ は丸めた誤差を残りの重みで補償する。AutoRound(原論文名 SignRound)は上に丸めるか下に丸めるかを学習する。
丸める向きは「上」か「下」の 2 択で、少し動かしても答えが飛び飛びにしか変わりません。だから勾配(= どちらへどれだけ動かせば良くなるかの傾き)が取れず、そのままでは学習に載りません。そこで丸める前の値に足す連続的な補正量を学習可能パラメータとして持ち、符号付き勾配降下(SignSGD。= 勾配の符号だけを見て一定幅で動かす更新法)で 200 ステップ更新します。llm-compressor の説明では学習可能パラメータは 3 つ(丸め補正 V と、クリッピング範囲の α・β)で、デコーダ層を 1 つずつ処理してブロック単位の出力再構成誤差を目的関数にします。効きが顕著なのは低ビットで、原論文は 2bit で 11 タスクの平均ゼロショット精度(= 例題を見せずにそのまま解かせたときの正解率)が 6.91〜33.22 ポイント改善したと報告し、4bit は「ほとんどのシナリオでほぼロスレス」としています。時間は 7B を単一 GPU で約 10 分です。
4bit なら重いレシピは不要だという証拠もあります。Llama-3.1-8B-Instruct の 13 タスク平均精度は、16bit の 0.6212 に対して best レシピが 0.6116(65 分)、default が 0.6106(13 分)、light が 0.6104(6 分)。5〜10 倍の時間差に対して精度差は 0.001 です。
もう 1 つ実務的に重要な性質があります。AutoRound は成果物を auto_round 形式だけでなく GPTQ 形式・AWQ 形式・GGUF 形式(gguf:q4_k_m など)へもエクスポートできます。「量子化アルゴリズム」と「配布ファイル形式」が別の軸だということが、ここでいちばんはっきり見えます。形式の話は第 6 章と第 7 章で扱います。
比較表と、どれを選ぶか
| 手法 | キャリブレーション | 代表ビット幅 | 量子化する対象 | 主な実装・ランタイム | 一言 |
|---|---|---|---|---|---|
| RTN | 不要 | 4〜8 | weight のみ | llama.cpp / bitsandbytes | 量子化が最速。低ビットで折れる |
| GPTQ | 必要 | 2/3/4/8 | weight のみ(W4A16) | vLLM / Transformers(gptqmodel) | 誤差を隣の重みで補償 |
| AWQ | 必要 | 4 のみ | weight のみ(W4A16) | vLLM / llm-compressor / TinyChat | 重要チャネルをスケールで守る |
| LLM.int8() | 不要 | 8 | 重み + 活性化 | bitsandbytes | 外れ値の列だけ fp16 |
| NF4 + DQ | 不要 | 4 | weight のみ | bitsandbytes(QLoRA) | 分位点ベースの目盛り |
| HQQ | 不要 | 1〜8 | weight のみ | Transformers / torchao / gemlite | 最適化型で量子化がいちばん速い |
| AutoRound | 必要 | 2/3/4/8 | weight のみ | llm-compressor / Transformers | 丸めを学習。GGUF へも出力 |
| SmoothQuant | 必要 | 8(W8A8) | 重み + 活性化 | llm-compressor / TensorRT 系 | 難しさを weight へ移す |
選び方は、公式ドキュメントが指す方向がおおむね一致しています。
- バッチサイズで決まる — NVIDIA の公式ガイダンスは、バッチサイズ 4 以下の推論はメモリ律速なので INT4 AWQ のような weight-only が有利、16 以上では重みと活性化の両方を量子化する方式を選べ、としています。llm-compressor 側も「低 QPS・レイテンシ重視・メモリ制約なら W4A16、高 QPS・スループット重視なら W8A8」と同じ切り分けです。
- 手元で 1 人が対話するなら — バッチサイズは実質 1 で完全にメモリ律速です。weight-only の 4bit がいちばん効きます。入手経路としては GGUF が圧倒的に楽で、これは第 6 章で扱います。
- GPU サーバで多並列に流すなら — AWQ / GPTQ / FP8 の系統です。形式の選び分けは第 7 章で扱います。
- 自分で量子化する手間を掛けたくないなら — Hugging Face の対応表では、キャリブレーション不要で速い on-the-fly は HQQ、使いやすさと QLoRA 学習は bitsandbytes、キャリブレーションを回して 4bit の精度を取るなら GPTQModel か AWQ です。
ビット幅の目安(Hugging Face の key takeaways、bf16 比)は、8bit がメモリ約 1/2(公式表記で「約 2 倍の削減」)で精度は bf16 に非常に近い、4bit が約 1/4 で比較的高精度、4bit 未満は目に見える低下(特に 2bit)。そして公式ドキュメントの締めの一文がいちばん重要です。常に、自分の具体的なタスクとハードウェアで精度と速度をベンチマークして、要件を満たすことを確認せよ。品質の測り方は第 9 章で扱います。
つまずきポイント
前者は GGUF(llama.cpp)のファイル形式の名前、後者は量子化アルゴリズムの名前で、そもそも層が違います。GPTQ / AWQ の成果物は safetensors と config.json の quantization_config で、.gguf ではありません。ただし AutoRound は GGUF へエクスポートできるので、境界は曖昧になってきています。
GPTQ / AWQ のキャリブレーションは、モデルを訓練するのではなく「各層にどんな数値が流れてくるか」を数百件のテキストで観測するだけです。GPTQ 論文は C4 からランダムに 128 × 2048 トークンを使い、「タスク固有データを一切見ないので結果は真に zero-shot」と明言しています。
W4A16 の A は 16bit です。GPTQ 論文自身が限界として「本手法の高速化はメモリ移動量の削減から来るもので、計算量は削減しない」と書いています。だからバッチサイズを上げて計算律速に入ると旨味が減ります。Marlin カーネルが「バッチ 16〜32 まで理想に近い高速化」を売りにしているのは、まさにそこが難所だからです。
AWQ 論文では AWQ が GPTQ を上回り、HQQ の公式ブログでは 4bit で AWQ group128(5.32)が HQQ group128(5.35)をわずかに上回ります。どの数値も、発表側が有利な条件を含みます。モデル・ビット幅・group_size・キャリブレーションデータのどれかが違えば順位は入れ替わります。
理解度チェック
Q1. GPTQ がキャリブレーションデータを必要とするのはなぜですか?
最小化する対象が「重みの誤差」ではなく「層の出力の誤差」だからです。目的の式には、その層に実際に流れ込む入力 X が入っています。誤差を配る量を決める Hessian(2·X·Xᵀ)も X から作るので、X が観測できなければアルゴリズムが定義できません。
Q2. AWQ が「重要な重み」を、重みの大きさではなく活性化の振幅で選ぶ根拠は?
実測です。OPT-1.3B・INT3・group 128 で、活性化基準の 1% を FP16 に残すと perplexity が 119.00 から 16.91 まで戻ります。同じ 1% を重みの大きさ基準で選ぶと 98.55、ランダムに選ぶと 109.38 で、ほとんど効きません。
Q3. AWQ のスケール s を大きくすればするほど良くなる、とはならないのはなぜですか?
s を上げすぎるとグループの最大値が変わり、目盛りの幅 Δ が広がってしまいます。すると守っていないチャネルの誤差が増幅されます。OPT-6.7B の実測では s = 2 が最良(11.92)で、s = 4 では 21.2% のチャネルで Δ が増え、12.36 に悪化しました。
Q4. NF4 は「4bit の浮動小数点」ですか?
いいえ。符号・指数・仮数への分解を持ちません。標準正規分布の分位点から作った 16 個の値のルックアップテーブルで、4bit はその表の中の位置を指すインデックスです。本物の 4bit 浮動小数点は FP4(符号 1 + 指数 2 + 仮数 1 など)で、別物です。
Q5. LLM.int8() と SmoothQuant は、活性化の外れ値への対処がどう違いますか?
LLM.int8() は「分ける」。外れ値を含む列だけを fp16 の行列積に分離し、残り 99.9% 超を int8 で計算して足します。SmoothQuant は「移す」。等価変換で外れ値の山を weight 側へ移し、全部を int8 で計算します。どちらも難しさを消してはいません。
まとめ
- 手法の違いは「目盛りをどう並べるか」と「丸め誤差をどう埋め合わせるか」の 2 点に還元できる。RTN は埋め合わせをしないので、3bit 域で崩壊する
- GPTQ は層の出力誤差を最小化し、丸めた誤差をまだ丸めていない重みへ配る。だから入力 X の統計(Hessian)=キャリブレーションが必要で、そのデータ分布に引っ張られる弱点も同じ理由から来る
- AWQ は重要チャネルを活性化の振幅で見つけ、等価なスケール変換で守る。重みを動かさないので分布ずれに強く、キャリブレーション量も少ない。ただし守りすぎると他が壊れる
- 外れ値には「分ける」(LLM.int8())と「移す」(SmoothQuant)の 2 つの構えがある。どちらも難しさを消しはしない
- NF4 は目盛りを分布に合わせ替えた非等間隔の型で、double quantization は scale 自身を量子化して 0.5 → 0.127 ビット/重みに削る。HQQ はキャリブレーションを捨てて量子化の速さを取り、AutoRound は丸め方を学習して低ビットで効く
- 選択の主軸はバッチサイズ。手元の 1 人利用はメモリ律速なので weight-only 4bit、多並列サービングは 重み + 活性化 の系統
次章では、ここで扱わなかった「もう一つの世界」— llama.cpp / GGUF の Q4_K_M という名前が、ブロックの中で実際に何ビットをどこに使っているのかを開けていきます。
参考リンク
- GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers(arXiv)
- AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration(arXiv)
- LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale(arXiv)
- SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models(arXiv)
- QLoRA: Efficient Finetuning of Quantized LLMs(arXiv、NF4 と double quantization)
- Optimize Weight Rounding via Signed Gradient Descent(SignRound / AutoRound、arXiv)
- Hugging Face Transformers: Selecting a quantization method
- Hugging Face Transformers: GPTQ
- Hugging Face Transformers: AWQ
- Hugging Face Transformers: AutoRound
- HQQ 公式ブログ(Half-Quadratic Quantization)
- NVIDIA TensorRT Model Optimizer: Best practices to choose the right quantization methods