「量子化されたモデルなら手元のPCでも動く」——ローカルLLMの話題になると、この量子化という言葉が必ず顔を出します。
ただ、実際に何をどう削っているのかまでは、私もぼんやりとしか説明できませんでした。
この記事では、量子化が何をする処理なのか、なぜモデルが軽くなるのかを順番に整理します。
量子化とは何か
量子化とは、モデルの重みをより少ないビット数のデータ型で表現し直す処理です。
学習を終えた直後のモデルは、ひとつの重みを32ビットの浮動小数点数(float32)で持っています。
これを8ビット整数(int8)や4ビット整数(int4)に置き換えるのが、量子化の中身。
数値の刻みが粗くなるぶん情報は落ちますが、そのかわりに容量と計算コストが下がるという取引になっているのです。
なぜモデルが軽くなるのか
効き方は単純で、重み1つが占めるバイト数がそのまま減ります。
float32 は1つあたり4バイト、int8 は1バイト、int4 は0.5バイト。
Hugging Face のドキュメントでも、int8 のモデルは float32 のおよそ4分の1のサイズになると説明されています(Quantization concepts)。
70億パラメータ(7B)のモデルに当てはめると、こういう計算になる。
# 重み1つあたりのバイト数 x パラメータ数
float32 : 4.0 byte x 7,000,000,000 = 28.0 GB
int8 : 1.0 byte x 7,000,000,000 = 7.0 GB
int4 : 0.5 byte x 7,000,000,000 = 3.5 GB
# ※実際には KV キャッシュなどの作業領域が別途必要
手元の16GBのマシンに載るかどうかが、この一段の差で決まるわけです。
int8 と int4 では性質が変わる
int8 は float32 の値域を256段階に、int4 は16段階に押し込めます。
段階が16しかない int4 では丸め誤差が大きくなるので、int8 より精度の落ち方がはっきり出る。
そのため int4 では、誤差を抑える工夫を組み込んだ GPTQ や AWQ といった手法が併用されるのが一般的です。
もうひとつ知っておくと納得しやすいのが、weight packing という仕組み。
ほとんどのハードウェアは4ビット単位のデータを直接扱えないため、int4 の値を2つ並べて1バイトに詰め込んで保存しています。
メモリから読み出す量が int8 の半分で済むので、計算そのものより「データを運ぶ時間」の短縮が効いてくるのです。
精度はどこまで落ちるのか
ここは「落ちる」とだけ覚えるより、どこで落ちるかを知っておくほうが役に立ちます。
量子化の誤差は、値のばらつきが大きいテンソルほど大きくなる。
そこでテンソル全体で1組の変換パラメータを共有する per-tensor ではなく、チャネルやブロック単位に分ける per-channel のほうが精度を保てます。
また、学習後に変換する PTQ(Post-Training Quantization)と、学習中から量子化の影響を織り込む QAT(Quantization-Aware Training)という2つの流派がある。
配布済みモデルをダウンロードして使う場面で目にするのは、ほぼ PTQ のほうです。
触ってみるときの入り口
Transformers なら、読み込み時に設定を渡すだけで4ビット量子化が有効になります(bitsandbytes のドキュメント)。
from transformers import BitsAndBytesConfig
import torch
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16,
)
# from_pretrained() に quantization_config を渡して読み込む
# ...
重みは4ビットで保持されますが、計算自体は16ビットや32ビットで行われる点に注意。
Ollama のようにローカル実行を前提としたツールでは、q4_K_M のような名前で量子化済みファイルが配布されています。
q のあとの数字がビット数を表すので、モデル名を見るだけでどれくらい削られているかは読み取れるのです。
最後に
量子化はモデルを小さくする魔法ではなく、精度とメモリのどちらを取るかを自分で選ぶ操作でした。
まず int4 で動かしてみて、出力が怪しいと感じたら int8 に上げる。
この順番で試すと、手元のマシンで扱えるモデルの上限が見えてきます。
以上です。












コメントを残す