首页
/ vLLM 投机解码(Speculative Decoding)配置与原理全指南:方法选型、--speculative-config 详解与无损性保障

vLLM 投机解码(Speculative Decoding)配置与原理全指南:方法选型、--speculative-config 详解与无损性保障

2026-09-06 18:40:23作者:裘晴惠Vivianne

本文是 vLLM 投机解码(Speculative Decoding)的中文技术指南。投机解码是一类让"小模型先行预测、大模型批量验证"的推理加速技术,专门用于降低 LLM 在中低 QPS、内存受限(memory-bound)负载下的逐 token 延迟(inter-token latency)。读完本文,你将掌握 vLLM 内置的全部投机方法(EAGLE / MTP / Draft Model / PARD / MLP / N-Gram / Suffix / 动态投机 / 自适应验证)如何选型、如何通过统一的 --speculative-configspeculative_config 完成离线与在线部署配置、自定义 proposer 接入方式,以及投机解码在 vLLM 中"无损保证"的确切含义与边界。

本文以 docs/features/speculative_decoding/README.md 为主干,并结合其下 EAGLE、MTP、Draft Model、PARD、MLP、N-Gram、Suffix、Dynamic Speculative Decoding、Adaptive Verification、Per-Request Acceptance Metrics 等系列指南,以及配置类 vllm/config/speculative.py 的实现细节整理而成。

一、投机解码的价值与适用场景

投机解码的核心思路是:先用一个更小、更快的 draft 模型(起草模型) 一次性生成 K 个候选 token(称为"投机深度"),再由 target 模型(目标模型) 对这些候选 token 做一次批量前向验证,一次解码步即可"吃掉"多个 token。相比逐个 token 串行生成,它用额外的计算换来了更少的串行解码轮次。

在 vLLM 中启用投机解码的最典型动机是降低 中低 QPS(queries per second)场景下的内存受限负载的逐 token 延迟。因为此时 GPU 算力并未打满,验证额外 draft token 的边际算力几乎免费,而减少的串行轮次则直接转化为更低的 TPOT。vLLM 将这一方法的无损性保证作为设计约束(详见本文「七、无损保证」一节),使其在加速的同时不改变目标模型的采样分布。

若希望为优化后的投机解码训练自己的 draft 模型,可参考 speculators.md,实现与 vLLM 无缝衔接的训练与集成流程。

二、vLLM 支持的投机方法总览

vLLM 支持两大类投机方法:

  • 模型类方法:EAGLE、MTP、Draft Model、PARD、MLP speculator 等,延迟降低效果最好;
  • 轻量方法:N-Gram、Suffix 等,无需额外加载模型,在流量高峰期不会给显存/算力带来增量压力,可获得中等程度的加速。

方法清单与对应文档如下:

2.1 方法选择速查表

下表是 vLLM 官方给出的定性选型起点。需要强调的是:实际收益取决于你的模型家族、流量模式、硬件与采样设置,请结合自身环境用可复现的测量来验证。

方法 低 QPS(延迟导向) 高 QPS(吞吐导向) 备注
EAGLE 高收益 中到高收益 强大的通用型模型类方法。
MTP 高收益 中到高收益 目标模型原生支持 MTP 时效果最佳。
Draft model 高收益 中收益 需要单独的 draft 模型。
Parallel Draft Model 高收益 中到高收益 draft 模型延迟低。
MLP speculator 中到高收益 中收益 存在兼容的 MLP speculator 时效果良好。
N-gram 低到中收益 中收益 轻量、易启用。
Suffix decoding 低到中收益 中收益 无需额外 draft 模型;投机深度动态可变。
Custom Proposer 视情况而定 视情况而定 自带自定义 proposer 类(实验性)。
Dynamic Speculative Decoding 高收益 高于基础 SD 方法 适用于 RL 或 QPS 波动的负载。
Adaptive Verification 高收益 高于基础 SD 方法 依据 drafter 置信度逐请求调整验证规模;目前仅支持 DSpark。

在真实环境中进行可复现测量,可使用离线示例 examples/features/speculative_decoding/spec_decode_offline.py(该脚本还演示了如何抽取请求级接受率),或参考 benchmark CLI 指南

三、模型类投机方法逐一详解

3.1 Draft Model(独立草稿模型)

Draft Model 方法使用一个与目标模型同词表、同家族的小模型作为起草器。离线模式下,通过 LLM(..., speculative_config={...}) 传入配置即可,例如以 Qwen3-8B 为目标、Qwen3-0.6B 为草稿模型、每次投机 5 个 token:

from vllm import LLM, SamplingParams

prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

llm = LLM(
    model="Qwen/Qwen3-8B",
    tensor_parallel_size=1,
    speculative_config={
        "model": "Qwen/Qwen3-0.6B",
        "num_speculative_tokens": 5,
        "method": "draft_model",
    },
)
outputs = llm.generate(prompts, sampling_params)

for output in outputs:
    prompt = output.prompt
    generated_text = output.outputs[0].text
    print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

等效的在线(server)启动方式通过 --speculative-config 传同一个 JSON:

vllm serve Qwen/Qwen3-4B-Thinking-2507 \
    --host 0.0.0.0 \
    --port 8000 \
    --seed 42 \
    -tp 1 \
    --max-model-len 2048 \
    --gpu-memory-utilization 0.8 \
    --speculative-config '{"model": "Qwen/Qwen3-0.6B", "num_speculative_tokens": 5, "method": "draft_model"}'

注意:--speculative-config 中既声明了目标模型的常规 serve 参数,也完整承载了投机相关配置——客户端请求补全的代码(如通过 OpenAI SDK 指向 http://localhost:8000/v1)完全无需改变。

弃用提示:早期通过 --speculative-model 单独指定草稿模型、再叠加 --num-speculative-tokens 等参数的做法已被弃用。所有投机解码相关配置都应统一放在 --speculative-config 中。

3.2 EAGLE / EAGLE-3

EAGLE(Extrapolation Algorithm for Greater Language-model Efficiency)是一种将特征层自回归与 token 采样解耦的高效起草器:它在特征层做自回归推测,预测特征后一次性解码出整棵草稿树,大幅提升草稿与目标分布的吻合度。

EAGLE(二代)示例:

from vllm import LLM, SamplingParams

prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

llm = LLM(
    model="meta-llama/Meta-Llama-3-8B-Instruct",
    tensor_parallel_size=4,
    speculative_config={
        "model": "yuhuili/EAGLE-LLaMA3-Instruct-8B",
        "draft_tensor_parallel_size": 1,
        "num_speculative_tokens": 2,
        "method": "eagle",
    },
)

EAGLE-3(method: "eagle3")示例:

llm = LLM(
    model="meta-llama/Meta-Llama-3-8B-Instruct",
    tensor_parallel_size=2,
    speculative_config={
        "model": "RedHatAI/Llama-3.1-8B-Instruct-speculator.eagle3",
        "draft_tensor_parallel_size": 2,
        "num_speculative_tokens": 2,
        "method": "eagle3",
    },
)

EAGLE 类型的 checkpoint 现已广泛分布于公开模型库(例如 yuhuili 与 RedHatAI 发布的系列 speculator 模型),可直接填入 model 字段使用。vLLM 的版本兼容性上有一项历史注意事项:若使用较早的 vLLM(低于 0.7.0),需要先运行社区提供的转换脚本对 speculative 模型做格式转换,再以 "model": "path/to/modified/eagle/model" 指向转换产物。

从源码角度看,EAGLE-3 之所以需要额外的约束,是因为它属于"需要返回中间层 hidden states"的起草器。在 vllm/config/speculative.py 中,compute_hash() 会将这些方法(eagle3extract_hidden_statesdflashdspark)单独标记为 uses_aux_hidden_states,并把 draft 模型的 eagle_aux_hidden_state_layer_ids(抽取出草稿特征所用的目标层号)纳入计算图哈希因子——不同层号意味着不同的计算图结构,vLLM 会据此保证缓存/图复用不会跨越不兼容的配置。

3.3 MTP(Multi-Token Prediction,原生多 token 预测)

MTP 的特点是不需要额外加载 draft 模型:目标模型本身在预训练阶段就带有多 token 预测头,vLLM 直接调用模型原生的 MTP 能力。适用条件:

  • 目标模型原生支持 MTP(vLLM 内置映射了大量 MTP 模型族,如 DeepSeek、MiMo、GLM、Kimi、Qwen3-Next、Exaone、MiniMax-M3、Gemma 4 等,见 vllm/config/speculative.py 中的 MTPModelTypes);
  • 希望以最小的额外配置获得模型级投机效果。

离线示例(MiMo-7B-Base,投机深度取 1):

from vllm import LLM, SamplingParams

prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

llm = LLM(
    model="XiaomiMiMo/MiMo-7B-Base",
    tensor_parallel_size=1,
    speculative_config={
        "method": "mtp",
        "num_speculative_tokens": 1,
    },
)

在线示例:

vllm serve XiaomiMiMo/MiMo-7B-Base \
    --tensor-parallel-size 1 \
    --speculative-config '{"method":"mtp","num_speculative_tokens":1}'

使用要点:

  • MTP 只对 vLLM 已支持的、带 MTP 能力的模型家族有效;若模型不支持 MTP,请改用 EAGLE 或 Draft Model 方法;
  • num_speculative_tokens 控制投机深度,从较小的值(如 1)起步通常是不错的默认选择;
  • vllm/config/speculative.py 中的 hf_config_override() 可以看到,vLLM 在配置阶段会自动把各家的原生模型类型(如 deepseek_v3deepseek_mtpMiMoForCausalLMmimo_mtp 等)改写为对应的 MTP 架构,并读取 num_nextn_predict_layers/mtp_num_hidden_layers 等字段补全 n_predict,因此用户侧通常只需声明 method: "mtp"

Gemma 4 Assistant 模型:MTP 路径而非通用 draft

Gemma 4 assistant checkpoint 在 vLLM 中被当作 Gemma 4 MTP speculator 处理,而不是通用 draft model,尽管它们同样通过 --speculative-configmodel 字段传入。为 Gemma 4 配置 assistant checkpoint 时必须使用 "method": "mtp",例如:

vllm serve google/gemma-4-E2B-it \
    --tensor-parallel-size 1 \
    --max-model-len 8192 \
    --speculative-config '{"method":"mtp","model":"gg-hf-am/gemma-4-E2B-it-assistant","num_speculative_tokens":1}'

E2B、E4B、12B、26B-A4B 与 31B 等 Gemma 4 IT assistant checkpoint 均受支持。Tower 类变体使用 model_type: gemma4_assistant,无编码器的 Gemma 4 Unified 变体(12B)使用 model_type: gemma4_unified_assistant;vLLM 内部会把两者都映射为 Gemma4MTPModel,并通过 proposer 将 assistant 层与目标模型共享 KV cache(配置阶段会把 num_kv_shared_layers 置 0,改由 proposer 在模型构建后建立跨模型 KV 共享,见 vllm/config/speculative.py)。

排障提示:如果启动日志中出现 SpeculativeConfig(method='draft_model', ...)(针对 Gemma 4 assistant checkpoint),说明当前安装的 vLLM 版本不包含该路径的 Gemma 4 MTP 支持——此时它会被当作通用 draft model 处理,多模态 Gemma 4 目标在初始化阶段可能失败。正确做法是升级到支持 Gemma 4 MTP 的 vLLM 版本,而不是强行把 assistant checkpoint 塞进通用 draft-model 投机流程。

3.4 Parallel Draft Model(PARD)

PARD 通过 parallel_drafting: true 开启 并行起草:所有投机 token 一次性并行生成(而非串行逐 token 递推),从而把 draft 阶段的延迟压到极低。PARD 权重经过专门的并行训练(例如 amd/PARD-Qwen3-0.6B)。

离线示例(Qwen3-8B 目标 + PARD 草稿,一次投机 12 个 token):

from vllm import LLM, SamplingParams

prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

llm = LLM(
    model="Qwen/Qwen3-8B",
    tensor_parallel_size=1,
    speculative_config={
        "model": "amd/PARD-Qwen3-0.6B",
        "num_speculative_tokens": 12,
        "method": "draft_model",
        "parallel_drafting": True,
    },
)

在线示例:

vllm serve Qwen/Qwen3-4B \
    --host 0.0.0.0 \
    --port 8000 \
    --seed 42 \
    -tp 1 \
    --max-model-len 2048 \
    --gpu-memory-utilization 0.8 \
    --speculative-config '{"model": "amd/PARD-Qwen3-0.6B", "num_speculative_tokens": 12, "method": "draft_model", "parallel_drafting": true}'

需要留意的是,parallel_drafting 仅在 vllm/config/speculative.py 中标注为 仅与 EAGLE 和 draft-model 两类方法兼容,且要求草稿模型经过并行起草训练,否则会破坏草稿分布。

3.5 MLP Speculator

MLP speculator(在配置中对应 method: "mlp_speculator")是一类把草稿预测同时条件化在上下文向量(context vectors)与已采样 token上的起草模型,训练与推理方式与 IBM 的 MLPSpeculator 系列一致:

from vllm import LLM, SamplingParams

prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

llm = LLM(
    model="meta-llama/Meta-Llama-3.1-8B-Instruct",
    tensor_parallel_size=1,
    speculative_config={
        "model": "ibm-ai-platform/llama3-8b-accelerator",
        "draft_tensor_parallel_size": 1,
        "method": "mlp_speculator",
    },
)

公开可用的 MLP speculator 权重以 IBM 的 *-accelerator 系列为主(覆盖 llama2/llama3、code llama、granite 等多档尺寸,如 llama3-8b-acceleratorllama2-70b-acceleratorgranite-3b-code-instruct-accelerator 等)。已知问题:ibm-ai-platform/llama3-70b-accelerator 会因 MLPSpeculatorConfig 缺少 num_attention_heads 属性而报 AttributeError,官方 issue 仍在跟踪修复中,使用该 70B 权重前请留意版本状态。

四、轻量投机方法详解

4.1 N-Gram 投机

N-Gram 完全不加载额外模型:它通过在提示词(prompt)内部做 n-gram 匹配来产生候选 token。适用于提示中本身包含大量可自重复文本的场景。配置 method: "ngram",并配合 prompt_lookup_min/prompt_lookup_max 界定 n-gram 窗口:

from vllm import LLM, SamplingParams

prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

llm = LLM(
    model="Qwen/Qwen3-8B",
    tensor_parallel_size=1,
    speculative_config={
        "method": "ngram",
        "num_speculative_tokens": 5,
        "prompt_lookup_max": 4,
    },
)

4.2 Suffix Decoding

Suffix Decoding(论文见 arXiv:2411.04975)是 N-Gram 的进阶:它同样利用"最近 n 个已生成 token"做模式匹配,但有三个关键差异——(1) 不仅能匹配 prompt,还能匹配历史生成文本;(2) 用频次统计提议最可能的后续;(3) 对每个请求在每步解码自适应地投机可变数量的 token,以获得更高的接受率。它对高重复度任务(代码编辑、agentic 循环如 self-reflection/self-consistency、RL rollout)效果尤其明显。

使用前需安装依赖 Arctic Inferencepip install arctic-inference)。由于投机 token 数是动态的,num_speculative_tokens 在这里表示上限,官方建议设高一些(如 16 或 32,32 为推荐默认):

from vllm import LLM, SamplingParams

prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

llm = LLM(
    model="Qwen/Qwen3-8B",
    tensor_parallel_size=1,
    speculative_config={
        "method": "suffix",
        "num_speculative_tokens": 32,
    },
)

Suffix Decoding 的相关键位在 vllm/config/speculative.py 中有直接对应的实现(suffix_decoding_max_tree_depth=24suffix_decoding_max_cached_requests=10000suffix_decoding_max_spec_factor=1.0suffix_decoding_min_token_prob=0.1),其机制是:全局 + prompt 两棵后缀树用于前缀匹配;spec factor 依据前缀匹配长度换算本次可投机的 token 数上限(max_spec_tokens = max_spec_factor * prefix_match_length);只有频次估计概率 ≥ suffix_decoding_min_token_prob 的 token 才被投机;全局后缀树缓存超过 suffix_decoding_max_cached_requests 时按 FIFO 淘汰,设为 0 则禁用全局缓存(仅保留 prompt 树)。

4.3 Hidden State Extraction(隐藏状态抽取,训练 EAGLE 用)

Hidden State Extraction 面向数据生产而非推理加速:它让 vLLM 在推理时把目标模型的中间层激活保存下来(.safetensors),供训练 EAGLE 类草稿模型、知识蒸馏或模型内部离线分析使用。通过 KV transfer 连接器(ExampleHiddenStatesConnectorkv_role="kv_producer")把 hidden states 写盘,完整脚本见 examples/features/speculative_decoding/extract_hidden_states_offline.py

import tempfile

from vllm import LLM, SamplingParams
from vllm.config.kv_transfer import KVTransferConfig
from vllm.distributed.kv_transfer.kv_connector.v1 import (
    example_hidden_states_connector,
)

with tempfile.TemporaryDirectory() as tmpdir:
    llm = LLM(
        model="Qwen/Qwen3-8B",
        speculative_config={
            "method": "extract_hidden_states",
            "num_speculative_tokens": 1,
            "draft_model_config": {
                "hf_config": {
                    "eagle_aux_hidden_state_layer_ids": [1, 2, 3, 4],
                },
            },
        },
        kv_transfer_config=KVTransferConfig(
            kv_connector="ExampleHiddenStatesConnector",
            kv_role="kv_producer",
            kv_connector_extra_config={
                "shared_storage_path": tmpdir,
            },
        ),
    )

在线方式性能更佳时建议使用 RAM 文件系统(如 /dev/shm/),客户端生成后尽快清理:

vllm serve Qwen/Qwen3-8B \
    --speculative_config '{"method": "extract_hidden_states", "num_speculative_tokens": 1, "draft_model_config": {"hf_config": {"eagle_aux_hidden_state_layer_ids": [1, 2, 3, 4]}}}' \
    --kv_transfer_config '{"kv_connector": "ExampleHiddenStatesConnector", "kv_role": "kv_producer", "kv_connector_extra_config": {"shared_storage_path": "/dev/shm/hidden_states"}}'

每个请求产出包含 hidden_states(形状 [num_tokens, num_extracted_layers, hidden_size])与 token_ids(形状 [num_tokens])的 .safetensors 文件,路径回填到 output.kv_transfer_params["hidden_states_path"]。相关配置键包括:

参数 默认值 说明
shared_storage_path /tmp hidden state 文件的保存目录(未逐请求指定路径时使用)
allow_custom_save_path False 是否允许客户端经 hidden_states_path 指定自定义路径;关闭时客户端路径被忽略并告警。仅建议在可信客户端环境开启,因为自定义路径可写入服务器任意位置
num_writer_threads 8 异步写盘的线程池大小
use_synchronization_lock True 使用文件锁,使并发读取者阻塞到写入完成;批量生成场景可关闭

逐请求选项(离线经 SamplingParams(extra_args={"kv_transfer_params": {...}}),在线作为 API 请求顶层字段 kv_transfer_params)包括 hidden_states_path(自定义保存路径)与 include_output_tokens(是否连生成 token 的 hidden states 一并保存,默认 False 只存 prompt 部分)。注意该特性与 chunked prefill 不兼容,需要禁用后者。

五、--speculative-config 配置模式完全解析

vLLM 已把投机解码的所有设置统一收敛到 --speculative-config(在线 CLI / 配置文件)或 speculative_config(Python 侧 LLM(...))这一个入口中,二者共用同一套 JSON schema。

CLI 传入一个 JSON 对象即可:

vllm serve <target-model> \
  --speculative-config '{
    "method": "draft_model",
    "model": "<draft-model>",
    "num_speculative_tokens": 5
  }'

Python 侧等价写法为 LLM(..., speculative_config={...})。下表是面向用户的高频键位(并非完整 schema,完整字段参考 engine arguments referencevllm.config.SpeculativeConfig 的 API 文档):

5.1 通用键

以下键在各类投机配置中都较常用;其中一部分仅对模型类方法(如 draft_modelmtpeagle3dflash)生效:

Key 类型 默认值 允许值 / 含义
method string None 投机方法。常用取值包括 draft_modelngramsuffixmtpeagle3dflash。若省略,vLLM 会在配置允许时尽可能根据其他字段推断方法。
model string None draft 模型、EAGLE head 或辅助模型标识。对 ngramngram_gpusuffixmtp 常可省略。
num_speculative_tokens integer > 0 None 每步提议的投机 token 数。对无法从模型元数据推断的方法而言是必填项。
draft_tensor_parallel_size integer >= 1 None draft 模型的张量并行度。
max_model_len integer >= 1 None draft 模型的最大上下文长度。
parallel_drafting boolean false 启用并行起草。仅兼容 EAGLE 与 draft-model 方法。
rejection_sample_method string standard standardsyntheticblock
synthetic_acceptance_rates list[float] None synthetic 拒绝采样下逐位置的无条件接受率。每项取值 [0, 1];列表长度必须等于 num_speculative_tokens;必须单调不增。
synthetic_acceptance_length float None synthetic 的目标平均接受长度,取值范围 [1, num_speculative_tokens + 1]。与 synthetic_acceptance_rates 互斥。
use_heterogeneous_vocab boolean false 允许 draft 与 target 使用不同词表。初始化时会构建 token 级交集,并把 draft logits 约束到共享 token 上。仅兼容 method=draft_model。开启时暂不支持概率式 draft 采样(draft_sample_method='probabilistic')。

Gemma 4 提醒(再次强调):Gemma 4 assistant checkpoint 应作为 Gemma 4 MTP speculator 处理,即用 "method": "mtp" 并把 assistant checkpoint 放进 model,参考 MTP 指南。若启动日志对 Gemma 4 assistant checkpoint 显示 SpeculativeConfig(method='draft_model', ...),则说明安装版本不含该路径的 Gemma 4 MTP 支持,应升级版本而非强行走通用 draft-model 路径。

5.2 N-Gram 专用键

Key 类型 默认值 含义
prompt_lookup_max integer >= 1 两个边界都省略时为 5;否则省略时镜像 prompt_lookup_min N-gram 窗口大小上限。
prompt_lookup_min integer >= 1 两个边界都省略时为 5;否则省略时镜像 prompt_lookup_max N-gram 窗口大小下限。

示例:

vllm serve <target-model> \
  --speculative-config '{
    "method": "ngram",
    "num_speculative_tokens": 4,
    "prompt_lookup_min": 2,
    "prompt_lookup_max": 5
  }'

5.3 Suffix Decoding 专用键

Key 类型 默认值 含义
suffix_decoding_max_tree_depth integer 24 前缀匹配与投机长度合计的最大树深。
suffix_decoding_max_cached_requests integer 10000 全局后缀树缓存的最大请求数;设 0 禁用全局缓存。
suffix_decoding_max_spec_factor float 1.0 投机长度相对前缀匹配长度的倍数上限。
suffix_decoding_min_token_prob float 0.1 投机某 token 所需的最低估计概率。

示例:

vllm serve <target-model> \
  --speculative-config '{
    "method": "suffix",
    "num_speculative_tokens": 8,
    "suffix_decoding_max_tree_depth": 24,
    "suffix_decoding_max_cached_requests": 10000,
    "suffix_decoding_max_spec_factor": 1.0,
    "suffix_decoding_min_token_prob": 0.1
  }'

5.4 跨词表 Draft 模型(Token-Level Intersection,TLI)

默认情况下 vLLM 要求 draft 与 target 共享同一词表。将 use_heterogeneous_vocab: true 开启后,即可使用 TLI(Token-Level Intersection) 算法,从而允许使用来自不同模型家族、带不同 tokenizer 的 draft 模型。

初始化时,vLLM 通过对 token 字符串做规范化后计算两个词表的交集,建立起两个词表间的映射;draft logits 在采样前被约束到共享 token 集合上,采样得到的 token ID 在送入拒绝采样前再翻译回 target 词表。示例如下(Qwen3-8B 目标 + SmolLM2-135M 草稿,跨家族混用):

from vllm import LLM, SamplingParams

llm = LLM(
    model="Qwen/Qwen3-8B",
    speculative_config={
        "method": "draft_model",
        "model": "HuggingFaceTB/SmolLM2-135M-Instruct",
        "num_speculative_tokens": 3,
        "use_heterogeneous_vocab": True,
    },
    gpu_memory_utilization=0.5,
)

对应字段定义见 vllm/config/speculative.pyuse_heterogeneous_vocab: bool = False,并注释限定 method='draft_model')。当前限制:TLI 仅支持 greedy draft 采样(draft_sample_method='greedy' 即默认值);概率式接受(draft 采样温度 > 0)尚不支持,将在未来版本加入。

5.5 配置注意事项(Notes)

  • CLI 的 --speculative-config 期望一个 JSON 对象;若使用 YAML 配置文件,应写成嵌套映射而非转义的 JSON 字符串;
  • tensor_parallel_size 不是 speculative_config 中的合法键,请改用 draft_tensor_parallel_size。源码中 tensor_parallel_size 字段被保留仅为向误传的用户发出告警(vllm/config/speculative.py);
  • temperaturetop_p 之类属于采样参数SamplingParams / 服务端 sampling_params),不要混入 --speculative-config
  • target_model_configdraft_model_configtarget_parallel_configdraft_parallel_configdraft_load_config 等内部字段由 vLLM 在 post-init 阶段自动填充,不应当由用户设置
  • use_heterogeneous_vocab 目前仅支持 greedy draft 采样。

六、进阶:动态投机、自适应验证与自定义 Proposer

6.1 Dynamic Speculative Decoding(按并发度调 K)

为什么需要动态 SD? 投机解码每步要为每个序列验证 K 个 token。当 batch size(BS)增大时,实际参与验证的序列数为 BS×K,计算量随之上升;一旦 BS×K 越过临界 BS,投机解码反而会拖慢解码速度(TPOT)。动态 SD(Dynamic SD)的做法是把 K 随并发度调优到最优值,从而在保持收益的同时避免过冲。

典型使用场景:

  • 并发波动的工作负载:同一个部署在并发升高时自动调低 K;
  • RL rollout 尾段:一轮 rollout 开始时 BS 高、尾段只剩少数长尾请求持续产出大量 token 而拖慢整体进度——此时需要把 K 调高来加速尾段。

启用方式是在某个 SD 方法的配置中加入 num_speculative_tokens_per_batch_size(一个 [start_bs, end_bs, optimal_K] 三元组列表,含义为当并发落在闭区间 [start_bs, end_bs] 时使用 optimal_K 个 draft token):

--speculative-config '{
    "method": "eagle",
    "model": "yuhuili/EAGLE-LLaMA3.1-Instruct-8B",
    "num_speculative_tokens": 3,
    "num_speculative_tokens_per_batch_size": [
      [1, 64, 3],
      [65, 128, 1],
      [129, 512, 0]
    ]
  }'

即:并发在 [1, 64] 时 K=3;在 [65, 128] 时 K=1;在 [129, 512] 时 K=0(不产生任何 draft token)。字段定义见 vllm/config/speculative.pynum_speculative_tokens_per_batch_size: list[tuple[int, int, int]],batch-size 区间左闭右闭)。在线示例:

# Dynamic SD + Eagle drafter
VLLM_USE_V2_MODEL_RUNNER=0 vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --speculative-config '{
    "method": "eagle",
    "model": "yuhuili/EAGLE-LLaMA3.1-Instruct-8B",
    "num_speculative_tokens": 3,
    "num_speculative_tokens_per_batch_size": [
      [1, 64, 3],
      [65, 128, 1],
      [129, 512, 0]
    ]
  }'

# Dynamic SD + Eagle3 drafter
VLLM_USE_V2_MODEL_RUNNER=0 vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --speculative-config '{
    "method": "eagle3",
    "model": "yuhuili/EAGLE3-LLaMA3.1-Instruct-8B",
    "num_speculative_tokens": 3,
    "num_speculative_tokens_per_batch_size": [
      [1, 16, 5],
      [17, 32, 4],
      [33, 64, 3],
      [65, 128, 1],
      [129, 512, 0]
    ]
  }'

已知限制:已在 Eagle、Eagle-3、DFlash 上测试通过,其他 SD 方法可能无法开箱即用;完整 cudagraph 仅在 Model Runner V2 下可用(MRv1 对该特性只支持 piece-wise cuda graph);不兼容数据并行--data-parallel-size > 1),因为各 DP rank 独立调度可能选出不同的 K 值,导致 DP 集合通信发散与死锁——启用 DP 时 vLLM 会自动禁用 num_speculative_tokens_per_batch_size 并回退到静态 num_speculative_tokens

6.2 Adaptive Verification(自适应验证)

投机解码的本质是"用更少的解码步换更多的计算"。在 batch size = 1、GPU 处于内存受限(算力空闲)时这是好交易:多余的 draft token 几乎免费;但在 batch size = 256 时则非常微妙——draft token 与真实 token 争抢同一份算力,每个被拒 token 都是浪费的计算,累积到一定程度吞吐反而下降。而逐位置的接受率衰减很快,算力空闲时该投机位"免费值得赌一把",一旦算力饱和这种赌注就产生真实的吞吐代价。拐点随负载与负载相关的接受率移动,因此没有任何静态 num_speculative_tokens 能同时适配所有并发度

Adaptive Verification 的思路是逐步决策验证多少草稿:每个(请求、位置)的草稿位按"生存概率"(该请求逐位置置信度的累计乘积)打分,得分最高的草稿位被准入,直到全局预算耗尽;草稿位跨请求竞争——高置信请求的第 5 位可以胜过低置信请求的第 1 位,于是高置信请求保留下整块草稿,低置信请求可能在 1~2 个 token 后被裁剪。预算来自启动时 profile 出的成本模型:vLLM 测量不同 shape 下每一步的代价,选择能最大化"每秒期望接受 token 数"的 token 数。实际效果是一个配置即可覆盖整个负载区间,基本消除了逐部署调 num_speculative_tokens 的需要。

支持范围:Adaptive Verification 需要逐位置接受率估计,当前仅支持带 confidence head 的 DSpark。默认关闭,开启方式(DeepSeek-V4-Flash-DSpark 示例):

vllm serve deepseek-ai/DeepSeek-V4-Flash-DSpark \
  --tokenizer-mode deepseek_v4 --trust-remote-code \
  --speculative-config '{
    "method": "dspark",
    "model": "deepseek-ai/DeepSeek-V4-Flash-DSpark",
    "num_speculative_tokens": 7,
    "draft_sample_method": "probabilistic",
    "enable_adaptive_verification": true
  }'

enable_adaptive_verification: false 则退化为对每个请求都验证完整草稿块。要求与限制

  • attention 后端必须容忍"设备端决定 query 长度"(CPU 侧长度只作为上界);依赖 CPU 长度做规划的 attention 后端会被 attention selector 排除,对硬编码后端(hard-wire)的模型则会在启动时被拒绝;
  • 必须使用完整 cudagraph(步进成本从捕获的图中测量),因此 --enforce-eager 会在启动时被拒绝;
  • 不支持 LoRA(逐 token 的 LoRA 映射基于 CPU 侧边界构建)与 pipeline parallelism(成本曲线与置信度只存在于最后一个 rank)。

成本 profile 调优:步进成本默认基于 8192 token 的合成 KV 上下文做 profile。服务超长上下文的部署可通过环境变量 VLLM_ADAPTIVE_VERIFICATION_PROFILE_CONTEXT_LEN 提高该值,以让 profile 读取更贴近真实的 cache 量:

export VLLM_ADAPTIVE_VERIFICATION_PROFILE_CONTEXT_LEN=131072

6.3 Custom Proposer Backend(实验性)

你还可以通过 custom_class 方法注入自己的 proposer 类:将 method 设为 custom_classmodel 设为自定义类的完整模块路径。自定义类须在实例化时接收一个 VllmConfig,并实现 propose 方法。示例配置:

  • speculative_config.method = "custom_class"
  • speculative_config.model = "your_module.YourCustomProposerClass"

对应 method 的取值空间(含 custom_classngrammedusamlp_speculatordraft_modelsuffix 等)见 vllm/config/speculative.py 中定义的 SpeculativeMethod 字面量类型。

七、投机解码的无损保证(Lossless Guarantees)

vLLM 中的投机解码在追求推理效率的同时维持输出质量,其"无损"含义从三个层面界定:

  1. 理论无损(Theoretical Losslessness):投机解码采样在理论上无损,精度上限取决于硬件数值精度。浮点误差可能造成输出分布的细微偏差(这是投机采样这一类方法本身已知的边界,详见加速 LLM 解码的投机采样原始论文)。

  2. 算法无损(Algorithmic Losslessness):vLLM 对投机解码的实现经过算法级无损验证,核心验证手段包括:

    • Rejection Sampler 收敛性:确保 vLLM 拒绝采样器的样本与目标分布一致,验证代码位于 tests/samplers/test_rejection_sampler.py
    • 贪心采样等价性:确认开启投机解码的贪心采样与不开启的贪心采样完全一致,验证 vLLM 的投机解码框架(连同 vLLM 前向传播与拒绝采样器)具备无损保证。此类性质由 tests/v1/spec_decode 下 e2e 测试断言实现(几乎全部测试都在验证该性质)。
  3. vLLM 的 Logprob 稳定性:vLLM 当前不保证 token logprob 跨运行的稳定性——同一请求在不同运行中可能出现不同输出。详见 FAQ 中 "Can the output of a prompt vary across runs in vLLM?"

即使在无损框架下,开启与关闭投机解码的输出仍可能因以下因素产生差异:

  • 浮点精度:硬件数值精度差异可能导致输出分布出现微小偏差;
  • Batch Size 与数值稳定性:batch size 的变化可能引起 logprob 与输出概率的波动(可能是批量算子中的非确定性行为或数值不稳定性所致)。

缓解策略参见上述 FAQ 条目。

八、已知功能不兼容(Known Feature Incompatibility)

  1. 截至 vllm<=0.15.0,pipeline parallelism 与投机解码不可组合;
  2. vllm<=0.10.0 不支持基于 draft model 的投机解码。

(若使用早于上述版本的 vLLM,请注意相应能力缺失;本文所有配置示例以当前仓库版本的实现为准。)

九、给 vLLM 贡献者与深入研究者的指引

  • 想进一步理解投机解码运行时机制,建议从配置入口 vllm/config/speculative.py 出发,其 SpeculativeConfig 定义了全部键位、类型约束与校验逻辑(例如 synthetic 接受率与目标平均接受长度的互斥换算 _acceptance_length_to_rates、Qwen3-Omni DSpark 的专门校验 _validate_qwen3_omni_dspark 等);
  • 需要测量与复现的读者可使用 examples/features/speculative_decoding/spec_decode_offline.py,或按 benchmarking CLI 文档 使用基准 CLI;逐请求接受率在响应 metrics.speculative_decoding 中给出(见 Per-Request Acceptance Metricsper-request metrics 文档);
  • 服务端聚合的 spec-decode 指标(如 vllm:spec_decode_num_drafts_totalvllm:spec_decode_num_draft_tokens_totalvllm:spec_decode_num_accepted_tokens_total)可从 /metrics 的 Prometheus 端点获取,与逐请求字段在纯 n==1 负载下按求和关系对账;
  • 训练自家 speculator 模型的集成流程见 speculators.md
  • 完整 CLI 参数语义以 engine arguments reference 为准,SpeculativeConfig 的 API 细节可查 vllm.config.SpeculativeConfig 的文档字符串,其中关于计算图哈希(compute_hash)、后缀解码默认值(suffix_decoding_*)、拒绝采样方法(rejection_sample_methoddraft_sample_method)等均有逐字段注释。

实战建议:方法选型时不要只看定性收益表——先在目标模型家族上确定是否原生支持 MTP、是否有现成的高质量 EAGLE/草稿权重,再从最低投机深度起步,配合 --per-request-spec-decode-metricssummary 级别)观察 mean_acceptance_lengthdraft_acceptance_rate,最后用基准脚本在目标负载/并发/采样设置下做可复现测量,再决定是否引入 Dynamic SD 或 Adaptive Verification 这类自适应机制。

登录后查看全文
热门项目推荐
相关项目推荐