LinuxでvLLMを使ったSpeculative Decodingの設定:精度を落とさずLLMを2〜3倍高速化

Artificial Intelligence tutorial - IT technology blog
Artificial Intelligence tutorial - IT technology blog

5分でできる設定

vLLMでLLMを動かしているのにSpeculative Decodingを有効にしていなければ、GPUの潜在的な速度の40〜60%を無駄にしています。このセクションでその機能をすぐに有効にする方法を説明します。

環境の準備

vLLMバージョン0.4.0以上が必要です:

pip install vllm --upgrade
python -c "import vllm; print(vllm.__version__)"

Speculative Decodingを有効にしてサーバーを起動

次のコマンドを実行してすぐに有効化できます — targetモデルはLlama 3.1 8B、draftモデルはLlama 3.2 1B:

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-8B-Instruct \
    --speculative-model meta-llama/Llama-3.2-1B-Instruct \
    --num-speculative-tokens 5 \
    --use-v2-block-manager \
    --gpu-memory-utilization 0.9

通常のvLLMコマンドと比べてわずか2行追加するだけです。自分でproduction環境に適用してみましたが、結果はかなり安定していました — A10GでLlama 3.1 8B + draft 1Bの組み合わせでスループットが約1.8〜2倍向上しました。

効果を素早くテストする

curl http://localhost:8000/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "meta-llama/Llama-3.1-8B-Instruct",
    "prompt": "CSVファイルを読み込むPythonスクリプトを書いてください:",
    "max_tokens": 200,
    "temperature": 0
  }'

テスト時はtemperature: 0に注意してください — speculative decodingはgreedy decodingで最も効果を発揮します。

Speculative Decodingの仕組み

仕組みを理解すれば、推測で調整するのではなく、適切にチューニングできます。LLM推論の核心的な問題:生成される各トークンは、モデル全体を通じて1回のforward passを実行する必要があります。GPU上の8Bモデルでは、このステップは膨大なメモリ帯域幅を消費します — 実際の計算量はそれほど多くないにもかかわらず。

Draft-then-Verifyの仕組み

Speculative Decodingは推論を2つのステップに分けます:

  1. Draft phase: 小さなdraftモデル(例:1B)がN個のトークンを連続して素早く生成します — これを「speculative tokens」と呼びます。
  2. Verify phase: 大きなtargetモデル(8B/70B)が、N個のトークンすべてをたった1回のforward passで検証します。

draftモデルが5トークンすべてを正しく予測したとします:targetモデルのforward pass 1回 + draftモデル1回のコストで5トークンを生成できました — 5回別々に実行する代わりに。これがスループットが大幅に向上する理由です。

では、draftがk番目のトークンで誤りを予測した場合は?targetモデルはその位置から拒否し、それ以前の正しいトークンを保持して再生成します。誤ったトークンは一切出力されません — outputはtargetモデル単独で実行した場合と完全に同一です。これがquantizationやpruningとの違いで、それらは品質とのトレードオフがあります。

最も効果的な場面

  • 低temperature(0.0〜0.6):draftモデルが正確に予測しやすい
  • 繰り返しパターンのあるoutput:コード、markdown、技術的なリスト
  • GPUがFLOPsではなくメモリ帯域幅でボトルネックになっている場合

temperatureが高い(0.8以上)創作系のライティングでは、draftモデルがtargetモデルのランダム性に追いつきにくいため、効果が低下します。

高度な設定

speculative tokensの数をチューニング

--num-speculative-tokensパラメータは実際の結果に大きく影響します:

# 控えめ:3トークン — acceptance rateが高く、スループットは適度に向上
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-8B-Instruct \
    --speculative-model meta-llama/Llama-3.2-1B-Instruct \
    --num-speculative-tokens 3

# 積極的:7トークン — ワークロードに合えばスループットがより向上
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-8B-Instruct \
    --speculative-model meta-llama/Llama-3.2-1B-Instruct \
    --num-speculative-tokens 7

シンプルなルール:draft tokensのacceptance rateが70%を超えているならnum-speculative-tokensを増やしましょう。50%未満なら減らすか、draftモデルを変更してください。

Ngram-based Speculative Decoding — draftモデル不要

適切なdraftモデルがない、またはGPUのVRAMが不足している場合は?vLLMはngram prompt lookupをサポートしています — 入力コンテキスト自体を使って次のトークンを予測します:

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-70B-Instruct \
    --speculative-model "[ngram]" \
    --num-speculative-tokens 5 \
    --ngram-prompt-lookup-max 4 \
    --use-v2-block-manager

この方法は、コード生成やRAGなど — outputが入力の語句を繰り返すことが多いタスクに特に有効です。追加のGPUメモリは不要で、フラグを2つ追加するだけです。

EAGLE Speculative Decoding

同じファミリーの小さいモデルをdraftとして使う代わりに、EAGLEはtargetモデルに特別に調整された専用のdraftモデルをトレーニングします — 実際のacceptance rateが15〜20%高くなります:

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-8B-Instruct \
    --speculative-model yuhuili/EAGLE-LLaMA3-Instruct-8B \
    --speculative-draft-tensor-parallel-size 1 \
    --num-speculative-tokens 4

HuggingFaceでEAGLE-{ModelName}形式のEAGLEモデルを探してください。Llama 3.x以外にも、Qwen2.5やMistral向けのモデルがあります — targetモデル名で検索すればすぐに見つかります。

マルチGPU設定

# targetモデルは2 GPU、draftモデルは1 GPUで実行
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-70B-Instruct \
    --tensor-parallel-size 2 \
    --speculative-model meta-llama/Llama-3.1-8B-Instruct \
    --speculative-draft-tensor-parallel-size 1 \
    --num-speculative-tokens 4 \
    --use-v2-block-manager

注意:--speculative-draft-tensor-parallel-sizeは--tensor-parallel-sizeの約数でなければなりません。

実践的なヒント

同じファミリーのdraftモデルを選ぶ

acceptance rateを高くするために、draftモデルはtargetモデルと同じtokenizerとアーキテクチャである必要があります:

  • Llama 3.1 8B → draft:Llama 3.2 1B または 3B
  • Llama 3.1 70B → draft:Llama 3.1 8B
  • Qwen2.5 7B → draft:Qwen2.5 0.5B または 1.5B
  • Mistral 7B → 同じファミリーの小さいdraftモデルなし — ngramを代わりに使用

異なるファミリーのdraftモデル(例:QwenをLlamaのdraftとして使用)は通常acceptance rateが30%未満で、速度改善にならず、むしろ遅くなることもあります。

有効化前にGPUメモリを計算する

# 必要なメモリの見積もり(fp16)
# Targetモデル 8B:  ~16 GB
# Draftモデル 1B:   ~2 GB
# KV cache:         ~4 GB
# 合計:             ~22 GB → GPU 24 GB必要(RTX 3090/4090またはA10G)

nvidia-smi --query-gpu=memory.total,memory.free --format=csv

VRAMが不足している場合は、draftモデルにint8 quantizationを使用してメモリを〜50%節約できます:

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-8B-Instruct \
    --speculative-model meta-llama/Llama-3.2-1B-Instruct \
    --speculative-model-quantization int8 \
    --num-speculative-tokens 5

実際のacceptance rateを監視する

vLLMはspeculative decodingが実際に効果的かどうかを確認するためのmetricsを公開しています:

import requests

metrics = requests.get("http://localhost:8000/metrics").text
for line in metrics.split("\n"):
    if "spec" in line.lower() and not line.startswith("#"):
        print(line)

監視すべき重要なMetrics:

  • vllm:spec_decode_draft_acceptance_rate:目標は0.6以上
  • vllm:spec_decode_efficiency:ベースラインとの比較効率
  • vllm:generation_tokens_per_second:実際のスループット

本番環境に移行する前にベンチマークを実施する

python benchmarks/benchmark_throughput.py \
    --model meta-llama/Llama-3.1-8B-Instruct \
    --dataset sharegpt \
    --num-prompts 200 \
    --speculative-model meta-llama/Llama-3.2-1B-Instruct \
    --num-speculative-tokens 5

ベンチマーク結果はワークロードによってかなり異なります:コード生成は通常2.5〜3倍向上し、会話型チャットは1.5〜2倍向上します。他の人の数値で判断しないでください — 実際のトラフィックに近いデータセットでベンチマークを実行することが意味を持ちます。

Speculative Decodingは基本的なvLLMガイドではほとんど言及されませんが、outputの品質に手を加えずにスループットを向上させることができる数少ない手法の一つです。唯一の条件:適切なdraftモデルを選択し、実際のアプリケーションの入力パターンに合わせてnum-speculative-tokensをチューニングすることです。

Share: