loading

Loading

探索与发现

首页 AI 开发实战

投机解码接受率好看,加速比为什么对不上

字数: (2219)
阅读: (8)
0
摘要:投机解码的接受率乘步长推算出的加速比,经常比线上实测高出三成以上。本文拆开性能模型里的三个偏差来源:ρ 随部署形态漂移、回退与验证的隐性成本、采样参数导致 α 失真,并给出落地前的观测方案与步长扫描方法。

部署投机解码时最容易踩的一个坑,是拿离线 benchmark 的接受率 × 步长算出理论加速比,然后跟线上 TPOT 一比,发现差了 30% 甚至更多。这事我遇到过两次,两个模型的接受率分别为 0.72 和 0.68,推算应该拿到 2.4 倍左右,实际上线只有 1.6 倍。问题不在推理引擎实现,而在推导加速比的那个公式少算了好几项。

先用性能模型把账摊开

把大模型单次 decode 的耗时归一化为 1,一轮投机的近似耗时是 1 + ρK,其中 ρ 是草稿单步与大模型单步的耗时比,K 是每次草拟的 token 数。这一轮的期望产出是 αK 个 token,α 为接受率。于是单位 token 相对耗时为:

(1 + ρK) / (αK)

加速条件就很直接了:只有 αK > 1 + ρK 才有正收益。以 ρ=0.2、α=0.6 为例,K=1 时相对耗时 2.0,投产就是纯亏;K=4 时刚好 1.0,也就是打平。K=4、α=0.4 时是 1.5,反向减速 50%。

讨论 σ(接受率)之前,先看一件被反复忽略的事:收益完全不来源于接受率本身;实际看的是单位 token 耗时的分母 αK 能不能跑赢验证的固定开销。这让投机解码变成了一个逐利用率的问题,而不是逐接受率的问题。

偏差一:ρ 是个活动靶

到处引用的 ρ ≈ 0.1 来自小模型与大模型参数差 10 倍左右的理想配置,比如 7B 配 72B。但生产环境的部署方式会把 ρ 推高:大模型跑多卡张量并行、小模型单卡跑,或者大模型仍在跑大 batch,小模型也跟着被 batch 拖慢。这时 ρ 变成 0.3 甚至更高,同样的 α 和 K,相对耗时立刻滑过 1.0。

阿里 InfoQ 那篇 RTP-LLM 的文章给了一组更接近真实的数量:小模型单次 decode 7ms,大模型单次 decode 并同时打 5 个 token 的分 18ms。按这组数,每轮 5×7+18=53ms,平均接受 3 个 token 时每 token 17.66ms,加速比 1.02;接受 4 个时才到 1.35。换句话说,接受 3 和接受 4 只差一个 token,收益差了 30 多个百分点,这条曲线在低步长处是抖的。你按验收率估算时填的 ρ,必须是你线上的栈实测出来的那个数。

同一组接受率数字在不同的部署形态下不具可比性。对外汇报加速比时,ρ 和 K 要和 α 写在一起,否则下一个人复现不出你的结果。

偏差二:验证预算不为零,回退也有成本

在性能模型里,验证只花 1 个单位时间。推理引擎的真实实现还得额外扣几笔:

  • 回退时 KV Cache 的修正。分歧点之后草稿写入的 KV 项需要清理,缓存指针要按接受前缀重新对齐。这件事在多并发批里尤其不好处理,也是很多线上故障的来源。
  • 候选拼接与批内重排。多请求的草稿步被取消后要整理 block,碎片越多这笔账越糟。
  • 验证时的额外长序列打分。大模型一次打 K+1 个位置,在 prefill 倾向的负载上 compute 还不完全是白送的,通常会增加几毫秒量级的成本,具体取决于序列长度。
  • 流式输出的解耦开销。本想让输出流与下一轮投机错开,避免队头阻塞,但解耦本身就有 token 已经接受、却还没有发下去的窗口。

上面每一项单独看都是小头,加在一起正好填掉常见报告里的 20%–40% 缺口。

偏差三:采样参数让 α 搬家

离线的 0.7–0.8 是在接近贪心的设定下测的。线上如果开着较高的 temperature 或 top_p,接受率会明显往下掉,那份资料也直接写了这一点,确定性解码更利于投机加速。于是一个更棘手的运维问题出现了:同一条推理服务里,写作类请求用高温度、抽取类请求用低温度,两组请求接受率分布完全不同,再拼进同一个 batcher 里,整批速度被低接受率的那组拖住。

按这种模型,K 并不是越大越好。接受率高时加大 K 才划算,接受率低时 K 取 3 就已经在临界点附近了。所以 num_speculative_tokens 更像是 tunning 参数,而不是一个寻优问题。

实际交付前最少做的几项

  1. 分层观测,不要只看全局 α。 把接受率按任务类型分开:摘要、结构化续写这类模式复用强的请求 α 高,开放式生成多语言请求 α 低,混合后平均值没有指导意义。
  2. 扫一遍 K 取 1/2/3/5/7。 在真实的请求分布下扫一遍并记录单位 token 相对耗时和 TPOT。别看论文里的理论曲线,它假设 ρ 不变,实际加 K 往往会让草案侧的显存压力上升,影响 ρ。
  3. 将验证吞吐与链长度分开。 定一个接受率的 go/no-go 阈值,再给合批后预估的填充浪费留一个归零开关。宁可先关掉,让一部分请求走普通 decode。
  4. 盯住有回退请求的比例。 在总体拒绝率之外,还有一个单独指标:发生回退且回退长度几乎等于 K 的请求占比。这类请求每个都是在为草稿多付的费用,严重时一小部分请求能吃掉相当一部分延迟优惠。

一句话判断标准

不要在代码里写 猜测速度 ≈ (1+αK)/(1+…) 然后把关。用来跟线上产品对齐的口径只有一个:接受率是高是低要结合每 token 的实际耗时来看;在你们自己的流量和引擎配置下,单位 token 相对耗时 < 1.0 才是加速的前提,而那个 1.0 只有装在这套栈上才有意义。

转载请注明出处: 皮哈皮阿

本文的链接地址: https://blog.phpat.com/ai-dev/644.html

手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

您可能对以下文章感兴趣
1 / 1
预览图