🎓 レベル:発展 | 重要度:B(推奨)
📎 前提:レイテンシとスループットのトレードオフ | 関連:LLMの推論サービングと最適化基盤
要点(BLUF)
- バッチング(レイテンシとスループットのトレードオフ)が「捌き方」の最適化なら、軽量化は「モデルそのものを小さく速くする」最適化です。3手法:量子化・蒸留・枝刈り。
- 量子化:重みの数値精度を下げる(float32 → int8 など)。サイズと計算量が減り高速化、わずかに精度低下。蒸留:大きな教師モデルの出力を小さな生徒モデルに学習させ、知識を移す。枝刈り:寄与の小さい重みをゼロにして削る。
- すべてサイズ・遅延・コストと精度のトレードオフ。「どこまで精度を犠牲にして、どれだけ軽くするか」を要件で決めます。アルゴリズムの理論的詳細(学習時量子化・蒸留の損失設計など)は機械学習・深層学習分野へ wikilink し、ここは運用上の使い分けと効果に集中します。
1. 3手法の概観
flowchart TB M["大きく重いモデル"] --> Q["量子化:精度ビット数を下げる"] M --> D["蒸留:小モデルに知識を移す"] M --> P["枝刈り:小さい重みを削除"] Q --> R["小さく速いモデル(精度はわずかに低下)"] D --> R P --> R
| 手法 | やること | 主な効果 | 主なコスト |
|---|---|---|---|
| 量子化 | float32→int8 等に精度を下げる | サイズ約1/4・高速化 | わずかな精度低下 |
| 蒸留 | 教師の出力を生徒が学習 | 小モデルで高い精度 | 学習の手間(教師が必要) |
| 枝刈り | 寄与の小さい重みを0に | スパース化・サイズ減 | 専用ランタイムが要ることも |
2. なぜ軽量化が効くのか
推論コストは「モデルの計算量 × リクエスト数」です。バッチングや台数増(ハードウェアとスケーリング)はリクエスト側を捌きますが、軽量化は1リクエストの計算量そのものを減らします。特にエッジ・モバイルや、LLM のように巨大なモデル(LLMの推論サービングと最適化基盤)では、軽量化が実用可否を分けます。
3. 動く最小例:枝刈りと量子化の効果を擬似的に測る
線形モデルの係数で、(a) 枝刈り(小さい係数を0にする)と (b) 量子化(係数を粗い格子に丸める)が、サイズを減らしつつ精度をどれだけ保つかを見ます。深層モデルでなくても、トレードオフの本質は同じです。
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
rng = np.random.default_rng(0)
X = rng.normal(0, 1, (1000, 50))
true_w = np.zeros(50); true_w[:10] = rng.normal(0, 1, 10) # 実質10次元
y = (X @ true_w + 0.3 * rng.normal(size=1000) > 0).astype(int)
k = 800
Xtr, Xte, ytr, yte = X[:k], X[k:], y[:k], y[k:]
model = LogisticRegression(max_iter=1000).fit(Xtr, ytr)
w = model.coef_[0].copy()
base_acc = accuracy_score(yte, model.predict(Xte))
def predict_with(weights, intercept, X):
return (X @ weights + intercept > 0).astype(int)
b = model.intercept_[0]
# (a) 枝刈り:絶対値が小さい下位60%の係数を0に
thr = np.quantile(np.abs(w), 0.60)
w_pruned = np.where(np.abs(w) < thr, 0.0, w)
acc_pruned = accuracy_score(yte, predict_with(w_pruned, b, Xte))
nonzero = int((w_pruned != 0).sum())
# (b) 量子化:係数を0.25刻みの格子に丸める(粗い表現)
w_quant = np.round(w / 0.25) * 0.25
acc_quant = accuracy_score(yte, predict_with(w_quant, b, Xte))
n_levels = len(np.unique(w_quant))
print(f"元モデル : 精度={base_acc:.3f} 非ゼロ係数={int((w!=0).sum())}/50")
print(f"枝刈り後 : 精度={acc_pruned:.3f} 非ゼロ係数={nonzero}/50 (60%削除)")
print(f"量子化後 : 精度={acc_quant:.3f} 係数の値の種類={n_levels}(粗い格子)")
出力:
元モデル : 精度=0.955 非ゼロ係数=50/50
枝刈り後 : 精度=0.945 非ゼロ係数=20/50 (60%削除)
量子化後 : 精度=0.950 係数の値の種類=11(粗い格子)
出力の意味:係数の60%をゼロにする枝刈りをしても精度は 0.955→0.945 とほぼ維持(元データが実質10次元なので、小さい係数はほぼノイズで削っても効かない)。係数を0.25刻みの粗い格子に量子化しても精度は 0.955→0.950 とほぼそのままで、係数の値はわずか11種類に圧縮されました。サイズ(非ゼロ数・表現精度)を大きく削っても精度はわずかしか落ちない——これが軽量化の効く理由です。ただし削りすぎれば精度は崩れるので、許容精度の範囲で軽量化率を選びます(深層モデルでの具体手法は機械学習分野へ)。
4. 運用の勘所
- 精度の許容低下を先に決める:「精度◯%以内の低下なら許容」を決め、その範囲で最大の軽量化を選ぶ。
- 量子化はまず試す価値が高い:実装が比較的容易で、サイズ約1/4・高速化が見込める。精度低下は多くの場合小さい。
- 蒸留は小モデルを本番に置きたいとき:教師(大)で学習し、生徒(小)を配信。学習コストはかかるが推論が軽い。
- 枝刈りは専用ランタイムとセットで:スパース化を活かすには対応する推論エンジンが要る。形だけ0にしても速くならないことがある。
- 軽量化後は必ず再評価する:精度だけでなく、公平性・分布別性能も確認(本番モデルの監視)。
なぜそうするのか
軽量化が成立するのは、学習済みモデルには冗長性があるからです。多くの重みは最終的な予測にほとんど寄与せず(§3 のノイズ係数)、数値精度も過剰なことが多い。だから精度をほぼ保ったままサイズと計算量を削れます。これがコスト(推論単価・メモリ・電力)に直結し、特にエッジや巨大モデルでは「動かせるか否か」を決めます。ただし冗長性には限りがあるので、削りすぎれば精度が崩れる——許容低下を決めて最大の軽量化を選ぶ、という設計になります。
⚠️ よくある落とし穴
- 軽量化後に再評価しない:精度・公平性が静かに劣化したまま本番投入する。必ず測り直す。
- 枝刈りで0にしただけで速くなると思う:スパース対応ランタイムがないと計算量は減らない。
- 量子化を全層一律でかける:感度の高い層まで粗くすると精度が崩れる。層ごとに感度を見る(深層は機械学習分野)。
- 理論の深追いをここでする:学習時量子化・蒸留の損失設計などは機械学習・深層学習へ。ここは運用効果に集中。
対応 lab
- なし(概念ノート)。枝刈り・量子化の効果は本文 §3 に同梱。LLM 特化の最適化は LLMの推論サービングと最適化基盤。
関連ノート
- レイテンシとスループットのトレードオフ(捌き方の最適化)
- ハードウェアとスケーリング(ハード側の最適化)
- LLMの推論サービングと最適化基盤(LLMの量子化・KVキャッシュ)
- 本番モデルの監視(軽量化後の性能確認)
- 第5章 推論の最適化 目次
- MLOps・AI基盤 全体目次