图解 Transformer 工作原理
图解 Transformer 工作原理
在上一篇文章中,我们探讨了注意力机制(Attention)——这是现代深度学习模型中一种广泛应用的方法。注意力机制助力提升了神经机器翻译应用的性能,而本文将聚焦 Transformer 模型:它借助注意力机制,大幅提升了这类模型的训练速度。在特定任务中,Transformer 的性能优于谷歌神经机器翻译模型,而其最大优势在于具备高度并行化能力。事实上,谷歌云也推荐将 Transformer 作为参考模型,以充分利用其云 TPU(张量处理单元)服务。接下来,我们将拆解该模型,深入探究其工作原理。
Transformer 模型出自论文《Attention Is All You Need》(《注意力就是你所需要的一切》)。其 TensorFlow 实现已纳入 Tensor2Tensor 工具包,哈佛大学自然语言处理小组还发布了带 PyTorch 实现的论文注释指南。本文将尽量简化概念,逐步展开介绍,希望能让非专业背景的读者也能轻松理解。
一、整体概览
我们先将模型视为一个“黑盒”:在机器翻译场景中,它接收一种语言的句子,输出另一种语言的译文。
| 输入(法语) | 模型 | 输出(英语) |
|---|---|---|
| Je suis étudiant(我是学生) | TRANSFORMER | I am a student |
打开这个“黑盒”,我们会发现其核心由三部分构成:编码组件、解码组件,以及两者之间的连接结构。
- 编码组件由若干个编码器堆叠而成(论文中采用6层堆叠,这个数字并非固定,可根据实际需求调整);
- 解码组件同样由相同数量的解码器堆叠组成;
- 所有编码器结构完全一致,但权重互不共享;同理,所有解码器结构也完全相同,权重亦独立。
(1)编码器结构
每个编码器包含两个子层,数据流向如下:
编码器的输入首先流经自注意力层——该层能让编码器在编码某个词时,同时参考输入句子中其他词的信息(后续将详细拆解);自注意力层的输出会传入前馈神经网络,且这个前馈神经网络会独立作用于每个位置的向量。
(2)解码器结构
解码器同样包含上述两个子层,但在两者之间增加了一个注意力层——该层帮助解码器聚焦输入句子中的相关部分(类似序列到序列(seq2seq)模型中注意力机制的作用),具体结构如下:
二、张量视角:数据流转过程
了解了模型的核心组件后,我们来看看各类向量/张量在组件间的流转过程——训练好的模型正是通过这种流转,将输入转化为输出。
与大多数自然语言处理(NLP)应用一致,Transformer 首先通过嵌入算法(embedding)将每个输入词转化为向量。
(1)词嵌入(Embedding)
以输入句子“Je suis étudiant”为例,每个词会被映射为一个固定维度(论文中为512维)的向量:
仅最底层的编码器需要进行词嵌入操作。对所有编码器而言,其输入都是一个由512维向量组成的列表:底层编码器的输入是词嵌入向量,而上层编码器的输入则是其正下方编码器的输出。这个列表的长度是一个超参数,通常设置为训练数据集中最长句子的长度。
(2)编码器的并行性
词嵌入完成后,每个词的向量会流经编码器的两个子层。这里要强调 Transformer 的一个关键特性:每个位置的词在编码器中都有独立的流转路径。在自注意力层中,这些路径存在依赖关系;但在前馈神经网络层中,不存在任何依赖关系,因此不同路径的计算可以并行执行——这也是 Transformer 训练速度快的核心原因之一。
为了更清晰地展示过程,我们换用更短的句子“Thinking Machines”(智能机器),看看编码器各子层的具体处理逻辑:
三、自注意力机制:核心原理拆解
自注意力机制是 Transformer 的核心,它能让模型在处理某个词时,关联输入序列中其他相关词的信息。比如处理句子“The animal didn't cross the street because it was too tired”(这只动物没有过马路,因为它太累了)时,自注意力机制能让模型在编码“it”(它)这个词时,自动关联到“animal”(动物),从而明确指代关系——这对人类来说很简单,但对算法而言并非易事。
简单来说,当模型处理输入序列中的每个词(每个位置)时,自注意力机制会让它参考输入序列中其他位置的信息,从而更精准地编码当前词。如果熟悉循环神经网络(RNN),可以将其理解为:RNN 通过维持隐藏状态,将之前处理过的词/向量的表征融入当前词的处理;而自注意力机制则是 Transformer 实现这一功能的核心方法——将其他相关词的“理解”融入当前词的编码中。
(1)自注意力的向量计算步骤
我们先通过向量运算理解自注意力的计算逻辑,再看实际应用中的矩阵优化实现。
步骤1:生成查询(Query)、键(Key)、值(Value)向量
对于编码器的每个输入向量(此处即词嵌入向量),通过与三个训练过程中习得的权重矩阵(WQ、WK、WV)相乘,生成三个新向量:查询向量(Q)、键向量(K)、值向量(V)。
- 词嵌入向量维度为512,而 Q、K、V 向量维度为64(这是架构设计选择,目的是让多头注意力的计算量基本保持稳定,并非必须小于嵌入向量维度)。
- 示例:嵌入向量 X1(Thinking)× WQ → Q1(查询向量);X1 × WK → K1(键向量);X1 × WV → V1(值向量);同理可得 X2(Machines)对应的 Q2、K2、V2。
步骤2:计算注意力得分
注意力得分用于衡量当前词与输入序列中其他词的关联程度。以处理第一个词“Thinking”为例,需计算它与输入序列中每个词的得分——得分通过查询向量与对应词的键向量的点积得到:
- 与自身的得分:Q1 · K1 = 112(点积结果)
- 与“Machines”的得分:Q1 · K2 = 96
步骤3:得分归一化(缩放)
为了让梯度更稳定,将所有得分除以键向量维度的平方根(论文中键向量维度为64,故除以√64=8):
- 112 ÷ 8 = 14
- 96 ÷ 8 = 12
步骤4:Softmax 激活
对缩放后的得分进行 Softmax 运算,将得分转化为0-1之间的概率(所有概率和为1),该概率即为每个词对当前词的“贡献权重”:
- Softmax(14, 12) → [0.88, 0.12]
- 这意味着在编码“Thinking”时,模型会将88%的注意力放在自身,12%的注意力放在“Machines”上。
步骤5:加权值向量
将每个词的值向量与对应的 Softmax 概率相乘——这样能保留重点关注词的值向量信息,同时弱化无关词的影响(比如乘以0.001这类极小值):
- V1 × 0.88(“Thinking”的加权值向量)
- V2 × 0.12(“Machines”的加权值向量)
步骤6:求和得到自注意力输出
将所有加权值向量求和,得到当前位置词的自注意力层输出向量:
- Z1(“Thinking”的自注意力输出)= V1×0.88 + V2×0.12
- 同理可得 Z2(“Machines”的自注意力输出)
这个输出向量会被传入前馈神经网络,完成编码器的一次完整处理。
(2)自注意力的矩阵计算实现
实际应用中,为了提升计算速度,自注意力会通过矩阵运算批量处理,步骤如下:
步骤1:构建输入矩阵与权重矩阵相乘
将所有词的嵌入向量按行堆叠,形成输入矩阵 X(每行对应一个词的嵌入向量);然后将 X 分别与权重矩阵 WQ、WK、WV 相乘,得到查询矩阵 Q、键矩阵 K、值矩阵 V:
步骤2:矩阵形式的注意力计算
将上述向量计算的步骤2-6浓缩为一个公式,直接得到自注意力层的输出矩阵 Z:
其中:
- $K^T$ 是键矩阵 K 的转置;
- $d_k$ 是键向量的维度(64);
- Softmax 作用于矩阵的每一行,确保每行的概率和为1。
四、多头注意力:提升模型表达能力
论文中进一步优化了自注意力层,提出了“多头注意力”(Multi-Headed Attention)机制,它从两个方面提升了注意力层的性能:
- 扩展模型关注不同位置的能力:比如在翻译“The animal didn't cross the street because it was too tired”时,“it”既需要关联“animal”,也需要关联“tired”,多头注意力能同时捕捉这些不同的关联;
- 提供多个“表征子空间”:多头注意力包含多组独立的 Q/K/V 权重矩阵(论文中使用8个注意力头),每组权重矩阵都会将输入嵌入(或下层编码器/解码器的输出)投影到不同的表征子空间,经过训练后,每组权重会专注于捕捉不同类型的关联(如语法关联、语义关联)。
(1)多头注意力的计算流程
- 对输入矩阵 X 分别与8组独立的 WQ、WK、WV 权重矩阵相乘,得到8组 Q、K、V 矩阵(对应8个注意力头);
- 每个注意力头独立执行上述自注意力矩阵计算,得到8个输出矩阵 Z0、Z1、...、Z7;
- 将8个输出矩阵拼接(Concatenate)成一个大矩阵;
- 将拼接后的矩阵与一个训练习得的权重矩阵 WO 相乘,得到多头注意力层的最终输出矩阵 Z——该矩阵融合了所有注意力头的信息,可传入前馈神经网络。
(2)多头注意力的可视化理解
以编码“it”为例:
- 其中一个注意力头主要关注“the animal”,将其表征融入“it”的编码;
- 另一个注意力头主要关注“tired”,同样将其表征融入“it”的编码;
- 所有8个注意力头的信息融合后,“it”的编码会包含多个维度的关联信息,让模型对“it”的指代理解更全面。
五、位置编码:捕捉序列顺序信息
前文描述的模型中,缺少一个关键功能:捕捉输入序列中词的顺序信息——自注意力机制本身不区分词的位置,无论词在序列中处于哪个位置,计算出的关联权重都是相同的。
为了解决这个问题,Transformer 为每个输入嵌入向量添加了一个“位置编码向量”(Positional Encoding)。这些向量遵循特定的模式,模型通过学习这种模式,能够识别每个词的位置以及不同词之间的距离。其核心思想是:将位置编码向量与嵌入向量相加后,在 Q/K/V 向量投影和点积注意力计算过程中,能产生有意义的距离表征。
(1)位置编码的形式
位置编码向量与嵌入向量维度相同(512维),其值介于-1和1之间。论文中采用正弦和余弦函数生成位置编码,具体公式如下(简化版):
对于位置 p(从0开始)和维度 i(从0开始):
- 若 i 为偶数:$PE(p, i) = \sin\left( \frac{p}{10000^{2i/d_{model}}} \right)$
- 若 i 为奇数:$PE(p, i) = \cos\left( \frac{p}{10000^{2i/d_{model}}} \right)$
这种设计的优势是能适应未见过的长序列——即使输入句子长度超过训练数据集中的最长句子,模型也能通过公式生成对应的位置编码。
(2)位置编码的示例
以嵌入向量维度为4(简化版)为例,位置编码与词嵌入的融合过程如下:
| 词 | 位置 | 词嵌入向量(简化4维) | 位置编码向量(简化4维) | 融合后向量(嵌入+位置编码) |
|---|---|---|---|---|
| Je | 0 | [0.2, 0.5, 0.8, 0.1] | [0.0001, 0.84, 1, 1] | [0.2001, 1.34, 1.8, 1.1] |
| suis | 1 | [0.1, 0.7, 0.2, 0.3] | [0.54, 1, 0.91, 0.0002] | [0.64, 1.7, 1.11, 0.3002] |
| étudiant | 2 | [0.4, 0.3, 0.6, 0.5] | [-0.42, 1, 0.0001, 0.9] | [-0.02, 1.3, 0.6001, 1.4] |
六、残差连接与层归一化
在每个编码器(和解码器)的子层(自注意力层、前馈神经网络层)中,都包含残差连接(Residual Connection)和层归一化(Layer Normalization)步骤,具体结构如下:
(1)残差连接
残差连接将子层的输入直接与子层的输出相加,其作用是避免深层网络训练时出现梯度消失问题——让信息能直接传递到深层网络,减少模型退化的风险。
(2)层归一化
层归一化对相加后的向量进行标准化处理(让向量的均值为0、方差为1),能加速模型训练收敛,同时提升模型的稳定性。
以编码器的自注意力层为例,完整流程为:
七、解码器的工作流程
编码器的输出会转化为一组注意力向量 K(键向量)和 V(值向量),并传递给每个解码器的“编码器-解码器注意力层”,帮助解码器聚焦输入序列中的相关部分。解码器的工作流程分为训练阶段和推理阶段,核心差异在于输入来源。
(1)解码器的特殊机制:掩码自注意力
解码器的自注意力层与编码器的自注意力层略有不同:为了符合自回归生成逻辑(只能基于已生成的词预测下一个词),解码器的自注意力层会对“未来位置”进行掩码(Masking)——在 Softmax 运算前,将未来位置的注意力得分设为负无穷(-inf),这样经过 Softmax 后,未来位置的权重会变为0,确保模型在生成当前词时,无法获取未来词的信息。
例如,在生成句子“I am a student”时,生成第一个词“I”时,会掩码“am”“a”“student”的位置;生成第二个词“am”时,会掩码“a”“student”的位置,以此类推。
(2)编码器-解码器注意力层
该层的工作方式与多头自注意力类似,但有一个关键区别:
- 查询矩阵 Q 来自解码器前一层的输出;
- 键矩阵 K 和值矩阵 V 来自编码器栈的最终输出;
通过这种设计,解码器能将生成的内容与输入序列的信息对齐(比如机器翻译中,目标语的词与源语的词对应)。
(3)解码过程(推理阶段)
解码过程是一个迭代过程,直到生成特殊的“句子结束”标记(<<eos>)为止,具体步骤如下:
- 初始输入:将起始标记(<sos>)传入最底层解码器;
- 嵌入与位置编码:对输入标记进行词嵌入和位置编码;
- 解码器处理:经过掩码自注意力层、编码器-解码器注意力层、前馈神经网络层,输出一个向量;
- 生成当前词:将输出向量传入线性层和 Softmax 层,得到词汇表中每个词的概率,选择概率最高的词作为当前步的输出;
- 迭代:将当前步生成的词作为下一次解码的输入,重复步骤2-4,直到生成<<eos>标记。
示例(输入“Je suis étudiant”,输出“I am a student”):
八、最终层:线性层与 Softmax 层
解码器栈输出的是一个浮点数向量,要将其转化为具体的词,需要经过线性层和 Softmax 层的处理:
(1)线性层
线性层是一个全连接神经网络,它将解码器输出的512维向量投影到一个“对数几率向量”(logits 向量)——向量的维度等于模型的输出词汇表大小。例如,若词汇表包含10000个独特的英语单词,logits 向量的维度就是10000,每个维度对应一个单词的得分。
(2)Softmax 层
Softmax 层将 logits 向量中的得分转化为概率(所有概率为正,且和为1),选择概率最高的维度对应的单词,作为当前步的输出词。
流程示意:
九、训练过程简要回顾
训练阶段的核心是让模型学习到“输入→正确输出”的映射关系,具体步骤如下:
(1)训练数据与标签
模型在标注训练数据集上进行训练,输入是源语言句子(如法语“Je suis étudiant”),标签是对应的目标语言句子(如英语“I am a student”)。目标语言句子会被转化为“独热编码向量”(one-hot encoding)——例如,若词汇表包含6个词(“a”“am”“i”“thanks”“student”“<<eos>”),则“am”对应的独热向量为[0, 1, 0, 0, 0, 0]。
(2)损失函数与参数优化
- 未训练的模型(参数随机初始化)会输出一个随机的概率分布,与标签的独热向量(理想概率分布)存在差异;
- 通过“交叉熵”(Cross-Entropy)或“KL散度”(Kullback–Leibler Divergence)衡量两者的差异,得到损失值(Loss);
- 利用反向传播(Backpropagation)算法,根据损失值调整模型的所有权重参数(包括 Q/K/V 权重矩阵、前馈网络权重、位置编码相关参数等),减小损失值;
- 重复上述过程,直到模型在训练数据集上的损失值稳定在较低水平,且在验证数据集上表现良好(避免过拟合)。
(3)解码策略
训练完成后,推理阶段的解码策略有多种:
- 贪心解码(Greedy Decoding):每一步都选择概率最高的词,简单高效,但可能错过全局最优解;
- 束搜索(Beam Search):每一步保留概率最高的 k 个候选词(k 为束宽,如 k=2),后续步骤基于这些候选词继续生成,最终选择整体概率最高的候选序列,能提升输出质量,但计算量更大。
十、深入学习资源推荐
如果想进一步深入理解 Transformer,推荐以下资源:
- 阅读原始论文《Attention Is All You Need》;
- 阅读 Transformer 官方博客:《Transformer: A Novel Neural Network Architecture for Language Understanding》;
- 观看 Łukasz Kaiser 的演讲(详细解读模型细节);
- 实操 Tensor2Tensor 仓库中的 Jupyter 笔记本;
- 探索 Tensor2Tensor 开源仓库。
致谢
感谢伊利亚·波洛苏欣(Illia Polosukhin)、雅各布·乌兹科雷特(Jakob Uszkoreit)、利昂·琼斯(Llion Jones)、卢卡斯·凯泽(Lukasz Kaiser)、尼基·帕尔马(Niki Parmar)和诺姆·沙泽尔(Noam Shazeer)对本文初稿提供的反馈。
原文发布于2018年6月27日
图解 Transformer 工作原理 | 翻译版
