用小模型"猜测"来换取大模型的推理加速

前言

上一篇我介绍了 vLLM 的 LoRA 动态加载,很多读者私信问:72B 这样的大模型推理太慢了,除了加显卡还有什么办法?

今天聊的投机解码(Speculative Decoding)就是答案。这是一个让我第一次看到就拍大腿的技术——不改模型、不加硬件、不损精度,推理速度直接翻倍

💡 什么是投机解码?

投机解码是一种推理加速技术,核心思想是让一个轻量的”草稿模型”快速生成多个候选 token,然后让目标大模型并行验证。由于验证比逐个生成快得多,整体推理速度可以提升 1.5-2.5 倍,且输出质量与原模型数学等价


为什么大模型推理慢

要理解投机解码为什么有效,得先搞清楚大模型推理的瓶颈在哪。

Memory Bound:显存带宽才是真凶

很多人以为大模型慢是因为计算量大。错了。

现代 GPU 的算力其实是过剩的。以 A100 为例:

  • 算力:312 TFLOPS(FP16)
  • 显存带宽:2 TB/s

一个 70B 模型(假设 FP16)的权重是 140GB。生成每个 token 都需要把这 140GB 权重从显存读到计算单元。按 2TB/s 的带宽算:

140GB ÷ 2TB/s = 70ms/token ≈ 14 tokens/s

而实际的矩阵运算只需要几毫秒。90% 以上的时间都在等数据搬运,GPU 大部分时候是闲着的

📊 Memory Bound(显存带宽受限):指 GPU 的计算单元等待数据从显存传输过来的状态。此时算力是过剩的,瓶颈在于数据搬运速度。

自回归生成的原罪

雪上加霜的是,Transformer 的自回归生成机制要求一个一个 token 地生成

输入:今天天气
第1步:今天天气 → 真(需要读取140GB)
第2步:今天天气真 → 不(需要读取140GB)
第3步:今天天气真不 → 错(需要读取140GB)
...

每生成一个 token,就要完整读取一遍模型权重。如果能一次生成多个 token,就能大幅减少权重读取次数。

问题是:怎么在不知道前一个 token 的情况下,预测下一个 token?


投机解码原理

投机解码的核心思想惊人地简单:让一个小模型先猜几个 token,再让大模型并行验证

具体流程

假设我们要用 72B 大模型生成文本,配一个 1.5B 的小模型:

Step 1:小模型快速生成5个草稿 token
        输入:"今天天气"
        小模型输出:"真不错啊!" (5个token)
        耗时:10ms

Step 2:大模型并行验证这5个 token
        输入:"今天天气" + "真不错啊!"
        大模型判断:
          - "真" ✅ 接受(和大模型自己生成的一致)
          - "不" ✅ 接受
          - "错" ❌ 拒绝(大模型想生成"好")
          - 后续 token 作废
        耗时:70ms(只读取一次权重!)

Step 3:采纳被接受的 token,从拒绝点重新开始
        已生成:"今天天气真不"
        大模型同时输出正确的第3个token:"好"
        继续下一轮...

关键洞察:大模型验证 5 个 token 和生成 1 个 token 的时间几乎一样(都是读取一次权重)!

如果小模型猜对了 3 个,我们就用 80ms 生成了 4 个 token(3 个猜对的 + 1 个大模型修正的),而传统方式需要 280ms。

为什么能保证输出质量

这是投机解码最精妙的地方:数学上可以证明,投机解码的输出分布与纯大模型完全一致

验证阶段用的是一个叫”投机采样”的算法。简单说:

  • 如果小模型和大模型对某个 token 的概率分布一致,直接接受
  • 如果不一致,按一定概率接受或拒绝,保证最终分布正确

🔬 数学等价:这不是近似,是数学等价。你可以把投机解码的输出和纯大模型对比,统计上是完全一致的。


在 vLLM 中配置投机解码

vLLM 从 0.4.0 开始原生支持投机解码,配置非常简单。

基础配置

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen2.5-72B-Instruct \
    --speculative-model Qwen/Qwen2.5-1.5B-Instruct \
    --num-speculative-tokens 5 \
    --tensor-parallel-size 4
参数 说明
--speculative-model 草稿模型,越小越好
--num-speculative-tokens 每轮猜测的 token 数,通常 3-7

使用 ngram 作为草稿模型

如果你不想额外加载一个小模型,vLLM 还支持用 ngram 作为草稿模型:

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen2.5-72B-Instruct \
    --speculative-model "[ngram]" \
    --num-speculative-tokens 5 \
    --ngram-prompt-lookup-max 4

ngram 模式会从输入 prompt 中查找可能的续写。对于有重复模式的文本(如代码、模板),效果出奇地好。

MLPSpeculator:专门训练的投机头

这是最先进的方案。不用完整的小模型,而是训练一个轻量的 MLP 头来预测 token:

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-70B-Instruct \
    --speculative-model ibm-fms/llama3-70b-accelerator \
    --num-speculative-tokens 5

💡 IBM 为 Llama 系列开源了一些 accelerator 模型,专门用于投机解码。


草稿模型选择指南

草稿模型的选择直接决定加速效果。

黄金法则

原则 说明
Tokenizer 必须一致 最重要!词表不同会导致验证失败
架构相似 同系列模型效果最好(Qwen配Qwen,Llama配Llama)
越小越好 草稿模型越小,生成越快,整体加速比越高
不要太笨 太小的模型猜测准确率太低,反而拖累性能

推荐配置

目标模型 推荐草稿模型 预期加速
Qwen2.5-72B Qwen2.5-1.5B 或 0.5B 1.8-2.2x
Llama-3.1-70B Llama-3.2-1B 1.6-2.0x
Mistral-Large Mistral-7B 1.5-1.8x
DeepSeek-V2-236B DeepSeek-V2-Lite-16B 1.4-1.7x

如何判断草稿模型是否合适

vLLM 会输出接受率(Acceptance Rate)的统计:

[Speculative Decoding] Acceptance rate: 0.72, Efficiency: 1.86x
  • 接受率 > 0.6:草稿模型选得不错
  • 接受率 < 0.4:考虑换一个更大/更准的草稿模型
  • 接受率 > 0.9:可以尝试增加 num-speculative-tokens

性能调优实战

num-speculative-tokens 的选择

这个参数很关键,不是越大越好:

猜测数 效果
2-3 加速效果有限
4-6 通常最优
8+ 验证开销增加,接受率下降

我的经验是从 5 开始,根据接受率调整:

  • 接受率高(>0.7):可以增加到 6-7
  • 接受率低(<0.5):减少到 3-4

显存规划

投机解码需要同时加载大模型和草稿模型:

总显存 ≈ 大模型显存 + 草稿模型显存 + KV Cache 开销

例如 72B + 1.5B:

  • 72B(FP16):~144GB
  • 1.5B(FP16):~3GB
  • KV Cache:取决于并发和上下文长度

💾 显存紧张时的方案

  • 使用量化版大模型(AWQ/GPTQ)
  • 选择更小的草稿模型(0.5B)
  • 减少 gpu-memory-utilization

批处理场景的权衡

投机解码在低并发场景效果最好。高并发时:

  • 大模型本身已经能充分利用 GPU
  • 草稿模型的开销变得不可忽略
  • 接受率通常会下降
并发数 建议
< 4 投机解码效果显著
4-16 效果一般,需要测试
> 16 可能不如关闭投机解码

实测数据

测试环境:

  • GPU:4×A100 80GB
  • 目标模型:Qwen2.5-72B-Instruct
  • 草稿模型:Qwen2.5-1.5B-Instruct
  • 输入长度:约 500 tokens
  • 输出长度:约 200 tokens
配置 吞吐量 首 token 延迟 加速比
纯 72B 16 tok/s 180ms 1.0x
+ 投机解码 (k=5) 28 tok/s 220ms 1.75x
+ 投机解码 (k=7) 31 tok/s 250ms 1.94x
ngram 模式 22 tok/s 185ms 1.38x

📊 几个发现

  • 首 token 延迟会略增:因为要同时加载草稿模型
  • 代码生成场景效果更好:接受率能到 0.8+,加速比接近 2.5x
  • 创意写作场景效果一般:大模型和小模型的分布差异大,接受率只有 0.5 左右

常见问题

Q1: 开启投机解码后反而变慢了?

检查这几点:

  1. 草稿模型是否太大?试试更小的版本
  2. 并发是否太高?投机解码适合低并发
  3. 接受率是否太低?查看日志中的 acceptance rate

Q2: 报错 “Tokenizer mismatch”?

草稿模型和目标模型的 tokenizer 不兼容。必须使用同系列模型。

Q3: 可以用不同架构的模型吗?

技术上可以(只要 tokenizer 一致),但效果通常很差。小模型”理解”大模型的能力有限。

Q4: 投机解码和量化可以同时用吗?

可以!而且推荐这样做:

python -m vllm.entrypoints.openai.api_server \
    --model TheBloke/Qwen2.5-72B-Instruct-AWQ \
    --speculative-model Qwen/Qwen2.5-1.5B-Instruct \
    --quantization awq \
    --num-speculative-tokens 5

量化减少显存占用,投机解码提升速度,双重优化。


与其他优化技术的组合

投机解码可以和其他优化技术组合使用,效果叠加。

投机解码 + 量化

python -m vllm.entrypoints.openai.api_server \
    --model TheBloke/Qwen2.5-72B-Instruct-AWQ \
    --speculative-model Qwen/Qwen2.5-1.5B-Instruct \
    --quantization awq \
    --num-speculative-tokens 5 \
    --tensor-parallel-size 2

量化减少显存占用,投机解码提升速度,双重优化。实测 AWQ 量化 + 投机解码可以在 2×4090 上跑 72B 模型,吞吐量约 25 tok/s。

投机解码 + Chunked Prefill

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen2.5-72B-Instruct \
    --speculative-model Qwen/Qwen2.5-1.5B-Instruct \
    --num-speculative-tokens 5 \
    --enable-chunked-prefill \
    --max-num-batched-tokens 8192

💡 Chunked Prefill 改善长 prompt 场景的公平性,投机解码加速生成阶段。两者互不冲突。


结语

投机解码是我见过最”聪明”的推理优化技术:

特性 说明
原理简单 让小模型猜,大模型验
效果显著 1.5-2.5 倍加速,取决于场景
零精度损失 数学上等价于原始输出
配置简单 vLLM 加两个参数就行

适用场景

  • ✅ 低并发、高延迟敏感的交互式应用
  • ✅ 代码生成、模板填充等有规律的任务
  • ⚠️ 高并发场景需要测试
  • ❌ 超短输出(<10 tokens)效果有限

如果你在用 70B 级别的大模型,强烈建议试试投机解码。这可能是性价比最高的优化手段——不加显卡、不换模型,推理速度直接翻倍。

下一篇我会介绍 vLLM 的多模态模型部署,敬请期待。


参考资料

版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:《 vLLM 高性能推理系列——投机解码加速 》

本文链接:http://localhost:3015/ai/vLLM-speculative-decoding.html

本文最后一次更新为 天前,文章中的某些内容可能已过时!