跳到内容

Speculative Decoding

警告

请注意,vLLM 中的推测性解码尚未优化,通常不会为所有提示数据集或采样参数带来显着的 token 间延迟降低。优化工作正在进行中,您可以在此处关注: Issue #4630

警告

目前,vLLM 中的推测性解码与流水线并行不兼容。

本文档展示了如何将 Speculative Decoding 与 vLLM 一起使用。推测性解码是一种提高内存密集型 LLM 推理中 token 间延迟的技术。

使用草稿模型进行推测

以下代码将 vLLM 配置为离线模式,使用草稿模型进行推测性解码,一次推测 5 个 token。

警告

在 vllm v0.10.0 中,不支持使用草稿模型进行推测性解码。如果您使用以下代码,将会得到一个 NotImplementedError

代码
from vllm import LLM, SamplingParams

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

llm = LLM(
    model="facebook/opt-6.7b",
    tensor_parallel_size=1,
    speculative_config={
        "model": "facebook/opt-125m",
        "num_speculative_tokens": 5,
    },
)
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 facebook/opt-6.7b \
    --host 0.0.0.0 \
    --port 8000 \
    --seed 42 \
    -tp 1 \
    --gpu_memory_utilization 0.8 \
    --speculative_config '{"model": "facebook/opt-125m", "num_speculative_tokens": 5}'

警告

注意:请使用 --speculative_config 设置所有与推测性解码相关的配置。之前单独指定模型(--speculative_model)并添加相关参数(例如 --num_speculative_tokens)的方法已被弃用。

然后使用客户端

代码
from openai import OpenAI

# Modify OpenAI's API key and API base to use vLLM's API server.
openai_api_key = "EMPTY"
openai_api_base = "https://:8000/v1"

client = OpenAI(
    # defaults to os.environ.get("OPENAI_API_KEY")
    api_key=openai_api_key,
    base_url=openai_api_base,
)

models = client.models.list()
model = models.data[0].id

# Completion API
stream = False
completion = client.completions.create(
    model=model,
    prompt="The future of AI is",
    echo=False,
    n=1,
    stream=stream,
)

print("Completion results:")
if stream:
    for c in completion:
        print(c)
else:
    print(completion)

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

以下代码将 vLLM 配置为推测性解码,其中建议通过匹配提示中的 n-grams 来生成。有关更多信息,请阅读 此线程

代码
from vllm import LLM, SamplingParams

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

llm = LLM(
    model="facebook/opt-6.7b",
    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}")

使用 Suffix Decoding 进行推测

以下代码将 vLLM 配置为推测性解码,其中建议使用 Suffix Decoding (技术报告) 生成。

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

Suffix Decoding 在重复性高的任务中可以实现更好的性能,例如代码编辑、代理循环(如自我反思、自我一致性)和 RL 回滚。

安装 Arctic Inference

Suffix Decoding 需要 Arctic Inference。您可以使用 pip install arctic-inference 安装它。

Suffix Decoding 推测 token

Suffix Decoding 将为每个请求在每个解码步骤推测动态数量的 token,因此 num_speculative_tokens 配置指定的是推测 token 的最大数量。建议使用较高的数字,例如 1632(默认值)。

代码
from vllm import LLM, SamplingParams

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

llm = LLM(
    model="facebook/opt-6.7b",
    tensor_parallel_size=1,
    speculative_config={
        "method": "suffix",
        "num_speculative_tokens": 32,
    },
)
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}")

使用 MLP 推断器进行推测

以下代码将 vLLM 配置为推测性解码,其中建议通过草稿模型生成,这些模型将草稿预测条件化在上下文向量和采样 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="meta-llama/Meta-Llama-3.1-70B-Instruct",
    tensor_parallel_size=4,
    speculative_config={
        "model": "ibm-ai-platform/llama3-70b-accelerator",
        "draft_tensor_parallel_size": 1,
    },
)
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}")

请注意,这些推测模型目前需要独立运行,不能与张量并行一起使用,尽管主模型可以使用张量并行运行(参见上面的示例)。由于推测模型相对较小,我们仍然看到了显著的速度提升。然而,此限制将在将来的版本中修复。

HF hub 上提供了各种此类推测模型

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

以下代码将 vLLM 配置为推测性解码,其中建议由基于 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-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",
    },
)

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 的草稿模型时需要考虑的几个重要事项

  1. HF 上的 EAGLE 模型仓库 中提供的 EAGLE 草稿模型应该可以直接被 vLLM 加载和使用,经过 Pull Request #12304。如果您使用的是 vllm 早于 Pull Request #12304 的版本,请使用 脚本 转换推测模型,并在 speculative_config 中指定 "model": "path/to/modified/eagle/model"。如果在使用最新版本的 vLLM 时仍然出现权重加载问题,请留下评论或提出问题。

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

  3. 当使用基于 EAGLE 的推断器与 vLLM 时,观察到的加速比低于参考实现 此处 报告的加速比。此问题正在调查中,并在此处跟踪: Issue #9565

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

Hugging Face hub 上提供了各种 EAGLE 草稿模型

基础模型 EAGLE on Hugging Face # EAGLE 参数
Vicuna-7B-v1.3 yuhuili/EAGLE-Vicuna-7B-v1.3 0.24B
Vicuna-13B-v1.3 yuhuili/EAGLE-Vicuna-13B-v1.3 0.37B
Vicuna-33B-v1.3 yuhuili/EAGLE-Vicuna-33B-v1.3 0.56B
LLaMA2-Chat 7B yuhuili/EAGLE-llama2-chat-7B 0.24B
LLaMA2-Chat 13B yuhuili/EAGLE-llama2-chat-13B 0.37B
LLaMA2-Chat 70B yuhuili/EAGLE-llama2-chat-70B 0.99B
Mixtral-8x7B-Instruct-v0.1 yuhuili/EAGLE-mixtral-instruct-8x7B 0.28B
LLaMA3-Instruct 8B yuhuili/EAGLE-LLaMA3-Instruct-8B 0.25B
LLaMA3-Instruct 70B yuhuili/EAGLE-LLaMA3-Instruct-70B 0.99B
Qwen2-7B-Instruct yuhuili/EAGLE-Qwen2-7B-Instruct 0.26B
Qwen2-72B-Instruct yuhuili/EAGLE-Qwen2-72B-Instruct 1.05B

Speculative Decoding 的无损保证

在 vLLM 中,推测性解码旨在提高推理效率,同时保持准确性。本节将推测性解码的无损保证分为三个关键领域:

  1. 理论无损 - 推测性解码采样在理论上是无损的,直到硬件数字精度的限制。浮点误差可能会导致输出分布产生细微变化,正如在 Accelerating Large Language Model Decoding with Speculative Sampling 中所讨论的。

  2. 算法无损 - vLLM 的推测性解码实现经过算法验证是无损的。关键的验证测试包括:

    • 拒绝采样器收敛性:确保 vLLM 的拒绝采样器的样本与目标分布一致。 查看测试代码
    • 贪婪采样相等性:确认使用推测性解码的贪婪采样与不使用推测性解码的贪婪采样相匹配。这验证了 vLLM 的推测性解码框架,当与 vLLM 前向传递和 vLLM 拒绝采样器集成时,提供了无损保证。 tests/spec_decode/e2e 中的几乎所有测试都使用 此断言实现 来验证此属性。
  3. vLLM Logprob 稳定性 - vLLM 目前不保证 token 对数概率(logprobs)的稳定性。这可能导致同一请求在不同运行中产生不同的输出。有关更多详细信息,请参阅 FAQ 中的“vLLM 中提示的输出是否可能在不同运行中有所不同?”部分。

虽然 vLLM 努力确保推测性解码的无损性,但由于以下因素,在有推测性解码和没有推测性解码的情况下生成的输出可能存在差异:

  • 浮点精度:硬件数值精度的差异可能导致输出分布略有不同。
  • 批处理大小和数值稳定性:批处理大小的变化可能导致 logprobs 和输出概率的变化,这可能是由于批处理操作中的非确定性行为或数值不稳定性。

有关缓解策略,请参阅 FAQ 中的“vLLM 中提示的输出是否可能在不同运行中有所不同?”条目。

vLLM 贡献者资源