大数跨境

首个输出 Token 如何生成?从 Prefill、末位 Logits 到 Decode

首个输出 Token 如何生成?从 Prefill、末位 Logits 到 Decode AI大模型智能体前沿
2026-10-25
2
导读:生成首个输出 Token,只需要末位 Logits;KV Cache 却早在各层计算时就在写入。把这条时序彻底理清。

导读首个输出 Token 由 Prefill 的末位 Logits 选出,后续 Token 再通过 Decode 逐步生成。两个阶段运行的是同一个模型:先并行处理已知 Prompt,逐层保存 Key 和 Value;再把刚选出的 Token 作为新输入,复用缓存继续计算。理清输入、输出与缓存的先后关系,就能理解首个输出 Token 从哪里来,以及后续生成为什么要逐步推进。

本文 3617 字,阅读约 9 分钟

同一个模型,为什么要分 Prefill 和 Decode?

Prefill:已知的 Prompt 为什么可以并行计算?

输出头:为什么生成只需要末位 Logits?

从 Logits 到 Token,还要做什么?

KV Cache:首个输出 Token 产生时,缓存里已经有什么?

Decode:复用历史,只计算新位置

TTFT 与 TPOT:两阶段的性能为什么不同?

沿着这条时序,继续算显存账

同一个模型,为什么要分 Prefill 和 Decode?

前两篇拆解了 Attention 和 RoPE。先把层级对齐:Q、K、V 用于 Attention 内部;Attention 与前馈网络、归一化和残差连接等共同组成一个 Transformer Block。多个 Block 加上输入嵌入、最终归一化与输出头,构成这里讨论的完整语言模型。

文本先分词、嵌入,再依次经过各个 Block。每层注意力用自己的参数计算查询 Q、键 K 和值 V:Q 与 K 的匹配决定注意力权重,再按权重汇总 V。多头结果合并后经过注意力输出投影,并通过残差连接、前馈等变换更新表示;RoPE 则在注意力内部旋转 Q、K,带入位置信息。

层与层之间传递的是隐藏向量。第 2 层接收第 1 层的输出,计算自己的 Q、K、V,依次处理到最后一层。各层的前馈网络参与加工表示;所有 Block 之后的输出头 lm_head 则把最终归一化后的表示转成词表分值,供后续选择 Token。

Q、K、V 到底在算什么?把因果 Mask 放进 attention 的每一步

Prompt 的全部 Token 已知,可以一起送进网络;回复的下一个 Token 要等上一个被选出来,才能继续往下算。

这就形成了两个阶段:Prefill(预填充)处理输入前缀,Decode(解码)继续生成。两者使用同一套模型权重和网络结构,区别在于本次输入多少个位置,以及历史计算的复用。首个输出 Token 用的是 Prefill 的计算结果,无需先额外跑一次单 Token 的 Decode。

后续生成会用到 KV Cache(键值缓存):它保存各层历史位置的 Key 和 Value,供新位置做注意力计算时复用。

下面以 Llama 一类 decoder-only(仅解码器)因果语言模型为例,开启 KV Cache,观察一个没有可复用前缀缓存的请求如何逐 Token 生成,不展开推测解码等优化。Token 是分词器处理的单位,可能对应一个字、词片段或其他文本片段,不能直接当成一个汉字。

Prefill:已知的 Prompt 为什么可以并行计算?

设 Prompt 有 个 Token,嵌入后的张量形状为 : 是批量大小, 是每个位置的隐藏维度。

在同一层里,所有位置的 Q、K、V 投影可以通过矩阵乘法一起计算。注意力使用因果掩码,限制每个位置能读取的范围:位置 1 只能看自己;位置 2 能看位置 1、2;位置 能看完整 Prompt。

关键在于,当前层读取的是上一层已经算好的表示,不需要等待当前层的前一个位置先出结果。第一层读取嵌入向量,之后各层读取上一层的输出。因果掩码约束的是能读取哪些位置,并不要求按位置先后计算;各位置可以并行完成这一层,再进入下一层。

在相同权重、位置和掩码条件下,关闭 dropout 后,这与逐个输入已知 Token、复用缓存计算历史状态,在数学上等价;实际浮点计算可能有微小数值差异。

Prefill 的“并行”也不要求工程上永远一次塞完。长 Prompt 可以分块处理;这里描述的是完整前缀的逻辑计算过程。

图中是一轮前向:内部依次经过各层,直到末端才选出新 Token;新 Token 作为下一轮输入,再经过同样的这些层。Prefill 与 Decode 都遵循这个顺序,区别在本轮输入位置数和历史 KV 的复用。

输出头:为什么生成只需要末位 Logits?

训练因果语言模型时,位置 的输出用来预测位置 的 Token,因此可以同时利用多个位置的监督信号。

推理续写也沿用这个预测关系。假设 Prompt 的 4 个 Token 是 A、B、C、D。A 位置的输出如果经过输出头,可能认为下一个 Token 更应该是 X;但输入已经给定为 B,模型就直接读取 B,无需选出 X 或替换输入。

前 3 个位置对应的后续输入都已给出,第 4 个位置的输出才用于预测尚未出现的第 5 个 Token。Prefill 直接处理给定序列,无需先逐个预测、再修正成输入文本。

因此,位置 的输出用于预测位置 ,也就是首个输出 Token。最后一个有效输入位置能读取完整 Prompt,前面的位置只能读取各自的前缀。

预测接下来分两步:先给词表中所有候选打分,再根据分值选择一个 Token。将经过所有层及最终归一化的末位向量记为 ,输出头 lm_head(也称 Unembedding)负责第一步:

这里 是训练学得的输出权重矩阵,推理时保持不变; 是给各候选分值加上的偏置向量,有些模型不带这一项。 已包含前面各层对上下文的处理结果,输出头无需再直接读取 Q、K、V。

省略批量维度, 的形状为 , 为 ,相乘得到 的结果。 是词表大小,结果 中每项都是一个可正、可负或为零的实数分值,称为 Logit;它还不是概率。

例如,假设隐藏维度为 4096、词表有 20 万个 Token,输出头就用一次矩阵乘法得到 20 万个分值。每项是隐藏向量与对应候选权重的点积,再加可选偏置;这些运算可并行执行,无需把整个模型运行 20 万遍。

“只需要末位 Logits”说的是生成决策的需求。实现可以先选出末位隐藏状态再做投影,也可以先计算多个位置的 Logits,最后只取末位。只看普通续写需要的结果,可以这样区分:

处理环节 Prefill 是否需要?
Token ID 查嵌入向量 各输入位置都需要
各层注意力:归一化、QKV 投影、RoPE、分数与 Softmax、汇总 V、输出投影及残差 仍需计算,因果掩码限制读取范围
各层前馈网络:归一化、前馈变换及残差 仍需计算,可在各位置并行处理
各层 K、V 写入缓存 各输入位置都需要,供后续复用
最终归一化后的词表投影,得到 Logits 前 个位置可省,末位需要
根据 Logits 做候选处理、贪心选择或采样 前 个位置无需做,末位用于选出首个输出 Token

省掉的是不需要的词表投影与选择,网络主体仍要处理输入。若要给已知文本评分或计算训练损失,前面位置的 Logits 也有用途。

从 Logits 到 Token,还要做什么?

有了 Logits,才能选出新 Token。这里看两种常用方式:贪心选择和随机采样。

贪心选择直接取分值最大的 Token ID:

它只关心谁的分值最大,无需先算概率。随机采样则先用 Softmax 把分值转换成总和为 1 的概率分布,再按概率抽取。加入温度缩放后的基本形式是:

是温度, 是候选 的概率。以三个候选为例,分值为 2、1、0,温度为 1 时,概率约为 66.5%、24.5%、9.0%;采样时最高分项也未必被选中。这些是模型对候选的相对倾向,不是答案正确的概率。

贪心只看排序,采样还取决于分值差距。三个分值一起加 100,Softmax 概率不变;将所有分值乘以同一个大于 1 的数,会拉大差距,使概率更集中。温度升高让分布更平缓,降低温度则更集中;公式不能直接代入零。

采样还可以筛选候选:Top-k 保留分值最高的 k 项;Top-p(核采样)保留按概率排序后累计达到阈值 p 的最小候选集合,再归一化、抽样。温度、Top-k、Top-p 可组合使用,负责调整概率与候选范围。

这里的 Softmax 作用于词表候选;注意力内部的 Softmax 则把 Q、K 匹配分数转为读取 V 的权重,两者作用的位置不同。

选出 Token ID 后,一条路径把它交给分词器,还原为可显示的文本;另一条路径把它留作下一次模型前向的输入。流式展示还可能等待文本片段凑齐,因此产出一个 Token 与界面显示一个字符并非一一对应。这里的文本还原,也与前面所说的模型 Decode 计算阶段分属不同操作。

KV Cache:首个输出 Token 产生时,缓存里已经有什么?

下一步要根据新选出的 预测 。如果每步都把完整前缀重新送进网络,就会反复计算历史位置。

因果注意力让这种重复可以避免:在这里采用的固定位置编码与注意力规则下,追加未来 Token 不会改变历史位置的表示,各层历史 K、V 可以继续复用。

所以,每个已经处理过的 Token,在每一层产生的 K、V 都分别保存。例如,4 个输入 Token 经过 3 层,就有 3 份逐层缓存,每份包含这 4 个位置的 K 和 V。各层参数与输入表示不同,缓存不能跨层混用;它们是请求的运行时状态,模型权重保持不变。

以常见张量排列、K 和 V 头维度相同的情况为例,每层两个缓存张量的形状均为:

是 KV 头数, 是每个头的维度。标准多头注意力中,KV 头数与 Query 头数相同;分组查询注意力(GQA)让多个 Query 头共享 KV 头,因此不能一概用 Query 头数计算缓存大小。

这里还有一个容易错位的时刻:首个输出 Token 刚选出来时,各层缓存只包含 Prompt 的 个位置。新 Token 还没被送入网络,因此还没有自己的 K、V。

接着前面的 4 Token 例子:Prefill 写入 4 个位置的 KV,并选出第 5 个 Token;下一次前向输入第 5 个 Token,才把已缓存的位置数扩展到 5,同时选出第 6 个 Token。这里数的是缓存中已有的有效位置,底层存储可以动态扩展,也可以提前分配。

Decode:复用历史,只计算新位置

进入 Decode 后,单个请求每次只输入刚选出的一个 Token ID,先通过嵌入层查出它的输入向量,再经过所有 Block。复用的是历史位置的 KV,新位置仍要从嵌入开始完整计算。

在每一层,新位置先生成该层自己的 Q、K、V;采用 RoPE 时,对 Q、K 做位置旋转。新 K、V 按当前位置写入该层缓存,新 Q 再读取包含历史和当前位置的 K、V,完成注意力计算。随后经过该层其余变换,把隐藏向量交给下一层;历史位置已有的 K、V 无需重算。

跑完所有层和最终归一化,输出头把这个位置的隐藏状态变成新的 Logits,再选出下一个 Token。如此继续,直到遇到终止 Token(EOS)或其他停止条件。

缓存减少了历史重算,但新位置仍要读取可见的历史 KV,上下文越长,这部分工作也越多。对标准全注意力而言,重算长度为 的完整前缀,需要处理随 增长的位置配对;使用缓存后,本步只计算一个新 Query 与 个 Key 的匹配,再加权读取 Value,注意力计算随 线性增长。这说的是单步注意力部分,投影、前馈等计算仍然存在。

TTFT 与 TPOT:两阶段的性能为什么不同?

从请求到首个输出 Token 的等待通常称为 TTFT(Time to First Token,首 Token 延迟);首个输出之后,平均生成每个后续 Token 的耗时称为 TPOT(Time per Output Token)。Prefill 是 TTFT 的重要计算部分,但排队、输入处理也可能计入;用户看到文字的时刻还受传输和界面缓冲影响。

两阶段处理的输入规模不同,也带来了常见的性能差异:

比较维度 Prefill Decode
单请求本次输入 完整 Prompt,或其中一个分块 普通生成中为 1 个新 Token
主要线性层计算 多位置矩阵乘法,便于复用权重 单请求近似矩阵与向量相乘;批量时仍可合并为矩阵乘法
KV 处理 为输入位置计算并写入各层 KV 写入新位置 KV,并读取已有缓存
常见瓶颈 输入规模足够时更容易受算力限制 小批量时更容易受显存带宽限制
关注的服务指标 TTFT,包含首个输出前的等待 TPOT,首个输出之后平均每 Token 耗时

Prefill 和 Decode 都属于推理。Prefill 中,同一批读入的模型权重可用于多个输入位置,数据搬运能支撑更多计算,输入规模足够时更容易受算力限制。小批量 Decode 每步只推进少量新位置,却仍需读取参与计算的权重和历史 KV,更容易受显存带宽限制。

显存带宽决定数据能多快从显存送到计算单元;显存容量决定能存多少权重和缓存。实际瓶颈还取决于批量大小、上下文长度、量化、缓存命中和多卡通信,要结合具体配置判断。

沿着这条时序,继续算显存账

生成会继续推进,模型权重保持不变,缓存中保存的有效位置却越来越多。理解了这条时序,下一步就能算这些按层、按位置保存的状态会占多少显存。

上下文从 1K 拉到 32K、128K,并发请求再增加时,哪些维度会直接放大开销?为什么后续注意力需要保留 K、V,而历史 Q 通常不用留?下一篇继续拆解 KV Cache 的显存账本。

参考资料

•Attention Is All You Need,因果掩码、逐位置前馈网络与输出层,§3.2–3.4: https://arxiv.org/html/1706.03762v7

•Transformers v4.57.1 缓存说明,历史状态复用与逐层缓存: https://huggingface.co/docs/transformers/v4.57.1/en/cache_explanation

•Transformers v4.57.1 Llama 实现,最终归一化、KV 写入与输出位置选择: https://github.com/huggingface/transformers/blob/v4.57.1/src/transformers/models/llama/modeling_llama.py

•Transformers v4.57.1 GenerationConfig,温度、Top-k 与 Top-p 的定义: https://huggingface.co/docs/transformers/v4.57.1/en/main_classes/text_generation

•Efficiently Scaling Transformer Inference,输入处理与自回归生成的性能权衡: https://arxiv.org/html/2211.05102v1

•vLLM Metrics,TTFT、TPOT 与服务阶段指标口径: https://docs.vllm.ai/en/latest/design/metrics/

THE END

文章仅做学术分享,如有侵权请联系删除,非常感谢!

【声明】内容源于网络
0
0
AI大模型智能体前沿
分享AI大模型智能体前沿知识,探寻多元应用,洞察未来趋势,带你一路 “卷” 赢行业!🔥
内容 1171
粉丝 0
AI大模型智能体前沿 分享AI大模型智能体前沿知识,探寻多元应用,洞察未来趋势,带你一路 “卷” 赢行业!🔥
总阅读25.0k
粉丝0
内容1.2k