なぜスペキュラティブ・デコーディングは高速なのか?
Why Is Speculative Decoding Fast? (jbarrow.ai)
要約
スペキュラティブ・デコーディングが高速なのは、処理する仕事量が少ないからではなく、GPUが行う仕事の種類が変わるためです。従来のトークンごとの生成ではメモリバウンドになりがちですが、スペキュラティブ・デコーディングは複数のトークンを並列処理することで、GPUの計算能力を最大限に活用し、特にバッチサイズが小さい場合にデコーディングを高速化します。
全文翻訳
なぜ推論的デコーディングは高速なのか?
それは、仕事量が減るからではありません。GPUが行う仕事の種類が変わるからです。
推論的デコーディングが高速なのは、トークンあたりの仕事量が少ないからだ、つまりドラフトの「検証」は同じトークンを生成するよりも少ないFLOPs(浮動小数点演算)で済む、という一般的な誤解があります。
これは真実ではありません。
推論的デコーディングの直感に反する点の1つは、実際にはより多くの仕事を行うということです!同じシーケンスに対して実行される浮動小数点演算(FLOPs)の総数は増加します。
では、なぜ高速なのでしょうか?計算効率のためではない場合、正確なドラフトモデルは何をもたらすのでしょうか?
答えは、GPUが行っている仕事の種類にあります。
SpecDec の簡単な復習
推論的デコーディングは、小さなドラフトモデルを使って有力なドラフトシーケンスを提案し、次に大きなモデルを使ってそのシーケンスを並列(単一のフォワードパスで)検証する技術です。
従来のLLMデコーディングは、一度に1トークンずつ行われます:
[画像: デコーダーLLMがプロンプトトークン "The quick" を受け取り、単一の次のトークン "brown" を出力している]
しかし、推論的デコーディングでは、一度に多くのトークンを調べ、大きなモデルによって生成されたであろうトークンを受け入れることができます:
[画像: デコーダーLLMがプロンプトと共に3つのドラフトトークンを一度に検証し、"brown" と "fox" を受け入れ、"hopped" を "jumped" に置き換えて拒否している]
仕事の種類
モデルがGPU上で実行されるとき、さまざまな種類の仕事が行われます。明白なのは、行列乗算、ドット積、その他のテンソル演算の実行です。しかし、あまり明白ではない重要なものとして、グローバルメモリからローカルメモリへの(そしてその逆の)データのロードがあります。
GPUが演算のためにデータのロードを待っている場合、それをメモリバウンドな操作と呼びます。すべてのデータがロードされているが、テンソル演算の完了を待っている場合は、コンピュートバウンドな操作と呼びます。
(これについてより深く探求したい場合は、以前の記事をご覧ください:A Visual Guide to the Roofline Model)
LLMが一度に1トークンずつデコードしているとき(最初の図)、それは非常に メモリバウンドです。トークンごとに、モデルのすべてのアクティブなパラメータ(例: Qwen3.5-27B の270億パラメータ)をストリーミングする必要があります。LLMが単一ステップで数千トークンのバッチデコードを行っているとき(2番目の図)、それはコンピュートバウンドです。
[画像: 5つのシーケンスが並列デコードされており、T = 5 トークンが単一ステップでデコーダーLLMに入力され、5つのトークンが出力されている]
LLMデコーディングがメモリバウンドな場合、コンピュートを無駄にしています。GPUはより多くの演算を行うことができますが、行っていません。例えば、1シーケンスではなく2シーケンスをデコードしていたとしても、トークンあたりの時間はほとんどかかりません。
推論的デコーディングは、その余分なコンピュートを利用してデコーディングを高速化する方法です(バッチサイズが小さい場合)。
広さ(Breadth)と深さ(Depth)のトレードオフ
具体的に考えてみましょう。あるフォワードパスで実行するのに最適なトークン数が5であるとします。もしそれより少なく実行すればメモリバウンドになり、多く実行すればコンピュートバウンドになります。
2つの選択肢があります。
第一に、5シーケンスのバッチサイズを実行し、それぞれが一度に1トークンずつデコードします。これを「広さ」(シーケンス間の)と呼びます:
[画像: 同じ T = 5 バッチを広さとして読み取ったもの:5つの個別のシーケンスがそれぞれ1トークンずつ進んでいる]
あるいは、推論的デコーディングを使用して1つのシーケンスで5トークンを実行し、そのシーケンスの「深さ」を高くすることができます:
[画像: 単一シーケンスからの T = 5 トークンがデコーダーLLMに入力され、5つのトークンのうち4つが受け入れられている]
どちらの場合も、GPUはほぼ同じ量の仕事を行います。最初のケースでは、5つのシーケンスすべてが少しずつ進歩し、2番目のケースでは、私たちの1つのシーケンスが大きく進歩します。
もしシーケンスを増やして同じ深さまで推論すると、コンピュートバウンドになるため、すべてを遅くするだけです。
しかし、小さなバッチサイズ(バッチサイズ << 最適トークン数)では、推論的デコーディングにより「無料」のコンピュートを利用して、すべてのシーケンスをより速く完了させることができます!
なぜこれが重要なのか?
これらはすべて些細なことに聞こえるかもしれません。推論的デコーディングは確かに小さなバッチサイズで物事を速くするので、それが同じ仕事量かそれ以上かどうかなんて誰が気にするでしょうか?
これは、実際の負荷をサーブし始めたり、推論的デコーディングの研究を掘り下げたりするときに重要になります。最適なバッチサイズが5であるのに、推論的デコーディングをオンにして実行している場合(以下の図を参照)、実際にはメモリを移動するために使用できたコンピュートを無駄にしています:
[画像: 5つのシーケンスがそれぞれ4トークン深く推論しており、単一ステップで T = 20 トークン、検証されたトークンの多くが拒否されている]
特に受け入れ率が100%でない場合、拒否されたトークンはすべて追加の無駄な仕事です。
これが、高スループット時にvLLMが推論的デコーディングを完全にオフにする設定を --speculative-disable-by-batch-size パラメータで提供している理由です。
そして、この図を見始めた研究者たちは疑問に思い始めました:なぜすべてのタイムステップで同じ深さまで推論するのでしょうか?余分なコンピュートがあるとき(小さなバッチサイズ)は深く推論し、バッチサイズが大きいときは浅く、あるいは動的にシーケンスごとに推論するのはどうでしょうか:
[画像: 5つのシーケンスが同じステップで異なる深さまで推論しており、合計 T = 13 トークン]