解决一个关键困惑:为什么算了所有位置的 hidden state,却只用最后一个来预测?以及"每个位置只能看前面"到底怎么回事。
| Topic | 每个位置预测"它的下一个词" + 因果掩码 causal mask |
| Objectives | 看清训练用全部位置、生成只读最后位置;理解因果掩码为何存在 |
| Prerequisites | Module 1 ✅(注意力、hidden state)、Module 2 概览 ✅ |
| Outcome | 能解释"最后位置的 hidden state 怎么来、为什么只用它" |
你以为"只有最后一个位置在预测"。真相是:每个位置都各自做一个预测——预测紧跟它后面的那个词,且只用它左边(含自己)的词。以 The cat sat on the(5 个位置)为例:
因为只有位置5预测的词是"新的、未知的"(第6个词还没有)。位置1~4预测的第2~5个词我们已经有了(就在输入里),生成时不关心。所以生成 = 只读最后一个位置的预测。
前 4 个预测没浪费:训练时全用——拿"位置i的预测"和"真实第i+1词"比,一次前向得 N 个训练信号,非常高效。训练用全部,生成读最后一个。
位置3的任务是用前3个词预测第4个词。它绝不能偷看第4个词(那就是答案,等于作弊)。所以规定每个位置只能看自己 + 前面的词——这叫因果掩码 causal mask。位置5恰好是最后一个,"它前面所有词"= 整句,所以它看到了全部。
层层计算,每层所有位置一起更新:
位置5的 hidden state = 它在每一层都从"前面所有词"吸收信息、逐层加工 N 次的结果。到最后一层,它已"读懂整句 + 知道该接什么"。这个向量才被送去下一步(输出层)变成词表概率。
要分清:因果掩码是一条始终生效的规则(注意力里把"看向未来"的分数设成 −∞,softmax 后≈0),每次前向都在跑。但"挡住真实存在的未来"这个效果:
不知道。模型是"无状态"的。每次循环它拿到的就是一个完整字符串,从头跑一遍前向、读最后一层最后位置——没有"我上次算过"的记忆。循环是外面的代码在做(把新词接到末尾 + 再调一次模型)。
因为位置3永远只看 1-3,它的 hidden state 在 "The cat sat" 和 "The cat sat on the" 里完全一样(后面加多少词都不影响它)。这种"前面位置不受后面影响"的稳定性,正是为什么可以把前面算过的结果缓存复用——实际生成时甚至不用重算前面,只算新加的那个。
不会。每一层都会同时更新全部 N 个位置。因果掩码只限制"每个位置可以从哪些位置收集信息",不是让后面几行等待或冻结。
假设 The cat sat on the 有 5 个 token,且 d_model=3,初始矩阵是:
一次注意力中,五行通常并行更新,但读取范围不同。下面把真正的“注意力权重 × V,再求和”展开(数字仅为示意):
每一行的系数都是该位置自己的 softmax 注意力权重,所以各自加起来等于 1。掩码让“不允许看的未来位置”权重变成 0:
pos3 只有 The / cat / sat 三项;on / the 的权重被 mask 成 0。pos5 可以给五个位置都分配权重,所以它的输出是整句五个 V 向量的加权和。经过注意力、残差、LayerNorm、FFN 后,第一层仍输出 X¹ [5×3];第二层继续得到 X² [5×3]……最后一层仍是 Xᴸ [5×3]。
[N×d_model] 的 hidden-state 列表,只取最后一行 h_last = Xᴸ[N],因为它负责预测位置 N+1。不是"只计算最后一行",而是"全部计算,最后只读取最后一行"(未使用 KV Cache 的朴素视角)。对。每一轮把当前全部已知 token作为上下文,预测一个新 token,再把它接到末尾:
所以抽象循环是:已有 N 个 token → 预测第 N+1 个 → 接到末尾 → 下一轮变成 N+1 个输入。
答完这 3 题,下一课下钻 输出层:最后位置那个向量,具体怎么变成"词表上每个词的概率"。