跳转至

推测解码指南

本指南介绍如何在 vLLM Ascend 中使用推测解码。推测解码是一种在内存受限的 LLM 推理中改善令牌间延迟的技术。

概述

vLLM Ascend 通过提议者-验证者架构实现推测解码:

  1. 提议者 (vllm_ascend/spec_decode/):使用多种方法生成草稿(推测)令牌——从简单的 n-gram 匹配到基于神经网络的草稿模型。
  2. 拒绝采样器 (vllm_ascend/sample/):根据目标模型的输出验证草稿令牌,接受匹配项并拒绝不匹配项,可选的优化包括块验证和熵验证

支持以下推测解码方法:

方法 描述
ngram 从提示中匹配 n-gram
suffix 基于后缀的模式匹配(需要 Arctic Inference)
medusa 嵌入在目标模型中的 Medusa 头
eagle 基于 EAGLE 的草稿模型
eagle3 基于 EAGLE-3 的草稿模型
mtp 使用共享嵌入头的多令牌预测
dflash 基于块扩散的并行草稿模型
dspark 使用顺序马尔可夫对数偏置头的半自回归块草拟
draft_model 通用外部草稿 LLM
extract_hidden_states 提取隐藏状态用于 EAGLE 训练

通用配置

所有推测解码方法在初始化模型或启动服务器时通过 speculative_config 参数进行配置:

  • method (str, 必填): 推测解码方法。必须是上表中列出的受支持方法名称之一。
  • num_speculative_tokens (int, 必填): 每次前向传播生成的推测令牌数量。当可用时,从草稿模型的 n_predict 配置(例如 MTP)或 suffix_decoding_max_tree_depth(后缀方法)自动填充。 > 注意: 对于 PD 分离部署,num_speculative_tokens 应满足以下条件之一: > > 1. 混合 Mamba 模型(例如 Qwen-Next 和 Qwen3.5 系列):num_speculative_tokens 在 P 节点和 D 节点上应相等。 > 2. 其他模型:P 节点上的 num_speculative_tokens 应为 1,D 节点上的 num_speculative_tokens 应大于或等于 1。
  • model (str, 可选): 草稿模型的路径或 HF 仓库 ID。对于 eagleeagle3dflashmedusadraft_model 是必需的。对于 mtp(重用目标模型)、ngramsuffixextract_hidden_states 会自动解析。
  • draft_tensor_parallel_size (int, 可选): 草稿模型的张量并行大小。只能是 1 或与目标模型的张量并行大小相同。
  • disable_padded_drafter_batch (bool, 默认值: False): 禁用推测解码的输入填充。如果设置为 True,推测输入批次可以包含不同长度的序列,这可能仅受某些注意力后端支持。注意: 仅对 eagleeagle3mtpdflashdraft_modelextract_hidden_states 方法有效。

离线推理 — 将 speculative_config 作为 Python 字典传递给 LLM()

from vllm import LLM

llm = LLM(
    model="path/to/target/model",
    speculative_config={
        "method": "eagle3",
        "model": "path/to/draft/model",
        "num_speculative_tokens": 3,
    },
)

在线服务 — 将 --speculative-config(或 -sc)作为 JSON 字符串传递:

vllm serve path/to/target/model \
  --speculative-config '{"method": "eagle3", "model": "path/to/draft/model", "num_speculative_tokens": 3}'

[!NOTE] 在 Ascend NPU 上,npu_fused_infer_attention_score 算子每轮解码最多支持 16 个令牌。因此,(num_speculative_tokens + 1) 必须 ≤ 16。

通过匹配提示中的 n-gram 进行推测

以下代码配置 vLLM Ascend 使用推测解码,其中通过匹配提示中的 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="meta-llama/Meta-Llama-3.1-8B-Instruct",
        tensor_parallel_size=1,
        speculative_config={
            "method": "ngram",
            "num_speculative_tokens": 5,
            "prompt_lookup_max": 4,
        },
    )
    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}")
    

使用基于 EAGLE 的草稿模型进行推测

以下代码配置 vLLM Ascend 使用推测解码,其中提议由基于 EAGLE(Extrapolation Algorithm for Greater Language-model Efficiency) 的草稿模型生成。

  • 离线推理

    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=4,
        distributed_executor_backend="mp",
        enforce_eager=True,
        speculative_config={
            "method": "eagle",
            "model": "yuhuili/EAGLE-LLaMA3.1-Instruct-8B",
            "draft_tensor_parallel_size": 1,
            "num_speculative_tokens": 2,
        },
    )
    
    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}")
    
  • 在线推理

    vllm serve meta-llama/Meta-Llama-3.1-8B-Instruct \
      --tensor-parallel-size 4 \
      --enforce-eager \
      --speculative-config '{"method": "eagle", "model": "yuhuili/EAGLE-LLaMA3.1-Instruct-8B", "draft_tensor_parallel_size": 1, "num_speculative_tokens": 2}'
    

使用基于 EAGLE 的草稿模型时需要考虑的几个重要事项:

  1. HF 仓库中的 EAGLE 模型 提供的 EAGLE 草稿模型应由 vLLM 直接加载和使用。此功能在 PR #4893 中添加。 如果您使用的 vLLM 版本在此拉取请求合并之前发布,请更新到更新版本。

  2. 基于 EAGLE 的草稿模型需要在没有张量并行的情况下运行 (即 speculative_config 中的 draft_tensor_parallel_size 设置为 1),尽管 主模型可以使用张量并行运行(参见上面的示例)。

  3. 使用基于 EAGLE-3 的草稿模型时,选项 "method" 必须设置为 "eagle3"。 也就是说,在 speculative_config 中指定 "method": "eagle3"

  4. 启用 EAGLE 后,主模型需要在一个解码过程中验证主模型和草稿模型生成的 (1 + K) 个令牌。 并且全图模式将固定验证阶段的令牌数量, 因此 cudagraph_capture_sizes 必须是一个捕获大小列表,其中每个大小计算为 n * (K + 1)n 是您想要支持的每个批次大小。 例如,要支持批次大小从 1 到 4 且 num_speculative_tokens = 4cudagraph_capture_sizes 应设置为 [5, 10, 15, 20]

使用 MTP 进行推测

MTP(多令牌预测)通过并行化预测多个令牌,将生成方式从单令牌转变为多令牌,从而提升推理性能。这种方法显著提高了生成吞吐量,并实现了推理速度的倍增加速——同时不牺牲输出质量。

  • 离线推理

    from vllm import LLM, SamplingParams
    
    prompts = [
        "The future of AI is",
    ]
    sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
    
    llm = LLM(
        model="deepseek-ai/DeepSeek-V3.2-Exp-W8A8",
        tensor_parallel_size=16,
        enable_expert_parallel=True,
        max_model_len=36768,
        max_num_seqs=10,
        quantization="ascend",
        trust_remote_code=True,
        gpu_memory_utilization=0.9,
        compilation_config={"cudagraph_mode": "FULL_DECODE_ONLY"},
        speculative_config={
            "method": "mtp",
            "num_speculative_tokens": 2,
            "disable_padded_drafter_batch": False,
        },
    )
    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}")
    
  • 在线推理

    vllm serve /deepseek-ai/DeepSeek-V3.2-Exp-W8A8 \
    --port 20004 \
    --data-parallel-size 1 \
    --tensor-parallel-size 16 \
    --enable-expert-parallel \
    --seed 1024 \
    --served-model-name dsv3 \
    --max-model-len 36768 \
    --max-num-batched-tokens 5000 \
    --max-num-seqs 10 \
    --quantization ascend \
    --trust-remote-code \
    --gpu-memory-utilization 0.9 \
    --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
    --speculative-config '{"num_speculative_tokens": 2, "method":"mtp", "disable_padded_drafter_batch": false}'
    

[!NOTE] 由于 DeepSeek 的 MTP 仅暴露单层权重,在 num_speculative_tokens > 1(尤其是 ≥ 3)的场景下,精度和性能无法得到有效保证。

num_speculative_tokens > 1 的全图模式下,每个 ACLGraph 的捕获大小必须是 (num_speculative_tokens + 1) 的整数倍。

使用DFlash进行推测解码

以下代码配置vLLM Ascend使用推测解码,其中提案由基于DFlash块扩散的并行草稿模型生成。

  • 离线推理

    from vllm import LLM, SamplingParams
    from vllm.config import CompilationConfig
    
    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,
        distributed_executor_backend="mp",
        max_model_len=4096,
        max_num_seqs=16,
        gpu_memory_utilization=0.8,
        enable_prefix_caching=False,
        speculative_config={
            "method": "dflash",
            "model": "z-lab/Qwen3-8B-DFlash-b16",
            "num_speculative_tokens": 7,
        },
        compilation_config=CompilationConfig(cudagraph_mode="FULL_DECODE_ONLY"),
    )
    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}")
    
  • 在线推理

    vllm serve Qwen/Qwen3-8B \
      --tensor-parallel-size 1 \
      --max-model-len 4096 \
      --max-num-seqs 256 \
      --gpu-memory-utilization 0.8 \
      --no-enable-prefix-caching \
      --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
      --speculative-config '{"method": "dflash", "model": "z-lab/Qwen3-8B-DFlash-b16", "num_speculative_tokens": 7}'
    

使用DSpark进行推测解码

以下代码配置vLLM Ascend使用推测解码,其中提案由带有顺序马尔可夫对数偏置头的DSpark半自回归块草稿器生成。

  • 离线推理

    from vllm import LLM, SamplingParams
    from vllm.config import CompilationConfig
    
    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,
        distributed_executor_backend="mp",
        max_model_len=4096,
        max_num_seqs=8,
        gpu_memory_utilization=0.8,
        enable_prefix_caching=False,
        speculative_config={
            "method": "dspark",
            "model": "deepseek-ai/dspark_qwen3_8b_block7",
            "num_speculative_tokens": 7,
            "enforce_eager": True,
        },
        compilation_config=CompilationConfig(cudagraph_mode="FULL_DECODE_ONLY"),
    )
    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}")
    
  • 在线推理

    vllm serve Qwen/Qwen3-8B \
      --tensor-parallel-size 1 \
      --max-model-len 4096 \
      --max-num-seqs 8 \
      --gpu-memory-utilization 0.8 \
      --no-enable-prefix-caching \
      --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
      --speculative-config '{"method": "dspark", "model": "deepseek-ai/dspark_qwen3_8b_block7", "num_speculative_tokens": 7, "enforce_eager": true}'
    

Adaptive verification

Adaptive verification lets DSpark choose how many draft tokens to verify for each request at every decode step. Requests with higher confidence can keep a longer draft, while low-confidence drafts are shortened to avoid unnecessary target-model computation. It is disabled by default and requires a DSpark model with a confidence head.

Enable it in speculative_config:

VLLM_USE_V2_MODEL_RUNNER=1 vllm serve Qwen/Qwen3-8B \
  --speculative-config '{
    "method": "dspark",
    "model": "deepseek-ai/dspark_qwen3_8b_block7",
    "num_speculative_tokens": 7,
    "enable_adaptive_verification": true
  }'

Adaptive verification takes effect only with model runner V2.

For more details, see the vLLM community documentation.

草稿KV滑动窗口

草稿模型通常在短上下文(例如4-8k个token)上训练。当运行上下文远超该范围时,草稿模型的注意力会超出分布范围,接受率骤降——投机解码反而变成开销。草稿KV滑动窗口仅限制草稿模型的注意力到最近的draft_window_size个token。目标模型仍然关注完整上下文,因此输出质量不变。

通过--additional-config配合eagle3dflashdspark方法启用:

vllm serve path/to/target/model \
  --tensor-parallel-size 8 \
  --speculative-config '{"method": "dspark", "model": "path/to/draft/model", "num_speculative_tokens": 7, "enforce_eager": true}' \
  --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \
  --additional-config '{"draft_window_size": 2048}'

对于离线推理,在additional_config中传入相同的键:

llm = LLM(
    model="path/to/target/model",
    speculative_config={
        "method": "dspark",
        "model": "path/to/draft/model",
        "num_speculative_tokens": 7,
        "enforce_eager": True,
    },
    additional_config={"draft_window_size": 2048},
)

additional_config与其他引擎选项一样被转发到EngineArgs

  • draft_window_size(int,可选):草稿注意力可以读取的最近token数量。省略该键(或删除它)以禁用窗口。典型值为512-4096;窗口在内部按块对齐,因此任何正值都有效。
  • 适用于默认模型运行器(MRV1)。在VLLM_USE_V2_MODEL_RUNNER=1下尚不支持。
  • 对于mtp,窗口被忽略(强制禁用):MTP复用目标模型自身的层,因此对其加窗口会改变目标模型的计算。

何时启用

  • 启用当草稿在短上下文上训练且随着对话增长接受率下降时:例如,早期的GLM-5.2 DSpark草稿在32k时平均接受率从约5(短上下文)下降到约1;512-1024的窗口将其恢复到约4.7-5.5,在32k时端到端吞吐量提升高达+381%。
  • 关闭当草稿在长上下文下已经稳定时——此时窗口只会移除草稿本可以使用的信息。特别是,如果草稿本身使用滑动窗口注意力训练(其配置带有原生的sliding_window),请将draft_window_size保持在该训练窗口或以上,或禁用该功能;小于训练窗口的窗口会显著降低接受率(在32k输入下,1024窗口对比2048原生SWA DSpark草稿观察到平均接受率下降-4.5%)。

通过服务器日志检查效果:SpecDecoding metrics: Mean acceptance length: ...行报告每个时间间隔的接受率——在实际工作负载下比较窗口关闭和开启的运行结果,然后再确定一个值。

使用后缀解码进行推测

以下代码配置 vLLM 使用推测解码,其中候选令牌通过后缀解码生成 (SuffixDecoding: Extreme Speculative Decoding for Emerging AI Applications)

与 n-gram 类似,后缀解码可以通过使用最后 n 个生成的令牌进行模式匹配来生成草稿令牌。与 n-gram 不同,后缀解码 (1) 可以针对提示和先前生成的内容进行模式匹配,(2) 使用频率计数来提出最可能的续写内容,以及 (3) 在每次迭代中为每个请求推测自适应数量的令牌,以获得更好的接受率。

后缀解码在具有高重复性的任务上可以实现更好的性能,例如代码编辑、智能体循环(例如自我反思、自我一致性)和强化学习展开。

[!NOTE] 后缀解码需要 Arctic Inference。您可以使用 pip install arctic-inference 进行安装。

  • 离线推理

```python 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,
    enforce_eager=True,
    speculative_config={
        "method": "suffix",
        "num_speculative_tokens": 15,
    },
)

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}")
```
  • 在线推理

    vllm serve meta-llama/Meta-Llama-3.1-8B-Instruct \
      --tensor-parallel-size 1 \
      --enforce-eager \
      --speculative-config '{"method": "suffix", "num_speculative_tokens": 15}'
    

提取隐藏状态

extract_hidden_states 方法是一种特殊的投机解码模式,它不执行实际的推测,而是从目标模型的指定层中提取隐藏状态并保存到磁盘。这主要用于为 EAGLE 风格的草稿模型收集训练数据。随后这些转储数据被用于训练 EAGLE/EAGLE-3 草稿模型。

[!NOTE] 此方法每个请求仅生成 1 个输出令牌。主要输出是保存到磁盘的隐藏状态,而不是生成的文本。

Ascend 上同时支持 Model Runner V1 和 Model Runner V2。通过以下方式启用 V2:

export VLLM_USE_V2_MODEL_RUNNER=1

[!NOTE] Model Runner V2 支持复用了上游 vLLM 的 ExtractHiddenStatesSpeculator (PR #49811)。Ascend 仅 为 HiddenStateCacheSpec 增加了 init_speculator 分发以及 NPU KV 分配/重塑。在 vLLM #51718(0828 pin)之后, 隐藏状态层保留私有的 [B, H, N, C] 缓冲区,因此它们不会覆盖 标准化的混合 Attention/Mamba 后备存储。

  • 离线推理

    import tempfile
    
    from safetensors import safe_open
    from vllm import LLM, SamplingParams
    
    def main():
        with tempfile.TemporaryDirectory() as tmpdir:
            llm = LLM(
                model="Qwen/Qwen3-8B",
                tensor_parallel_size=1,
                speculative_config={
                    "method": "extract_hidden_states",
                    "num_speculative_tokens": 1,
                    "draft_model_config": {
                        "hf_config": {
                            # Layer indices to extract hidden states from
                            "eagle_aux_hidden_state_layer_ids": [2, 18, 34],
                        }
                    },
                },
                kv_transfer_config={
                    "kv_connector": "ExampleHiddenStatesConnector",
                    "kv_role": "kv_producer",
                    "kv_connector_extra_config": {
                        "shared_storage_path": tmpdir,
                    },
                },
            )
    
            prompts = ["Hello, how are you?", "What is machine learning?"]
            sampling_params = SamplingParams(max_tokens=1)
            outputs = llm.generate(prompts, sampling_params)
    
            for output in outputs:
                print("Prompt:", output.prompt)
                print("Prompt token ids:", output.prompt_token_ids)
    
                hidden_states_path = output.kv_transfer_params.get("hidden_states_path")
                print("Hidden states saved to:", hidden_states_path)
    
                with safe_open(hidden_states_path, "pt") as f:
                    token_ids = f.get_tensor("token_ids")
                    hidden_states = f.get_tensor("hidden_states")
                    print("Shape:", hidden_states.shape)
                    # Shape: (num_tokens, num_layers, hidden_size)
    
    if __name__ == "__main__":
        main()
    
  • 在线推理

    为提升性能,建议在线使用时使用挂载于RAM的文件系统(如/dev/shm/),以便客户端在文件生成后立即清理。

    vllm serve Qwen/Qwen3-8B \
      --tensor-parallel-size 1 \
      --speculative-config '{"method": "extract_hidden_states", "num_speculative_tokens": 1, "draft_model_config": {"hf_config": {"eagle_aux_hidden_state_layer_ids": [2, 18, 34]}}}' \
      --kv-transfer-config '{"kv_connector": "ExampleHiddenStatesConnector", "kv_role": "kv_producer", "kv_connector_extra_config": {"shared_storage_path": "/dev/shm/hidden_states"}}'
    

关键配置参数:

  1. num_speculative_tokens:必须设置为 1。此方法不执行实际推测,因此该值是固定的。

  2. eagle_aux_hidden_state_layer_ids:要从中提取隐藏状态的层索引列表。例如,[2, 18, 34] 从第 2、18 和 34 层提取。

  3. kv_connector:必须设置为 "ExampleHiddenStatesConnector" 以启用将隐藏状态保存到磁盘。

  4. kv_role:对于提取模式,必须设置为 "kv_producer"

  5. shared_storage_path:隐藏状态将保存为 .safetensors 文件(每个请求一个文件)的目录。

动态推测解码

动态推测解码在运行时自适应调整草稿令牌数量(K),而不是始终使用固定的 num_speculative_tokens。这有助于在并发度和草稿置信度变化时,继续发挥推测解码的收益。vLLM Ascend 当前提供两种方案:

  • 基于置信度的验证长度(DSpark / DFlash):根据草稿置信度调整每个请求的验证长度。DSpark 使用专用的 confidence head;DFlash 为 head-free,使用草稿令牌的 max(softmax(logits)) 作为置信度代理。
  • 基于 Batch Size(自回归):通过 num_speculative_tokens_per_batch_size 按并发区间选择共享的 K。

基于置信度的验证长度(DSpark / DFlash)

该方案按请求自适应调整参与验证的草稿令牌数量。当草稿模型对靠后位置的令牌信心较低时,可以降低验证开销;当信心较高时,仍可保留更长的推测长度。

[!NOTE] 这是一个探索性功能,目前仅面向 model runner v1。支持的动态方法为 "dspark"(confidence head)与 "dflash"(head-free)。在 DSpark 草稿模型中,目前仅支持 Qwen 系列;GLM、DeepSeek 等模型的支持正在陆续实现。

工作原理

dynamic_spec_config.method 设置为 "dspark""dflash" 时,对应的提议者会:

  1. 按常规方式运行草稿模型,生成最多 num_speculative_tokens 个草稿令牌。
  2. 估计每个请求在各个位置上的接受可能性:
  3. DSpark:DSpark confidence head 的 sigmoid 输出。
  4. DFlash(head-free):argmax 草稿令牌的 max(softmax(logits))(无需额外神经网络头)。
  5. 基于这些置信度分数,周期性重新计算共享的每请求验证预算。
  6. 将该预算分配给各个请求(每个请求至少保留 min_verify_tokens),使每个请求只验证一段草稿令牌前缀。

得到的每请求验证长度会在模型运行器收集目标模型验证阶段所用的草稿令牌时被消费。

配置

通过 additional_config.dynamic_spec_config 启用该方案。完整参数说明请参见附加配置

你仍需配置匹配的 speculative_configmethod: "dspark""dflash"、草稿模型路径以及 num_speculative_tokens)。动态解码只决定每个请求实际验证其中多少个草稿令牌。请将 dynamic_spec_config.method 设置为与 speculative_config 相同的方法名。

离线推理示例

from vllm import LLM, SamplingParams
from vllm.config import CompilationConfig

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,
    distributed_executor_backend="mp",
    max_model_len=4096,
    max_num_seqs=8,
    gpu_memory_utilization=0.8,
    enable_prefix_caching=False,
    speculative_config={
        "method": "dspark",
        "model": "deepseek-ai/dspark_qwen3_8b_block7",
        "num_speculative_tokens": 7,
    },
    additional_config={
        "dynamic_spec_config": {
            "method": "dspark",
            "method_params": {
                "initial_verify_budget_per_req": 5,
                "budget_update_interval": 50,
                "budget_threshold": 0.7,
            },
        },
    },
    compilation_config=CompilationConfig(cudagraph_mode="FULL"),
)
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}")

基于 Batch Size(自回归)

该方案根据当前并发度(batch size)选择共享的推测长度 K,适用于 MTP、EAGLE / EAGLE-3、DFlash、n-gram 等自回归草稿方法。

为什么需要它?

推测解码方法在解码阶段需要为每个序列验证 K 个令牌。随着 batch size(BS)增大,有效 batch 变为 BS * K,从而增加验证阶段的计算量。当 BS * K 超过临界 batch size 时,推测解码反而可能损害解码速度(TPOT)。基于 Batch Size 的动态推测解码会将 K 调整到合适的值,使推测解码继续保持收益。

使用场景

  • 同一部署下并发度变化:随着并发增加,K 会减小。
  • RL rollout 场景:开始时 BS 较高,随后因少量长尾请求而缩小;在 rollout 后期 K 可以再次增大。
  • 当前支持 MTP、EAGLE-3、DFlash、n-gram 等方法。Suffix Decoding 本身已是按请求动态的,外层动态 K 是冗余或冲突的;这是上游约束,并非 Ascend 特有问题。该路径当前不支持 DSpark 以及 MTP + DCP + DSD

--speculative-config 配置格式

要使用基于 Batch Size 的动态推测解码,请在受支持方法的 speculative config 中添加 num_speculative_tokens_per_batch_size。它是一个二维列表,每个条目为 [start_bs, end_bs, optimal_K]:当并发度落在 [start_bs, end_bs] 范围内时,使用 optimal_K 个草稿令牌。例如:

--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,即不生成草稿令牌

在线示例

Dynamic SD Eagle 草稿模型
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 草稿模型
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]
    ]
  }'

块验证和熵验证

vLLM Ascend 为推测解码中的拒绝采样器提供了两个可选优化:块验证熵验证。这些功能以少量输出精度为代价,换取推理吞吐量的提升。

[!WARNING] 块验证和熵验证都会修改令牌接受标准,并可能导致轻微的精度下降(例如,与标准拒绝采样器相比,输出令牌略有不同)。在生产环境中启用它们之前,请评估对特定工作负载的质量影响。

块验证

块验证使用累积概率乘积将所有草稿令牌作为一个整体进行评估,而不是独立检查每个令牌。这可以提高接受率并减少拒绝采样的开销,尤其是在 num_speculative_tokens >= 3 时。

熵验证

熵验证根据目标分布的熵调整接受阈值:

  • 高熵(不确定分布)→ 较低的有效阈值 → 接受更多令牌
  • 低熵(确定分布)→ 较高的有效阈值 → 更严格的拒绝

这个熵感知阈值由两个参数控制:

  • posterior_threshold(默认值:0.95,范围:(0, 1]):修改后阈值的上限。即使熵非常低,有效阈值也不会超过此值。
  • posterior_alpha(默认值:0.4,范围:>= 0):控制熵对阈值的影响程度。alpha 值越高,阈值对熵变化越敏感,导致推测令牌的接受率更高,但精度损失也更大。您需要根据特定的模型和数据集调整此值。当 alpha 为 0 时,熵不起作用,阈值等于 posterior_threshold

使用方法

  • 在线推理

    vllm serve <model> --additional-config \
        '{"rejection_sampler_config": {"enable_block_verify": true, \
        "enable_entropy_verify": true, "posterior_threshold": 0.95, \
        "posterior_alpha": 0.4}}'
    
  • 离线推理

    llm = LLM(
        model,
        additional_config={
            "rejection_sampler_config": {
                "enable_block_verify": True,
                "enable_entropy_verify": True,
                "posterior_threshold": 0.95,
                "posterior_alpha": 0.4,
            }
        },
    )
    

这两个功能可以独立或同时启用。同时使用时,块验证的累积接受率将与熵验证的熵调整阈值相结合。

合成拒绝采样

除了默认的 standard 拒绝采样(当 target_prob / draft_prob >= uniform 时接受草稿令牌)之外,vLLM Ascend 还支持合成拒绝采样。在此模式下,位置 i 处的每个草稿令牌以配置的概率 rates[i] 被接受,并与均匀随机抽取进行比较——与目标/草稿概率匹配无关。第一个被拒绝的位置会停止请求并发出一个恢复令牌;如果所有草稿令牌都被接受,则追加奖励令牌。

为什么使用它

合成模式将验证器的接受逻辑与草稿模型质量解耦,使您能够以受控的固定接受率驱动推测解码流水线。它适用于:

  • 基准测试验证路径(内核 + 采样器 + 令牌组装)在已知接受率下的吞吐量/延迟,而不依赖于训练良好的草稿模型。
  • 内核验证——因为接受简化为 uniform < rate,结果完全由输入决定。端到端测试在相同输入下将 Ascend Triton 内核(SYNTHETIC_MODE=True)与 PyTorch 参考实现进行比较,两者必须逐位一致。
  • 压力测试推测流水线的高接受率场景。

[!WARNING] 合成模式接受草稿令牌,无论它们是否与目标分布匹配,因此生成的输出在语义上不正确。仅将其用于基准测试和验证,切勿用于生产环境服务

如何使用

合成模式通过 speculative_config 与提议者设置一起配置:

  • rejection_sample_method(str,默认值:"standard"):设置为 "synthetic" 以启用。
  • synthetic_acceptance_rates(list[float],可选):每个位置的接受率。长度必须为 num_speculative_tokens,每个条目在 [0, 1] 范围内,并且单调不递增。
  • synthetic_acceptance_length(float,可选):目标平均接受长度,范围为 [1, num_speculative_tokens + 1],内部解析为等效的 synthetic_acceptance_rates 调度。与 synthetic_acceptance_rates 互斥;当 rejection_sample_method == "synthetic" 时必须且只能提供其中一个。

离线推理

from vllm import LLM

llm = LLM(
    model="path/to/target/model",
    speculative_config={
        "method": "eagle3",
        "model": "path/to/draft/model",
        "num_speculative_tokens": 3,
        "rejection_sample_method": "synthetic",
        "synthetic_acceptance_rates": [0.9, 0.6, 0.3],
    },
)

在线服务

vllm serve path/to/target/model \
  --speculative-config '{"method": "eagle3", "model": "path/to/draft/model", \
    "num_speculative_tokens": 3, "rejection_sample_method": "synthetic", \
    "synthetic_acceptance_rates": [0.9, 0.6, 0.3]}'

Ascend 特定说明

上游vLLM的合成路径使用tl_rand64(fp64)直接在Triton内核内部生成每个token的均匀随机数。NPU Triton支持tl_rand64/fp64,因此vLLM Ascend按模型运行器路径重新实现了合成模式:

  • MRV1vllm_ascend/sample/rejection_sampler.py + vllm_ascend/ops/triton/reject_sample.py):内核接收均匀概率作为显式指针参数,而不是在内部生成。概率由generate_uniform_probs以fp64生成(与上游一致,因此抽取永远不会恰好采样到0.0),并转换为fp32用于贪心内核,仅支持fp32的Ascend Triton内核随后将其与rates[pos]比较。
  • MRV2vllm_ascend/worker/v2/spec_decode/rejection_sampler_utils.py,在启用V2模型运行器时使用):均匀抽取在_probabilistic_rejection_kernel内部SYNTHETIC_MODE=True生成,使用1元素块的fp32 tl.rand并限制在[2^-31, 1)范围内——等同于上游的includes_zero=False语义。支持贪心和采样温度两种模式,第一个被拒绝的位置后跟一个从目标分布重新采样/奖励的token,与standard模式相同。

配置接口(rejection_sample_methodsynthetic_acceptance_ratessynthetic_acceptance_length)在两条路径上完全相同。