LLaMA-70Bのモデルパラメータは、FP16(16ビット浮動小数点)で保存すると約140GBになります。これはNVIDIA A100(80GB)1枚には載り切らず、最低2枚のGPUが必要です。個人の環境では到底扱えません。
しかし、もし各パラメータを16ビットではなく4ビットで表現できれば、モデルサイズは約35GBに縮小され、A100 1枚やRTX 4090(24GB VRAM)でも動作可能になります。さらに、メモリ帯域のボトルネックが緩和されるため、推論速度も大幅に向上します。
この「ビット数を減らしてモデルを圧縮する」技術が量子化(quantization)です。2023年以降、GPTQ、AWQ、GGUFなどの実用的な量子化手法が次々と提案され、LLMの民主化に大きく貢献しました。
量子化を理解することは、以下のような場面で直接役立ちます。
- ローカルLLMの運用: 自宅のGPUやCPUでLLMを動かす際に、どの量子化形式を選ぶべきかの判断に不可欠です
- 推論サーバーのコスト最適化: 同じGPUでより大きなモデルを動かせるため、性能あたりのコストを大幅に削減できます
- エッジデバイスへの展開: スマートフォンや組み込みデバイスでLLMを動かすための基盤技術です
本記事の内容
- 量子化の基礎(線形量子化の数式)
- 量子化誤差の理論的分析
- GPTQ: Hessian情報を用いたポストトレーニング量子化
- AWQ: 活性化を考慮した重み量子化
- GGUF: CPU推論のためのフォーマット
- Pythonでの量子化シミュレーション
- 各手法の性能比較
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
量子化の基礎
なぜ量子化が必要か — メモリの壁
LLMの推論において、最大のボトルネックはメモリ帯域です。1トークンを生成するために、モデルの全パラメータをGPUメモリ(HBM)から演算ユニットに読み出す必要があります。
LLaMA-70Bの場合、FP16で全パラメータを読み出すのに必要な時間は:
$$ t = \frac{2 \times 70 \times 10^9 \text{ bytes}}{2 \times 10^{12} \text{ bytes/s}} = 70 \text{ ms} $$
これは毎秒14トークンに相当します。もし4ビット量子化を行えば:
$$ t = \frac{0.5 \times 70 \times 10^9 \text{ bytes}}{2 \times 10^{12} \text{ bytes/s}} = 17.5 \text{ ms} $$
毎秒57トークンに向上します。つまり、量子化はモデルサイズの削減と推論速度の向上を同時に達成できます。
線形量子化(Uniform Quantization)
最も基本的な量子化は、浮動小数点の値を等間隔の整数にマッピングする線形量子化(uniform quantization)です。
$b$ ビットで量子化する場合、表現可能な整数は $\{0, 1, \ldots, 2^b – 1\}$ です。浮動小数点値 $w$ を整数 $w_q$ に変換する量子化関数は:
$$ w_q = \text{clamp}\left(\left\lfloor \frac{w}{s} \right\rceil + z, \, 0, \, 2^b – 1\right) $$
ここで $\lfloor \cdot \rceil$ は最近接整数への丸め、$s$ はスケールファクター、$z$ はゼロポイントです。
逆量子化(dequantization)は:
$$ \hat{w} = s \cdot (w_q – z) $$
この式が意味するところを直感的に説明しましょう。スケールファクター $s$ は「整数1ステップが浮動小数点でどれだけの幅に対応するか」を決めます。ゼロポイント $z$ は「浮動小数点の0がどの整数値に対応するか」を決めます。
対称量子化(symmetric quantization)では $z = 0$ とし:
$$ s = \frac{\max(|w|)}{2^{b-1} – 1} $$
非対称量子化(asymmetric quantization)では:
$$ s = \frac{\max(w) – \min(w)}{2^b – 1}, \quad z = \left\lfloor -\frac{\min(w)}{s} \right\rceil $$
対称量子化は実装が簡単(ゼロポイントの計算が不要)で高速ですが、重みの分布が非対称な場合に表現力が落ちます。LLMの重みは概ね0を中心に対称に分布するため、対称量子化が広く使われています。
グループ量子化(Group Quantization)
全パラメータに対して1つのスケールファクターを使う(per-tensor quantization)と、外れ値の影響でスケールが大きくなり、量子化の解像度が低下します。
これを解決するのがグループ量子化です。重みベクトルを $g$ 個のグループに分割し、各グループごとにスケールファクターとゼロポイントを持ちます。典型的なグループサイズは $g = 128$ です。
グループ $i$ のスケールファクターは:
$$ s_i = \frac{\max(|\bm{w}_{[ig:(i+1)g]}|)}{2^{b-1} – 1} $$
グループサイズが小さいほど量子化誤差は減りますが、スケールファクターの保存に追加のメモリが必要です。4ビット量子化でグループサイズ128の場合、スケールファクター(FP16)のオーバーヘッドは:
$$ \text{オーバーヘッド} = \frac{16 \text{ bit}}{128 \times 4 \text{ bit}} = 3.125\% $$
これは許容範囲であり、精度の向上に比べて十分小さいコストです。
では次に、量子化によってどの程度の誤差が生じるかを理論的に分析しましょう。
量子化誤差の理論
ラウンドトゥニアレストの限界
最も素朴な量子化手法はRTN(Round-to-Nearest)です。各重みを独立に最近接の量子化レベルに丸めます。
1つの重み $w$ の量子化誤差は:
$$ \epsilon = w – \hat{w} = w – s \cdot \left(\left\lfloor \frac{w}{s} \right\rceil – z\right) $$
丸め誤差が $[-s/2, s/2]$ の一様分布に従うと仮定すると、1要素あたりの平均二乗誤差は:
$$ \mathbb{E}[\epsilon^2] = \frac{s^2}{12} $$
8ビット量子化であれば $s$ が十分小さいため誤差は無視できますが、4ビットや3ビットになると量子化レベルの間隔 $s$ が大きくなり、誤差が急速に増大します。
なぜ単純な丸めでは不十分か
RTNが問題になるのは、全ての重みが出力に同じ影響を持つわけではないからです。ある重みの量子化誤差がモデルの出力に与える影響は、その重みに掛かる入力(活性化)の大きさに依存します。
線形層 $\bm{y} = \bm{W}\bm{x}$ を考えます。重み行列 $\bm{W}$ を $\hat{\bm{W}}$ に量子化した場合の出力誤差は:
$$ \|\bm{y} – \hat{\bm{y}}\|^2 = \|(\bm{W} – \hat{\bm{W}})\bm{x}\|^2 $$
入力 $\bm{x}$ の各チャネルの大きさが異なるため、$\bm{x}$ の値が大きいチャネルに対応する重みの量子化誤差は出力に大きな影響を与えます。GPTQとAWQは、この洞察に基づいて量子化精度を改善します。
ここからは、それぞれの手法がどのようにこの問題に取り組んでいるかを見ていきましょう。
GPTQ: Hessian情報に基づく量子化
OBQ(Optimal Brain Quantization)の発展
GPTQ(Frantar et al., 2023)は、OBQ(Optimal Brain Quantization) を大規模モデルに適用可能にした手法です。基本的なアイデアは、重みを1列ずつ量子化し、量子化誤差を残りの未量子化の重みで補正することです。
目的関数
GPTQは、各線形層の出力誤差を最小化します。重み行列 $\bm{W} \in \mathbb{R}^{m \times n}$ を持つ線形層で、キャリブレーションデータ $\bm{X} \in \mathbb{R}^{n \times N}$($N$ はサンプル数)を使った目的関数は:
$$ \min_{\hat{\bm{W}}} \|\bm{W}\bm{X} – \hat{\bm{W}}\bm{X}\|_F^2 $$
これは $\bm{W}$ の各行に独立に分解できます。行ベクトル $\bm{w} \in \mathbb{R}^n$ に対して:
$$ \min_{\hat{\bm{w}}} (\bm{w} – \hat{\bm{w}}) \bm{X}\bm{X}^T (\bm{w} – \hat{\bm{w}})^T = \min_{\hat{\bm{w}}} (\bm{w} – \hat{\bm{w}}) \bm{H} (\bm{w} – \hat{\bm{w}})^T $$
ここで $\bm{H} = \bm{X}\bm{X}^T \in \mathbb{R}^{n \times n}$ はHessian行列です。$\bm{H}$ は入力の二次モーメントを表し、「どの重みの方向の誤差が出力に大きな影響を与えるか」を符号化しています。
列ごとの量子化と残差補正
GPTQは重みを1列ずつ順に量子化します。列 $j$ の重みを量子化したとき、量子化誤差 $\delta_j = w_j – \hat{w}_j$ を残りの未量子化列 $k > j$ で補正します。
最適な補正量は、Hessian行列の逆行列を用いて計算できます。列 $j$ を量子化した後の列 $k$($k > j$)の補正量は:
$$ \delta_{w_k} = -\frac{\delta_j \cdot [\bm{H}^{-1}]_{jk}}{[\bm{H}^{-1}]_{jj}} $$
この式の意味を理解しましょう。$[\bm{H}^{-1}]_{jk}$ は列 $j$ と列 $k$ の間の「相互作用の強さ」を表します。この値が大きいほど、列 $j$ の量子化誤差を列 $k$ で効果的に補正できます。分母の $[\bm{H}^{-1}]_{jj}$ は列 $j$ 自体の「量子化感度」で、この値が大きいほど列 $j$ の量子化誤差が出力に与える影響が大きいことを意味します。
Choleskyベースの効率的な実装
素朴に実装すると $O(n^3)$ の計算量がかかりますが、GPTQはHessian逆行列のCholesky分解を利用して効率化します。$\bm{H}^{-1} = \bm{L}\bm{L}^T$ と分解すると、列ごとの量子化と補正がCholesky因子を使って効率的に計算できます。
さらに、GPTQは列を128列ずつのブロックにまとめて処理し、ブロック内の補正はレジスタ上で完結させることで、メモリアクセスを削減しています。これにより、LLaMA-70Bの量子化が数時間で完了します。
GPTQの限界
GPTQは精度面で優れていますが、いくつかの制約があります。
- キャリブレーションデータが必要: 128〜256サンプル程度のテキストデータが必要です
- 列の処理順序に依存: 列の量子化順序が結果に影響します
- 推論時のdequantizationコスト: 量子化された重みを演算のたびに復元する必要があります
これらの制約の一部を解決しようとしたのが、次に紹介するAWQです。
AWQ: 活性化を考慮した重み量子化
Salient Weightsの発見
AWQ(Activation-aware Weight Quantization, Lin et al., 2024)は、GPTQとは異なるアプローチで量子化精度を改善します。AWQの出発点は、LLMの重みには少数の重要な重み(salient weights) が存在するという観察です。
重みの重要度は、対応する活性化(入力)チャネルの大きさで決まります。入力 $\bm{x}$ のチャネル $j$ の平均的な大きさ $s_j = \mathbb{E}[|x_j|]$ が大きい場合、重み列 $j$ の量子化誤差は出力に大きな影響を与えます。
AWQの実験では、全チャネルの約1%が非常に大きな活性化を持ち、これらのチャネルに対応する重みを高精度で保持することが量子化精度の鍵であることが示されています。
スケーリングによる保護
重要な重みを保護する素朴な方法は、FP16のまま残す「混合精度」です。しかし、混合精度はハードウェアの効率が悪く、実用的ではありません。
AWQは、全ての重みを同じビット幅で量子化しつつ、重みにスケーリングを適用することで重要な重みの量子化誤差を相対的に小さくするというアイデアを使います。
重み $w_j$ にスケーリングファクター $s_j > 1$ を掛けてから量子化すると:
$$ \hat{w}_j = \text{Q}(w_j \cdot s_j) / s_j $$
量子化関数 $\text{Q}$ の丸め誤差は $O(1/s_j)$ に比例して小さくなります。ただし、$s_j$ を大きくしすぎると $w_j \cdot s_j$ がクリッピングされるリスクがあります。
最適スケーリングの探索
AWQは、各チャネルの最適なスケーリングファクターをグリッドサーチで求めます。チャネル $j$ のスケーリングファクター $s_j$ を $s_j = (\mathbb{E}[|x_j|])^\alpha$ とパラメータ化し、$\alpha \in [0, 1]$ の範囲でグリッドサーチします。
$$ \alpha^* = \arg\min_{\alpha \in [0,1]} \|\bm{W}\bm{x} – \text{Q}(\bm{W} \cdot \text{diag}(\bm{s}^\alpha)) \cdot \text{diag}(\bm{s}^{-\alpha}) \cdot \bm{x}\|^2 $$
$\alpha = 0$ はスケーリングなし(通常のRTN)、$\alpha = 1$ は活性化の大きさに完全に比例したスケーリングに対応します。
AWQの実験では、$\alpha \approx 0.5$ が多くの層で最適であることが報告されています。これは「活性化の大きさの平方根」に比例するスケーリングが、量子化誤差の低減とクリッピングリスクのバランスを最もよく取ることを意味します。
GPTQとの比較
| 特性 | GPTQ | AWQ |
|---|---|---|
| 量子化手法 | 列ごとの量子化 + 残差補正 | スケーリング + RTN |
| 推論速度 | dequantize が必要 | カーネル最適化が容易 |
| 量子化速度 | 数時間(Cholesky計算) | 数分(グリッドサーチ) |
| 精度(4bit) | ★★★★☆ | ★★★★★ |
| 実装複雑度 | 高い | 中程度 |
AWQはGPTQより量子化が高速で、推論カーネルの最適化も容易であり、2024年以降は実用の主流になりつつあります。
次に、GPU以外の環境でLLMを動かすための量子化フォーマットであるGGUFを見ていきましょう。
GGUF: CPU推論のための量子化フォーマット
llama.cppのエコシステム
GGUF(GPT-Generated Unified Format)は、llama.cppプロジェクトで使われる量子化フォーマットです。主にCPU推論を想定しており、GPUがなくてもLLMを動かせることが最大の特徴です。
多様な量子化タイプ
GGUFの特徴は、1つのフォーマット内で複数の量子化タイプを使い分けられることです。主なタイプは:
| タイプ | ビット数 | グループサイズ | 説明 |
|---|---|---|---|
| Q2_K | 2.5bit | 16 | 超軽量。品質低下が大きい |
| Q3_K_M | 3.4bit | 16 | 軽量かつ実用的 |
| Q4_K_M | 4.5bit | 32 | 最も広く使われる |
| Q5_K_M | 5.5bit | 32 | 品質重視 |
| Q6_K | 6.6bit | 16 | FP16に近い品質 |
| Q8_0 | 8.5bit | 32 | ほぼ劣化なし |
「K」はk-quants(k量子化)の略で、層の重要度に応じて量子化ビット数を自動的に変える混合精度戦略を採用しています。例えばQ4_K_Mでは、Attention層の重みは5ビット、FFN層の重みは4ビットで量子化されます。
SuperBlockアーキテクチャ
GGUFのk-quantsは、SuperBlockと呼ばれる二段階の量子化構造を持ちます。
- SuperBlock(256要素): 全体のスケールファクター(FP16)を持つ
- SubBlock(32要素): SuperBlock内のサブグループ。各SubBlockは相対スケールを持つ
この階層的な構造により、スケールファクターのオーバーヘッドを最小限に抑えながら、細かい粒度の量子化を実現しています。
Q4_K_Mの場合、256要素あたりのストレージは:
- 256 × 4ビット(量子化された重み)= 128バイト
- 8 × 6ビット(SubBlockスケール)= 6バイト
- 1 × 16ビット(SuperBlockスケール)= 2バイト
- 1 × 16ビット(SuperBlock最小値)= 2バイト
合計138バイト / 256要素 = 4.3ビット/要素 です。
imatrix(Importance Matrix)による品質改善
llama.cppはさらにimatrixという仕組みをサポートしています。キャリブレーションデータを使って各重みの重要度を事前に計算し、量子化時に重要な重みの精度を優先的に保持します。
これはAWQのアイデアと本質的に同じで、活性化の大きさに基づく重要度行列を量子化に組み込むものです。imatrixありのQ4_K_Mは、imatrixなしのQ5_K_Mに匹敵する品質を達成することがあります。
理論的な背景を理解したところで、次にPythonで量子化を実装して性能を比較してみましょう。
Pythonによる量子化の実装
RTN量子化とAWQ風量子化の比較
まず、基本的なRTN量子化とAWQ風のスケーリング付き量子化を実装し、量子化誤差を比較します。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
def symmetric_quantize(weights, n_bits):
"""対称線形量子化を行う。"""
qmax = 2**(n_bits - 1) - 1
scale = np.max(np.abs(weights)) / qmax
w_q = np.clip(np.round(weights / scale), -qmax, qmax)
w_deq = w_q * scale
return w_deq, scale
def group_quantize(weights, n_bits, group_size=128):
"""グループ量子化を行う。"""
n = len(weights)
w_deq = np.zeros_like(weights)
qmax = 2**(n_bits - 1) - 1
for i in range(0, n, group_size):
group = weights[i:i+group_size]
scale = np.max(np.abs(group)) / qmax
if scale == 0:
scale = 1e-10
w_q = np.clip(np.round(group / scale), -qmax, qmax)
w_deq[i:i+group_size] = w_q * scale
return w_deq
def awq_quantize(weights, activations, n_bits, group_size=128, alpha=0.5):
"""AWQ風のスケーリング付き量子化を行う。"""
# チャネルごとの活性化の大きさ
act_scales = np.mean(np.abs(activations), axis=0)
# スケーリングファクター
s = np.power(act_scales + 1e-10, alpha)
# スケーリングを適用して量子化
scaled_weights = weights * s[np.newaxis, :]
n_out, n_in = weights.shape
w_deq_scaled = np.zeros_like(scaled_weights)
qmax = 2**(n_bits - 1) - 1
for i in range(0, n_in, group_size):
for row in range(n_out):
group = scaled_weights[row, i:i+group_size]
scale = np.max(np.abs(group)) / qmax
if scale == 0:
scale = 1e-10
w_q = np.clip(np.round(group / scale), -qmax, qmax)
w_deq_scaled[row, i:i+group_size] = w_q * scale
# スケーリングを戻す
w_deq = w_deq_scaled / s[np.newaxis, :]
return w_deq
# テスト用のデータ生成
n_out, n_in = 256, 512
n_samples = 64
# 重み行列(正規分布 + 少数の外れ値)
W = np.random.randn(n_out, n_in) * 0.02
# 一部のチャネルに外れ値
outlier_channels = [10, 50, 100, 200, 300]
for ch in outlier_channels:
W[:, ch] *= 10
# 活性化(一部のチャネルが支配的)
X = np.random.randn(n_samples, n_in) * 0.1
for ch in outlier_channels:
X[:, ch] *= 5
# 真の出力
Y_true = X @ W.T
# 各手法で量子化
bits_list = [2, 3, 4, 5, 6, 8]
errors_rtn = []
errors_group = []
errors_awq = []
for bits in bits_list:
# RTN(per-tensor)
W_rtn = np.zeros_like(W)
for row in range(n_out):
W_rtn[row], _ = symmetric_quantize(W[row], bits)
Y_rtn = X @ W_rtn.T
errors_rtn.append(np.mean((Y_true - Y_rtn)**2))
# グループ量子化
W_group = np.zeros_like(W)
for row in range(n_out):
W_group[row] = group_quantize(W[row], bits, group_size=128)
Y_group = X @ W_group.T
errors_group.append(np.mean((Y_true - Y_group)**2))
# AWQ
W_awq = awq_quantize(W, X, bits, group_size=128, alpha=0.5)
Y_awq = X @ W_awq.T
errors_awq.append(np.mean((Y_true - Y_awq)**2))
# 可視化
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 左: 量子化ビット数ごとの出力MSE
axes[0].semilogy(bits_list, errors_rtn, 'o-', label='RTN (per-tensor)', markersize=8)
axes[0].semilogy(bits_list, errors_group, 's-', label='Group Quant (g=128)', markersize=8)
axes[0].semilogy(bits_list, errors_awq, '^-', label='AWQ-style (α=0.5)', markersize=8)
axes[0].set_xlabel('Quantization Bits')
axes[0].set_ylabel('Output MSE (log scale)')
axes[0].set_title('Quantization Error vs Bit Width')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[0].set_xticks(bits_list)
# 右: 4ビットでの重みの分布
W_flat = W[0]
W_rtn_flat, _ = symmetric_quantize(W_flat, 4)
axes[1].hist(W_flat, bins=100, alpha=0.5, label='Original (FP32)', density=True)
axes[1].hist(W_rtn_flat, bins=100, alpha=0.5, label='Quantized (4-bit)', density=True)
axes[1].set_xlabel('Weight Value')
axes[1].set_ylabel('Density')
axes[1].set_title('Weight Distribution: Original vs 4-bit Quantized')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('quantization_comparison.png', dpi=150, bbox_inches='tight')
plt.show()
左のグラフから、3つの重要な傾向が読み取れます。
- ビット幅が減るほど誤差が指数的に増大する: 特にRTNでは2ビット→3ビットの間で誤差が1桁以上変化しています。これは量子化ステップ幅 $s$ がビット幅に対して指数的に変化するためです
- グループ量子化は一貫してRTNを上回る: 外れ値を含むチャネルのスケールが局所的に設定されるため、他のチャネルの解像度が犠牲にならないことが効いています
- AWQ風手法は低ビットほど効果が顕著: 4ビット以下でAWQの優位性が際立っており、これは活性化の大きいチャネルの重みが優先的に保護されるためです
右のグラフでは、4ビット量子化による重み分布の離散化が可視化されています。16個の量子化レベルに丸められていることが確認できます。
AWQのスケーリングパラメータ $\alpha$ の最適化
次に、$\alpha$ を変えたときの量子化誤差の変化を観察します。
# αを変えたときの出力MSEを計算
alphas = np.linspace(0, 1, 21)
errors_by_alpha = []
for alpha in alphas:
W_awq = awq_quantize(W, X, n_bits=4, group_size=128, alpha=alpha)
Y_awq = X @ W_awq.T
mse = np.mean((Y_true - Y_awq)**2)
errors_by_alpha.append(mse)
# RTN(α=0に相当)の誤差
W_group_4 = np.zeros_like(W)
for row in range(n_out):
W_group_4[row] = group_quantize(W[row], 4, group_size=128)
Y_group_4 = X @ W_group_4.T
mse_rtn = np.mean((Y_true - Y_group_4)**2)
plt.figure(figsize=(8, 5))
plt.plot(alphas, errors_by_alpha, 'o-', color='#00d4ff', markersize=6)
plt.axhline(y=mse_rtn, color='gray', linestyle='--', alpha=0.7, label='Group Quant (no scaling)')
best_alpha = alphas[np.argmin(errors_by_alpha)]
plt.axvline(x=best_alpha, color='#ffa726', linestyle='--', alpha=0.7,
label=f'Optimal α = {best_alpha:.2f}')
plt.xlabel('Scaling Exponent α')
plt.ylabel('Output MSE')
plt.title('AWQ Scaling Parameter Optimization (4-bit)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('awq_alpha_optimization.png', dpi=150, bbox_inches='tight')
plt.show()
print(f"最適 α: {best_alpha:.2f}")
print(f"Group Quant MSE: {mse_rtn:.6f}")
print(f"AWQ最小 MSE: {min(errors_by_alpha):.6f}")
print(f"誤差改善率: {(1 - min(errors_by_alpha)/mse_rtn)*100:.1f}%")
このグラフから、$\alpha$ が0(スケーリングなし)から増加するにつれて誤差が減少し、最適値を超えると再び増加するU字型のカーブが確認できます。これは先述した「量子化誤差の低減」と「クリッピングリスクの増大」のトレードオフを反映しています。最適な $\alpha$ は約0.45〜0.55の範囲にあり、AWQの論文の報告と一致しています。
実用上の選択ガイド
どの量子化手法を選ぶべきか
目的に応じた推奨フォーマットをまとめます。
GPU推論(サーバー): AWQ 4-bit(INT4)がデファクトです。vLLM、TGIなどの主要フレームワークでネイティブサポートされ、推論カーネルが最適化されています。
GPU推論(精度重視): GPTQ 4-bit + グループサイズ128 が精度面で安定しています。Hugging Faceのモデルハブに多くの変換済みモデルがあります。
CPU/Apple Silicon推論: GGUF Q4_K_M が最もバランスが良いです。llama.cppのエコシステムで広くテストされています。
メモリ制約が厳しい環境: GGUF Q3_K_M か Q2_K。品質低下は顕著ですが、RAMが8GBしかない環境でも7Bモデルが動作します。
ビット幅と品質の一般的な目安
| ビット幅 | モデルサイズ(対FP16比) | 品質劣化の目安 |
|---|---|---|
| 8-bit | 50% | ほぼ劣化なし |
| 4-bit | 25% | 多くのタスクで実用的 |
| 3-bit | 19% | 単純なタスクなら実用可能 |
| 2-bit | 12.5% | 顕著な品質低下 |
まとめ
本記事では、LLMの量子化技術をRTN、GPTQ、AWQ、GGUFの4つの手法を中心に解説しました。
- 量子化の本質は、浮動小数点の重みを低ビットの整数に変換することで、メモリ使用量と推論速度を同時に改善すること
- GPTQはHessian行列を用いた残差補正により、量子化誤差を未量子化重みで補償する
- AWQは活性化チャネルの重要度に基づくスケーリングで、重要な重みの量子化精度を優先的に保護する
- GGUFはCPU推論に最適化された階層的量子化フォーマットで、k-quantsとimatrixにより品質と圧縮率のバランスを取る
- 4ビット量子化が精度と圧縮のスイートスポットであり、AWQ 4-bitがGPU推論のデファクト、GGUF Q4_K_MがCPU推論のデファクトになりつつある
次のステップとして、以下の記事も参考にしてください。