vLLM は INT5〜7 を動かせる
vLLM の重み量子化といえば、長らく4bitか、8bitか、量子化せずに放置するかのどれかだった。
4bit は粗すぎる。KLD は上がるし、皆が思う以上に推論の精度は落ちる。使い物にはなるが、その程度にしかならないくらいになる。
一方で、 8bit はデカい。RTX 3090 x2で Qwen3.8-27B をvLLMで動かそうとすると、8bit では重みだけで約 29.3 GiB を占める。そこにフルコンテキストの KV キャッシュや MTP、Vision Tower を足すと、OOM 不可避だろう。
llama.cpp はこの様な問題が(あまり)ない。gguf は、4bit と 8bit 以外の幅広いビット数に対応している。
今回は、まあまあ最近マージされた hummingkernel のおかげで INT5〜7 を動かせることとその性能、そして embed_tokens と lm_head の量子化は避けるべきかどうかについて解説する。
1. vLLM で INT5〜7 が動く仕組み
vLLM の PR #46389 がマージされて、vLLM で 5/6/7 bit が動くようになった。
その PR では humming-kernels の HummingLinearKernel が追加され、これのおかげで CompressedTensorsWNA16 の対応ビット幅テーブル(WNA16_SUPPORTED_TYPES_MAP)に [2, 3, 4, 5, 6, 7, 8] が追加された。
つまり、2 から 8 までのすべてのビット幅がサポートされている。これを使わない手はない。
2. 中間ビットのつよさ
INT5 や INT6 はどれほど強いのかをみるため、Hugging Face 上に公開されている Qwen3.8-27B の量子化モデル 26 種類(GPTQ、AWQ、AMD Quark、compressed-tensors、FP8 など)をダウンロードし、同一の評価で比較した。
評価には、github-code-clean、 proof-pile-2、peS2oから抽出した 8,192 および 32,768 トークンの長文を使用し、合計 589,788 箇所の次トークン予測位置について、BF16 元モデルに対する KLD と Top-1 一致率を測定した。
公平を期すため、全モデルを BF16 に逆量子化して同一の F.linear 実装で走らせ、カーネル差によるブレを排除している。

- 左上: 17〜20 GiB 付近に集まっている。KLD は 0.010〜0.030 nats
- 右下: 27〜34 GiB 付近に集まっている。KLD は 0.0006〜0.0012 nats
- 中央:
Minachist/Qwen3.8-27B-INT5-Flat-5.7bpw-AutoRound(18.5 GiB / 5.73 bpw): KLD 0.00373, Top-1 98.10%Minachist/Qwen3.8-27B-INT6-Flat-6.6bpw-AutoRound(21.3 GiB / 6.60 bpw): KLD 0.00151, Top-1 98.73%Minachist/Qwen3.8-27B-INT6-Mixed-AutoRound(23.6 GiB / 7.30 bpw): KLD 0.00099, Top-1 98.94%
Top-1 一致率を見てみると:

4bit の限界は 97.2% 付近にあるが、INT5-Flat は 5.73 bpw でありながら 98.10% を記録、INT6-Mixed では 98.94% と、8bit クラス(99.1〜99.3%)にちょっと手が届く位置くらいにはいる。
4bit から 1〜2 bit 増やすと、量子化誤差は 1/3 から 1/10 に激減する。4bit では耐えられないが、8bit は載らないという場合(つまりほぼ常時)、この中間ビットはまさに最適解だろう。
3. embed_tokens と lm_head について
あと、Qwen3.8-27B の語彙サイズは 248,320、隠れ層次元は 5,120で、これを BF16 のまま持つと、embed_tokens で 2.37 GiB、lm_head も 2.37 GiB、合計で 4.74 GiB の VRAM を消費する。これは結構大きい。
モデルの作者がそれを無視する理由は、多分 vLLM の実装漏れがある。vLLM には CompressedTensorsEmbeddingWNA16Int という量子化埋め込みを実行できるカーネルが最初からあるが、vllm/model_executor/models/qwen3_5.py(Qwen3.8 もこのクラスを通る)の中で、
self.embed_tokens = VocabParallelEmbedding(self.vocab_size, config.hidden_size)
と書かれており、quant_config も prefix も渡していない。そのため、量子化された embed を読み込もうとするとクラッシュする。
embed_tokens と lm_head は量子化すべきか
語彙ヘッドを BF16 のままにして中間層を INT4 に削るのと、語彙ヘッドを INT8 に落として浮いた 2.33 GiB で中間層を INT6 や INT7 に引き上げるのと、どちらが性能が高いかも実測した。

このグラフでは、横軸を embed_tokens、lm_head、MTP、Vision Tower を除いた Transformer 本体のみのサイズにして描画している。この結果から見て、少なくともこのモデルにおいては、embed_tokens と lm_head を BF16 で残す理由はあまりなく、INT8 に落とすのが正解である。実際多くの GGUF の作者は、その層は普通に量子化している。BF16 との比較なら 4.74 GiB 近くの削減ができる。
4. linear_attn(再帰層)を INT5/6 にして長文で発散しないのか?
再帰層を INT5 や INT6 に落としたら、文脈が長くなるにつれて丸め誤差が蓄積し、長文で発散するというような話をよく聞くので、それも実測した。
長文コンテキストにおいて、トークン位置をビン(0-512、512-2k、2k-8k、8k-32k)に分割し、位置ごとの平均 KLD を測定した。

文脈が長くなるにつれて、KLD はむしろ単調に減少している。最初(0-512)の KLD に対し、最深部(8k-32k)での KLD は 0.52〜0.62 倍まで下がっている。
この後 65,536 トークンや 131,072 トークンまで測定を延長した実験でも、INT8 に対する INT6 の KLD 比率はすべてのビンで 1.20〜1.27 になっていて、誤差が発散する兆候はなかった。
自分が思うに、その説は linear_attn.{in_proj_a,in_proj_b} を量子化してはいけない、という考えが linear_attn 全体に間違って広まったという感じだろう。
まとめ
- vLLM は INT5〜7 をネイティブに実行できる:
HummingLinearKernelにより、sm75 以降で追加のビルドなしに動作する。 - 中間ビットはつよい: 4bit と 8bit の間に、INT5/6/7 ができたのはでかい。1 bit 上げるだけで、精度は上がるから。
「量子化は 4bit か 8bit」という古い常識に縛られる必要はもうない。 限られた VRAM で最大の文脈長と最高の品質を両立させたいなら、ぜひ INT5〜7 の世界に足を踏み入れてみてほしい。
Read this post in English: vLLM Can Run INT5–7