大模型数学速成(13):因果掩码与解码循环
第 12 篇 我们把 hidden 变成 logits,再用交叉熵衡量「下一个 token 猜得准不准」。但还有一条硬规则没写进公式:位置 $t$ 不许看见未来的 token——否则训练会作弊、推理也无法自回归。
这一篇把 因果掩码(Causal Mask) 讲清楚,并和 第 11 篇 的 Prefill / Decode、第 12 篇 的并行 CE 收成一张对照表。
这是「大模型数学速成」续篇第 13 篇。建议先读 第 05 篇 Attention、第 11 篇 KV Cache、第 12 篇 交叉熵。下一篇讲 采样(Temperature / top-k / top-p)。
一、为什么需要因果掩码?
Self-Attention 默认是双向的:每个 query 可以和所有 key 算分(第 05 篇)。
对语言模型来说这会出问题:
| 场景 | 若允许看见未来 | 后果 |
|---|---|---|
| 训练 | 位置 $t$ 的预测能「偷看」$t{+}1,t{+}2,\ldots$ 的真实词 | 损失虚低,模型学不到接龙 |
| 推理 | 生成第 $t$ 个词时,后面的词还不存在 | 根本没有未来可看 |
所以 GPT 类自回归 LM 使用 因果注意力(Causal / Autoregressive Attention):
位置 $i$ 的 query 只能 attend 到位置 $j \le i$ 的 key/value。
编码器式双向模型(如部分 BERT、ViT)不需要这条规则;解码器式 LLM 必须要。
二、Mask 加在哪里?
回顾缩放点积注意力(单头示意):
\[\mathbf{S} = \frac{Q^\top K}{\sqrt{d}}, \qquad \mathbf{P} = \mathrm{softmax}(\mathbf{S}), \qquad \mathbf{O} = V \mathbf{P}^\top\]
因果约束作用在 Softmax 之前的分数矩阵 $\mathbf{S}$ 上:对非法的 $(i,j)$($j > i$)设为「负无穷」,Softmax 后权重变为 0。
\[\tilde{S}_{i,j} = \begin{cases} S_{i,j} & \text{if } j \le i \\ -\infty & \text{if } j > i \end{cases}\]
\[P_{i,j} = \frac{e^{\tilde{S}_{i,j}}}{\sum_{k=1}^{T} e^{\tilde{S}_{i,k}}}\]
实现上常用一个很大的负数(如 $-10^{4}$ 或 dtype 允许的最小值)代替真正的 $-\infty$,避免 NaN。
下三角示意图($T=4$)
行 = query 位置 $i$,列 = key 位置 $j$;✓ 可见,✗ 被 mask:
1 | j=0 j=1 j=2 j=3 |
对应 下三角(含对角线) 为 1、上三角为 0 的布尔掩码——所以常叫 causal lower-triangular mask。
三、手算:mask 怎样改变 Softmax?
设 $T=3$,某 query 行($i=1$,只能看 $j=0,1$)的原始分数:
\[\mathbf{s} = [1.0,\ 2.0,\ 3.0]\]
无 mask(非法)
\[\mathrm{softmax}(\mathbf{s}) \approx [0.090,\ 0.245,\ 0.665]\]
最大权重落在未来位置 $j=2$。
有因果 mask($s_2 \leftarrow -\infty$)
\[\mathbf{s}' = [1.0,\ 2.0,\ -\infty]\]
\[\mathrm{softmax}(\mathbf{s}') \approx [0.269,\ 0.731,\ 0]\]
未来位置权重精确为 0;概率在合法前缀上重新归一化。
四、训练:教师强制 + 因果 mask = 整句并行
训练时输入一整句 token(长度 $T$),目标是每个位置预测下一个真实 token(第 12 篇 的 CE)。
| 步骤 | 做什么 |
|---|---|
| 1 | 嵌入 + 各层 Transformer,Attention 带因果 mask |
| 2 | 每个位置 $i$ 的 hidden 经 lm_head 得 logits |
| 3 | 位置 $i$ 的标签通常是 token $i{+}1$(边界与特殊符依实现) |
| 4 | 对有效位置平均交叉熵,一次反传 |
关键:有了因果 mask,位置 $i$ 的表示数学上不依赖 $i$ 之后的输入,因此可以整句一次前向,而不必像推理那样逐步生成——这叫 teacher forcing(教师强制):标签来自真实下一词,不是模型自己滚出来的词。
1 | 输入: [BOS] 今 天 气 不错 |
五、推理:Prefill 与 Decode 里的 mask
与 第 11 篇 对齐:
Prefill(prompt 一次算完)
- 输入长度 $S_0$ 的 prompt
- 做一次 $S_0 \times S_0$ 的因果 Attention(下三角)
- 把各层 K/V 写入 Cache
- 通常用最后一个位置的 logits 采出第 1 个新 token
Decode(每次 1 个新 token)
- 只算新位置的 Q(以及新 K/V)
- 与 Cache 中全部历史 K 做点积——历史天然都是 $\le$ 当前时刻,不必再拼一块上三角未来
- 新 K/V 追加进 Cache
| 阶段 | 序列长度 | Mask 形态 | Cache |
|---|---|---|---|
| 训练 | 整句 $T$ | 完整下三角 $T\times T$ | 一般不长期缓存 |
| Prefill | prompt $S_0$ | 下三角 $S_0\times S_0$ | 初始化 |
| Decode | 历史 $S$ + 1 | 新 query 对 $1..S{+}1$ 全可见(无未来) | 追加 1 列 |
Decode 时「因果」体现在:根本没有未来 token 参与计算;Prefill/训练则靠显式 mask 禁止偷看。
六、和双向注意力差在哪?(一眼表)
| 因果(LLM 解码) | 双向(如部分 Encoder / ViT) | |
|---|---|---|
| 可见范围 | $j \le i$ | 全体 $j$ |
| 典型任务 | 语言建模、聊天生成 | 理解、分类、部分编码 |
| 训练并行 | 可以(+ teacher forcing) | 可以 |
| 生成方式 | 自回归逐步 | 通常不逐步「接龙出词」 |
多模态里:图像 patch 往往在 prompt 侧一次编进序列,文本侧仍对文本位置保持因果;图像 token 之间是否双向取决于具体架构——读代码时看 mask 构造即可。
七、实现时容易踩的坑(概念级)
- Off-by-one:标签是「当前词」还是「下一个词」必须与 shift 一致,否则 CE 对不齐。
- Padding:pad 位置既不当 query 有效位,也不应被 attend(额外 padding mask,与因果 mask 按位组合)。
- 数值:用有限大负数代替 $-\infty$ 时,极端长度下仍可能 underflow;框架融合 kernel 一般已处理。
- 和 RoPE:第 07 篇 的位置旋转与因果 mask 正交——一个改 Q/K 相位,一个改可见集合。
八、小结
| 概念 | 要点 |
|---|---|
| 因果 | 位置 $i$ 只看 $j\le i$ |
| 做法 | Softmax 前把未来 score 置 $-\infty$ |
| 形状 | 下三角可见 |
| 训练 | 整句并行 + teacher forcing + CE |
| Prefill | 对 prompt 做下三角 Attention,填 Cache |
| Decode | 无未来 token;Cache 追加,自然因果 |
大模型数学速成续篇第 13 篇完。下一篇 采样:Temperature / top-k / top-p——有了 logits,如何从分布里「抽出」下一个 token。
系列导航
| 篇号 | 标题 | 状态 |
|---|---|---|
| 12 | 输出层、Logits 与交叉熵 | ✅ |
| 13 | 因果掩码与解码循环(本篇) | ✅ |
| 14 | 采样:Temperature / top-k / top-p | ✅ |
完整大纲见工作区 docs/MATH_SERIES_OUTLINE_V2.md。








