1. Transformer的诞生背景与技术突破
在2017年之前,自然语言处理领域长期被RNN(循环神经网络)及其变种LSTM、GRU所统治。这些序列模型采用"时间步递进"的处理方式,虽然能够处理变长序列,但存在两个根本性缺陷:
1.1 RNN/LSTM的先天不足
python复制# 典型RNN的前向传播实现
class VanillaRNN(nn.Module):
def __init__(self, input_size, hidden_size):
super().__init__()
self.Wxh = nn.Parameter(torch.randn(hidden_size, input_size))
self.Whh = nn.Parameter(torch.randn(hidden_size, hidden_size))
self.bias = nn.Parameter(torch.zeros(hidden_size))
def forward(self, x):
# x shape: (seq_len, batch, input_size)
h = torch.zeros(x.size(1), self.hidden_size)
outputs = []
for t in range(x.size(0)):
h = torch.tanh(x[t] @ self.Wxh.t() + h @ self.Whh.t() + self.bias)
outputs.append(h)
return torch.stack(outputs)
这种串行计算模式导致:
- 计算效率低下:必须等待前一个时间步完成才能计算当前步,无法利用现代GPU的并行计算能力
- 长程依赖衰减:信息通过隐藏状态h逐层传递,经过多个时间步后,早期信息会严重衰减(即使LSTM引入细胞状态c也难以完全解决)
1.2 Transformer的革命性创新
2017年Google在《Attention Is All You Need》论文中提出的Transformer架构,通过三大创新彻底改变了游戏规则:
- 自注意力机制:每个词元(token)可以直接关注序列中任何位置的词元,建立全局依赖关系
- 位置编码:通过正弦位置编码注入序列顺序信息,替代传统的递归结构
- 多头注意力:从多个子空间并行学习不同层面的特征关系
python复制# Transformer与前代模型的对比实验数据(WMT2014英德翻译任务)
+-------------------+---------+----------+---------------+
| Model | BLEU | Params | Training Time |
+-------------------+---------+----------+---------------+
| LSTM | 23.3 | 65M | 7 days |
| LSTM+Attention | 26.1 | 79M | 9 days |
| Transformer | 28.4 | 65M | 3.5 days |
+-------------------+---------+----------+---------------+
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Transformer核心架构解析
2.1 整体框架设计
Transformer采用经典的编码器-解码器结构,但通过堆叠相同的层实现模块化设计:
code复制输入序列 → [编码器 × N] → [解码器 × N] → 输出序列
2.1.1 编码器组件
- 输入嵌入层(Input Embedding)
- 位置编码(Positional Encoding)
- 多头自注意力(Multi-Head Self-Attention)
- 前馈网络(Position-wise FFN)
- 残差连接与层归一化(Add & Norm)
2.1.2 解码器特有机制
- 掩码多头注意力(Masked Multi-Head Attention)
- 编码器-解码器注意力(Encoder-Decoder Attention)
2.2 自注意力机制详解
2.2.1 计算流程数学表达
给定输入矩阵X(n×d_model),自注意力的计算可分为四步:
-
线性投影:
math复制Q = XW^Q, \quad K = XW^K, \quad V = XW^V其中投影矩阵W^Q, W^K ∈ ℝ^{d_model×d_k}, W^V ∈ ℝ^
-
注意力评分:
math复制\text{Attention}(Q,K,V) = \text{softmax}(\frac{QK^T}{\sqrt{d_k}})V缩放因子√d_k防止点积过大导致梯度消失
-
多头并行计算:
math复制\text{MultiHead} = \text{Concat}(head_1,...,head_h)W^O每个head独立计算注意力,h通常取8
-
代码实现示例:
python复制class MultiHeadAttention(nn.Module):
def __init__(self, d_model=512, h=8):
super().__init__()
self.d_k = d_model // h
self.h = h
self.Wq = nn.Linear(d_model, d_model)
self.Wk = nn.Linear(d_model, d_model)
self.Wv = nn.Linear(d_model, d_model)
self.Wo = nn.Linear(d_model, d_model)
def forward(self, x):
batch = x.size(0)
# 投影到Q/K/V空间
Q = self.Wq(x).view(batch, -1, self.h, self.d_k).transpose(1,2)
K = self.Wk(x).view(batch, -1, self.h, self.d_k).transpose(1,2)
V = self.Wv(x).view(batch, -1, self.h, self.d_k).transpose(1,2)
# 计算注意力
scores = (Q @ K.transpose(-2,-1)) / math.sqrt(self.d_k)
attn = torch.softmax(scores, dim=-1)
context = attn @ V
# 合并多头输出
context = context.transpose(1,2).contiguous().view(batch, -1, self.h*self.d_k)
return self.Wo(context)
2.2.2 注意力可视化案例
分析句子"The animal didn't cross the street because it was too tired"中"it"的注意力分布:
python复制# 假设已经训练好的模型
model = TransformerModel()
text = "The animal didn't cross the street because it was too tired"
inputs = tokenizer(text, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs, output_attentions=True)
# 获取第5层第3个头的注意力权重
attn_weights = outputs.attentions[4][0, 2]
# it(第7个token)对其他词的注意力分布
it_attention = attn_weights[6]
# 可视化显示
plt.bar(range(len(it_attention)), it_attention)
plt.xticks(range(len(text.split())), text.split(), rotation=45)
plt.show()
典型输出结果会显示"it"对"animal"的注意力权重最高(约0.7),而对"street"的注意力仅约0.05,准确捕捉了代词指代关系。
2.3 位置编码的奥秘
由于自注意力本身不具备位置感知能力,Transformer通过位置编码注入序列顺序信息:
math复制PE_{(pos,2i)} = \sin(pos/10000^{2i/d_{model}})
math复制PE_{(pos,2i+1)} = \cos(pos/10000^{2i/d_{model}})
这种设计的精妙之处在于:
- 可学习的位置关系:不同频率的正余弦函数组合可以表示相对位置
- 长度外推性:可以处理比训练时更长的序列
- 数值稳定性:值域范围在[-1,1]之间,与词嵌入尺度匹配
python复制class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0)/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):
return x + self.pe[:x.size(1)]
3. 编码器层的实现细节
3.1 残差连接与层归一化
每个子层(Self-Attention/FFN)都采用相同的结构:
code复制子层输出 = LayerNorm(x + Sublayer(x))
这种设计带来三大优势:
- 缓解梯度消失:残差连接保证梯度直接回传
- 稳定训练:层归一化维持数据分布
- 加速收敛:允许使用更大学习率
python复制class SublayerConnection(nn.Module):
def __init__(self, size, dropout):
super().__init__()
self.norm = nn.LayerNorm(size)
self.dropout = nn.Dropout(dropout)
def forward(self, x, sublayer):
"残差连接后接层归一化"
return self.norm(x + self.dropout(sublayer(x)))
3.2 前馈网络设计
位置感知的前馈网络(Position-wise FFN)由两个线性变换和ReLU激活组成:
math复制FFN(x) = \max(0, xW_1 + b_1)W_2 + b_2
典型实现中:
- 中间维度d_ff通常是d_model的4倍(如d_model=512时d_ff=2048)
- 使用GeLU激活函数的效果通常优于ReLU
python复制class PositionwiseFFN(nn.Module):
def __init__(self, d_model, d_ff, dropout=0.1):
super().__init__()
self.w1 = nn.Linear(d_model, d_ff)
self.w2 = nn.Linear(d_ff, d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
return self.w2(self.dropout(F.gelu(self.w1(x))))
4. 解码器关键技术解析
4.1 掩码自注意力机制
解码器在训练时需要防止当前位置看到未来信息,通过注意力掩码实现:
python复制def subsequent_mask(size):
"生成下三角掩码矩阵"
mask = torch.triu(torch.ones(size, size), diagonal=1)
return mask.masked_fill(mask==1, float('-inf'))
# 示例:序列长度=5
mask = subsequent_mask(5)
"""
[[0, -inf, -inf, -inf, -inf],
[0, 0, -inf, -inf, -inf],
[0, 0, 0, -inf, -inf],
[0, 0, 0, 0, -inf],
[0, 0, 0, 0, 0]]
"""
4.2 编码器-解码器注意力
解码器的第二层注意力使用:
- Q:来自解码器上一层的输出
- K/V:来自编码器的最终输出
这种设计使得解码器可以动态关注输入序列的不同部分,类似于传统Seq2Seq模型中的注意力机制。
5. 完整Transformer实现示例
python复制class Transformer(nn.Module):
def __init__(self, src_vocab, tgt_vocab, N=6, d_model=512, h=8, d_ff=2048, dropout=0.1):
super().__init__()
self.encoder = Encoder(src_vocab, N, d_model, h, d_ff, dropout)
self.decoder = Decoder(tgt_vocab, N, d_model, h, d_ff, dropout)
self.generator = nn.Linear(d_model, tgt_vocab)
def forward(self, src, tgt, src_mask, tgt_mask):
memory = self.encoder(src, src_mask)
output = self.decoder(tgt, memory, tgt_mask, src_mask)
return self.generator(output)
class Encoder(nn.Module):
def __init__(self, vocab, N, d_model, h, d_ff, dropout):
super().__init__()
self.embed = nn.Embedding(vocab, d_model)
self.pe = PositionalEncoding(d_model)
self.layers = nn.ModuleList([
EncoderLayer(d_model, h, d_ff, dropout) for _ in range(N)
])
def forward(self, x, mask):
x = self.pe(self.embed(x))
for layer in self.layers:
x = layer(x, mask)
return x
class EncoderLayer(nn.Module):
def __init__(self, d_model, h, d_ff, dropout):
super().__init__()
self.self_attn = MultiHeadAttention(d_model, h)
self.ffn = PositionwiseFFN(d_model, d_ff)
self.sublayer = nn.ModuleList([
SublayerConnection(d_model, dropout) for _ in range(2)
])
def forward(self, x, mask):
x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))
return self.sublayer[1](x, self.ffn)
6. 训练技巧与优化策略
6.1 学习率调度
采用带热启动的线性学习率调度:
math复制lrate = d_{model}^{-0.5} \cdot \min(step^{-0.5}, step \cdot warmup^{-1.5})
典型warmup_steps=4000
python复制class TransformerScheduler:
def __init__(self, optimizer, d_model, warmup=4000):
self.optimizer = optimizer
self.d_model = d_model
self.warmup = warmup
self.step_num = 0
def step(self):
self.step_num += 1
lr = (self.d_model ** -0.5) * min(
self.step_num ** -0.5,
self.step_num * (self.warmup ** -1.5)
)
for p in self.optimizer.param_groups:
p['lr'] = lr
self.optimizer.step()
6.2 标签平滑(Label Smoothing)
通过软化目标分布提升模型泛化能力:
python复制class LabelSmoothing(nn.Module):
def __init__(self, size, padding_idx, smoothing=0.1):
super().__init__()
self.criterion = nn.KLDivLoss(reduction='sum')
self.padding_idx = padding_idx
self.confidence = 1.0 - smoothing
self.smoothing = smoothing
self.size = size
def forward(self, x, target):
true_dist = x.data.clone()
true_dist.fill_(self.smoothing/(self.size-2))
true_dist.scatter_(1, target.data.unsqueeze(1), self.confidence)
true_dist[:, self.padding_idx] = 0
mask = torch.nonzero(target.data == self.padding_idx)
if mask.dim() > 0:
true_dist.index_fill_(0, mask.squeeze(), 0.0)
return self.criterion(x, true_dist)
7. 典型问题与解决方案
7.1 梯度不稳定问题
现象:训练初期出现NaN损失
解决方案:
- 使用梯度裁剪(
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)) - 增加预热步数(warmup_steps=8000)
- 使用Adam优化器的默认参数(β1=0.9, β2=0.98)
7.2 长序列处理技巧
问题:序列长度超过训练时的最大长度
解决方法:
- 相对位置编码(如Transformer-XL的递归机制)
- 局部注意力窗口(如Longformer的稀疏注意力)
- 内存压缩(如Reformer的LSH注意力)
7.3 多头注意力的头数选择
实验表明不同任务的最佳头数不同:
code复制+----------------+---------+
| 任务类型 | 推荐头数 |
+----------------+---------+
| 机器翻译 | 8 |
| 文本分类 | 4 |
| 语音识别 | 2 |
| 图像生成 | 16 |
+----------------+---------+
8. 模型变体与发展方向
8.1 高效Transformer变体
-
稀疏注意力:
- Sparse Transformer:固定注意力模式
- Longformer:滑动窗口注意力
- BigBird:随机+局部+全局注意力
-
内存优化:
- Reformer:局部敏感哈希(LSH)注意力
- Linformer:低秩投影注意力矩阵
-
架构改进:
- Universal Transformer:递归深度自适应
- Transformer-XL:片段级递归机制
8.2 跨模态扩展
-
视觉Transformer(ViT):
- 将图像分块为序列处理
- 在ImageNet上达到SOTA
-
多模态Transformer:
- CLIP:联合训练图像-文本对
- DALL·E:文本到图像生成
-
音频Transformer:
- Wav2Vec 2.0:自监督语音表示学习
- Jukebox:音乐生成模型
在实际项目中,我们通常需要根据具体任务需求调整模型架构。例如处理长文档时,可以结合Transformer-XL的递归机制;在资源受限环境中,可以考虑使用DistilBERT等蒸馏模型。理解基础架构的原理是灵活应用各种变体的关键。
