8月28日、KDNuggetsが「Quantization and Pruning Methods to Make Your LLM Leaner」と題した記事を公開した。70BパラメータのモデルをFP16で保存すると、ロードするだけで約140GBのVRAMが必要になる。A100が4枚、リクエストを1件も処理する前からそれだけのハードウェアを用意しなければならない計算だ。同じモデルを4ビット量子化(AWQまたはGPTQ)すると35〜40GB程度まで圧縮できる。クラスタが不要になり、ハイエンドワークステーション1台で動く。本記事はその実現方法を、量子化・プルーニング5手法の実装コード付きで解説したものだ。
量子化とプルーニングの違い
まず両者を整理しておく。
量子化はパラメータの「精度」を下げる。FP16の重み(例:0.0023847)を4ビット整数で近似する。パラメータ数は変わらず、保存形式だけが変わる。
プルーニングはパラメータを「削除」する。不要なニューロン間の接続、アテンションヘッド、場合によってはレイヤー全体を取り除く。パラメータ数そのものが減る。
両者は競合せず、組み合わせて使える。
5手法の概要
| 手法 | 種別 | サイズ削減 | 再学習 | 用途 |
|---|---|---|---|---|
| bitsandbytes (NF4) | 量子化 | 約4x | 不要(QLoRA対応) | 素早い導入・ファインチューニング |
| GPTQ | 量子化 | 約4x | キャリブレーションのみ | GPU推論、事前量子化モデルが豊富 |
| AWQ | 量子化 | 約4x | キャリブレーションのみ | 本番GPU推論、品質・速度のバランス |
| SparseGPT | プルーニング | 約2x(50%スパース) | 不要(一発で重み更新) | 大規模モデル、2:4スパース対応 |
| Wanda | プルーニング | 約2x(50%スパース) | 不要(単一フォワードパス) | 超大規模モデル、プルーニング速度優先 |
最初に試すべき手法:bitsandbytes(NF4 4ビット量子化)
多くのチームが最初に使うべき手法だ。関数1回の呼び出しで動く手軽さでありながら、ファインチューニングまで対応する唯一の手法でもある。
NF4(NormalFloat4)というデータ型は、ニューラルネットワークの重みが正規分布に近い分布を持つという性質に基づいて設計されている。4ビットで表現できる値を重みが集中する領域に配置するため、単純な整数丸めより精度劣化が少ない。
さらに重要なのがQLoRA対応だ。4ビットでロードしたモデルに対して、ベースの重みを凍結したまま小さなアダプタ重みだけを学習できる。ファインチューニングの計画があるなら、この手法が出発点になる。
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch
model_id = "meta-llama/Llama-3.1-8B-Instruct"
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # 重みを16ビットではなく4ビットでロード
bnb_4bit_quant_type="nf4", # 正規分布に最適化されたNF4形式
bnb_4bit_compute_dtype=torch.bfloat16, # 計算時はbfloat16にアップキャスト
bnb_4bit_use_double_quant=True, # スケール定数自体も量子化(約0.4ビット追加節約)
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=bnb_config,
device_map="auto",
)
inputs = tokenizer("Explain quantization in one sentence.", return_tensors="pt").to(model.device)
output = model.generate(**inputs, max_new_tokens=40)
print(tokenizer.decode(output[0], skip_special_tokens=True))
bnb_4bit_use_double_quant=True は見落とされがちだが、量子化のスケール定数をさらに量子化するオプションで、精度コストはほぼゼロで追加のメモリ削減が得られる。
本番GPUサービングで使われる2手法:GPTQ・AWQ
GPTQ
2022年にFrantar らが発表した手法。レイヤー単位で量子化を進め、各重みを丸めたときの誤差をヘッセ行列の近似を使って周辺の重みに補正する。誤差が累積しにくい設計になっている点が、単純な丸め処理との大きな違いだ。
数百サンプル規模のキャリブレーションデータが必要で、実運用に近いテキストを使うと精度が向上する。Hugging Face Hub上には事前量子化済みのGPTQモデルが多数公開されており、キャリブレーション作業なしにそのまま利用できる点も実務上の強みだ。
AWQ
AWQの核心は「すべての重みが同じように重要ではない」という観察にある。活性化値の大きさを使って重要な重みを特定し、それらを保護しながら量子化する。現行の高速カーネルとの相性が良く、品質と推論速度のバランスでGPTQより優れる場面が多い。GPTQとAWQの選定基準としては、事前量子化モデルの入手しやすさを重視するならGPTQ、本番推論の速度・品質バランスを優先するならAWQが一般的な指針となる。
※編集部の考察:元記事にはGPTQとAWQの具体的なベンチマーク数値の記載は確認できなかった。両手法の比較数値を参照したい場合は、MIT HAN Labによる量子化ベンチマーク調査等の一次資料を参照することを推奨する。
プルーニングの2手法:SparseGPT・Wanda
SparseGPT
GPTQと同様にヘッセ行列を使い、重みを削除しながら残った重みを即座に補正する。2:4スパースパターン(4つごとに2つをゼロにする)に対応しており、NVIDIAのAmpere世代以降のGPUが持つスパース演算のハードウェアアクセラレーションを活用できる。50%スパースで推論を実質2倍速にできる可能性がある。
Wanda
Wandaはヘッセ行列を計算しない。重みの絶対値と入力活性化のノルムの積でスコアを付け、スコアの低い重みを削除するだけだ。Wandaの論文では、単純な大きさだけで判断する「magnitude pruning」がLLMでは機能しにくいことも示されている。Wandaはその欠点を、活性化の情報を組み合わせることで補う。単一フォワードパスで完結するため、超大規模モデルでプルーニング自体の処理時間が問題になる場面に向いている。
圧縮を「やりすぎ」た場合のリスク
Red Hatが50万件以上の量子化モデル評価を行った調査によると、精度劣化の程度はモデル・タスク・手法によって大きく異なる。文法的に正しい文章が出力されているからといって、特定タスクの精度が保たれているとは限らない。圧縮後のモデルは必ず、実際に使うタスクに近いベンチマークで評価することが必要だ。
なお、量子化ビット幅・スパース率・タスク特性の三者は互いに影響し合う。たとえばコード生成など厳密な出力が求められるタスクでは、同じ4ビット量子化でも精度の落ち方が大きくなる傾向がある。汎用的な「安全な圧縮率」は存在せず、ユースケースごとに評価サイクルを回すことが前提となる。
詳細はQuantization and Pruning Methods to Make Your LLM Leanerを参照していただきたい。