词条 · AI · 入门
自回归解码
模型一次只能吐出一个词,下一个词要等前一个吐完才能算——所以输出越长,等得越久。
也称:Autoregressive Decoding、autoregressive decoding、自回归生成、逐 token 生成
先看一个麻烦
你有台很快的机器,八张卡,模型也不大。你问它”3 加 5 等于几”,它答”8”,用了 40 毫秒。很好。
然后你让它把一段两千字的文档翻译一遍。它用了 30 秒。
这里有点不对劲。同一个模型、同一台机器,处理量差不多(读一段、写一段),怎么会差出七百倍?加卡也几乎不改善——第二张卡插上去,30 秒变成 28 秒。
更奇怪的是:这 30 秒里,GPU 的利用率低得可怜。机器大部分时间在等。
它在等什么?
几个朴素猜测
猜测一:模型太大,算不过来。那换个小的。1.5B 的模型比 70B 的快,但快的倍数远小于参数量的倍数,而且长输出依然慢——两千字还是要好几秒。参数量不是主因。
猜测二:那就并行。两千字切成十段,十张卡同时翻,最后拼起来。
这个想法在”读”的一侧成立,在”写”的一侧不成立。原因是:第 501 个词要用到第 500 个词是什么。你没法在不知道第 500 个词的情况下算第 501 个——那不是算力问题,是数据依赖。切成十段并行的前提是十段之间互不依赖,而这里后一段依赖前一段的每一个字。
猜测三:那批量处理。一次塞进 32 个请求,让 GPU 吃饱。
这个有效,但它优化的是吞吐,不是延迟。32 个用户的总等待时间下降了,每个用户自己的等待时间没变——甚至略微变长。如果你只有一个请求,而且它必须在 600 毫秒内出结果,批处理帮不上。
猜测四:缓存。既然每一步的输入都包含上一步的全部内容,那重复计算的部分一定很多,缓存掉应该就好了。
这个方向对,而且确实是标准做法(见 KV Cache)。但它省下的是每一步内部重算历史的开销,把单步从”随长度增长”压到接近常数。步数一步没少。缓存之后两千字依然要走两千步——只是每步便宜了。
四次撞墙的位置不一样,但暴露的是同一件事:慢不在算力,在于生成有个不能被绕开的顺序。要理解这个顺序为什么不能绕,得看它每一步实际在做什么。
机制
模型生成文字的方式是:每次只算出一个词,把它接到已有文字的末尾,然后拿着变长了的文字再算下一个。这叫自回归——“回归到自己身上”,因为它的输入包含自己刚才的输出。
第 1 步 输入:[问题] → 算 → "答"
第 2 步 输入:[问题] 答 → 算 → "案"
第 3 步 输入:[问题] 答 案 → 算 → "是"
第 4 步 输入:[问题] 答 案 是 → 算 → "8"
↑
每一步的输入都包含上一步的输出
每一步内部是高度并行的——几十亿次乘法同时铺在几千个 CUDA 核上,这部分 GPU 很擅长。但步与步之间是严格串行的,因为第 步的输入里有第 步的结果。
所以总时间是:
是输出的 token 数, 是单步耗时。注意 (输入长度)不在乘法里——读入的部分可以一次性并行算完(这一步叫 prefill)。所以「读两千字」很快,「写两千字」很慢,两者根本不是同一种操作。
由什么决定?主要是把模型权重从显存搬到计算单元这一趟的时间。每生成一个词,都要把全部权重过一遍。这是内存带宽问题,不是算力问题——这解释了为什么 GPU 利用率低:核心在等数据到货。KV Cache 消除的是重算历史的开销,但消除不了这个搬运,所以它降不了步数。
给个实测量级:一篇 2026 年的论文里,一个多模态智能体输出 48 个 token 的动作 JSON 用了 411 毫秒,也就是约 8.6 ms/token。这个数与任务难度无关——简单任务和难任务,只要输出一样长,就一样慢。
顺带解释了另一个现象:为什么”想清楚再答”会变慢。让模型先写一段推理再给结论,等于把 从 5 涨到 300。慢 60 倍不是因为思考本身费力,是因为思考被写成了字,而每个字都要排一次队。
为什么值得知道
延迟由输出长度决定,不由问题难度决定。这条最反直觉,也最有用。让模型”想清楚再答”通常意味着让它多写字,而多写字就是线性地多花时间。一个要求模型输出思维链的提示词,可能把延迟乘上 5 倍——不是因为思考费力,是因为思考被写出来了。反过来,压缩输出格式(短字段名、省掉解释、只回一个枚举值)是实打实的延迟优化。
有截止时间的场景要单独设计。如果某个动作必须在 600 毫秒内发出,而在线生成要 411 毫秒,那留给感知和执行的余量只有不到 200 毫秒。这时唯一的出路是把生成挪出关键路径:提前在空闲时把可能要用的输出准备好,事件到来时只做一次廉价的选择。这正是预期策略树那类方法的思路。
加硬件的收益有限。 受内存带宽限制, 由任务定,两者都不随卡数下降。加卡提升的是能同时服务多少人,不是单个请求多快。要真正压 或步数,得动结构:量化(少搬字节)、投机解码(用小模型草拟、大模型一次校验多个 token)、更短的输出格式。
“流式输出”是体验优化,不是延迟优化。逐字显示让人觉得快,但最后一个字的到达时间一点没变。如果下游是程序而不是人——比如要解析完整 JSON 才能执行——流式一点用都没有,你等的还是全长。
收束
回到开头:读一段快,写一段慢七百倍,加卡没用,GPU 还闲着。
三个朴素猜测各撞一次墙,撞的位置连起来就是答案。换小模型没解决,说明瓶颈不在参数量;切段并行不能用在输出侧,说明存在不可绕开的数据依赖——第 501 个词要用第 500 个词;批处理有效但只对吞吐有效,说明延迟和吞吐是两个独立的东西,优化一个不等于优化另一个。
机制本身很朴素:,输出长度乘单步时间,输入长度不参与。GPU 闲着是因为每一步都在等权重从显存搬过来,这是带宽问题不是算力问题。
于是那个”写两千字要 30 秒”就不再奇怪了:它不是模型笨,是它被迫排了两千次队,每次队都得把整个模型搬一遍。
这条式子的实用形态是一句话:要让它快,就让它少说话。而如果任务本身有截止时间,唯一可靠的办法不是让它说得更快,是让它提前说完。
来源
提到这个词条的文章
- 自回归解码站在关键路径上:GUI 智能体的 650ms 截止时间问题 2026-08-02