Feng技术成长记头像
关注
【人工智能】【深度学习】深入理解 Transformer:注意力机制、编码器与解码器封面图

【人工智能】【深度学习】深入理解 Transformer:注意力机制、编码器与解码器

Transformer 架构深度解析:从整体结构到训练推理

Transformer 由输入表示、自注意力、前馈网络、残差连接、层归一化和输出生成等模块组成。本文按照数据流对这些模块进行说明,并解释各模块的作用。

参考:Vaswani 等,2017,《Attention Is All You Need》。



目录


第一章 Transformer 的总体架构

1.1 为什么需要 Transformer

在 Transformer 出现之前,机器翻译等序列任务主要依赖 RNN、LSTM 这类循环神经网络。它们有一个共同特点:信息要一步接一步地向后传递,即处理第 t t t 个词时必须先算完第 t − 1 t-1 t1 个词。这种顺序计算带来两个问题:

  1. 信息衰减:前面的信息每向后传递一步就要经过一次变换,传到序列末尾时往往已经很弱了。即使 LSTM 用门控机制缓解了这个问题,长距离依赖仍然难以建模。
  2. 无法并行:GPU 擅长同时计算大量独立任务,但 RNN 每一步都依赖前一步的结果,天然无法并行,训练效率很低。

Transformer 用自注意力机制解决了这两个问题:它让序列中任意两个位置的词直接建立联系,不需要一步步传递;同时整段序列可以同时送入模型并行计算。

需要提醒的是,自注意力要计算序列中所有词两两之间的关系,计算量和显存开销随序列长度的平方增长。所以 Transformer 不是"在所有场景下都更快",它的优势在于并行训练长距离依赖建模

1.2 编码器—解码器的整体结构

原始 Transformer 采用 Encoder–Decoder(编码器—解码器)结构,整体可以理解成两个分工明确的部分:

  • 编码器(Encoder):负责理解输入序列,把源语言转换成语义丰富的上下文表示;
  • 解码器(Decoder):负责根据编码器提供的上下文,逐步生成目标序列。

编码器和解码器都由若干层堆叠而成,原论文中都是 6 层。

图 1 Transformer 原始 Encoder–Decoder 总体架构

在这里插入图片描述

图中左侧为编码器,右侧为解码器。编码器输出作为解码器交叉注意力的输入,解码器最终通过 Linear 和 Softmax 输出 token 概率。

原始 Transformer 的关键配置
配置项原论文设置
编码器层数6
解码器层数6
模型维度 d m o d e l d_{model} dmodel512
注意力头数 h h h8
每个头的维度64
前馈层隐藏维度2048

各子层在模块边界处保持 d m o d e l = 512 d_{model}=512 dmodel=512,因此残差连接可以直接进行相加。

编码器的每一层包含两个子模块,外加两组残差连接与层归一化(Add & Norm):

  • 多头自注意力层(Multi-Head Self-Attention);
  • 位置前馈神经网络(Position-wise Feed-Forward Network)。

解码器的每一层包含三个子模块,外加三组 Add & Norm:

  • 带掩码的多头自注意力层:处理目标序列内部的关系,且不允许看到未来的词
  • 交叉注意力层(Cross-Attention):Q 来自解码器,K、V 来自编码器的输出,让解码器"读取"源语言的信息;
  • 位置前馈神经网络。

1.3 数据如何流过整个模型

一句话概括整个数据流:

源序列经过编码器变成上下文表示(Context / Memory),解码器结合这个表示和目标前缀,一步步预测出下一个 token。

详细过程如下:

  1. 源序列的每个 token 先查词嵌入表得到向量,再加上位置编码;
  2. 向量依次穿过 6 层编码器,每层都在做"注意力收集信息 + 前馈加工信息",最终输出整句话的上下文表示;
  3. 解码器接收右移后的目标序列(训练时是真实目标右移,推理时是已生成的词),先用掩码自注意力处理目标内部关系,再通过交叉注意力读取编码器的上下文表示;
  4. 最后一个线性层把解码器的输出映射到词表大小的维度,再用 Softmax 变成"下一个词的概率分布"。

第二章 核心组件详解

2.1 输入表示:词嵌入与位置编码

2.1.1 词嵌入(Input Embedding)

神经网络不能直接处理文字。常规做法是先建一个词表,把每个 token 映射成整数 ID,再查表得到对应的稠密向量,这个过程就是词嵌入(Embedding)。

为什么不用 one-hot 编码? 原文给出了两个关键原因:

  1. 维度爆炸:one-hot 的维度等于词表大小,词越多维度越高,而且绝大多数位置都是 0,空间非常稀疏;
  2. 语义孤立:one-hot 编码下任意两个不同词的向量都正交。比如"我"和"爱"用 one-hot 表示后点积为 0,余弦相似度为 0,模型完全无法学习到它们之间其实关系紧密。
    在这里插入图片描述

词嵌入矩阵可以表示为:

E ∈ R ∣ V ∣ × d m o d e l E \in \mathbb{R}^{|V| \times d_{model}} ERV×dmodel

其中 ∣ V ∣ |V| V 是词表大小, d m o d e l d_{model} dmodel 是模型的隐藏维度。原论文中 d m o d e l = 512 d_{model}=512 dmodel=512。也就是说,词嵌入本质上是一张 ∣ V ∣ × 512 |V| \times 512 V×512 的大表,每个词对应一行。输入某个词的 ID 时,取对应的一行向量即可,所以从实现角度看,Input Embedding 就是一个查表操作

这个词嵌入矩阵是训练出来的,因此最终得到的稠密向量在维度上是有相关性的,相似的词可以学习到相似的表示。另外,原论文在嵌入后还要乘以 d m o d e l \sqrt{d_{model}} dmodel

X e m b = d m o d e l   E [ x ] X_{emb} = \sqrt{d_{model}}\,E[x] Xemb=dmodel E[x]

这样做的目的是让嵌入向量的数值尺度与位置编码在同一量级,避免相加时位置编码被"淹没"。

2.1.2 位置编码(Positional Encoding)

自注意力有个"缺点":它本身完全不关心词的顺序。把"我爱你"和"你爱我"分别作为一个集合输入注意力层,模型看到的信息是相同的。因此必须额外告诉模型每个词在序列中的位置。

Transformer 采用固定编码(Fixed Positional Encoding),用正弦和余弦函数生成位置向量:

P E ( p o s , 2 i ) = sin ⁡ ( p o s 10000 2 i / d m o d e l ) PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i / d_{model}}}\right) PE(pos,2i)=sin(100002i/dmodelpos)

P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s 10000 2 i / d m o d e l ) PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i / d_{model}}}\right) PE(pos,2i+1)=cos(100002i/dmodelpos)

参数含义如下:

  • p o s pos pos:词在序列中的位置,即"第几个词";
  • i i i:向量中的维度下标;
  • d m o d e l d_{model} dmodel:模型维度,论文默认取 512;
  • 偶数维用正弦公式,奇数维用余弦公式。

这个编码有几个好性质:

  • 数值被控制在 [ − 1 , 1 ] [-1, 1] [1,1] 之间,不会出现数值爆炸;
  • i i i 较小时分母小、波长短(变化快),负责捕捉近距离的位置差异;
  • i i i 较大时分母大、波长长(变化平缓),负责捕捉远距离的位置差异。
    在这里插入图片描述
    在这里插入图片描述

直观理解:低维度上相邻位置的值差别很大,容易区分相邻的词;高维度上数值变化平缓,但提供了"广度",相当于给每个词一个绝对坐标。这样一来,低维负责"精确区分邻居",高维负责"整体定位",两者配合就构成了位置信息。

最终每个词的输入表示就是词嵌入与位置编码相加:

X = X e m b + P X = X_{emb} + P X=Xemb+P

2.2 自注意力机制:Q、K、V 是什么

2.2.1 自注意力要解决什么问题

自注意力的思路是:对于当前这个词,应该从整句话中"有重点地"收集其他词的信息。某个词与当前词关系越大,它在当前词的表示中占的权重就越高。

2.2.2 Q、K、V 的含义

Q、K、V 是自注意力机制的核心。它们不是人工指定含义的向量,而是通过训练得到的三个矩阵,对输入 X X X 做线性变换得到的:

Q = X W Q , K = X W K , V = X W V Q = XW^Q, \qquad K = XW^K, \qquad V = XW^V Q=XWQ,K=XWK,V=XWV

三个矩阵各司其职,可以类比成一次"信息检索":

  • Q(Query,查询):负责"我要找什么"。每个查询向量代表当前位置希望从别处获取的信息类型;
  • K(Key,键):负责"我有什么"。每个键向量代表序列中某个位置提供的信息特征,用来和查询做匹配;
  • V(Value,值):负责"真正给什么"。匹配成功后,真正被加权聚合进结果的就是值向量。

整个过程就像是"拿着查询去匹配一堆键,匹配度高的键,其对应的值就更多地进入结果"。

图中展示输入 X X X 经过三组投影得到 Q Q Q K K K V V V,再通过 Q K T QK^T QKT 计算相关性,使用 Softmax 得到权重,最后对 V V V 加权求和。

2.2.3 为什么要对 X 做线性变换

有人可能会问:直接用原始向量做点积判断相关性不行吗?原文用一个例子解释了这个问题:

假如输入只有"我"和"爱"两个字,用 one-hot 表示成 [ 0 , 1 ] [0,1] [0,1] [ 1 , 0 ] [1,0] [1,0],两者点积直接为 0,相当于模型认为它们毫无关系。但实际上"我"和"爱"联系非常紧密。

如果直接拿原始向量算相似度,很多本应相关的词之间会因为编码方式而"失联"。所以需要通过 W Q 、 W K 、 W V W^Q、W^K、W^V WQWKWV 把输入 X X X 变换到新的空间,让"我"和"爱"在这种变换后具备可比较的关系,再通过点积衡量相关性——点积越大,相关性越高。

2.2.4 带掩码的自注意力的计算过程

带掩码自注意力使用的公式是:

A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k + M ) V Attention(Q, K, V) = softmax\left(\frac{QK^T}{\sqrt{d_k}} + M\right)V Attention(Q,K,V)=softmax(dk QKT+M)V
S c o r e s = Q K T d k Scores = \frac{QK^T}{\sqrt{d_k}} Scores=dk QKT

其中 M M M 是可选的掩码矩阵。可以把整个计算过程看成“先算相关性,再按相关性取信息”。完整计算分五步:

  1. 计算 Q K T QK^T QKT,得到每个查询与所有键之间的匹配分数矩阵;
  2. 除以 d k \sqrt{d_k} dk ,对分数做缩放;
  3. 加上掩码 M M M,屏蔽不允许关注的位置(后面会讲);
  4. 对每一行做 Softmax,把分数归一化成概率分布,也就是每个位置的注意力权重;
  5. 用权重对 V V V 加权求和,得到当前位置新的表示。

为什么要除以 d k \sqrt{d_k} dk 假设各维度近似独立且方差相近,查询向量与键向量的点积会随着维度 d k d_k dk 增大而产生更大的波动,方差量级约为 d k d_k dk。如果直接把这些分数送入 Softmax,概率容易过度集中,函数进入饱和区,梯度变小,训练也会变得不稳定。因此,除以 d k \sqrt{d_k} dk 的本质是控制点积的尺度,而不是简单地把向量限制到 [ − 1 , 1 ] [-1,1] [1,1]

为什么要做 Softmax? 归一化后分数变成概率分布,每个位置的权重之和为 1,这样才能正确地按"重要性"加权聚合 V V V
在这里插入图片描述

2.3 多头注意力机制

在前面的基础上,多头注意力就容易理解了。前面讲的都是"单头"注意力:整个 512 维向量在同一套 Q、K、V 下计算一种关系。但一句句子里同时存在多种关系,比如邻近关系、主谓关系、指代关系等,单头很难面面俱到。

多头注意力的做法是:把 d m o d e l = 512 d_{model}=512 dmodel=512 维拆成 8 个头,每个头负责 64 维,各自独立计算注意力:

h e a d i = A t t e n t i o n ( Q W i Q , K W i K , V W i V ) head_i = Attention(QW_i^Q, KW_i^K, VW_i^V) headi=Attention(QWiQ,KWiK,VWiV)

每个头都拥有自己独立的 W i Q 、 W i K 、 W i V W_i^Q、W_i^K、W_i^V WiQWiKWiV,因此可以在不同的子空间里学习不同种类的依赖关系。

计算完之后,把 8 个头的输出拼接起来,再乘一个输出矩阵 W O W^O WO

M u l t i H e a d ( Q , K , V ) = C o n c a t ( h e a d 1 , . . . , h e a d h )   W O MultiHead(Q, K, V) = Concat(head_1, ..., head_h)\,W^O MultiHead(Q,K,V)=Concat(head1,...,headh)WO

不同注意力头在不同子空间中提取关系,随后通过 Concat 和输出投影重新融合。

为什么要乘 W O W^O WO 这其实是在问:拼接后的向量为什么还要再做一次线性变换?原因是:拼接只解决了"把多条线索放在一起",但没有决定"每条线索该占多大分量"。目前各头的信息顺序是"定死"的,乘上 W O W^O WO 后,模型可以自己学习谁更重要、如何组合这些视角。用原文的一句总结就是:

多头注意力的本质是"学习如何把多条线索重新组合成最有用的信息"。
在这里插入图片描述

2.4 前馈神经网络

每个编码器层和解码器层里,注意力之后都会接一个前馈神经网络(Feed-Forward Network)。它由三部分构成:输入层、隐藏层、输出层。

F F N ( x ) = m a x ( 0 , x W 1 + b 1 )   W 2 + b 2 FFN(x) = max(0, xW_1 + b_1)\,W_2 + b_2 FFN(x)=max(0,xW1+b1)W2+b2

原论文使用 ReLU 作为激活函数,输入输出维度为 512,中间隐藏层维度为 2048。

【前馈神经网络结构图】

输入和输出保持模型维度不变,中间层先扩展表示空间,再通过非线性激活完成特征加工。

前馈网络也可以分两步理解:

  1. 先把输入向量线性变换到更高维(512 → 2048),再经过 ReLU 引入非线性;
  2. 再经过一次线性变换(2048 → 512),把表示映射回原来的维度。

隐藏层维度更大,配合 ReLU 的非线性,让网络具备逼近任意函数的能力。一句话概括前馈网络的作用:

把自注意力收集来的上下文信息,深度加工成更高层次的语义特征。

注意力与前馈网络的分工可以这样记:注意力负责"从其他位置收集信息",前馈网络负责"在当前位置加工信息"

2.5 残差连接与层归一化

2.5.1 层归一化(Layer Normalization)

层归一化在特征维度上,对每个样本分别做归一化,公式如下:

x ^ = x − μ σ 2 + ϵ , y = γ x ^ + β \hat{x} = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}}, \qquad y = \gamma\hat{x} + \beta x^=σ2+ϵ xμ,y=γx^+β

其中 μ \mu μ σ 2 \sigma^2 σ2 是该样本在该层向量的均值与方差, γ \gamma γ β \beta β 是可学习的缩放和偏移参数。

为什么要做层归一化? 训练过程中,各层表示的数值尺度会不断变化。LayerNorm 通过对每个 token 的特征维度进行标准化,让后续子层接收到更稳定的输入,从而降低优化难度。早期文献常用 Internal Covariate Shift(内部协变量偏移)解释归一化的动机,但现代研究认为,归一化带来的平滑优化、梯度稳定等作用同样重要,不能只用“内部协变量偏移”一个观点概括。
原因一:在这里插入图片描述
原因二:
在这里插入图片描述

为什么要对"自注意力的输出"归一化? 自注意力内部做了 Softmax 和加权求和,其输出分布依然随输入变化而变化。归一化它,可以让后续前馈网络接到的输入分布更稳定,防止某一层的数值异常传导到整条链路。
在这里插入图片描述

为什么引入 γ \gamma γ β \beta β 归一化会改变原始向量的尺度和偏移。为了避免这种变换限制模型的表达能力,LayerNorm 增加了可学习的缩放与平移参数: γ \gamma γ 可以放大或缩小不同维度, β \beta β 可以整体调整表示的位置。更准确地说,它们不是简单地“恢复全部绝对信息”,而是让模型在稳定数值范围的同时,仍能学习合适的表示尺度。

2.5.2 残差连接(Residual Connection)

每个子层的输出都采用"原始输入 + 子层输出"的形式:

y = x + S u b l a y e r ( x ) y = x + Sublayer(x) y=x+Sublayer(x)

可以理解为:

Add = 全局依赖信息(子层学习到的结果)+ 原始词义信息(输入本身)

为什么要加残差连接? 核心目的是解决梯度消失问题。反向传播时,梯度要从最后一层一层地传回第一层,每经过一层,梯度就要乘以这一层的权重。如果权重小于 1,多层相乘后梯度会指数级衰减,传到前面几层时几乎变成 0,前面的参数就再也学不到东西了。

残差连接让原始信息和梯度都能绕过中间复杂的非线性变换,获得更直接的传播路径,相当于给梯度开了一条"高速公路"。它显著降低了深层网络的优化难度,但并不意味着只要加入残差就一定不会出现梯度消失;实际训练效果还与归一化位置、初始化和学习率等因素有关。
在这里插入图片描述

输入一条路径经过子层变换,另一条路径直接跳过子层,二者在末端相加,为信息和梯度提供更短的传播路径。

2.6 掩码注意力机制

掩码(Mask)的本质是:在计算注意力分数时,把不允许被关注的位置的分数设为一个极小的值(数学上相当于 − ∞ -\infty ),这样经过 Softmax 之后这些位置的权重趋近于 0,模型就不会把注意力分给它们。

Transformer 里有两种掩码,作用完全不同:

填充掩码(Padding Mask):模型训练时要一次处理一个批次的多条序列,而句子长短不一。为了让它们能组成规则的矩阵并行计算,较短的句子会用特殊填充符(如 [PAD])补齐到相同长度。填充掩码的作用就是把 [PAD] 位置的注意力分数也设为 − ∞ -\infty ,防止模型把注意力分配到这些没有任何语义的填充符上。

因果掩码(Causal Mask):用在解码器的自注意力中。解码器在预测第 t t t 个词时,只能看到第 t t t 个词以及它之前的词,不能提前"偷看"后面的答案。因果掩码就是把当前位置之后的分数全部屏蔽,保证"用已知的信息预测未知的信息"。

图 6 填充掩码与因果掩码的作用

【掩码注意力示意图】

填充掩码屏蔽无意义的 [PAD] 位置,因果掩码屏蔽当前位置之后的未来 token。被屏蔽位置经过 Softmax 后的权重接近 0。

一句话解释掩码的意义:过滤掉无效信息(填充符)和未来信息(未生成的词)。至于为什么要填充:是为了让长度不一的序列在 GPU 上能够组成张量并行计算。

2.7 推理过程

先明确一点:Transformer 论文中编码器是 6 层,一个 token 要依次经过 6 次编码器的处理,解码过程同理。推理阶段和训练阶段最大的区别在于:推理时没有真实答案可看

推理的整体流程是:

  1. 把源序列完整送入编码器,经过 6 层编码得到上下文表示。编码器输出在当前样本的生成过程中保持不变;
  2. 解码器从起始符开始,根据因果掩码只能读取已经生成的前缀,并据此预测下一个 token;
  3. 选出下一个 token 后,将它追加到前缀中,重复上述过程,直到生成结束符或达到最大长度。

在工程实现中,解码器通常还会缓存历史 token 的 Key 和 Value,避免每一步都重复计算整个前缀,这就是推理阶段常说的 KV Cache。

图 7 Transformer 推理阶段的自回归生成过程

【推理流程图】
在这里插入图片描述

编码器只需运行一次;解码器根据当前已生成的前缀,循环预测下一个 token,直到生成结束符。

一句话总结推理过程:

把"训练时解码器的输入(真实标签)“换成"自己上一秒生成的词”,一个一个往外蹦,直到吐出结束符。

2.8 训练过程

训练阶段采用教师强制(Teacher Forcing):不依赖模型自己生成的词,而是直接把真实的目标序列右移一位后送入解码器,让模型并行预测每一个位置的下一个 token。

核心流程就是标准的深度学习训练循环:

Transformer 训练 = 前向传播(编码器 → 解码器 → 输出概率)→ 计算 Loss(对比预测与真实标签)→ 反向传播(梯度从输出层传回每一层)→ 更新参数(所有 W W W 矩阵和偏置),循环往复直到 Loss 收敛。

训练与推理的关键差异:

对比项训练阶段推理阶段
解码器输入真实目标序列(右移一位)自己生成的前缀
是否并行可整段并行必须逐词自回归
掩码因果掩码防止偷看答案同样的因果掩码
效率高(一次前向算出整句概率)低(每生成一个词算一次)

在这里插入图片描述

训练不断经历“预测—计算损失—反向传播—更新参数”的循环。


第三章 总结

如果把 Transformer 看成一条信息加工流水线,它的每个模块都在回答一个明确的问题。

Transformer 整体可以沿着"输入 → 加工 → 输出"这条主线来理解:

  1. 输入表示:词嵌入解决"文字怎么变成向量"的问题(为什么不用 one-hot:维度爆炸 + 语义孤立);位置编码解决"顺序信息从哪来"的问题(低维捕捉近距离,高维捕捉远距离)。
  2. 信息加工:自注意力用 Q、K、V 完成"查询—匹配—取回"的全局信息收集;多头注意力让模型从多个子空间并行提取不同类型的依赖关系;前馈网络把收集到的信息做深层加工;残差连接和层归一化负责让深层网络"训得动、训得稳";掩码保证模型不看无效信息和未来信息。
  3. 生成输出:编码器把源序列编码成上下文表示,解码器结合上下文与已生成的前缀自回归地逐词输出,最后经线性层和 Softmax 得到概率分布。

几个值得反复体会的"一句话":

  • 多头注意力的本质是"学习如何把多条线索重新组合成最有用的信息";
  • 注意力负责收集信息,前馈网络负责加工信息;
  • Add = 全局依赖信息 + 原始词义信息,残差是梯度的"高速公路";
  • 推理过程 = 把训练时的真实标签换成自己上一步生成的词,一个一个往外蹦。

掌握这些核心组件的职责与数据流,再看 BERT、GPT 等后续模型时,就会发现它们都是在 Transformer 这个骨架上做的延伸和改造。


参考文献

  1. Vaswani, A., et al. (2017). Attention Is All You Need. NeurIPS.
  2. 相关 Transformer 算法原理与实战教程。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/m0_74821874/article/details/163956102

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--