Transformer 通俗详解:从注意力机制到 BERT 与 GPT
- Positional Encoding
- Encoder
- Decoder
- Masked Attention
- Cross-Attention
- Feed Forward Network
- Add & Norm
如果一开始就直接阅读论文结构图或代码,很容易陷入局部细节,却看不清这些模块为什么会被组合在一起。
本文将从 Transformer 出现的原因讲起,逐步解释注意力机制、自注意力、编码器、解码器,以及 Encoder-Decoder、Encoder-only 和 Decoder-only 三种常见架构。
为什么 RNN 之后还需要 Transformer
在 Transformer 出现之前,RNN、GRU 和 LSTM 是处理文本、语音和时间序列的主流模型。
假设输入句子是:
我正在学习深度学习。
RNN 会按照顺序依次处理每个 token:
我
正在
学习
深度
学习
每处理一个 token,RNN 都会更新一次隐藏状态:
Ht=f(Xt,Ht−1)��=�(��,��−1)
其中:
- Xt�� 是当前时间步的输入;
- Ht−1��−1 是上一时间步的隐藏状态;
- Ht�� 是当前时间步的隐藏状态。
隐藏状态可以理解为模型对前文的记忆。
这种结构符合人类按顺序阅读的直觉,但也带来了两个明显问题。
RNN 难以充分并行
计算 Ht�� 之前,必须先得到 Ht−1��−1。
因此,第 10 个 token 必须等前 9 个 token 计算完成后才能处理。即使 GPU 非常擅长并行计算,RNN 仍然需要沿着时间轴逐步执行。
序列越长,等待的时间越多。
长距离信息需要层层传递
假设一句话中的第 100 个词需要参考第 1 个词。
在 RNN 中,第 1 个词的信息必须经过许多次隐藏状态更新,才能传递到第 100 个词。
随着传递距离增加,早期信息可能逐渐减弱。LSTM 和 GRU 通过门控机制缓解了这个问题,但没有改变“按时间步逐个计算”的基本方式。
Transformer 提出了一种不同的思路:
不再让信息沿序列一步一步传递,而是让序列中的任意两个 token 直接建立联系。
这个目标主要通过注意力机制实现。
Transformer 想解决什么问题
Transformer 最初用于机器翻译。
例如,把英文句子:
I love you.
翻译成中文:
我爱你。
这是一个典型的序列到序列任务:
英文源序列
模型
中文目标序列
输入和输出都是序列,而且长度不一定相同。
Transformer 保留了编码器—解码器的整体思想:
- 编码器负责理解输入序列;
- 解码器负责生成输出序列。
真正的变化在于:
Transformer 不再使用 RNN 逐步传递信息,而是使用注意力机制直接建立序列内部的关系。
注意力机制在做什么
注意力机制解决的问题非常直观:
面对大量信息,当前应该重点关注哪些部分?
假设要回答一个问题:
Transformer 为什么比 RNN 更容易并行?
面前有以下几条资料:
- Transformer 使用自注意力处理序列;
- 今天的天气很好;
- RNN 必须按照时间步逐步计算;
- 苹果是一种水果。
显然,第 1 条和第 3 条更重要。
注意力机制不会平均对待所有信息,而是根据当前任务,为不同信息分配不同权重。
最终输出可以表示为:
Output=∑iαiviOutput=∑�����
其中:
- vi�� 表示第 i� 条信息;
- αi�� 表示这条信息的注意力权重。
权重越大,说明当前更需要关注这条信息。
因此,注意力机制本质上是一种动态加权汇聚:
模型根据当前需求,动态决定每条信息应该贡献多少。
Query、Key 和 Value 是什么
注意力机制通常使用三个核心概念:
| 名称 | 缩写 | 直观含义 |
|---|---|---|
| Query | Q | 我现在想找什么 |
| Key | K | 每条信息用来匹配的特征 |
| Value | V | 真正需要取出的内容 |
可以用图书馆查书来类比。
假设你想找一本介绍 Transformer 的书:
- Query:你想查找“Transformer”;
- Key:每本书的标题、关键词和分类标签;
- Value:每本书实际包含的内容。
模型首先使用 Query 和每个 Key 比较,计算匹配程度。
匹配程度越高,对应 Value 的权重越大。
Query
当前想找什么
与每个 Key
计算匹配分数
Key 1
Key 2
Key 3
Softmax
得到注意力权重
对 Value 加权汇聚
Value 1
Value 2
Value 3
Key 和 Value 经常来自同一个对象,但承担的角色不同:
- Key 用来判断这条信息是否相关;
- Value 是确定相关后真正取出的内容。
缩放点积注意力如何计算
Transformer 使用缩放点积注意力:
Attention(Q,K,V)=softmax(QKT√dk)VAttention(�,�,�)=softmax(�����)�
这个公式可以拆成四个步骤。
第一步:计算 Query 和 Key 的匹配分数
S=QKT�=���
假设序列中有多个 Query 和多个 Key,那么 S� 是一个分数矩阵。
矩阵中的第 i� 行、第 j� 列表示:
第 i� 个 Query 和第 j� 个 Key 的匹配程度。
点积越大,通常表示二者越匹配。
第二步:缩放分数
S′=QKT√dk�′=�����
dk�� 是 Key 向量的维度。
当向量维度很高时,点积结果可能变得很大。如果直接送入 softmax,输出可能过于极端,例如:
[0.9999, 0.0001, 0.0000][0.9999, 0.0001, 0.0000]
这样会使训练不够稳定。
除以 √dk��,可以把分数缩放到更合适的范围。
第三步:使用 softmax 得到权重
A=softmax(QKT√dk)�=softmax(�����)
例如,原始分数为:
[3.0, 1.0, 0.2][3.0, 1.0, 0.2]
经过 softmax 后可能变成:
[0.84, 0.11, 0.05][0.84, 0.11, 0.05]
这些权重都大于 0,并且总和等于 1。
第四步:使用权重汇聚 Value
O=AV�=��
也就是:
O=softmax(QKT√dk)V�=softmax(�����)�
权重越大的 Value,对最终结果贡献越大。
因此,整个注意力公式可以理解为:
先判断应该关注谁,再把被关注的信息汇聚起来。
Q、K、V 是怎样产生的
在 Transformer 中,Q、K、V 通常不是原始输入,而是由输入向量经过三个不同的线性变换得到:
Q=XWQ�=���
K=XWK�=���
V=XWV�=���
其中:
- X� 是输入序列的向量表示;
- WQ��、WK�� 和 WV�� 是通过训练学习得到的权重矩阵。
虽然 Q、K、V 可能来自同一个输入 X�,但它们经过不同变换,因此承担不同角色:
- WQ�� 学习如何表达“我想寻找什么”;
- WK�� 学习如何表达“我可以怎样被匹配”;
- WV�� 学习如何表达“我真正携带什么信息”。
什么是自注意力
自注意力,也叫 Self-Attention,是 Transformer 的核心组件。
它的特点是:
Query、Key 和 Value 都来自同一个序列。
假设输入句子是:
小明把书放在桌上,因为他马上还要看。
在处理“他”这个 token 时,自注意力可以让它直接关注“小明”,从而学习到两者之间的指代关系。
在自注意力中,输入序列 X� 同时生成 Q、K 和 V:
Q=XWQ�=���
K=XWK�=���
V=XWV�=���
然后计算:
SelfAttention(X)=softmax(QKT√dk)VSelfAttention(�)=softmax(�����)�
序列中的每个 token 都可以和其他 token 直接计算关系。
小明
把书
放在桌上
他
这与 RNN 有明显区别。
RNN 中,远距离信息需要经过多个时间步传递;自注意力中,两个位置可以通过一次注意力计算直接交互。
自注意力为什么适合并行计算
在 RNN 中,第 t� 个时间步依赖第 t−1�−1 个时间步,因此不能同时计算所有位置。
而在自注意力中,整个序列的 Q、K、V 可以同时计算。
所有 token 之间的关系也可以通过一次矩阵乘法得到:
QKT���
矩阵乘法非常适合 GPU 并行计算。
因此,在训练阶段,Transformer 通常比 RNN 更容易利用现代计算硬件。
不过需要注意:
Transformer 在训练时可以并行处理多个位置,但自回归模型在推理时仍然需要逐 token 生成。
为什么需要多头注意力
单个注意力头只能在一个表示空间中计算关系。
但语言中的关系是多种多样的。
一句话中可能同时存在:
- 主语和谓语的关系;
- 代词和指代对象的关系;
- 局部词语搭配;
- 长距离语义依赖;
- 时间和地点关系;
- 句法结构。
多头注意力的思想是:
让模型同时从多个不同角度观察同一个序列。
每个头都有自己独立的投影矩阵:
Qi=QWQi��=����
Ki=KWKi��=����
Vi=VWVi��=����
第 i� 个注意力头的输出为:
headi=Attention(Qi,Ki,Vi)head�=Attention(��,��,��)
然后将所有头的结果拼接起来:
MultiHead(Q,K,V)=Concat(head1,…,headh)WOMultiHead(�,�,�)=Concat(head1,…,headℎ)��
输入 Q、K、V
注意力头 1
注意力头 2
注意力头 3
注意力头 4
拼接 Concat
线性变换
可以粗略理解为:
- 一个头可能更关注语法关系;
- 一个头可能更关注语义关系;
- 一个头可能更关注局部关系;
- 一个头可能更关注长距离依赖。
这些关注方式并不是人为规定的,而是模型在训练过程中自行学习出来的。
需要特别说明:
Self-Attention 和 Multi-Head Attention 不是同一个维度的概念。
Self-Attention 描述的是 Q、K、V 是否来自同一个序列;Multi-Head 描述的是注意力是否在多个子空间中并行计算。
因此,可以有:
- 单头 Self-Attention;
- 多头 Self-Attention;
- 单头 Cross-Attention;
- 多头 Cross-Attention。
为什么 Transformer 需要位置编码
自注意力虽然能处理 token 之间的关系,但它本身并不知道 token 的顺序。
例如:
我爱你。
和:
你爱我。
包含完全相同的三个词,但语序不同,含义也不同。
如果只把 token embedding 输入自注意力,模型更像是在处理一个无序集合。
因此,Transformer 需要额外加入位置信息。
输入向量通常写成:
Xt=E(xt)+Pt��=�(��)+��
其中:
- E(xt)�(��) 是第 t� 个 token 的 embedding;
- Pt�� 是第 t� 个位置的位置编码。
可以理解为:
- Token Embedding:这个 token 是什么;
- Position Encoding:这个 token 在哪里。
相加后得到的是:
位于这个位置上的这个 token。
正弦余弦位置编码
原始 Transformer 使用正弦余弦位置编码:
PE(pos,2i)=sin(pos100002i/d)��(���,2�)=sin(���100002�/�)
PE(pos,2i+1)=cos(pos100002i/d)��(���,2�+1)=cos(���100002�/�)
其中:
- pos��� 表示 token 在序列中的位置;
- d� 表示 embedding 总维度;
- i� 表示维度对编号;
- 偶数维使用 sinsin;
- 奇数维使用 coscos。
不同维度使用不同频率的正弦和余弦函数。
低频维度变化较慢,高频维度变化较快,因此每个位置都能得到一组独特而有规律的位置表示。
位置编码并不一定只能使用正弦余弦。现代模型还会使用:
- 可学习位置编码;
- 相对位置编码;
- 旋转位置编码;
- 其他位置表示方法。
它们解决的核心问题相同:
告诉模型 token 的位置和顺序。
先理解完整的 Encoder-Decoder Transformer
原始 Transformer 采用 Encoder-Decoder 架构,最初用于机器翻译。
假设任务是英译法:
源句子:I love you.
目标句子:Je t'aime.
整体结构如下:
源语言序列
Embedding
加位置编码
Encoder
Encoder 输出
目标语言序列
Embedding
加位置编码
Decoder
Linear
Softmax
目标 token 概率
编码器负责理解源序列,解码器负责生成目标序列。
之所以先讲完整的 Encoder-Decoder,是因为它包含了 Transformer 中最核心的三种注意力形式:
- Encoder Self-Attention;
- Decoder Masked Self-Attention;
- Encoder-Decoder Cross-Attention。
理解了这套完整结构,再看 Encoder-only 和 Decoder-only 就会容易很多。
Encoder 的作用是什么
Encoder 的任务是:
把输入序列中的每个 token 转换成融合了上下文的信息表示。
例如,源句子是:
I love you.
输入 Encoder 之前,每个 token 主要只有自身的词义和位置信息。
经过 Encoder 后,love 的向量不再只是表示单词 love,而是表示:
在“I love you”这个句子上下文中的
love。
Encoder 通常由多个相同的 Encoder Block 堆叠而成。
输入
多头自注意力
Add & Norm
前馈网络 FFN
Add & Norm
输出
每个 Encoder Block 主要包含:
- Multi-Head Self-Attention;
- Feed Forward Network;
- 残差连接;
- Layer Normalization。
Encoder 中的自注意力
Encoder 使用普通的多头自注意力。
它的 Q、K、V 都来自 Encoder 当前层的输入:
Q=XWQ,K=XWK,V=XWV�=���,�=���,�=���
因为完整源句子已经给出,所以 Encoder 中的每个位置都可以关注源序列中的所有位置。
例如源句子是:
I love you.
那么:
I可以关注I、love、you;love可以关注I、love、you;you也可以关注完整句子。
Encoder 不需要因果 mask,因为它不是在逐步生成源序列,而是在理解已经完整给出的输入。
不过,如果一个 batch 中使用了 <pad> 补齐序列,通常会使用 padding mask,避免模型关注补齐位置。
因此,“Encoder 不使用 mask”更准确的说法是:
Encoder 不使用防止看见未来的因果 mask,但可能使用 padding mask。
Encoder 中的 FFN
自注意力完成的是不同 token 之间的信息交换。
交换完成后,每个 token 的向量还需要进一步进行非线性加工,这一步由 Position-wise Feed Forward Network 完成。
常见形式为:
FFN(x)=σ(xW1+b1)W2+b2FFN(�)=�(��1+�1)�2+�2
其中,σ� 可以是 ReLU、GELU 等激活函数。
FFN 的特点是:
对序列中的每个位置,使用同一套前馈网络分别处理。
例如:
Token 1 向量
同一个 FFN
Token 2 向量
同一个 FFN
Token 3 向量
同一个 FFN
这里画成三个 FFN 是为了表示三个位置分别计算,但它们共享同一套权重。
可以这样记:
- Attention:负责 token 与 token 之间的信息交流;
- FFN:负责加工每个 token 自己的特征。
残差连接和 LayerNorm
Transformer 的每个主要子层外部都带有残差连接。
残差连接可以表示为:
Y=X+Sublayer(X)�=�+Sublayer(�)
其中:
- X� 是子层原始输入;
- Sublayer(X)Sublayer(�) 是注意力层或 FFN 的输出。
它的含义是:
保留原始信息,再加上子层学习到的新信息。
这样做有助于梯度传播,也让深层 Transformer 更容易训练。
Layer Normalization 则用于稳定每个 token 向量的数值分布。
可以粗略地理解为:
把每个 token 的特征调整到更适合后续网络处理的数值范围。
原始 Transformer 使用的是 Post-Norm 形式:
LayerNorm(X+Sublayer(X))LayerNorm(�+Sublayer(�))
现代模型中也常见 Pre-Norm 形式:
X+Sublayer(LayerNorm(X))�+Sublayer(LayerNorm(�))
二者顺序有所不同,但目的都是改善训练稳定性。
多层 Encoder 在做什么
一个 Encoder Block 只能完成一次信息交流和特征加工。
因此,Transformer 会堆叠多个 Encoder Block。
Embedding + 位置编码
Encoder Block 1
Encoder Block 2
Encoder Block 3
Encoder 最终输出
随着层数增加,token 表示会逐渐发生变化。
可以粗略理解为:
- 较低层学习局部关系;
- 中间层组合句法和语义信息;
- 较高层形成更抽象的上下文表示。
这不是严格的固定分工,但有助于理解深层网络为什么需要堆叠多层。
Decoder 的作用是什么
Decoder 的任务是:
根据已经生成的目标端内容,并结合 Encoder 提供的源序列信息,预测下一个目标 token。
Decoder 也由多个 Decoder Block 堆叠而成。
目标序列输入
Masked 多头自注意力
Add & Norm
Encoder-Decoder Attention
Encoder 输出
Add & Norm
前馈网络 FFN
Add & Norm
输出
每个 Decoder Block 主要包含:
- Masked Multi-Head Self-Attention;
- Encoder-Decoder Attention;
- Feed Forward Network;
- 残差连接与 Layer Normalization。
Decoder 中的两种注意力承担不同作用。
Decoder 中的 Masked Self-Attention
Decoder 的第一种注意力是 Masked Self-Attention,也叫因果自注意力。
它的 Q、K、V 都来自 Decoder 自身:
Q=XdecWQ�=�dec��
K=XdecWK�=�dec��
V=XdecWV�=�dec��
但当前位置不能看到未来位置。
假设 Decoder 输入为:
<bos> Je t'aime .
当处理 Je 所在的位置时,它只能看到:
<bos> Je
不能看到后面的:
t'aime .
否则模型在训练时就会提前知道答案。
这种限制通过因果 mask 实现:
Mij={0,j≤i −∞,j>i���={0,�≤� −∞,�>�
将 mask 加到注意力分数上:
softmax(QKT√dk+M)softmax(�����+�)
未来位置对应的分数会变成 −∞−∞,经过 softmax 后,其注意力权重变成 0。
因此,Masked Self-Attention 的作用是:
让 Decoder 只能根据当前位置以及之前的目标 token 进行计算,不能偷看未来。
Decoder 中的 Encoder-Decoder Attention
Decoder 的第二种注意力叫 Encoder-Decoder Attention,也称为 Cross-Attention。
在这一层中:
- Query 来自 Decoder;
- Key 和 Value 来自 Encoder 输出。
即:
Q=HdecWQ�=�dec��
K=HencWK�=�enc��
V=HencWV�=�enc��
它的直观含义是:
Decoder 拿着当前的生成需求,去查询 Encoder 中的源句子信息。
例如生成法语单词 t'aime 时,Decoder 可能会重点关注英文源句子中的 love 和 you。
Decoder 当前表示
作为 Query
Cross-Attention
Encoder 输出
作为 Key 和 Value
与当前生成相关的源句信息
Decoder 中的两次“看”可以概括为:
- Masked Self-Attention:看目标端已经出现了什么;
- Cross-Attention:看源句子中应该关注哪里。
目标序列为什么要右移一位
训练机器翻译模型时,我们已经知道正确答案。
假设正确目标序列是:
Je t'aime . <eos>
Decoder 的输入是:
<bos> Je t'aime .
训练标签是:
Je t'aime . <eos>
对应关系如下:
| Decoder 当前位置的输入 | 模型需要预测 |
|---|---|
<bos> |
Je |
Je |
t'aime |
t'aime |
. |
. |
<eos> |
所谓“右移一位”,就是:
在目标序列开头加入
<bos>,并让每个位置的输出去预测下一个 token。
这样,模型在每个位置上都会学习“根据已有前文预测后面的 token”。
训练时为什么可以并行处理 Decoder
看到 Decoder 逐 token 生成,很多初学者会认为训练时也必须一步一步执行。
实际上,训练时完整目标序列已经存在,因此可以把右移后的目标序列一次性输入 Decoder:
<bos> Je t'aime .
然后同时计算所有位置的输出。
Masked Self-Attention 会保证:
- 第一个位置只能看第一个位置;
- 第二个位置只能看前两个位置;
- 第三个位置只能看前三个位置;
- 以此类推。
因此:
- 训练阶段可以并行计算多个位置;
- 推理阶段因为未来 token 尚不存在,只能逐 token 生成。
推理阶段如何生成序列
推理时没有正确目标句子,因此 Decoder 只能从 <bos> 开始。
第一步:
输入:
<bos>
预测:Je
第二步:
输入:
<bos> Je
预测:t'aime
第三步:
输入:
<bos> Je t'aime
预测:.
第四步:
输入:
<bos> Je t'aime .
预测:<eos>
当生成 <eos>,或者达到最大长度时,生成过程结束。
Je
t'aime
.
实际系统中,模型不会总是简单选择概率最大的 token,还可能使用:
- 贪心搜索;
- 束搜索;
- Top-k 采样;
- Top-p 采样;
- 温度调节。
这些方法决定了模型从概率分布中怎样选择下一个 token。
Encoder-Decoder Transformer 的完整流程
以英译法为例:
源句子:I love you.
目标句子:Je t'aime.
整个流程如下:
源序列 Token
源 Embedding + 位置编码
Encoder Block 1
Encoder Block 2
Encoder 最终输出
右移后的目标序列
目标 Embedding + 位置编码
Decoder Block 1
Decoder Block 2
线性输出层
Softmax
下一个目标 token 的概率
Encoder 负责理解完整源句子。
Decoder 先通过 Masked Self-Attention 查看目标端前文,再通过 Cross-Attention 查询 Encoder 输出,最后预测下一个目标 token。
这就是标准 Encoder-Decoder Transformer 的核心流程。
从完整架构理解 Encoder-only
理解完整的 Encoder-Decoder Transformer 后,Encoder-only 就比较容易了。
Encoder-only 模型只保留 Transformer 的 Encoder 部分。
输入序列
多层 Transformer Encoder
上下文表示
分类、抽取或理解任务
它使用普通的双向 Self-Attention。
也就是说,一个 token 可以同时看到它前面和后面的所有 token。
例如句子:
我今天去了公园。
处理“去了”时,模型既可以看到前面的“我今天”,也可以看到后面的“公园”。
这种结构非常适合需要完整上下文理解的任务,例如:
- 文本分类;
- 情感分析;
- 命名实体识别;
- 语义匹配;
- 信息抽取;
- 文本向量表示;
- 检索和排序。
BERT 是典型的 Encoder-only 模型。
BERT 为什么适合文本理解
BERT 的核心预训练任务之一是掩码语言模型。
例如:
我今天去了
[MASK]。
模型需要根据左右两侧上下文预测被遮挡的词。
它既能利用左边的“我今天去了”,也能利用右边可能出现的其他内容。
因此,BERT 学到的是双向上下文表示。
需要注意:
Encoder-only 并不是绝对不能用于生成,只是它本身最擅长的是编码和理解完整输入。
如果要让它进行生成,通常还需要额外设计输出结构。
从完整架构理解 Decoder-only
Decoder-only 模型只保留 Transformer Decoder 中的自回归生成部分。
它没有独立的 Encoder,因此也没有 Cross-Attention。
每个 Block 通常主要包含:
- Masked Multi-Head Self-Attention;
- FFN;
- 残差连接;
- LayerNorm。
Prompt 和已有 token
多层 Decoder-only Transformer
预测下一个 token
Decoder-only 会把任务说明、输入内容和已经生成的内容放在同一个序列中。
例如做翻译时,可以输入:
请把下面的英文翻译成中文:
I love you.
中文:
这些内容都作为前文 token。
模型使用 Masked Self-Attention 读取前文,然后继续生成:
我爱你。
它不需要单独的 Encoder,也不需要 Cross-Attention。
GPT 类模型属于 Decoder-only 架构。
GPT 为什么只靠预测下一个 token 就能完成很多任务
GPT 的基本训练目标是:
P(xt∣x1,x2,…,xt−1)�(��∣�1,�2,…,��−1)
也就是根据前面的 token,预测下一个 token。
表面上看,这只是文本续写任务。
但许多任务都可以转换成文本续写。
翻译
把下面的英文翻译成中文:
I love machine learning.
中文:
模型继续生成:
我喜欢机器学习。
问答
问题:Transformer 为什么需要位置编码?
回答:
模型继续生成答案。
分类
文本:这部电影非常精彩。
情感类别:
模型继续生成:
正面。
摘要
请总结下面这段文章:
……
摘要:
模型继续生成摘要。
这些任务表面上不同,但都可以统一成:
给定前文,生成后续 token。
这也是 Decoder-only 架构非常适合构建通用大语言模型的重要原因。
Encoder-only、Decoder-only 和 Encoder-Decoder 的关系
这三种架构不是三个毫无关联的模型。
它们都建立在 Transformer Block 和注意力机制之上,只是保留的模块不同。
Transformer 架构思想
Encoder-Decoder
Encoder-only
Decoder-only
理解输入并生成输出
重点理解完整输入
重点进行自回归生成
可以这样理解:
更多推荐


所有评论(0)