0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

# Qwen3.5-2B で Multi-Token Prediction を試す ① 投機的デコーディングと MTP の基礎

0
Last updated at Posted at 2026-05-24

はじめに

ローカル LLM を触っていて毎回気になるのは、やっぱり decode の遅さです。1 token ずつ生成する以上、どうしてもじりじりした体感になります。

ここを速くするアプローチは大きく2つあると思っていて、

  1. モデル自体を軽くする (量子化、蒸留、パラメータ削減、より小さなモデルに乗り換える)
  2. モデルはそのままで decode を効率化する (投機的デコーディング、KV cache 最適化など)

このシリーズで追いかけたいのは 2 のほうです。「いま使っているモデルをそのまま、もっと速く動かしたい」というやつ。モデルを小さくすれば確かに速くはなりますが、それは「別のモデルにした」だけで、頭の中のモデル選定が振り出しに戻ります。そうではなくて、今あるモデルの decode をどう速くするか、という話を扱いたいです。

そもそも LLM の decode が遅いのは、処理がメモリ律速 だからです。GPU は 1 step ごとにモデルの重みを VRAM から全部読み出していますが、ボトルネックになっているのは演算より重み転送のほうです。重みを1回読むコストに対して 1 token しか進まないので、演算ユニットはわりと暇しています。

ここに「複数 token を一気に予測して、外したら戻す」という 投機的デコーディング (Speculative Decoding) を差し込むと、同じ重み読み出しコストで複数 token 進められる可能性が出てきます。これが今回の主題です。

Qwen3.5 はモデル本体に Multi-Token Prediction (MTP) ヘッド が組み込まれていて、別の draft モデルを用意せず投機ができるらしい。それなら手元の RTX 4070 12GB で素直に試せそうです。

先に結論を書くと、Qwen3.5-2B FP16 で baseline 115.4 tok/s → MTP-FP16-n1 で 133.5 tok/s (+15%)。効果は出ています。ただし先読み数を増やすほど逆効果になる、というのが今回いちばん面白かった観察です。

シリーズは3本に分けます。長くなったので。

  • ① 本記事: 投機的デコーディングと MTP の基礎 + FP16 ベースライン
  • ② 量子化 (FP8 / AWQ4) × MTP の比較 + Ollama 参考値
  • ③ 番外編: Qwen3.5-4B + D-Flash

投機的デコーディングとは何か

通常のテキスト生成は1 token ずつです。

LLM は前までの token 列を入力として次 token を予測し、それを入力に足してまた次を予測します。1 step ごとに forward を1回回すので、生成 token 数 = forward 回数 です。

これは GPU からするともったいない計算です。重みを VRAM から読み出すコストに対して、1回あたりの計算量が少ないからです。

投機的デコーディングは、ここに draft (下書き)検証 (verify) の2段階を入れます。

  1. 安いモデル、もしくは専用ヘッドで、次の k token を まとめて先読み する (draft)
  2. target モデルが、その k token を 1回の forward で並列に検証 する (verify)
  3. 連続して正しかった分は採用、間違えたところで打ち切って次へ

連続で受理 (accept) された token が多いほど、forward 1回で複数 token 進めるので速くなります。逆に当たらないと、draft 側の計算が無駄になります。

このため、投機的デコーディングには2軸の評価が要ります。

  • 受理率 (acceptance rate): draft のうち受理された割合
  • 1 step で進める token 数: 受理率 × 先読み数 (おおよそ)

「受理率が高いほど速い」は単純すぎる話で、先読み数を増やせば受理率は下がります。このトレードオフの最適点を探すのが、num_speculative_tokens のチューニングです。

Qwen3.5 と内蔵 MTP の話

Qwen3.5 は 2026 年に Alibaba がリリースしたシリーズで、いくつか面白い特徴があります。

  • Gated DeltaNet + Mamba のハイブリッドアーキ: 通常の softmax attention に加えて、SSM 系の線形 attention レイヤを混ぜています
  • ネイティブマルチモーダル: 0.8B / 2B / 4B / 9B / ... の全サイズに Vision Encoder が同梱
  • MTP ヘッド内蔵: 投機的デコーディング用の Multi-Token Prediction ヘッドが target モデル自体に組み込まれている

最後の MTP が今回の主役です。

通常の投機的デコーディング (たとえば EAGLE や draft-model 方式) は、target とは別に小さな draft モデルを動かします。VRAM もモデル管理コストも増えます。

MTP では、target モデル自体が複数 token を先読みする能力を持っています。専用の小さなヘッドが target の特徴量から複数 token を一気に提案し、target が同じ forward でその検証も行います。

vLLM では qwen3_next_mtp という method 名で実装されています。Qwen3-Next 系列で導入された機構が Qwen3.5 にも引き継がれている形です。同じ MTP 系には deepseek_mtp / mimo_mtp / glm4_moe_mtp などモデル別の実装があります。

ちなみに Mamba ハイブリッドの方は、今回は VRAM 制約 という形で効いてきます。Mamba の SSM state は max-model-len に関係なく固定サイズで常に確保されるため、コンテキスト長を下げても VRAM 消費がほとんど減りません。これが起動設定の細部に響きます。

実行環境

項目 内容
GPU RTX 4070 12GB
実 VRAM 11.59 GiB
OS Ubuntu
vLLM vllm/vllm-openai:nightly (v0.21.1rc1.dev243)
API OpenAI 互換 /v1/chat/completions
temperature 0
thinking enable_thinking: false
concurrency 1
max_model_len 2048

vLLM は Docker で起動しました。起動コマンドは MTP なしならこんな感じです。

vllm serve Qwen/Qwen3.5-2B \
  --dtype auto \
  --attention-backend flash_attn \
  --max-num-batched-tokens 4096 \
  --max-model-len 2048 \
  --gpu-memory-utilization 0.93

MTP を有効にするには --speculative-config を足します。

vllm serve Qwen/Qwen3.5-2B \
  --dtype auto \
  --speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 1}' \
  --attention-backend flash_attn \
  --max-num-batched-tokens 4096 \
  --max-model-len 2048 \
  --gpu-memory-utilization 0.93

num_speculative_tokens は今回 1 / 3 / 5 の3パターン測ります。

計測方法

プロンプトは2条件にしました。

ケース 入力 出力 目的
Medium 約100 tokens 256 tokens 通常利用
Long 約200 tokens 512 tokens decode 区間を伸ばして MTP 効果を見る

各構成で warmup 1 回、本計測 5 回。tok/s と elapsed_ms の中央値を採用しました。

Qwen3.5 は thinking モードがデフォルト有効ですが、今回は enable_thinking: false を指定して 通常の日本語回答だけを生成 する設定で計測しています。

MTP 構成は /metrics の前後差分から受理率を計算しています。

curl -s http://localhost:8000/metrics | grep spec_decode | grep -v "^#"

spec_decode_num_draft_tokens_totalspec_decode_num_accepted_tokens_total の差分で、acceptance rate = accepted / draft を出します。

FP16 ベースライン結果

まずは MTP の効果が一番素直に見える FP16 から。

構成名は Base-{量子化} を baseline、MTP-{量子化}-n{先読み数} を MTP ありで統一します (シリーズ通して同じ命名)。

Medium (出力 256 tokens)

構成 tok/s baseline 比 acceptance
Base-FP16 115.4 1.00 -
MTP-FP16-n1 126.4 1.10 0.52
MTP-FP16-n3 119.7 1.04 0.36
MTP-FP16-n5 94.3 0.82 0.22

Long (出力 512 tokens)

構成 tok/s baseline 比 acceptance
Base-FP16 116.0 1.00 -
MTP-FP16-n1 133.5 1.15 0.60
MTP-FP16-n3 120.6 1.04 0.36
MTP-FP16-n5 91.1 0.79 0.21

MTP の効果はちゃんと出ていますが、面白いのは num_speculative_tokens を増やすほど逆効果になる という傾向です。

n=1 → n=3 で受理率が 0.60 → 0.36 に落ちます。先読みを3つ連続で当て続けるのはかなり難しい、ということです。結果として n=3 では1 step あたりの実効 token 数が n=1 より増えず、オーバーヘッドだけが積み上がります。

n=5 になると受理率が 0.21 まで下がり、ベースラインより遅くなります。先読み5 token の draft コストが利益を上回った結果です。

vLLM 公式ドキュメントは「とりあえず n=1」と書いていますが、少なくとも Qwen3.5-2B FP16 + RTX 4070 + 日本語回答 (thinking off) の組み合わせでは、公式推奨の n=1 がそのまま最適 という結果になりました。

まとめと次回予告

今回分かったこと。

  • 投機的デコーディングは受理率と先読み数の両方で決まる
  • Qwen3.5 は target 内蔵 MTP ヘッドを持ち、別 draft モデル不要で投機できる
  • FP16 ベースライン 115.4 tok/s に対し、MTP n=1 で 133.5 tok/s (+15%)
  • 受理率は n=1 で 0.60、n=3 で 0.36、n=5 で 0.21 と急落
  • n=3/n=5 はオーバーヘッドが利益を上回り、n=5 はベースラインより遅い
  • n=1 が最速 (公式推奨通り)

次回 ② では、これを FP8 / AWQ4 にも当てはめて量子化との組み合わせを比較します。「全量子化で n=1 だけが有効」という傾向が他の量子化でも同じか、Ollama との比較も見ていきます。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?