§ 8.2 · Section

自回归生成

Autoregressive Generation

你有没有想过一个问题:为什么 ChatGPT 的答案是一个字一个字往外冒的?这不是为了看起来像在打字,也不是网速慢——它真的只能一次算出一个字。要写 500 个字的答案,就得把整个上千亿参数的模型完整跑 500 遍,一遍也省不掉,而且这 500 遍必须严格排队、一个接一个。这一节要把这个机制彻底讲透,因为 LLM 世界里几乎所有关于"慢"和"贵"的问题,源头都在这里——包括那个所有人都在问的"为什么输出比输入贵五六倍"。

生活场景
🍜 一个只会做"下一勺"的厨师

想象一家餐厅后厨有位规矩极其古怪的厨师。他做菜的方式是这样的:

每往锅里加一样东西之前,他必须先把锅里已有的所有东西从头到尾看一遍——一片一片数清楚,闻一遍味道,然后才决定"下一勺该加盐还是加糖"。加完这一勺,他不许接着往下加,必须重新从头再把锅里的东西看一遍,才能决定下一勺。

你在窗口外面看着,会觉得他慢得不可理喻——菜是一勺一勺变出来的,而且越到后面越慢,因为锅里的东西越来越多,每次"从头看一遍"的时间也越来越长。

但他并不是笨,而是他的能力恰好只有一种:给定"目前锅里的全部内容",判断"下一勺该放什么"。他没有菜谱,没有"我心里已经想好这道菜是宫保鸡丁"这种全局计划。整道菜就是这一个能力被反复调用几百次的结果。

把"自回归"这个词拆开

自回归这三个字听着玄,拆开看其实很直白:「自」= 拿自己的输出当输入;「回归」= 统计学里"预测一个数值"的意思。合起来说白了就是我拿我刚才写出来的东西,当成下一步的题干,来预测再下一个该写什么。

它对应的英文缩写是 AR(Autoregressive)。你会在论文里频繁看到"AR model"这个说法,指的就是这类"从左到右一个一个往外吐"的模型。GPT 里那个 G 就是 Generative(生成式),而它的生成方式正是自回归。

整个过程用文字写出来,不带任何数学也能看懂:

你输入: "今天天气"

第 1 次前向(跑一遍完整模型)
  输入 = [今天, 天气]
  模型输出 = 词表里每个 token 的概率
       "很"    35%
       "怎么"  22%
       "不"    15%
       ... (约 10 万个候选,各有一个概率)
  挑一个 → "很"
  现在已生成: "今天天气很"

第 2 次前向(重新跑一遍完整模型)
  输入 = [今天, 天气, 很]      ← 注意:把刚生成的"很"也塞回去了
  模型输出 = 概率分布
       "好"    41%
       "热"    19%
       "冷"    12%
  挑一个 → "好"
  现在已生成: "今天天气很好"

第 3 次前向(再重新跑一遍完整模型)
  输入 = [今天, 天气, 很, 好]
  模型输出 = 概率分布
       "。"      33%
       ","      21%
       "<EOS>"   18%
  挑一个 → "。"

第 4 次前向
  输入 = [今天, 天气, 很, 好, 。]
  挑出 → <EOS>   ← 模型自己写下"结束"标记
  停止。

总共: 输出 4 个 token,跑了 4 次完整的模型前向。

请特别注意最后一行。输出 N 个 token = 跑 N 次完整模型。这条等式是本节的核心,后面所有结论都是它的推论。

Analogy · 蒙眼下山,每一步都要重新摸一遍脚下

好比你蒙着眼在下山。你没有地图,没有全局视野,唯一能做的就是:用脚在四周试探一圈,感觉哪边是下坡,然后迈一步。

迈完这一步,你不能凭刚才的印象继续走——因为位置变了,你必须重新用脚试探一圈,才能决定下一步。走一百步,就得试探一百次。

更要紧的是:你无法"撤回"。一旦这一步迈出去,你就站在新位置上了,之前那一步是好是坏,你只能在这个新位置上继续往下走。模型也一样——它一旦吐出了一个词,这个词就成了后续所有推理的既定前提,不会回头改。

这解释了一个非常常见的现象:模型有时候会一路把话说错到底。它在第 3 个 token 上选了一个略微不妙的词(比如把一个人名记错了),后面 200 个 token 全都建立在这个错误之上,越编越圆、越圆越离不开。因为在它的世界里,已经写下的东西就是事实这就是"幻觉"最常见的一种发生机制,跟"模型撒谎"没关系,纯粹是自回归这个结构决定的。

为什么不能一次把 100 个字全算出来

这是最值得追问的一个"为什么"。既然 Transformer 号称"能并行处理整个序列"(第 7 章反复强调过),那为什么生成的时候反而变成了串行?

答案在于:并行的前提是"输入已经全部知道"。训练的时候,整篇文章都摆在那儿,模型可以一次把 2048 个位置全算完(用一个叫"因果掩码"的技巧遮住未来,让每个位置只能看到自己左边的内容)。但生成的时候,第 51 个 token 是什么,取决于第 50 个 token 是什么;而第 50 个 token 此刻还不存在——你没法并行算一个还不存在的东西。

这件事在学术上有一句极其精准的表述。2022 年 11 月 Google 团队的投机解码论文(Leviathan et al.,arXiv 2211.17192)开篇就点明:解码 K 个 token 需要 K 次串行的模型前向这一句话是解释"为什么 LLM 逐字输出"最权威的依据,值得记住。

用一个日常例子对比就更清楚了:

场景能不能并行为什么
批改 100 份已经写好的作业能,找 100 个人同时改100 份都在手上,互不依赖
写一篇 100 句的文章,每句要接上一句不能第 2 句必须等第 1 句定稿
LLM 处理你的输入(prefill)你的整段话已经在手上了
LLM 生成答案(decode)不能下一个 token 依赖上一个

最后两行就是本节最重要的一张表,它把一次推理明确切成了两个性质完全不同的阶段。下面我们把这两个阶段单独讲。

★ prefill 与 decode:两个完全不同的阶段

这两个词是业界标准术语,几乎所有推理框架(vLLM、TensorRT-LLM、SGLang)的文档里都在用。搞懂它们,你就同时搞懂了 LLM 的延迟、吞吐和定价。

Prefill(预填充)——处理你输入的那一段。你的 prompt 已经完整地摆在那儿了,所以模型可以把整段话的所有 token 一次性并行算完,一次前向就搞定,不管你的输入是 100 个 token 还是 10 万个。它的特点是:

Decode(解码)——一个一个往外吐答案。每一步只算一个新 token,严格串行,一步也不能省。它的特点恰好和 prefill 相反:

维度Prefill(读你的问题)Decode(写答案)
并行性整段并行,一次前向搞定严格串行,N 个 token 跑 N 次
瓶颈在哪算力(compute-bound)显存带宽(memory-bound)
决定什么指标TTFT(等第一个字多久)TPOT(每个字多久)
GPU 利用率高,吃满算力低,大量时间在搬权重
能否缓存复用(前缀相同就复用,这是缓存折扣的来源)不能
API 上对应input tokensoutput tokens

★ 这就是"输入便宜、输出贵"的全部答案

现在我们可以来解那笔谁都想问的账了。把 §8.1 提过的价格表再摊一遍(查询日期 2026 年 8 月 6 日,单位:每百万 token 美元):

模型输入价输出价倍数
GPT-5.6 Sol$5$306 倍
Claude Opus 4.7$5$255 倍
Claude Sonnet 4.6$3$155 倍
DeepSeek-V4-Flash$0.14(缓存命中 $0.0028)$0.282 倍

为什么普遍是五六倍?因为处理 100 万个输入 token 只需要少量几次前向,而生成 100 万个输出 token 需要整整 100 万次前向。

把这个差距换算成可感知的量。假设你给模型一份 1000 token 的问题,它回答了 1000 token:

Prefill(读你的 1000 token 问题)
    1 次前向,一次算完 1000 个位置
    → 对 GPU 来说是「一次大批量的矩阵乘法」,正中舒适区

Decode(写 1000 token 的答案)
    1000 次前向,每次只产出 1 个 token
    每一次都要把整个模型的权重(几百 GB 级别)
    从显存搬到计算单元过一遍
    → 1000 次搬运,1000 次串行等待

同样是 1000 个 token,硬件付出的代价差了三个数量级。
价格上只差 5~6 倍,其实已经算厂商厚道了
(因为「攒批」能把 decode 的成本摊薄给多个用户分担)。

顺便说清另一件事:为什么缓存命中能便宜到十分之一甚至五十分之一?因为缓存省掉的正是 prefill——如果你这次请求的开头一大段和上次完全一样,服务端把上次 prefill 算出来的中间结果留着,这次直接拿来接着用,一次矩阵乘法都不用做。这就像餐厅后厨提前熬好的一大锅高汤:不是每来一位客人都要重新熬八小时,舀一勺就上菜。工程启示极其明确:把不变的长内容(系统提示词、知识库、示例)放在提示词最前面,变化的部分放最后,缓存命中率才高。

还有一个不太被注意的推论:让模型"少说废话"是最直接的省钱手段。同样解决一个问题,一个啰嗦的提示词让它输出 2000 token,一个精炼的提示词让它输出 300 token,成本差近七倍——而这七倍是花在最贵的那一段上的。

KV Cache:不重算前面的部分

看到这里你可能会有个疑问:如果每生成一个 token 都要"把已生成的全部内容重新喂一遍",那生成第 1000 个 token 时是不是要重新算前 999 个 token 的注意力?那不是慢得没边了?

问得非常好,而答案是:确实是那么定义的,但工程上有个救命的优化,叫 KV Cache。

回忆第 7 章:注意力要给每个 token 算出三样东西——Q(Query,我要问什么)、K(Key,我的标签是什么)、V(Value,我的内容是什么)。关键洞察是:已经生成过的那些 token,它们的 K 和 V 是不会变的。第 5 个 token 的 K 和 V,在生成第 6 个、第 100 个、第 1000 个 token 时都完全一样。

所以做法很朴素:把算过的 K 和 V 都存起来,下一步直接拿来用,只算新 token 那一个的 Q、K、V。

没有 KV Cache(朴素做法):
  生成第 1 个 token → 算 1 个位置的 KV
  生成第 2 个 token → 重算 2 个位置的 KV
  生成第 3 个 token → 重算 3 个位置的 KV
  ...
  生成第 N 个 token → 重算 N 个位置的 KV
  总计算量 ≈ 1+2+3+...+N ≈ N²/2     ← 平方级,灾难

有 KV Cache:
  生成第 1 个 token → 算 1 个新位置,存进 cache
  生成第 2 个 token → 只算 1 个新位置,读 cache 里的 1 个
  生成第 3 个 token → 只算 1 个新位置,读 cache 里的 2 个
  ...
  总计算量 ≈ N                        ← 线性,可以接受

代价:cache 要占显存,而且随序列长度线性膨胀。

这就像你在图书馆查资料做笔记你不会每查一本新书都把之前所有书重读一遍,而是把每本书的要点抄在便签上贴着——查新书时扫一眼那排便签就行。KV Cache 就是那排便签

那 cache 要占多少显存?按定义可以直接推出一个公式:

KV Cache 显存占用(按定义推导,非某篇论文的结论)

  = 2 × 层数 × 序列长度 × KV头数 × head维度 × batch大小 × 每个数占的字节数
    ↑
    这个 2 是因为要存 K 和 V 两份

读法:
  「层数」    每一层 Transformer 都有自己的一套 KV,都得存
  「序列长度」 对话越长,存的越多  → 线性增长
  「batch」   同时服务的用户越多,存的越多 → 线性增长
  「KV头数」  这一项是唯一能被架构优化砍掉的

核心结论(这才是要记住的):
  KV Cache 随「序列长度」和「同时服务的用户数」线性增长。
  它是长上下文推理最主要的显存瓶颈。

请注意,这个公式我标注为「按定义推导」——它不出自某篇特定论文,就是把"要存什么"数一遍的结果。同时我刻意不给"某某模型占多少 GB"这类具体数字,因为那高度依赖具体层数、头数、精度和实现,随便报一个数只会误导人。该记住的是趋势而不是数字:线性增长,且是长上下文的头号显存瓶颈。

为了砍掉 KV Cache,架构被改了三次

上面那个公式里,唯一能动的项是"KV 头数"。于是整个行业沿着这一项做了三代优化,这条线索非常清楚:

方案全称做法KV Cache 变化
MHAMulti-Head Attention标准做法:每个注意力头有自己独立的 K 和 V基准(最占显存)
MQAMulti-Query Attention所有头共用同一套 K 和 V降到 1/头数(极省,但效果有损)
GQAGrouped-Query Attention折中:头分成几组,每组共用一套 K/V降到 1/组数(当前主流选择)
MLAMulti-head Latent Attention把 K/V 压缩成一个低维的"潜向量"再存,用时再还原大幅下降,DeepSeek 采用

换成大白话说这四种的区别好比一间办公室里八个人各干各的活。MHA 是八个人各配一台打印机,谁也不用等,但设备成本八倍;MQA 是八个人合用一台,省到极致但要排队、还容易互相将就;GQA 是分成两组、每组一台,既不太挤也不太浪费——这是今天多数模型的选择;MLA 走了另一条路,它不是减少设备数量,而是把要打印的文件先压缩成摘要存着,用的时候再展开,这是 DeepSeek 系列的招牌手法。

这四个缩写以后你在模型技术报告里会反复见到,它们背后的动因全在那个显存公式里——为了让 KV Cache 别把显存吃光。

训练和推理,其实是两套不同的活儿

这里有一个特别容易被忽略、但一说就通的关键区别。模型在训练时其实是"并行"的,只有在推理时才变成串行。很多人以为训练也是一个字一个字来的,那就完全误解了训练成本从何而来。

训练时,一整篇文章已经摆在那儿了。模型可以一次把整篇文章的每个位置都当成一道"填空题"同时做:第 1 个位置猜第 2 个字,第 2 个位置猜第 3 个字,第 100 个位置猜第 101 个字——这几千道题一次算完。为了防止作弊(第 5 个位置偷看第 6 个字的答案),加了一个叫因果掩码(Causal Mask)的东西,把每个位置右边的内容遮住。这套做法有个专门的名字叫 Teacher Forcing(教师强制)——每一步都拿标准答案当作前文,而不是拿模型自己上一步的输出。

这就像老师批一整本作业:一百道填空题,老师可以同时批完,因为每道题的题干都是印在卷子上的既定内容,互不干扰。可等到考试的时候,学生必须一道一道往下做,而且第二道题的条件恰好是第一道题的答案——一旦第一道错了,后面全跟着歪

这个差异带来一个有名的问题叫 Exposure Bias(曝光偏差):训练时模型看到的前文永远是完美的标准答案,推理时看到的前文却是自己刚吐出来的、可能有瑕疵的内容。它从没被训练过"如何在自己写歪了之后补救"——这又是幻觉和跑题的另一层结构性原因。换成大白话:一个只做过带答案的练习册、从没独立考过试的学生,第一次真上考场必然不适应。

把速度指标算成能感知的数

厂商宣传里那些"每秒 100 token"的数字,得换算成日常能体会的量才有意义。业界经验值是 1 个 token 大约对应 0.6~0.7 个汉字(因为一个汉字约 0.6 个 token,反过来算),我们粗略按 1 token ≈ 1.5 个汉字来估:

吐字速度换算成汉字直观感受
10 token/秒约 15 字/秒比正常人朗读稍快,看着略显吃力
30 token/秒约 45 字/秒比你能读进去的速度快,体感流畅
100 token/秒约 150 字/秒刷屏,眼睛完全跟不上

关键的心理学阈值是:只要吐字速度超过人的阅读速度(每秒二十几个汉字),再快对体验的提升就很有限了。所以工程上优化 TTFT(让第一个字尽快出来)的收益,常常比继续拉高 TPOT 更明显——用户忍不了的是开头那段空白,而不是后面吐得不够快。这就像地铁进站:让人焦虑的是站台屏幕上"暂无信息",而不是列车时速再快十公里。

模型怎么知道该停下来

既然是"一个一个往外吐",那它凭什么停?总不能吐到宇宙尽头。答案有三条,按优先级排:

由此得到一条很实用的排查经验:答案在句子中间突然断掉 → 先查 max_tokens;答案该长却很短 → 查是不是提前吐了 EOS(提示词没说清要多详细);答案停不下来、一直重复 → 采样参数问题,这是 §8.4 的主题。

流式输出:为什么你看到它在"打字"

既然每一步都产出一个 token,那服务端有两个选择:等全部生成完,一次性把整段返回给你;或者每算出一个 token 就立刻推给你。后者就是流式输出(streaming)。

流式并不会让生成变快一分一秒——总耗时完全一样。它改变的只有一件事:你的等待感受。让用户在 0.5 秒后看到第一个字、然后持续有字冒出来,体验上远好过让他对着空白转圈 15 秒然后一次性出现一整段。这就像餐厅排队,队伍缓慢但一直在动,比队伍完全不动却告诉你"很快就好"要让人安心得多。

技术上流式用的是 SSE(Server-Sent Events,服务器推送事件)——一个 HTTP 长连接,服务端边算边往里写数据。工程上要留意两件事:一是中途出错怎么办(已经推给用户的字收不回来了,得设计"追加一条错误提示"而不是"替换整个回答");二是别在前端逐 token 重排整个页面,那会卡到没法看,要用追加而不是重绘。

投机解码:一个"不改变答案"的加速魔法

既然串行是死结,能不能作弊?能,而且这个办法优雅得让人拍案。

2022 年 11 月,Google 的 Leviathan 等人提出投机解码(Speculative Decoding),论文名《Fast Inference from Transformers via Speculative Decoding》(arXiv 2211.17192),后来入选 ICML 2023 Oral。思路是这样的:

常规做法:
  大模型 → 猜第 1 个 → 猜第 2 个 → 猜第 3 个 → ...
  每一步都要跑一次庞大的大模型,慢。

投机解码:
  Step 1  让一个「小模型」(draft model,草稿模型)
          快速连猜 K 个 token,比如猜 4 个:
              "今天天气很好"

  Step 2  把这 4 个猜测一起交给大模型,
          让大模型「一次前向并行验证」——
          注意:验证 4 个 token 和验证 1 个 token
          对大模型来说耗时几乎一样
          (因为 decode 是显存带宽瓶颈,不是算力瓶颈!)

  Step 3  大模型逐个核对:
              "今" ✓  "天" ✓  "天" ✓  "气" ✗
          前 3 个采纳,从第 4 个开始丢掉重来。

  结果:一次大模型前向,采纳了 3 个 token
        → 相当于把 3 步压成 1 步。

这里有两个点必须讲清,否则会误解:

为什么这招管用?因为它把"最贵的资源"换成了"最便宜的资源"。decode 阶段真正的瓶颈是搬权重(显存带宽),不是算数(算力)。既然权重都搬来了,顺手验证 4 个 token 和验证 1 个几乎不多花时间——那不如让小模型先胡乱猜几个,赌它猜对。猜对了净赚,猜错了也只损失一点小模型的算力。

打个比方:这相当于一位大领导审文件。原来是秘书写一句、领导批一句,来回跑八趟。现在改成秘书一口气写四句草稿一起递上去,领导一眼扫完:"前三句照准,第四句重写。"领导的批阅动作只花了一次,却推进了三句——而且最终定稿和领导逐句亲自写出来的一字不差。

自回归的先天局限:三件它做不好的事

理解了机制,就能预判它的短板。这三条不是"训练不够",是结构决定的:

值得一提的是,学界一直在尝试非自回归的路子。扩散语言模型就是一条——它借用画图模型的思路,先生成一整段"模糊的草稿",再一轮一轮把它"洗清楚",理论上可以并行出很多 token。好比画画:自回归是从左上角开始一笔一笔画到右下角,扩散是先铺一层大色块、再一遍遍加细节。这条路线在 2025 年前后开始出现能用的模型,但截至目前,主流生产级 LLM 仍然清一色是自回归的。别把研究方向当成已发生的事实。

Recap · 收束

自回归的全部内容就一句话:每生成一个新 token,都要把已生成的全部内容重新当输入喂一遍——输出 N 个 token 就是 N 次串行的完整模型前向。投机解码论文那句"解码 K 个 token 需要 K 次串行前向"是最权威的表述。

由此切出两个性质相反的阶段:prefill 整段并行、计算密集、决定"等第一个字多久"(TTFT)、中间结果可缓存复用;decode 严格串行、显存带宽密集、决定"每个字多久"(TPOT)。这一条直接解释了主流 API 输出价是输入价 5~6 倍(GPT-5.6 Sol 6 倍、Claude Opus 4.7 5 倍),以及缓存命中能便宜到十分之一甚至五十分之一。

工程上靠 KV Cache 把重算从平方级降到线性,代价是显存随序列长度和并发用户数线性增长——这正是 MQA / GQA / MLA(DeepSeek 用 MLA)这一串架构优化的全部动因。想再快,就用投机解码:小模型猜几个、大模型一次并行验证,关键在于它保证不改变输出分布,是无损加速,论文在 T5-XXL 上报告 2×–3× 且输出完全一致。

下一节讲那张"桌子"到底有多大:上下文窗口,以及为什么塞进去的东西越多,中间那段越容易被漏掉。

☰ 主页
学海无涯 · 智能篇 · § 8.2