LLM 学习系列(3 / 5)
1. Inference(推理):把模型运行起来
Inference 是使用模型根据输入计算输出的过程。 本篇讨论常规文本生成:模型参数固定,不通过这次对话更新权重。
Prompt(提示输入) 是本次请求给模型的初始内容;上下文(Context) 则包括它及已经生成的 Token。真实聊天请求还可能包含系统指令、历史消息和对话模板,详见 Context。
贯穿全篇的例子
延续前两篇,输入
小猫跑了一天,它很,教学 ID 为[10, 11, 12, 13, 14, 15, 16],按需添加 BOS(ID1)。假设本次依次生成
累 · 。 · <EOS>,对应[17, 18, 2]。本篇追踪:如何选出这些 ID、每一步计算什么、缓存里增加什么,以及何时停止。切分、数值和耗时均为教学示意。
这里有两种「解码」:模型 Decode 是计算后续 Token 的生成阶段;Tokenizer Decode 是把 ID 还原成文字。流式输出可以边生成边还原文字,不需要等全部生成结束。
2. Autoregressive(自回归):用已有内容预测下一项
自回归 LLM 是将文本序列的联合概率分解为一系列条件概率,并按照已有 Token 逐步预测下一个 Token 的语言模型。
对于 Token 序列 ,其概率分解为:
这里 表示概率,竖线 表示「给定」, 表示把各项相乘; 是 的简写。这表示预测 时只以之前的 Token 为条件。第一项没有前文,实际模型常用 BOS 等起始条件。
贯穿例子的生成过程是:
每一步生成的 Token 都会成为下一步的输入,因此后续输出依赖此前已经生成的结果。这也是「自回归」名称的来源。
2.1 从概率到 Token:贪心与采样
LM Head 给出整个词表的 Logits(原始分数),Softmax 将它们转换为概率。概率分布本身还不是一个选定的 Token,需要生成策略来做选择。
沿用架构篇的示意分布:
| 候选 | 累 | 困 | 饿 | 其余 Token 合计 |
|---|---|---|---|---|
| 概率 | 0.60 | 0.20 | 0.10 | 0.10 |
- Greedy(贪心):选择最大分数项,本例是
累。由于 Softmax 保持排序,实现上直接取最大 Logit 即可。 - Sampling(采样):按概率随机抽取,本例可能选到
累,也可能选到困。它增加输出多样性,也带来不确定性。
常见采样控制项如下:
| 设置 | 做什么 | 本例中的含义 |
|---|---|---|
| Temperature(温度) | 对 Logits 除以正数 后做 Softmax | 更集中于高分项; 更分散 |
| Top-k | 只保留分数最高的 k 个候选,再归一化采样 | k=2 只保留 累、困,概率变为 0.75、0.25 |
| Top-p(核采样) | 按概率降序,保留累计概率达到 p 的最小候选集合 | 按表中精确示意值,p=0.8 保留 累、困 |
温度公式为:
表中的 Top-k 和 Top-p 分别从原分布单独演示;实际框架可以组合这些设置,执行顺序会影响结果。某些接口把 temperature=0 特判为贪心,它不是把 0 直接代入公式。
例子推进 · 真正决定输出的是选择步骤
如果选到
累,就追加 ID17,下一步基于小猫跑了一天,它很累计算。如果采样选到困,后续上下文就变了,后面的概率也要重新计算。温度改变的是选择分布,不会修改模型参数,也不是「事实正确率」旋钮。
2.2 何时停止
常见停止条件包括:
- EOS:模型生成配置指定的结束 Token。本例 ID
2表示结束。 - Stop Sequence(停止序列):推理系统检测到预设的文本或 Token 模式,可能包含多个普通 Token。
- 最大输出长度:例如
max_new_tokens=2只允许新增两个 Token,不包含原 Prompt 的长度。
例子推进 · 句号和结束标记不同
本次选出
[17, 18, 2],即累 · 。 · <EOS>。输出文字通常只显示累。。如果最大新增 Token 数为 2,则生成句号后就因长度限制停止,不需要再生成 EOS。
2.3 自回归约束的边界
- 自回归不等于 Decoder-only:GPT、LLaMA、Qwen 等 Decoder-only 模型采用自回归生成;T5 等 Encoder-Decoder 模型的 Decoder 同样可以自回归生成。
- 自回归不等于所有语言模型:BERT 的 Masked Language Modeling(遮住部分 Token,再用双向上下文预测)不属于典型的自回归生成。
- 自回归不等于逻辑推理:它描述序列概率的分解与生成方式,不代表模型一定执行了人类意义上的推理。
普通生成循环需要先选出前一个 Token,才能确定后一个 Token 的条件。推测解码(Speculative Decoding) 等技术可以先提出多个候选,再由目标模型并行验证,一次接受多个 Token;它优化执行方式,不取消自回归概率的条件依赖。
3. Prefill 与 Decode:每一步实际算什么
LLM 推理通常分为两个阶段:
| 阶段 | 输入与行为 | 主要特征 |
|---|---|---|
| Prefill(预填充) | 处理输入序列,建立 KV Cache,并产出用于选择首个输出 Token 的 Logits | 多个已知输入位置可并行计算;影响首 Token 延迟 |
| Decode(逐 Token 解码) | 复用缓存,处理刚生成的 Token,产出再下一个 Token 的 Logits | 每步通常只输入一个新 Token;影响后续生成速度 |
例子推进 · 首 Token 来自 Prefill 的结果
阶段 本次送入模型的 Token 前向后缓存包含 用本次结果选出 Prefill BOS + 7 个输入 Token 输入的 8 个位置 累(ID17)第 1 步 Decode 累前 9 个位置 。(ID18)第 2 步 Decode 。前 10 个位置 <EOS>(ID2),停止刚选出的 Token 要在下一次前向时才写入它自己的 KV。因此刚选出
累时,缓存里还没有累;选出 EOS 后可以直接停止,不必再把 EOS 送进模型。
「并行」指同一层里多个已知位置可一起算;网络各层仍依次执行。长 Prompt 也可能分块做 Prefill,以上表格描述的是逻辑过程。
4. KV Cache(键值缓存):复用历史计算
KV Cache 是自回归 LLM 在推理时保存历史 Token 的 Key 和 Value 张量,从而避免在生成每个新 Token 时重复计算整个历史序列的机制。
在 Transformer 的 Attention 计算中,Decode 阶段的每一层只需为当前 Token 计算新的 Query、Key 和 Value。Q 用来查询,K 用于匹配,V 是被聚合的信息,定义见 架构篇。
历史 KV 为什么能不变? 在参数、位置编码规则和已有前缀保持不变时,因果注意力不让旧位置读取新追加的位置,所以旧位置各层的表示及 K/V 不需要更新。缓存按层保存,新 Token 的 K/V 也逐层追加。
之所以只缓存 Key 和 Value,是因为后续每个 Token 都需要用自己的 Query 查询全部历史 Key,并聚合对应的 Value;历史 Query 不会再次参与后续步骤,因此没有缓存价值。
例子推进 · 处理
累时复用什么第 1 步 Decode 输入
累的 ID17。它经过 Embedding,在每一层计算自己的 Q/K/V;该层的 Q 读取缓存中的 BOS 和前 7 个 Token 的 K/V,也使用当前累的 K/V,再经过 FFN 等组件,预测句号。模型仍需计算当前 Token 的完整前向。省掉的是旧位置重复执行 Embedding、各层投影和 FFN 等计算,而不是让当前 Token 跳过 Transformer。
KV Cache 避免了对历史 Token 的 Key/Value 投影及相关前向过程进行重复计算,但没有消除注意力计算。当前 Query 仍需读取全部历史 Key 和 Value,因此在标准注意力中,单步 Decode 的注意力开销仍随上下文长度线性增长。
4.1 显存占用
对于常规 MHA/GQA/MQA、各层维度相同、每条序列都缓存 个位置的情况,KV Cache 的显存占用近似为:
其中:
| 符号 | 含义 |
|---|---|
| Key 和 Value 两份缓存 | |
| Transformer 层数 | |
| Batch Size 或并发序列数 | |
| 每条序列已缓存的 Token 数 | |
| 每层的 KV Head 数量 | |
| 每个 Head 的维度 | |
| 每个元素的字节数,例如 FP16/BF16 通常为 2 |
FP16/BF16 是两种 16 位浮点数格式,每个元素通常占 2 字节;GiB 是 字节。代入 、、、、、,得到:
这只是 KV Cache,不包含模型权重、当前计算的中间结果和框架开销。 序列长度不同时应按各条实际缓存长度求和;MLA、滑动窗口等布局也要按实际结构计算,不能直接套用。
4.2 常见优化各自省什么
| 技术 | 主要作用 |
|---|---|
| GQA/MQA | 共享 KV 头,减少每 Token 的缓存元素数,结构见架构篇 |
| KV Cache 量化 | 用更少位数近似表示缓存数值,降低每元素存储量 |
| 分页管理 | 把缓存分块分配,减少碎片和预留空间浪费;不减少有效 KV 本身的元素数 |
| 前缀缓存(Prefix Cache) | 对完全匹配且计算配置兼容的前缀复用 KV,避免重复 Prefill;能否共享物理存储取决于实现 |
Note
KV Cache 不会把新知识学进权重。请求结束后通常会被回收,也可能作为前缀缓存供兼容请求复用;复用计算结果和训练模型是两件事。
5. 性能指标:等待多久,生成多快
常见性能指标包括:
- TTFT(Time to First Token):从请求开始到收到首个输出 Token 的时间,包含排队、输入处理、Prefill、选择首 Token 和传输等耗时。
- TPOT(Time per Output Token):首个 Token 之后,每生成一个 Token 的平均时间,主要反映 Decode 性能。
- Throughput(吞吐量):系统单位时间内处理或生成的 Token 数,通常还需说明是单请求还是服务整体吞吐量。
例子推进 · 用一条时间线理解延迟
假设客户端发出请求后,
0.20 s收到累,0.25 s收到。。以这两个可见输出 Token 为统计对象,TTFT 为200 ms,它们之间的 TPOT 为50 ms。对 个输出 Token,常用平均值为 。EOS 是否计入、时间取客户端还是服务器端,需要统一口径。
50 ms/Token对应约20 Token/s的后续输出速度,不等于包含首 Token 等待时间的整次请求平均速度。
并发批处理可以提高整体吞吐量,却也可能让单个请求等得更久。因此比较性能时,要同时看输入长度、输出长度、并发量和延迟。
6. 为什么训练能并行,而生成要依赖前一步
生成时未来 Token 未知;训练时完整文本已知,可以一次输入多个位置。本节只解释这种信息边界,第 4 篇再讲损失和参数更新。
6.1 Teacher Forcing(教师强制):使用真实历史
Teacher Forcing 是在训练中把数据里的真实历史作为输入,而不把模型预测的历史接回去。 它强制的是输入上下文,不是强制输出正确答案。
以训练文本 小猫跑了一天,它很累。 为例:
输入:BOS 小猫 跑 了 一天 , 它 很 累 。
目标:小猫 跑 了 一天 , 它 很 累 。 EOS两行按位置配对:输入 很 所在位置负责预测 累,输入 累 所在位置负责预测 。。即使前一处把 累 预测成了 困,后一处仍使用数据中的 累,而不会改成 困。
6.2 Causal Mask:为什么看不到未来答案
Causal Mask(因果掩码) 限制输入位置 只能读取位置 到 ,该位置负责预测的是 。这里「当前位置」是输入位置,不是待预测答案的位置。
在 Softmax 之前,给未来位置的 Attention 分数加上 。因为:
这些位置经过 Softmax 后权重为 0,可见位置则重新归一化。实现可以用等价的屏蔽计算,不一定真的构造完整掩码矩阵。
例子推进 · 答案虽然在数组里,但不能被读取
训练时
累已经出现在输入序列后面;很所在位置要预测累,却被掩码挡住,不能读取那个位置。否则模型只要复制右边的 Token 就能答对,推理时却没有那个答案可复制。若为了手算只画出
跑、一天、未来的累三个位置,假设分数是[2, 1, 5]。虽然未来位置分数最高,屏蔽后仍变成[2, 1, -∞],Softmax 约为[0.73, 0.27, 0]。真实计算会对全部可见位置归一化,这里只演示遮挡的效果。
「因果」在这里指序列的单向依赖,并不是因果推断。Teacher Forcing 提供全部已知输入,Causal Mask 保证各位置不能偷看答案,两者配合实现并行的下一 Token 训练。
同样的约束也用于 Prefill;普通单 Token Decode 的 KV 中没有未来位置,但仍须遵守缓存有效长度等边界。
例子收束 · 从生成转向训练
本次生成走完了「输入编码 → Prefill 选出
累→ Decode 选出。和 EOS → 输出累。」,参数始终未变。训练篇 接下来回答:为什么模型能把
累排到较前的位置?它会用同一条句子展示损失如何驱动权重更新。
Note
Inference 的「推理」是机器学习中的通用术语,并不专指逻辑推理或 Chain-of-Thought。无论模型是在续写一个单词还是求解复杂问题,只要是在使用训练完成的模型计算输出,都属于 Inference。