Transformer 是一种基于自注意力机制(Self-Attention) 的深度学习模型,由 Google 团队在 2017 年的论文《Attention Is All You Need》中首次提出,它革命性的改变了自然语言处理(NLP)领域,并取代了传统的 RNN 和 CNN,成为 BERT、GPT 等现代预训练模型的核心架构。
其文章链接如下:https://arxiv.org/pdf/1706.03762
其核心思想及原理如下:
1. 核心思想
- 传统模型的缺陷:
- RNN 难以并行计算,且长距离依赖能力弱;
- CNN 的局部感受野限制了全局信息捕获。
- Transformer 的突破:
- 完全基于注意力机制,无需循环或卷积结构;
- 支持并行计算,高效处理长序列;
- 通过自注意力直接建模序列中任意两个位置的关联。
2. 整体架构
Transformer 由 编码器(Encoder) 和 解码器(Decoder) 堆叠组成,每个编码器和解码器包含多个相同的层。
编码器(Encoder)
- 输入嵌入层:将输入词转换为向量(Word Embedding)。
- 位置编码(Positional Encoding):为序列中的每个位置添加位置信息(如正弦/余弦函数)。
- 多头自注意力层:计算序列中每个词与其他词的关系权重。
- 前馈神经网络(FFN):对每个位置的向量进行非线性变换。
- 残差连接 & 层归一化:每个子层后使用残差连接和归一化,加速训练。
解码器(Decoder)
- 输入嵌入 & 位置编码:与编码器类似,但处理目标序列。
- 掩码多头自注意力层:防止解码时看到未来信息(通过掩码矩阵)。
- 编码器-解码器注意力层:将解码器的查询与编码器的键、值交互。
- 前馈神经网络 & 残差连接:同编码器。
3. 自注意力机制(Self-Attention)
自注意力是 Transformer 的核心,其计算分为四步:
步骤 1:生成 Q、K、V 矩阵
- 输入序列的每个词向量通过线性变换生成:
- 查询(Query):当前词需要关注哪些词?
- 键(Key):其他词如何响应查询?
- 值(Value):实际传递的信息。
步骤 2:计算注意力分数
- 通过点积计算每个查询与所有键的相似度:
- A t t e n t i o n S c o r e = Q ⋅ K T Attention Score = Q \cdot K^T AttentionScore=Q⋅KT
- 缩放分数(防止梯度消失):
- Scaled Score = Q ⋅ K T d k ( d k 为键的维度 ) \ \text{Scaled Score} = \frac{Q \cdot K^T}{\sqrt{d_k}} \quad (d_k \text{为键的维度}) \ Scaled Score=dkQ⋅KT(dk为键的维度)
为什么公式中需要除dk呢?
在Transformer的自注意力机制中,将查询(Q)和键(K)的点积结果除以根号下dk的主要原因是为了控制点积的数值范围,防止梯度消失,从而稳定模型训练。假设查询 Q 和键K的每个元素是独立随机变量,均值为0,方差为1, Q ⋅ K T \ Q \cdot K^T \ Q⋅KT 的每个元素是 d_k个独立乘积之和,当 d_k 较大时,点积结果的绝对值会显著增大,这会导致 Softmax 函数的输入过大,进入梯度饱和区(极端值区域),梯度趋近于0,模型难以更新参数除根号下dk缩放后的数值范围更稳定,Softmax 的输入分布在梯度敏感区域(非饱和区),梯度可以有效传播。
步骤 3:应用 Softmax
- 对每一行分数进行 Softmax,得到注意力权重(和为 1)。
步骤 4:加权求和
- 用注意力权重对 Value 加权求和,得到最终的注意力输出:
-
Attention
(
Q
,
K
,
V
)
=
Softmax
(
Q
K
T
d
k
)
V
\ \text{Attention}(Q, K, V) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V \
Attention(Q,K,V)=Softmax(dkQKT)V
具体示意图如下:
4. 多头注意力(Multi-Head Attention)
多头注意力(Multi-Head Attention)是 Transformer 模型的核心组件之一,通过并行计算多个独立的注意力头,使模型能够同时关注输入序列中的不同特征模式,从而增强模型的表达能力和泛化性能。以下是其详细原理与实现步骤:
- 单一注意力的局限性:
传统的自注意力机制可能仅捕捉到一种类型的关联(如局部依赖或语法结构),无法充分建模复杂的上下文关系。 - 多头注意力的优势:
- 允许模型在不同子空间中学习多样化的特征表示。
- 类似 CNN 中多通道卷积核的设计,提升模型的灵活性和鲁棒性。
实现步骤
多头注意力的计算流程可分为以下四步:
线性投影生成多组 Q、K、V
将输入的 Query(Q)、Key(K)、Value(V)通过 h 个独立的线性变换,拆分为多个头(Head):
Head
i
=
Attention
(
Q
W
i
Q
,
K
W
i
K
,
V
W
i
V
)
\ \text{Head}_i = \text{Attention}(Q W_i^Q, K W_i^K, V W_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 \
WiQ,WiK,WiV 是第 ( i ) 个头的可学习参数矩阵。
- 每个头的维度为 d k = d model / h \ d_k = d_{\text{model}} / h \ dk=dmodel/h ,其中 d model \ d_{\text{model}} \ dmodel 是模型总维度(如 512), h 是头数。
每个头独立计算自注意力
每个头执行标准的缩放点积注意力(Scaled Dot-Product Attention):
Attention
(
Q
,
K
,
V
)
=
Softmax
(
Q
K
T
d
k
)
V
\ \text{Attention}(Q, K, V) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V \
Attention(Q,K,V)=Softmax(dkQKT)V
- 每个头输出的维度为 d v = d model / h \ d_v = d_{\text{model}} / h \ dv=dmodel/h
拼接所有头的输出
将 h 个头的输出按特征维度拼接:
Concat
(
Head
1
,
Head
2
,
.
.
.
,
Head
h
)
∈
R
n
×
d
model
\ \text{Concat}(\text{Head}_1, \text{Head}_2, ..., \text{Head}_h) \in \mathbb{R}^{n \times d_{\text{model}}} \
Concat(Head1,Head2,...,Headh)∈Rn×dmodel
n 为序列长度,
d
model
=
h
×
d
v
\ d_{\text{model}} = h \times d_v \
dmodel=h×dv
线性变换合并结果
通过可学习矩阵 ( W^O ) 将拼接后的结果映射回原始维度:
MultiHead
(
Q
,
K
,
V
)
=
Concat
(
Head
1
,
.
.
.
,
Head
h
)
W
O
\ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{Head}_1, ..., \text{Head}_h) W^O \
MultiHead(Q,K,V)=Concat(Head1,...,Headh)WO
多头注意力的作用
功能 | 说明 |
---|---|
多样化特征学习 | 不同头关注不同模式(如局部依赖、全局语义、语法结构等)。 |
并行计算效率 | 多个头可并行计算,充分利用 GPU 的并行能力,提升训练速度。 |
抗过拟合能力 | 通过多组参数学习,降低对单一注意力模式的依赖,增强泛化性。 |
解耦复杂关系 | 将复合关系分解为多个子问题(如“苹果”指水果或公司,不同头分别处理)。 |
以 机器翻译任务 为例:
- Head 1:关注词语的位置关系(如动词与主语的远距离依赖)。
- Head 2:捕捉词性搭配(如形容词修饰名词)。
- Head 3:识别语义关联(如“银行”与“钱”或“河流”的不同上下文)。
- Head 4:处理指代消解(如代词“它”指向的实体)。
代码实现示例(PyTorch)
import torch
import torch.nn as nn
class MultiHeadAttention(nn.Module):
def __init__(self, d_model=512, h=8):
super().__init__()
self.d_model = d_model
self.h = h
self.d_k = d_model // h
# 定义线性投影矩阵
self.W_Q = nn.Linear(d_model, d_model)
self.W_K = nn.Linear(d_model, d_model)
self.W_V = nn.Linear(d_model, d_model)
self.W_O = nn.Linear(d_model, d_model)
def forward(self, Q, K, V, mask=None):
batch_size = Q.size(0)
# 线性投影并分头 [batch_size, seq_len, h, d_k]
Q = self.W_Q(Q).view(batch_size, -1, self.h, self.d_k).transpose(1, 2)
K = self.W_K(K).view(batch_size, -1, self.h, self.d_k).transpose(1, 2)
V = self.W_V(V).view(batch_size, -1, self.h, self.d_k).transpose(1, 2)
# 计算缩放点积注意力
scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.d_k))
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attn_weights = torch.softmax(scores, dim=-1)
context = torch.matmul(attn_weights, V)
# 拼接多头结果并输出
context = context.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)
output = self.W_O(context)
return output
5. 位置编码(Positional Encoding)
- Transformer 模型通过自注意力机制捕捉序列中元素的全局依赖关系,但其本身不具备处理序列顺序的能力(因为自注意力是位置无关的),位置编码(Positional Encoding) 的作用是为模型注入序列的位置信息,使其能够区分不同位置的词。以下是其核心原理与实现方法:
- 编码方式:位置编码需满足以下条件:
- 唯一性:每个位置有唯一的编码。
- 相对位置感知:编码能反映位置间的相对距离(如位置 5 和 6 的编码差异应与位置 10 和 11 的差异相似)。
- 泛化性:能处理比训练时更长的序列(如训练时最大长度 512,推理时处理 1000 长度的文本)。
- P E ( p o s , 2 i ) = sin ( p o s 1000 0 2 i / d ) P E ( p o s , 2 i + 1 ) = cos ( p o s 1000 0 2 i / d ) \ PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d}}\right) \ \ PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right) \ PE(pos,2i)=sin(100002i/dpos) PE(pos,2i+1)=cos(100002i/dpos)
pos
为位置,i
为维度索引。
** 其他位置编码方法**
方法 | 原理 | 优点 | 缺点 |
---|---|---|---|
可学习位置编码 | 随机初始化位置嵌入向量并通过训练更新 | 灵活性高,适应任务特性 | 无法泛化到超出训练长度的序列 |
相对位置编码 | 直接建模词对之间的相对距离(如 Transformer-XL) | 更符合语言特性 | 计算复杂度较高 |
旋转位置编码(RoPE) | 通过复数空间旋转操作融合位置信息(如 LLaMA 模型) | 理论优雅,支持任意长度外推 | 实现复杂 |
位置编码的代码实现(PyTorch)
import torch
import torch.nn as nn
class PositionalEncoding(nn.Module):
def __init__(self, d_model: int, max_len: int = 5000):
super().__init__()
position = torch.arange(max_len).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
pe = torch.zeros(max_len, d_model)
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
self.register_buffer('pe', pe) # 不参与训练
def forward(self, x: torch.Tensor) -> torch.Tensor:
# x: [batch_size, seq_len, d_model]
x = x + self.pe[:x.size(1)]
return x
6. 前馈神经网络(Feed-Forward Network)
- 每个位置的向量独立通过两层全连接网络:
- FFN ( x ) = ReLU ( W 1 x + b 1 ) W 2 + b 2 \ \text{FFN}(x) = \text{ReLU}(W_1x + b_1)W_2 + b_2 \ FFN(x)=ReLU(W1x+b1)W2+b2
- 作用:增强模型的非线性表达能力。
7. 残差连接与层归一化
- 残差连接:将输入直接加到子层输出上 ( x + Sublayer ( x ) ) (x + \text{Sublayer}(x)) (x+Sublayer(x)),缓解梯度消失。
- 层归一化:对每个位置的向量进行归一化,加速训练。
8. 解码器的掩码自注意力
- 掩码机制:在解码时,通过掩码矩阵(上三角为负无穷)阻止模型看到未来信息,确保自回归生成。
在计算过程中,t时刻应该只能看到0到t-1时刻的数据,但是在实际训练过程中,网络能够看到全局信息,所以增加了MASK,本质上是使网络在计算时可以使用全局信息,但是在输出时不要输出t时刻之后的值,将t时刻及其以后的值替换为一个非常大的负数,经过softmax后,该部分将变为0,不会被输出计算进去。
9. 模型输出
- 解码器最终输出通过线性层和 Softmax,生成目标序列的概率分布。
10. 为什么 Transformer 比 RNN/CNN 更好?
特性 | RNN/CNN | Transformer |
---|---|---|
长距离依赖 | 依赖短,易遗忘远距离信息 | 直接建模任意位置关系 |
并行计算 | 序列顺序处理,难以并行 | 所有位置同时计算 |
计算复杂度 | RNN:O(n),CNN:O(n log n) | 自注意力:O(n²) |
实际训练速度 | 慢(受限于序列长度) | 快(充分利用 GPU 并行) |
11. 应用与变体
- 经典模型:
- BERT:仅用编码器的双向预训练模型。
- GPT:仅用解码器的自回归生成模型。
- T5:编码器-解码器结构的通用文本生成模型。
- 优化方向:
- 稀疏注意力(如 Sparse Transformer):降低计算复杂度。
- 位置编码改进(如 Rotary Position Embedding):更灵活的位置表示。
总结
Transformer 通过自注意力机制实现了全局信息的高效捕获,解决了传统模型的序列处理瓶颈。其并行计算能力和灵活性使其成为 NLP 领域的基石,并逐步扩展到计算机视觉(ViT)、语音处理等领域。理解其原理是掌握现代深度学习模型(如 ChatGPT)的关键基础。
感谢您阅读到最后!😊总结不易,希望多多支持~🌹 点赞👍收藏⭐评论✍️,您的三连是我持续更新的动力💖~