はじめに — MTP で速くしていたら、出力が少しブレた
これまで、手元の GPU でローカル LLM を動かし、vLLM でのサービング(モデルに文章を生成させて提供する仕組み)を最適化・高速化する話を何本か書いてきました。そのうち、投機デコードの一種である MTP(Multi-Token Prediction) で生成速度を上げる話は、①基礎・②量子化との組み合わせとして扱ってきました。MTP は、小さな予測役に先を何トークンか読ませて、本体モデルがまとめて確認することで速くする仕組みです。
その MTP を使い込んでいくうちに、ひとつ気になることが出てきました。temperature=0(毎回いちばん確率の高い単語を選ぶ、いわば「同じ入力なら同じ出力」のはずのモード)なのに、MTP を入れると出力が少しブレるのです。
同じ質問なのに、MTP ありとなしで文章が微妙に違う。「投機デコードはロスレス(品質を落とさない)」と聞いていたのに、これはどういうことか——気になって、RTX 4070 上の Qwen3.5-2B で調べてみました。この記事は、その切り分けの記録です。
ショートサマリー
MTP で temperature=0 の出力が変わっても、それは品質の劣化ではありません。 答えの中身は正しいまま、変わるのは「第 5 位は〜」↔「5 つ目は〜」のような、どちらでもいい言い回しだけでした。原因は投機そのものではなく、コンピュータの計算につきまとう、ごく小さな誤差が表に出ただけです。同じことは、MTP を切って複数の質問を同時に送るだけでも起きました。
つまり 「出力が変わった=壊れた」ではありません。速度のメリットは、安心して使って大丈夫です。なぜそうなるのか、そして「計算誤差なのか、本物のバグなのか」をどう見分けるのかを、順に見ていきます。
対象読者
- ローカル LLM を vLLM などで動かしていて、投機デコード(MTP / EAGLE / Medusa)で速くしてみたい人
-
temperature=0なのに出力が揺れて「あれ?」となったことがある人 - LLM の回帰テストやスナップショット比較など、出力の再現性を気にしている人
前提知識は多くなくても読めるように、用語は本文で都度補足します。投機デコードの基礎(先読み → 確認 → 採用、という流れ)をもう少し丁寧に知りたい方は、既刊の「MTP①基礎」をどうぞ。本記事は続編ですが、単体で読めます。
背景:「ロスレス」なら、出力も同じになるはずでは?
MTP の先読みは、本体モデルの確認を通ったものだけが採用されます。この採否の判定がうまくできていて、本体だけで生成したときと同じ確率のバランスが理論的に保証される——これが「ロスレス」と呼ばれる理由です(Leviathan et al. 2023、Chen et al. 2023)。
とくに temperature=0 では話はもっと単純です。毎回いちばん確率の高い単語を選ぶだけなので、採用されるのは「本体も選んだはずの単語」だけ。だから素朴には、こう思えます。
「temperature=0 なら、MTP を付けても付けないときと 1 文字も違わないはずだ」
この予想が実測でどう崩れるか、が本記事の中身です。
検証環境
自分の RTX 4070(12GB)で計測しました。条件は以下です。
- ハードウェア: NVIDIA RTX 4070 (12GB), Ubuntu, Xorg + GNOME (常時 ~0.7GB 消費)
- 推論エンジン: vllm/vllm-openai:nightly (v0.21.1rc1.dev243)。CUDA はイメージ同梱版
- モデル: Qwen/Qwen3.5-2B (FP16)
- 投機: --speculative-config '{"method":"qwen3_next_mtp","num_speculative_tokens":N}' (N=1,3,5)
- 生成条件: temperature=0, max_tokens=400, enable_thinking=false
- プロンプト: 日本語 12 件 (フィボナッチ・都道府県 top5・素数列挙・惑星の順序 ほか)
- 手順: MTP なし / MTP n=1 / n=3 / n=5 を順に起動し、各プロンプトを 2 回ずつ送信
以降、本記事では MTP を使わない(先読み数 n=0 にあたる)通常の生成を「ベースライン」と呼びます。比較はすべてこの状態を基準にします。
なお今回は 12 問・各 2 回という小規模な観察です。厳密な統計というより、傾向をつかむための実測として読んでください。
計測は 2 つの見方で行います。ひとつは、同じ条件で同じ質問を 2 回投げて一致するか(乱数のせいではないと確かめるため)。もうひとつは、ベースラインと MTP で出力が一致するか、です。
結果①:同じ条件なら毎回同じ。でも MTP のあり/なしで変わる
同じサーバ・同じ設定なら、出力は毎回まったく同じでした。 ベースライン(MTP なし)でも MTP ありでも、同じ質問を 2 回投げれば、12 問すべてで 1 文字まで一致します。つまり、ゆらぎはサイコロ(乱数)のせいではありません。
ところが、ベースラインと MTP を比べると、決まって同じところが変わります。全 12 問のうち何問が完全に一致したかを数えると、こうなりました(■ が長いほど、よく一致している=再現できている、という意味です)。
| 比べたもの | 一致した問題数(全 12 問中) | 受理率※ |
|---|---|---|
| 同じ条件で 2 回くり返す |
████████████ 12 問 |
— |
| MTP あり vs なし(n=1) |
███░░░░░░░░░ 3 問 |
71% |
| MTP あり vs なし(n=3) |
███░░░░░░░░░ 3 問 |
45% |
| MTP あり vs なし(n=5) |
████░░░░░░░░ 4 問 |
33% |
※受理率=各 MTP 構成で、先読みしたトークンのうち本体の確認を通って採用された割合。高いほど先読みがよく当たっている、という意味です(「同じ条件で 2 回」は比較ではないので受理率はありません)。
おもしろいのは、先読みする数 n を増やしても、一致する問題数はほとんど変わらないことです(3 → 3 → 4)。受理率は n が増えるほど下がる(71% → 45% → 33%)のに、一致する数はほぼ横ばいです。これは、出力が変わるかどうかが「どれだけ深く先読みしたか」ではなく、「先読みをしたか、しなかったか」だけで決まっているようだ、と読めます(一致数の差は 1 問なので、あくまで傾向としてですが)。
結果②:変わるのは「言い回し」だけで、意味は同じ
分かれた出力を 1 つずつ読むと、壊れているものは 1 つもありませんでした。違いはどれも、意味がほぼ同じ言い回しの差です。
- 「第 5 位は〜」↔「5 つ目は〜」
- 「以下にリストします」↔「以下に示します」
そして、答えが 1 つに決まる問いは完全に一致しました(惑星の順序・素数の列挙・フィボナッチの値)。さらに、短い出力ほど分かれにくい傾向がありました。今回のうち出力が短かった 2 件は、どの構成でも一度も分かれませんでした。この「長いと変わる/短いと変わらない」が、次の理由の核心です。
分岐は品質劣化ではありません。確率のバランスは保たれていて、変わるのは「同点の場面でどちらの言い回しを選ぶか」だけです。事実・数値・結論は保たれます。
なぜ起きるのか:計算の「足す順番」で答えがわずかに変わる
短く言うと——コンピュータの足し算は足す順番で答えがほんのわずか変わり、それが「ほぼ同点」の場面で 1 位と 2 位を入れ替えてしまうからです。
MTP はトークンをまとめて確認するので、ベースラインの 1 個ずつ処理とは計算の順番が変わります。すると、単語ごとの点数(logits)が、目に見えないくらいごくわずかに変わります。
ほとんどの場面では、1 位の単語が 2 位を大きく引き離しています。だからこの程度のズレはまったく影響しません。ところが 1 位と 2 位がほぼ同点の場面だけは、この小さなズレで順位が入れ替わります。そこで別の単語が選ばれ、そこから先が枝分かれしていきます。この流れを図にすると、こうです。
なぜ「よく変わるのに品質は同じ」なのか
1 か所で順位が入れ替わる確率はごく小さいのですが、長い文章なら「どこか 1 か所」で起きやすくなります。だから長い出力ほど変わりやすく、短い出力は変わりません。もし品質が落ちているなら文章の冒頭から崩れるはずですが、実際には分岐は文中にばらけていました。これは「小さなズレが積み重なった跡」であって、品質が偏った跡ではありません。
結果③:投機を切っても、まとめて処理するだけで変わる
原因が「計算の順番の違い」なら、投機以外でも同じことが起きるはずです。そこで MTP を完全に切ったベースラインで、同じ質問を 2 通りの送り方で比べました。
- 単発:1 件ずつ順番に送る。
- 同時:12 件を一斉に送る(まとめて処理させる)。
結果、12 問中 4 問しか一致しませんでした(8 問で文章が変わった、ということです)。同じサーバでの連続計測なので、違いは「他の質問と一緒に処理されたか」だけです。中身はやはり言い回しの差でした。
これは Thinking Machines Lab が 2025 年に指摘した「バッチ不変性の欠如」——まとめて処理する組み合わせが変わると計算の順番も変わり、同じ入力でも出力が変わる——を Qwen3.5 上で再現した形です。
さらにおどろいたことに、サーバをいったん止めて起動し直すだけで、ベースラインどうしでも 12 問中 5 問しか一致しませんでした(残り 7 問は変わった、ということです)。同じ設定・temp0 なのに、です。これは、起動のたびに内部の計算プログラムが組み直され、選ばれる計算のやり方が少し変わることがあるためと考えられます。ここでも、結局は計算の順番が変わっています。
まとめると、temperature=0 でも出力を変えるきっかけは 3 つあり、ベースラインと一致した問題数(全 12 問中)で並べるとこうなります。どれも一致は半分以下まで落ちます。
| きっかけ | 一致した問題数(全 12 問中) | 何が変わるか |
|---|---|---|
| 投機(MTP のあり/なし) |
███░░░░░░░░░ 3〜4 問 |
まとめて確認するので計算の順番 |
| まとめ処理(1 件ずつ↔同時) |
████░░░░░░░░ 4 問 |
一緒に処理する質問の組み合わせ |
| 起動し直し |
█████░░░░░░░ 5 問 |
起動ごとの計算のやり方 |
3 つとも、正体は同じと考えてよさそうです。「計算の順番が変わる → 点数がわずかにズレる → ほぼ同点の場面で単語が入れ替わる」——入口が違うだけで、起きていることはまったく同じです。
この表がいちばん言いたいのは、こういうことです。temperature=0 でも「毎回 1 文字までそっくり同じ出力」が返るのは、MTP のあり/なし・質問の送り方・サーバの起動が、すべて同じそろい方をしているときだけ。裏を返せば、どれか一つを変えるだけで完全一致は崩れます。
- MTP を入れれば変わる(結果①)
- MTP を使っていなくても、質問をまとめて同時に送るだけで変わる(結果③の前半)
- そもそも同じ質問でも、サーバをいったん止めて立ち上げ直すだけで変わる
「文章がブレたから MTP を疑う」と考えがちですが、実際には MTP だけの話ではありません。並列で送る・再起動する——ふだん何気なくやっている操作でも、同じことは起きます。
考察:再現性が欲しいときにどうするか
出力が変わったとき、まず「品質が落ちたのか、ただの計算誤差なのか」を見分けます。目安はこうです。
- 変わったのが言い回しだけで、事実・数値・答えが同じ → 計算誤差です。バグではありません。
- 冒頭から崩れる/答えの正誤が変わる/同じ言葉を繰り返すループになる → これは別問題です。量子化の破綻などを疑います(計算誤差では説明できない規模です)。
そのうえで、1 文字単位の再現性がどうしても要る場面(回帰テストや監査)では、次を意識します。
- 完全一致を前提にしたテストは temp0 でも壊れます。 1 文字比較ではなく、意味(数値・答え・正誤)が同じかで判定します。
- 再現性が要るなら「同じサーバ・同じ設定」に固定します。 その範囲でだけ出力が決まる、と割り切ります。
- 投機の速度メリットは使って問題ありません。 品質は落ちていません。「速いが 1 文字一致は保証しない」と期待値を合わせるだけです。速度そのものは既刊「MTP②量子化比較」で扱っています。
まとめ
- 投機デコード(MTP)は品質を落としません。ですが
temperature=0でもベースラインと 1 文字単位で一致するとは限りません。 - 変わったのが言い回しだけで事実が保たれているなら、それは計算誤差の跡であってバグではありません。
- きっかけは「投機」「まとめ処理」「起動し直し」の 3 つで、正体は同じ計算誤差です。再現性が要るなら 1 文字比較ではなく意味で判定し、同じサーバ・同じ設定に固定します。
今後は FP8 / AWQ4 量子化や別 GPU でも同じことが起きるか確かめてみたいと思っています。
参考文献
- Leviathan et al., "Fast Inference from Transformers via Speculative Decoding," 2023. arXiv:2211.17192
- Chen et al., "Accelerating Large Language Model Decoding with Speculative Sampling," 2023. arXiv:2302.01318
- Thinking Machines Lab, "Defeating Nondeterminism in LLM Inference," 2025.
- 既刊:① 投機的デコーディングと MTP の基礎、② 量子化と MTP の組み合わせ比較。本記事はその続編です。