🚀

vLLMのSpeculative Decodingによる推論高速化を試す

に公開

はじめに

この記事では、Speculative DecodingによるLLMの推論高速化をvLLMで試し、簡単なベンチマークを行った結果を共有します。

Speculative Decodingについて

最初に、Speculative Decodingについて簡単に解説します。

Speculative Decodingとは、大型のモデルの推論をする際、より小型のモデルを利用して推論を高速化する手法です。この本来の出力を得たい大型のモデルをTarget Model、高速化のための小型のモデルをDraft Modelと言います。

Speculative Decodingでは通常の推論とは違い、推論の際にまず小型のDraft Modelが一定のDraft Tokens分の生成を行い、候補となるトークン列を提案します。その後Target ModelはこのDraft Tokensに対して確率分布を元にこの提案が正しいかを一括で検証します。検証の結果Target Modelによって受理された先頭部分は採用し、不採用となった位置の直後のトークン(Bonus Token)だけをTarget Modelが生成します(全てが採用された場合も同様に1トークン生成)。このBonus Tokenの生成は検証と同時に行えるので、これに追加の計算は必要ありません。これらの処理を何度も繰り返し出力を行います。

LLMは生成の際に1トークンずつ逐次的に生成するので、5トークン出力しようとした場合5回分forwardをしなければなりません。一方、その逐次的な性質を逆に利用すると、既存のトークン列に対してそれが尤もらしいかの検証はバッチ処理で1回のforwardで行うことが出来ます。

この性質を利用し、複数回処理が必要で時間のかかる生成部分は軽量な小型モデルの方で行い、1回で済む検証部分のみを大型のモデルで行うことで推論を高速化する手法がSpeculative Decodingになります。

より詳しい解説は下記記事等をご確認ください。

https://www.jiang.jp/posts/20250129_speculative_decoding/

Speculative Decodingによるパフォーマンス改善には、大きく以下の2つの変数が存在します。

  • Draft Modelの選定: 本体のモデルと出力の類似度が高く、軽量であればあるほど良い。また、Tokenizerが同一であることが基本的に前提となる。一般的には同シリーズの小型モデルが使われることが多い。
  • Draft Tokensの数: 一度に何トークンDraft Modelに生成させるか。長くなればなるほど採用できた場合の高速化効果は上がるが逆に採用率(Acceptance Ratio)が低くなり、不採用となった部分は無駄な計算となる。

vLLMでは、speculative_configという引数を通じてこれらを指定します。以下は公式ドキュメントの例です。

llm = LLM(
    model="facebook/opt-6.7b",
    tensor_parallel_size=1,
    speculative_config={
        "model": "facebook/opt-125m",
        "num_speculative_tokens": 5,
    },
)

vLLMでのより詳細な例は以下の公式ドキュメントをご確認ください。

https://docs.vllm.ai/en/latest/features/spec_decode.html

ベンチマーク

それでは、実際にベンチマークを行いSpeculative Decodingの効果を検証していきます。

今回は以下の環境・設定でベンチマークを行います。

  • GPU: NVIDIA A100 80GB x 1
  • vLLM version: v0.8.5.post1
  • CUDA version: 12.4
  • Torch version: 2.6.0+cu124
  • Target Model: Qwen3-32B
  • Draft Model: Qwen3-0.6B, Qwen3-1.7B
  • num_speculative_tokens: 1, 2, 3, 4, 5
  • max_concurrency: 1, 2, 4, 8, 16, 32, 64

ベンチマークの実行には、vLLMが公式に提供しているベンチマークスクリプトを利用します。

ベンチマークの実行手順は以下の通りです。

  1. vLLMリポジトリのクローン
    git clone https://github.com/vllm-project/vllm.git
    
  2. ベンチマーク用データのダウンロード
    wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json
    
  3. vLLMによる推論サーバの起動
    a. Speculative Decodingなしの場合
    vllm serve Qwen/Qwen3-32B --disable-log-requests --max-model-len 8192
    
    b. Speculative Decodingあり、Draft Model=Qwen3-0.6Bの場合
    # num_speculative_tokens=N の場合 (Nを1から5まで変えて実行)
    vllm serve Qwen/Qwen3-32B --disable-log-requests --max-model-len 8192 --speculative_config '{"model": "Qwen/Qwen3-0.6B", "num_speculative_tokens": N}'
    
    c. Speculative Decodingあり、Draft Model=Qwen3-1.7Bの場合(デフォルトだと推論時OOMが発生したためgpu-memory-utilizationを下げています)
    # num_speculative_tokens=N の場合 (Nを1から5まで変えて実行)
    vllm serve Qwen/Qwen3-32B --disable-log-requests --max-model-len 8192 --speculative_config '{"model": "Qwen/Qwen3-1.7B", "num_speculative_tokens": N}' --gpu-memory-utilization 0.85
    
  4. ベンチマークスクリプトの実行
    # max-concurrency=N の場合 (Nを1から64まで変えて実行)
    python3 vllm/benchmarks/benchmark_serving.py   --backend vllm   --model Qwen/Qwen3-32B   --endpoint /v1/completions   --dataset-name sharegpt   --dataset-path ./ShareGPT_V3_unfiltered_cleaned_split.json --num-prompts 1000  --max-concurrency N --save-result
    

ベンチマーク結果

上記で実行したベンチマークの結果を図や表にまとめました。今回は指標として主に以下の3つを利用します。

  • Output Throughput: 1秒間で生成された出力トークン数。ある測定区間で生成されたトークン数を経過時間で割ることで求められる指標で、大きい方が良い。
  • TPOT (Time Per Output Token): 下記のTTFTを除いた1トークン出力の間にかかる時間。Inter-Token Latency(ITL)とも呼ばれる。ストリーミングで出力が流れてくる際のそのストリーミング速度のような指標で、小さい方が良い。
  • TTFT (Time To First Token): 最初のトークンの生成までにかかった時間。ユーザの入力に対するレイテンシのような指標で、小さい方が良い。

num_speculative_tokensによるパフォーマンスの変化(Draft Model=Qwen3-0.6B)

Draft ModelにQwen3-0.6Bを利用し、num_speculative_tokensを1から5まで変化させた際のパフォーマンスの変化の図です。
黒色のBaselineはSpeculative Decoding無しの素のパフォーマンスです。


Draft Model=Qwen3-0.6BでのDraft Tokensの数によるThroughputの変化


Draft Model=Qwen3-0.6BでのDraft Tokensの数によるTPOTの変化


Draft Model=Qwen3-0.6BでのDraft Tokensの数によるTTFTの変化

num_speculative_tokensによるパフォーマンスの変化(Draft Model=Qwen3-1.7B)

Draft ModelにQwen3-1.7Bを利用し、num_speculative_tokensを1から5まで変化させた際のパフォーマンスの変化の図です。
黒色のBaselineはSpeculative Decoding無しの素のパフォーマンスです。


Draft Model=Qwen3-1.7BでのDraft Tokensの数によるThroughputの変化


Draft Model=Qwen3-1.7BでのDraft Tokensの数によるTPOTの変化


Draft Model=Qwen3-1.7BでのDraft Tokensの数によるTTFTの変化

Draft Modelの違いによるパフォーマンスの変化

num_speculative_tokens=3の時のDraft Modelの違いによるパフォーマンスの変化の図と表です。
黒色のBaselineはSpeculative Decoding無しの素のパフォーマンスです。


Draft ModelによるThroughputの変化

Output Throughput (tokens/s) – higher is better

max_concurrency baseline Qwen3-0.6B (spec3) Qwen3-1.7B (spec3)
1 23.1 41.6 42.5
2 44.0 71.0 80.1
4 85.9 138.7 147.7
8 163.9 251.0 260.3
16 304.3 404.4 413.4
32 533.8 572.8 574.8
64 807.2 678.9 557.9


Draft ModelによるTPOTの変化

Mean TPOT (ms) – lower is better

max_concurrency baseline Qwen3-0.6B (spec3) Qwen3-1.7B (spec3)
1 43.1 25.5 25.1
2 45.0 29.7 26.3
4 46.0 30.3 28.4
8 48.0 33.0 32.0
16 51.3 40.5 39.6
32 57.3 56.6 57.1
64 75.1 96.9 78.3


Draft ModelによるTTFTの変化

Mean TTFT (ms) – lower is better

max_concurrency baseline Qwen3-0.6B (spec3) Qwen3-1.7B (spec3)
1 92.2 120.3 119.9
2 132.8 191.9 182.0
4 136.9 188.0 188.1
8 142.8 195.0 196.4
16 164.8 214.6 225.3
32 225.8 301.1 404.0
64 405.4 680.4 8742.9

Acceptance Ratioの変化

参考までに、各条件下でのDraft TokensのAcceptance Ratioの変化の図も貼っておきます。
Draft Tokensが小さく、Draft Modelが大きいほどAcceptance Ratioが高いという直感通りの傾向が出ていることが分かると思います。


各Draft Model、Draft TokensごとのAcceptance Ratio

結果から分かること

  • num_speculative_tokensは2から3あたりがベスト: 今回の設定においては、どちらのDraft Modelの場合でもnum_speculative_tokensは2から3あたりがベストとなり、それより小さな値や大きな値はパフォーマンスを悪化させました。Draft Tokensが小さすぎると得られるメリットが少なく、逆に大きくしすぎると不採用の場合に発生する無駄な計算がパフォーマンスを悪化させていることが分かります。
  • 同時処理数が多くなると逆にパフォーマンスが悪化する: 同時処理数が多くなると、Speculative Decodingを取り入れた結果逆にパフォーマンスが悪化してしまうことが分かります。Target Modelによる検証ステップではforwardこそ1度で済むもののDraft Tokens分のより大きなバッチでの処理が必要となり、メモリ帯域よりも計算量自体がボトルネックとなっていきます。その結果Large batchでの性能がBaselineよりも落ちることが知られています。
  • Qwen3-1.7BをDraft Modelにした方がパフォーマンスが良い: 今回の設定ではQwen3-1.7BをDraft Modelにした方がパフォーマンスが基本的に良いという結果になりました。ただし、同時処理数が大きい場合はQwen3-0.6Bを採用したほうが良い結果になっています。いずれにせよ、実際のユースケースの状況においてこの辺りは変化するので、実ユースケースを基にしたベンチマークがパフォーマンス最適化に必要となります。
  • TTFTは悪化する: Speculative Decodingは原理上TTFTを悪化させます。例えばDraft Tokens=1の設定でも、Draft Modelによる生成+Target Modelによる検証という2ステップが挟まるので、これは避けられない問題です。

まとめ

この記事では、Speculative DecodingによるLLMの推論高速化をvLLMで試し、簡単なベンチマークを行った結果を共有しました。

Speculative Decodingが常にパフォーマンスを向上させるわけではなく、実際にはTTFTが悪化したり、同時処理数によってパフォーマンスがBaselineよりも悪化する可能性があることが分かりました。

実ユースケースを鑑みて各種設定を弄りながらうまく取り入れていくと、実際の利用シーンにおいても恩恵を得られると思います。

Discussion