Transformer:从 token 向量到下一词预测#
TL;DR#
本页内容8 个部分
1. 从一句话生成另一句话#
以翻译为例,输入是“小明 开始 工作”,输出是“Xiaoming starts working”。为方便说明,假设这里每个词恰好对应一个 token;实际分词不一定如此。
循环神经网络 RNN 按位置递推:计算当前位置的状态,需要前一位置已经算出的状态。因此,同一句话中各位置的这段计算不能同时完成。Transformer 改用 Self-Attention,中文称自注意力:每个位置直接从允许读取的位置汇集信息,不再沿输入顺序递推隐藏状态。[1]
原论文的模型由 Encoder(编码器)和 Decoder(解码器)组成。编码器把源句变成一组含上下文信息的向量;解码器读取这些向量,以及已经知道的输出前缀,预测下一个输出 token。原始 Transformer 是 Encoder–Decoder 架构;常见的 decoder-only 语言模型是另一种组织方式。
论文标题中的“All You Need”强调序列建模不需要循环或卷积结构。完整模型仍包含 embedding、前馈网络、残差连接、归一化与输出层,并非只有 Attention。[1]
2. 一层的输入与输出是什么#
2.1 token ID 先转换为可训练的向量#
token ID 是词表中的整数编号。模型用它查找 embedding 表中的一行。设词表大小为 ,向量维度为 ,embedding 参数矩阵为 。token ID 为 时,查表结果为:
这也等价于用 one-hot 向量选择 的一行,但实际实现通常直接查表,不必构造长度为 的 one-hot 向量。 的元素是模型参数,训练时接收梯度并由优化器更新;整数 ID 本身不参与优化。
向量有 个数,但通常不能把某个坐标固定解释成“人物”或“动作”。含义来自整个向量及后续计算,不是人为给每一维指定一个语义标签。
2.2 序列是一张矩阵,位置编码说明词序#
设源句有 个 token。每个 token 对应一行,序列对应 的矩阵。原始 Transformer 将缩放后的 embedding 与位置编码相加。位置 从 0 开始,输入向量定义为:
这里 是进入第一层编码器前的整段输入, 是同样长为 的位置向量。位置编码让计算能够区分“小明先出现”和“工作先出现”,同时保持矩阵形状不变。原论文使用正弦与余弦构造位置编码;具体定义见位置编码。[1]
2.3 层是一次完整的向量变换#
一层编码器包含 Attention、前馈网络及其残差与归一化操作。输入整段序列的向量,输出仍是同样多行、同样维度的向量。
用 表示第 层的完整计算, 表示该层输出:
“第一层输出”不是一个新词,也不是词表概率,而是整段序列的新向量。例如“小明/开始/工作”输入 3 行,第一层仍输出 3 行;第二层接收这 3 行,继续计算。每一层有自己的参数,同一层则在不同位置使用同一组参数。
Attention 中,不同位置都读取上一层的向量。第一层“工作”的计算不需要等待第一层“小明”先算完;第二层则必须使用第一层已经算出的结果。序列位置之间可以并行计算,层与层仍按深度依次计算。
3. Attention 怎样汇集其他位置的信息#
3.1 同一个输入生成 Q、K、V#
Self-Attention 先把输入矩阵 做线性投影,得到 Query、Key、Value,记为 。对单个注意力头,定义为:
, 是可训练参数; 是该头的向量维度。它们是对同一组输入做不同变换,不是三份不同的句子。
对位置 , 用来与各位置的 计算匹配分数;这些分数决定各个 在汇集结果中占多大权重。Attention 权重不是下一个词的概率,而是当前头在位置之间分配的权重。
分开生成 Q、K、V,是为了让匹配方式与传递内容可以分别学习。 Q、K 用来决定从哪些位置读取信息、各占多大权重;V 则是按这些权重实际汇集的内容。Q 与 K 也使用不同投影,使一个位置作为查询时使用的特征,不必与它被其他位置匹配时使用的特征相同。
例如,计算“工作”的新向量时,用“工作”的 Q 与“小明/开始/工作”各自的 K 比较,再按得到的权重对各位置的 V 加权求和。计算“小明”的新向量时,则换成“小明”的 Q;此时“工作”的 K 参与匹配,“工作”的 V 提供内容。因此,每个位置都需要生成 Q、K、V,才能同时参与读取与被读取。
这不要求 Q、K、V 的数值一定不同,而是允许模型通过不同参数学习各自的投影。将它们全部设为原向量也能计算注意力,但会限制匹配和内容表示的选择。
3.2 算出“工作”位置的汇集结果#
下面是专门用于手算的二维例子,不是论文训练得到的 embedding。假设输入已经包含位置信息,且只有一个头,各个投影矩阵均取二维单位矩阵。因此,本例的 都等于输入矩阵:
“工作”在第三行,查询向量为 。这里是编码器,没有因果遮罩,可以读取源句的全部位置。用点积衡量匹配,再除以 ;本例 ,三个匹配分数为:
softmax 将分数转换为非负、总和为 1 的权重。设匹配分数为 ,对应权重为 。代入得到:
按这些权重对 Value 向量加权求和。用 表示“工作”位置的注意力输出:
结果中已经包含“小明”和“开始”的向量贡献。“工作”无需先经过多个相邻位置,便能在这一层直接读取“小明”。这些权重只是本例参数产生的数值,不代表一个训练完成的模型应当怎样理解这句话。
3.3 一次计算所有位置,再组合多个头#
把各位置的点积排成矩阵,便得到缩放点积注意力的一般表达式:
是遮罩矩阵:允许读取的位置取 0,禁止读取的位置取 ,经过 softmax 后对应权重为 0。softmax 对每一行单独计算;每一行对应一个查询位置。忽略 padding 时,编码器 self-attention 的 全为 0。
中,第 行第 列就是“位置 读取位置 ”的匹配分数。除以 用来控制点积随维度增大而扩大的尺度,减轻 softmax 进入梯度较小区域的情况。[1]
多头注意力为每个头设置不同的投影参数,分别汇集信息,再拼接并投影回 维。设头数为 ,其定义为:
不同头允许模型使用不同的投影与权重;不保证某个头固定负责语法、另一个固定负责人物关系。拼接不增加序列位置数,最终输出仍是 。原始 base 模型使用 、8 个头,每个头的 。[1]
4. Attention 输出怎样成为完整的一层输出#
4.1 残差与归一化保留并调整输入信息#
Attention 之后,原始 Transformer 将注意力输出加回输入,再做 LayerNorm;随后经过 FFN,再做一次残差与 LayerNorm。设 为第一段处理结果,一层编码器定义为:
残差相加让当前输入保留一条直接传递的计算路径,不必完全由 Attention 或 FFN 重建。LayerNorm 对每个位置自身的特征坐标计算均值和方差,调整数值尺度;它不在多个 token 之间取平均。
这是原论文的 Post-LN 顺序,即相加后归一化。将 LayerNorm 放在 Attention、FFN 前面属于 Pre-LN 变体,不能与原始结构的顺序混用。Dropout 在训练时随机屏蔽部分分量,推理时关闭。[1] [2]
4.2 FFN 分别变换每个位置#
FFN 是前馈网络,也常称 MLP。它对每行向量做相同的非线性变换,不读取其他位置。原论文使用两次线性变换和 ReLU:
这里 是一个位置的行向量, 逐坐标保留正数、将负数置零。第一层线性变换把 维映射到内部维度 ,第二层再映射回 维。base 配置使用 、。同一层 FFN 在各位置共享参数,各层之间不共享这组参数。[1]
Attention 负责跨位置汇集信息,FFN 负责对每个位置已有的信息做非线性变换。FFN 不单独接触“小明”位置,却可以处理“工作”向量里已经汇集的小明信息。
4.3 算完第一层,再确定第二层输入#
继续二维手算例子。令注意力输出投影为单位矩阵,关闭 dropout。为便于手算,每次 LayerNorm 都取缩放参数 、偏移参数 ,暂忽略数值稳定项 ;本例各次方差均非零。LayerNorm 的计算定义为:
表示逐坐标相乘。真实实现使用正的 ;忽略它只是为了本次手算得到简洁数字。
“工作”的输入为 ,注意力输出为 。相加并归一化后:
再取一个用于演示的 FFN:,可由 、、偏置为零实现,其中 是二维单位矩阵。它的内部维度也取 2,与论文的配置不同。“工作”位置的完整第一层输出为:
其他位置执行同样的计算。按未舍入数值计算,本例的完整第一层输出、也就是第二层输入为:
第二层接收整个矩阵,不是只接收“工作”的 。 第二层使用自己的参数,从这个矩阵重新生成 Q、K、V,并计算新的输出。
二维 LayerNorm 会把许多不同输入映射成相同结果,因此本例中“开始”和“工作”的输出恰好相同。这个简化例子只用于核对数据流和计算,不能作为实际模型表示能力的示例。
5. 编码器与解码器怎样完成翻译#
5.1 编码器输出供解码器读取#
原始 base 模型堆叠 6 层编码器与 6 层解码器。编码器最后一层输出记为 ,源句有 个位置,则 。它保留每个源位置的向量,不把整句压成单个向量。
解码器每层先处理输出前缀,再从 汇集源句信息,最后经过 FFN。每段操作都带残差与 Post-LN。其中跨注意力 Cross-Attention 的 Query 来自当前解码器的隐藏向量,Key 和 Value 来自编码器最终输出;不同解码器层对同一 使用自己的投影参数。[1]
| 注意力位置 | Q 来自哪里 | K、V 来自哪里 | 允许读取哪些位置 |
|---|---|---|---|
| 编码器 self-attention | 上一层源句向量 | 同一组源句向量 | 完整源句中的有效位置 |
| 解码器 masked self-attention | 上一层目标序列向量 | 同一组目标序列向量 | 当前及之前的输入位置 |
| 解码器 cross-attention | masked self-attention 及其残差归一化的输出 | 编码器最终输出 | 完整源句中的有效位置 |
源句位置数和目标句位置数可以不同。Cross-Attention 的权重矩阵为 :每个目标查询位置分别对源句各位置分配权重。
5.2 解码器输入右移,防止读到答案#
训练时,目标句已知。将目标序列右移一位,以句首符号 BOS 开始作为解码器输入;EOS 是结束符号。对翻译算例,位置对齐如下:
| 目标位置 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 解码器输入 | BOS | Xiaoming | starts | working |
| 该位置的预测目标 | Xiaoming | starts | working | EOS |
例如位置 2 用 BOS 和 Xiaoming 预测 starts。它不能读取输入位置 3 的 starts,否则训练时已经获得待预测答案。
设解码器查询位置为 、被读取的位置为 ,因果遮罩定义为:
因此,“允许读取当前输入位置”与“禁止读取当前预测目标”并不冲突:右移后的当前输入是目标的前一个 token。训练使用真实目标前缀,这种方式称为 teacher forcing;各位置可以并行计算,但必须遵守遮罩。[1]
5.3 隐藏向量经输出层才成为词表概率#
设解码器最后一层在目标位置 的输出为 ,目标词表大小为 。输出矩阵 和偏置 将其映射为词表分数;再用 softmax 得到下一 token 的概率:
是完整源句, 是目标前缀, 表示全部模型参数。这里 softmax 在目标词表上归一化;Attention 中的 softmax 则在允许读取的位置上归一化,两者用途不同。
假设某次前向计算为正确标签依次给出概率 0.6、0.5、0.25、0.8。这些是假设的模型输出,不是从二维编码器算例直接推得。忽略 padding,使用自然对数,按 4 个目标位置平均的负对数似然为:
这是不使用 label smoothing 的教学算例。原论文训练还使用系数为 0.1 的 label smoothing,将监督分布从单个正确 token 扩展为平滑分布,实际目标不等于此处的纯 NLL。[1]
对纯 NLL,设正确标签为 ,位置数为 ,单个 logit 的梯度为:
在词表项等于正确标签时取 1,否则取 0。正确标签概率为 0.6、 时,对应 logit 的梯度为 。若独立优化这个 logit,梯度下降会增加它;真实模型的 logits 由共享参数生成,不能据此保证每个训练位置的正确概率都单独提高。
梯度通过输出层向前面的解码器、编码器和 embedding 传播。优化器随后更新参数。前向计算改变隐藏向量,训练更新改变模型参数;它们不是同一种“更新”。
5.4 生成时逐个扩展输出前缀#
推理时没有真实目标句。模型先编码源句得到 ,然后以 BOS 为解码器输入。假设每次选择概率最大的 token:
- 输入 BOS,预测并选择 Xiaoming。
- 输入 BOS、Xiaoming,预测并选择 starts。
- 输入 BOS、Xiaoming、starts,预测并选择 working。
- 输入 BOS、Xiaoming、starts、working,预测并选择 EOS,结束生成。
这是演示用的贪心选择,输出不保证正确。论文翻译评测使用 beam search,保留多个候选前缀。源句没有变化时,编码结果 可以复用;但下一个目标 token 仍依赖已经选择的输出,不能像 teacher forcing 那样同时获得整句预测所需的真实前缀。[1]
6. 原始结构的局限性与后续变体#
全注意力显式比较所有位置对,长序列会产生二次增长的注意力矩阵。 长度为 的 self-attention 在每个头中产生 个匹配分数。这里描述的是原始稠密注意力的计算与存储方式;整层还包含线性投影和 FFN,不能把整层全部开销都归结为注意力矩阵。[1]
Encoder–Decoder 需要独立的源序列和目标序列数据流。 在翻译中,编码器可以读取完整源句,解码器只读取目标前缀;直接把这套原始结构称为“今天语言模型的全部架构”会混淆注意力方向与模块组成。Decoder-only 模型通常只保留因果自注意力堆栈,不另设读取独立编码结果的 cross-attention。
全局注意力本身没有局部窗口约束。 如果将 self-attention 换为 SWA(Sliding Window Attention,滑动窗口注意力),变化在于遮罩限制可读位置,而不是层与层之间只传递一个窗口。下一层仍接收上一层的完整序列向量;每个查询只从其中的局部范围汇集信息。跨多层时,局部范围以外的信息可能通过中间位置传播,但“允许存在传播路径”不保证模型实际保留了该信息。
参考资料#
- Vaswani 等,2017, Attention Is All You Need。本文按原始 Encoder–Decoder、Post-LN 与 base 配置解释;二维向量和概率算例为本文构造,不是论文实验。
- PyTorch 2.15:Transformer。核对编码器、解码器接口、输入输出形状及
norm_first约定;完整模型还需在模块外配置 embedding 和词表输出层。
延伸阅读#
- SWA:滑动窗口注意力与跨层信息传递:局部窗口怎样减少直接读取,又怎样通过多层传递窗口外的信息?