Transformer 入门教程:原理、架构与实战详解
1. Transformer 概述
Transformer 是一种基于自注意力机制(Self-Attention)的序列到序列(sequence-to-sequence)模型。它彻底改变了自然语言处理(NLP)领域的设计范式,显著提升了模型性能。相较于传统的循环神经网络(RNN)或卷积神经网络(CNN),Transformer 摒弃了顺序处理的限制,能够并行计算,极大地加速了训练和推理速度。
Transformer 广泛应用于机器翻译、文本摘要、问答系统、文本生成、情感分析等各种 NLP 任务,也是当前大语言模型(LLM)如 BERT、GPT 系列的基础架构。
2. 核心机制:自注意力机制
2.1 自注意力原理
自注意力机制允许模型在编码输入序列时,捕捉任意位置之间的依赖关系。每个位置的输出不仅考虑自身的输入,还关注整个序列中所有位置的信息。
计算公式如下: $$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$
其中:
- $Q$ (Query):查询向量
- $K$ (Key):键向量
- $V$ (Value):值向量
- $d_k$:键向量的维度,用于缩放以防止点积过大导致梯度消失
2.2 多头注意力(Multi-Head Attention)
Transformer 使用多头注意力进一步增强了模型的表达能力。通过并行运行多个注意力头,模型可以从不同的子空间或角度捕获序列的多种依赖关系,最后将结果拼接并线性变换。
3. 架构设计:Encoder-Decoder
Transformer 通常由一个 Encoder(编码器)和一个 Decoder(解码器)组成。
3.1 Encoder 部分
Encoder 负责将输入序列编码为固定长度的向量表示。标准的 Transformer Encoder 堆叠了多个相同的层,每层包含两个子层:
- 多头自注意力机制
- 前馈神经网络(Feed-Forward Network)
每个子层周围都有残差连接(Residual Connection)和层归一化(Layer Normalization)。
3.2 Decoder 部分
Decoder 根据 Encoder 的输出向量和自身生成的部分序列逐步解码出目标输出。Decoder 堆叠了多个相同的层,每层包含三个子层:
- 掩码多头自注意力机制:防止当前位置看到未来的信息
- 多头注意力机制:关注 Encoder 的输出
- 前馈神经网络
同样配合残差连接和层归一化。
4. 关键组件详解
4.1 位置编码(Positional Encoding)
由于自注意力机制本身不具备位置信息(即对输入序列的顺序不敏感),Transformer 通过添加位置编码来引入序列中元素的位置信息。常用的方法包括正弦余弦函数编码或可学习的位置嵌入。
4.2 残差连接与层归一化
Transformer 采用了残差连接和层归一化技术。残差连接有助于缓解深度网络中的梯度消失或爆炸问题,提高模型训练稳定性;层归一化则加速收敛并稳定训练过程。
4.3 并行计算优势
由于自注意力机制无需按顺序处理输入序列,Transformer 可以天然地进行并行计算,极大地加速了模型训练和推理速度,这是其优于 RNN 的关键特性之一。
5. 代码实现示例
以下是一个简化的 PyTorch 多头注意力模块实现示例,展示核心逻辑:
import torch
import torch.nn nn
math
(nn.Module):
():
(MultiHeadAttention, ).__init__()
d_model % num_heads ==
.d_k = d_model // num_heads
.num_heads = num_heads
.w_q = nn.Linear(d_model, d_model)
.w_k = nn.Linear(d_model, d_model)
.w_v = nn.Linear(d_model, d_model)
.fc = nn.Linear(d_model, d_model)
():
batch_size = q.size()
q = .w_q(q).view(batch_size, -, .num_heads, .d_k).transpose(, )
k = .w_k(k).view(batch_size, -, .num_heads, .d_k).transpose(, )
v = .w_v(v).view(batch_size, -, .num_heads, .d_k).transpose(, )
scores = torch.matmul(q, k.transpose(-, -)) / math.sqrt(.d_k)
mask :
scores = scores.masked_fill(mask == , -)
attention = torch.softmax(scores, dim=-)
out = torch.matmul(attention, v)
out = out.transpose(, ).contiguous().view(batch_size, -, .num_heads * .d_k)
.fc(out)


