上周帮朋友压测一个私有化部署的 32B 模型,他盯着监控里不到 40% 的 GPU 利用率直挠头:卡没吃满,出字速度却只有每秒十几个 token,用户已经在群里抱怨"打字机卡了"。他本能地想加卡,我拦住了他——你这大概率不是算力不够,先试试投机解码,硬件一分钱不用加,速度能翻两三倍。
他半信半疑,反问了我一句差点把我问住的话:"找个小模型替它写,那输出还是大模型的吗?"
这问题问得好,因为投机解码(Speculative Decoding,也叫"猜测解码")最反直觉的地方就在这——它不是"小模型先答、大模型润色",而是小模型只管猜,大模型只管判,最后吐出来的每一个 token(词元,模型处理文本的最小单位),在数学上都和大模型自己一个字一个字生成完全等价。
说到这我得先岔一句,别把它和"AI 猜你喜欢"那种推荐算法搞混,一点关系没有,虽然名字里都带个"猜"。
先说为什么慢。大模型生成文本是自回归的,说白了就是一个字一个字往外蹦,每蹦一个字都要把整个模型完整跑一遍前向计算。32B 的模型跑一遍,大头根本不在 GPU 的算力上,而在显存带宽上——十几 GB 的权重得从显存搬到计算单元,搬一趟才算出一个 token。你的算力利用率停在 40%,是因为计算单元大部分时间都在干等数据到位。这也解释了一个怪现象:你调大并发,总吞吐上去了,但单条请求的出字速度还是那么磨叽——瓶颈压根不在算力上。
投机解码的思路特别糙,但特别妙:反正大模型判一个 token 要跑一遍完整前向,判一串也是跑一遍,那为什么不一次多判几个?流程是这样的:用一个体积小得多的模型(叫 draft model,草稿模型)连猜 5 个 token,把这 5 个候选一次性丢给大模型,大模型一次前向就能同时验证这 5 个位置——猜对的直接收下,全是白赚的;猜错的位置从大模型自己的概率分布里重新采一个。一次大模型前向,从"产出一个 token"变成"最多产出六个",这就是提速的全部秘密。
那"一字不差"是怎么保证的?靠的是拒绝采样那套数学。验证时如果小模型给的 token 大模型也认可,直接收下;如果小模型想说 A、大模型想说 B,也不是简单扔掉重来,而是按各自概率做一次调和式的取舍,最终可以严格证明:这样生成的序列分布,和大模型自回归生成完全一致。换句话说,你没有拿质量换速度,一个字都没换。
顺嘴再岔一下,这也是我愿意在生产环境用它、却不太敢随便上 INT4 量化的原因——量化是拿画质换流畅,投机解码是纯白嫖,心里踏实得多。
第一次跑的时候我还是翻车了。当时我随手挑了个 7B 模型当草稿,结果它和大模型连词表都对不上——tokenizer(分词器,负责把文本切成 token 的组件)不同,直接报错。后来才搞明白,草稿模型必须和大模型共享同一套词表,这是硬前提。现在通行的做法是直接拿官方配套的小杯模型,0.5B 到 3B 那种,词表天然对齐,省心。
还有个坑更隐蔽。我拿单请求测,出字速度从 22 token/s 拉到了 58,美滋滋地推上高并发,结果并发一上来速度不升反降,比不开还慢。查了半天才知道:投机解码本质是"拿空闲算力换时间",GPU 本来没吃满,它才有余力去验证草稿;等并发把算力拉满,再塞草稿模型的计算进去,反而挤占正经活。所以 vLLM 这类推理框架在高并发场景下是建议关掉投机解码的——它省时间,但不省总算力,人挤人的时候这笔买卖不划算。
还有个账容易被忽略:草稿模型的接受率决定一切。草稿太弱,猜五个错四个,大模型白跑好几趟验证,整体反而更慢。我们后来把草稿从 7B 换成同系列的 1.5B(同源蒸馏的,写作路数接近),接受率从三成多拉到七成,加速比才算稳住。挑草稿别光看参数小,和大模型"口音"接近比什么都重要。
说到这你也看出来了,投机解码不是免费午餐,更像一门排班的艺术:低并发、长输出、GPU 喂不饱的场景,它就是白捡的提速;高并发的生产集群,它可能帮倒忙。草稿模型还得跟着大模型版本一起维护,多一份运维成本,大规模部署里这不是小事。
对了,还有个坑差点忘了讲——有些推理框架默认就开了投机解码但藏在配置里不吭声,你会发现自己测的延迟数据和别人怎么都对不上,排查半宿才发现两边环境配置不一样。做性能对比测试之前,先把这开关查清楚,能少熬两小时夜。
回到我朋友那台机器。他后来配了个 1.5B 草稿模型,同样一张卡,出字速度翻了将近三倍,省下的预算够给团队加半年下午茶。但他现在纠结一个新问题:以后推理框架的调度越来越聪明,能不能自动判断"现在这波流量该不该开投机"?你那边呢——你们生产环境开着投机解码吗,接受率能到多少?还是干脆宁可加卡也不碰这玩意?评论区聊聊,我也想知道大家都怎么选的。