跳转至

推测解码指南

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

概述

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

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

支持以下推测解码方法:

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

通用配置

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

  • method(str,必填):推测解码方法。必须是上表中列出的受支持方法名称之一。
  • num_speculative_tokens(int,必填):每次前向传播生成的推测 Token 数量。如果可用,会自动从草稿模型的 n_predict 配置(例如 MTP)或 suffix_decoding_max_tree_depth(suffix 方法)填充。 > 注意:对于 PD 分离部署,num_speculative_tokens 应满足以下任一条件: > > 1. 混合 Mamba 模型(例如 Qwen-Next 和 Qwen3.5 系列):P 节点和 D 节点上的 num_speculative_tokens 应相等。 > 2. 其他模型:P 节点上的 num_speculative_tokens 应为 1,D 节点上的 num_speculative_tokens 应大于或等于 1。
  • model(str,可选):草稿模型的路径或 HF 仓库 ID。eagle、eagle3、dflash、medusa 和 draft_model 需要此项。对于 mtp(复用目标模型)、ngram、suffix 和 extract_hidden_states 会自动解析。
  • draft_tensor_parallel_size(int,可选):草稿模型的张量并行大小。只能为 1 或与目标模型的张量并行大小相同。
  • disable_padded_drafter_batch(bool,默认:False):禁用推测解码的输入填充。如果设置为 True,推测输入批次可以包含不同长度的序列,这可能仅由某些注意力后端支持。注意: 仅对 eagle、eagle3、mtp、dflash、draft_model 和 extract_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 个 Token。因此,(num_speculative_tokens + 1) 必须 ≤ 16。

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

以下代码配置 vLLM Ascend 使用推测解码,其中提议通过匹配 Prompt 中的 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(用于提升语言模型效率的外推算法) 的草稿模型生成。

  • 离线推理

    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. EAGLE 模型的 HF 仓库 中可用的 EAGLE 草稿模型应 由 vLLM 直接加载和使用。该功能在 PR #4893 中添加。 如果你使用的是该 pull request 合并之前发布的 vLLM 版本,请更新到较新的版本。

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

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

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

使用 MTP 进行推测

MTP(Multi-Token Prediction)通过并行预测多个 token 来提升推理性能,将生成方式从单 token 生成转变为多 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="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 的 fullgraph 模式下,每个 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 使用推测解码,其中提案由带有顺序 Markov logit-bias 头的 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}'
    

自适应验证

自适应验证允许 DSpark 在每个解码步骤中为每个请求选择要验证的草稿 token 数量。置信度较高的请求可以保留更长的草稿,而低置信度的草稿会被缩短,以避免不必要的目标模型计算。该功能默认禁用,并且需要带有置信度头(confidence head)的 DSpark 模型。

在 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
  }'

自适应验证仅在模型运行器 V2(model runner V2)下生效。

更多细节,请参阅 vLLM 社区文档。

草稿 KV 滑动窗口

草稿模型通常在短上下文(例如 4-8k token)上训练。当运行中的上下文远远超出该范围时,草稿模型的注意力会偏离分布(out-of-distribution),接受率随之崩溃——投机解码变成了额外开销。草稿 KV 滑动窗口仅限制草稿模型的注意力只能读取最近的 draft_window_size 个 token。目标模型仍然关注完整上下文,因此输出质量不变。

可通过 --additional-config 配合 eagle3、dflash 和 dspark 任一方法启用:

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 输入下,对原生 2048 SWA 的 DSpark 草稿模型使用 1024 窗口,观察到平均接受率降低 -4.5%)。

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

使用 Suffix Decoding 进行投机解码

以下代码将 vLLM 配置为使用投机解码,其中提案由 Suffix Decoding 生成 (SuffixDecoding: Extreme Speculative Decoding for Emerging AI Applications)。

与 n-gram 类似,Suffix Decoding 可以通过使用最近生成的 n 个 token 进行模式匹配来生成草稿 token。与 n-gram 不同的是,Suffix Decoding(1)可以同时针对 prompt 和之前的生成内容进行模式匹配,(2)使用频率计数来提出最可能的下文,(3)在每次迭代中为每个请求推测自适应的 token 数量,以获得更好的接受率。

Suffix Decoding 在重复性较高的任务上可以获得更好的性能,例如代码编辑、智能体循环(agentic loops,如自我反思、自洽性)以及 RL rollout。

[!NOTE] Suffix Decoding 需要 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 个输出 Token。主要输出是保存到磁盘的隐藏状态,而不是生成的文本。

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 固定版本)之后, 隐藏状态层保留私有的 [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 文件保存的目录(每个请求一个文件)。

动态推测解码

动态推测解码会在运行时调整草稿 Token 数量(K),而不是始终使用固定的 num_speculative_tokens。这有助于在并发和草稿置信度变化时保持推测解码的收益。vLLM Ascend 目前提供两种方法:

  • 基于置信度的验证长度(DSpark / DFlash):根据草稿置信度调整每个请求的验证长度。DSpark 使用专用的置信度头;DFlash 为无头,并使用所生成草稿 Token 的 max(softmax(logits)) 作为置信度代理。
  • 基于批大小(自回归):通过 num_speculative_tokens_per_batch_size 从并发范围中选择共享的 K。

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

该方法会调整每个请求验证的草稿 Token 数量。当草稿模型对后续 Token 的置信度较低时,它可以降低验证成本;同时,在置信度较高时仍允许更长的推测。

[!NOTE] 这是一个探索性功能,目前仅面向 Model Runner V1。支持的动态方法为 "dspark"(置信度头)和 "dflash"(无头)。在 DSpark 草稿模型中,目前仅支持 Qwen 系列;对 GLM 和 DeepSeek 等模型的支持正在逐步添加。

工作原理

当 dynamic_spec_config.method 设置为 "dspark" 或 "dflash" 时,对应的提议器:

  1. 照常运行草稿模型,生成最多 num_speculative_tokens 个草稿 Token。
  2. 为每个请求估计每个位置的接受可能性:
  3. DSpark:DSpark 置信度头的 sigmoid 输出。
  4. DFlash(无头):argmax 草稿 Token 的 max(softmax(logits))(无额外神经头)。
  5. 根据这些置信度分数,定期重新计算每个请求共享的验证预算。
  6. 将该预算分配到各个请求(每个请求至少保留 min_verify_tokens),因此每个请求仅验证草稿 Token 的前缀。

由此得到的每个请求的验证长度会在模型运行器为目标模型验证步骤收集草稿 Token 时被使用。

配置

通过 additional_config.dynamic_spec_config 启用此方法。完整参数参考请参见附加配置。

仍然需要一个匹配的 speculative_config(method: "dspark" 或 "dflash"、草稿模型路径以及 num_speculative_tokens)。动态解码仅决定每个请求中这些草稿 Token 有多少会被验证。将 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}")

基于批大小(自回归)

此方法根据当前并发度(批大小)选择一个共享的推测长度 K。它适用于 MTP、EAGLE / EAGLE-3、DFlash 和 n-gram 等自回归草稿方法。

为什么需要?

SD 方法在解码期间需要为每个序列验证 K 个 Token。随着批大小(BS)增加,有效批大小变为 BS * K,从而增加验证期间的计算需求。当 BS * K 超过临界批大小时,推测解码可能会降低解码速度(TPOT)。基于批大小的动态 SD 会将 K 调整到最优值,使推测解码保持有益。

使用场景

  • 同一部署上的可变并发度:随着并发度增加,K 减小。
  • 以高 BS 开始、随后缩减为少量长尾请求的 RL rollout 工作负载:在 rollout 后期,K 可以再次增加。
  • 当前支持 MTP、EAGLE-3、DFlash、n-gram 及类似方法。Suffix Decoding 已经是按请求动态的,因此外层动态 K 是冗余或冲突的;这是上游约束,而非 Ascend 特有。DSpark 和 MTP + DCP + DSD 当前在此路径上不受支持。

--speculative-config 模式

要使用基于批大小的动态 SD,请在受支持方法的推测配置中添加 num_speculative_tokens_per_batch_size。它是一个列表的列表,其中每个条目为 [start_bs, end_bs, optimal_K]:当并发度位于 [start_bs, end_bs] 内时,使用 optimal_K 个草稿 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,即不会生成草稿 Token。

在线示例

动态 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]
    ]
  }'
动态 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 为推测解码中的拒绝采样器提供两个可选优化:Block Verify 和 Entropy Verify。这些功能以少量输出精度换取更高的推理吞吐量。

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

块验证

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

熵验证

Entropy Verify 根据目标分布的熵调整接受阈值:

  • 高熵(不确定分布)→ 更低的实际阈值 → 接受更多 Token
  • 低熵(确定分布)→ 更高的实际阈值 → 更严格的拒绝

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

  • posterior_threshold(默认值:0.95,范围:(0, 1]):修改后的阈值上限。即使熵非常低,实际阈值也不会超过该值。
  • posterior_alpha(默认值:0.4,范围:>= 0):控制熵对阈值的影响强度。更高的 alpha 会使阈值对熵变化更敏感,从而提高推测 Token 的接受率,但也会带来更大的精度损失。需要根据特定模型和数据集调整该值。当 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,
            }
        },
    )
    

这两个功能可以独立启用,也可以同时启用。同时使用时,Block Verify 的累计接受率会与 Entropy Verify 的熵调整阈值相结合。

合成拒绝采样

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

为什么使用它

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

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

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

如何使用

synthetic 模式通过 speculative_config 配置,与 proposer 设置一起使用:

  • 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 的 synthetic 路径直接在 Triton 内核中使用 tl_rand64(fp64)抽取每个 Token 的均匀随机数。NPU Triton 不支持 tl_rand64/fp64,因此 vLLM Ascend 按 model-runner 路径重新实现了 synthetic 模式:

  • MRV1(vllm_ascend/sample/rejection_sampler.py + vllm_ascend/ops/triton/reject_sample.py):内核通过显式指针参数接收均匀概率,而不是在内部生成它们。概率由 generate_uniform_probs 以 fp64 生成(与上游一致,因此抽样永远不会恰好得到 0.0),并转换为 fp32 供 greedy 内核使用,随后仅支持 fp32 的 Ascend Triton 内核将其与 rates[pos] 进行比较。
  • MRV2(vllm_ascend/worker/v2/spec_decode/rejection_sampler_utils.py,在启用 V2 model runner 时使用):均匀抽样在 _probabilistic_rejection_kernel 内部生成,并设置 SYNTHETIC_MODE=True,使用 1 元素块的 fp32 tl.rand,并钳制到 [2^-31, 1)——等价于上游的 includes_zero=False 语义。同时支持 greedy 和 sampling 温度,并且第一个被拒绝的位置之后会从目标分布中重新采样/追加 bonus Token,与 standard 模式一致。

两个路径上的配置接口(rejection_sample_method、synthetic_acceptance_rates、synthetic_acceptance_length)完全相同。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 vl/vllm-ascend