5.1 Transformer与注意力机制
📚 本章概述
Transformer是深度学习领域的重要突破,彻底改变了序列建模的方式。本章将深入讲解Transformer架构的核心原理、注意力机制的工作原理,以及如何实现一个完整的机器翻译系统。
🎯 学习目标
- 理解Transformer架构的核心组件
- 掌握自注意力机制和多头注意力的原理
- 学会实现位置编码和前馈网络
- 能够构建完整的序列到序列模型
- 理解Transformer在NLP任务中的应用
🔍 核心概念
1. 注意力机制(Attention Mechanism)
什么是注意力?
注意力机制模仿人类认知过程,让模型能够"关注"输入序列中最重要的部分。
自注意力(Self-Attention)公式:
Attention(Q, K, V) = softmax(QK^T / √d_k) V其中:
- Q (Query): 查询向量,表示我们想要关注什么
- K (Key): 键向量,表示每个位置提供的信息
- V (Value): 值向量,表示每个位置的实际内容
- d_k: 键向量的维度,用于缩放
2. 多头注意力(Multi-Head Attention)
多头注意力允许模型同时关注不同位置的多个表示子空间:
MultiHead(Q, K, V) = Concat(head_1, ..., head_h) W^O
head_i = Attention(QW_i^Q, KW_i^K, VW_i^V)优势:
- 并行处理多个注意力模式
- 捕捉不同类型的依赖关系
- 提高模型的表示能力
3. 位置编码(Positional Encoding)
由于Transformer没有循环结构,需要显式编码位置信息:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))作用:
- 为每个位置生成唯一的编码
- 保持相对位置关系
- 允许模型理解序列顺序
🏗️ Transformer架构详解
编码器(Encoder)结构
输入嵌入 → 位置编码 → [多头注意力 → 残差连接 → 层归一化 → 前馈网络 → 残差连接 → 层归一化] × N解码器(Decoder)结构
输出嵌入 → 位置编码 → [掩码多头注意力 → 残差连接 → 层归一化 → 编码器-解码器注意力 → 残差连接 → 层归一化 → 前馈网络 → 残差连接 → 层归一化] × N → 线性层 → Softmax💻 代码实现解析
1. 多头注意力实现
class MultiHeadAttention(nn.Module):
"""
多头注意力机制实现
参数:
d_model: 模型维度(输入和输出的特征维度)
num_heads: 注意力头的数量
"""
def __init__(self, d_model, num_heads):
super().__init__()
# 确保模型维度可以被头数整除
assert d_model % num_heads == 0, "d_model必须能被num_heads整除"
self.d_model = d_model # 模型总维度
self.num_heads = num_heads # 注意力头数量
self.d_k = d_model // num_heads # 每个头的维度
# 线性变换层:将输入投影到Q、K、V空间
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, query, key, value, mask=None):
"""
前向传播过程
参数:
query: 查询张量,形状为(batch_size, seq_len, d_model)
key: 键张量,形状为(batch_size, seq_len, d_model)
value: 值张量,形状为(batch_size, seq_len, d_model)
mask: 掩码张量,用于屏蔽无效位置
返回:
多头注意力输出,形状为(batch_size, seq_len, d_model)
"""
# 1. 线性变换:将输入投影到Q、K、V空间
Q = self.W_q(query) # 形状: (batch_size, seq_len, d_model)
K = self.W_k(key) # 形状: (batch_size, seq_len, d_model)
V = self.W_v(value) # 形状: (batch_size, seq_len, d_model)
# 2. 分割多头:将d_model维度分割为num_heads * d_k
batch_size, seq_len = Q.size(0), Q.size(1)
# 重塑张量形状:(batch_size, seq_len, num_heads, d_k)
Q = Q.view(batch_size, seq_len, self.num_heads, self.d_k)
K = K.view(batch_size, seq_len, self.num_heads, self.d_k)
V = V.view(batch_size, seq_len, self.num_heads, self.d_k)
# 3. 转置以进行批量矩阵乘法:(batch_size, num_heads, seq_len, d_k)
Q = Q.transpose(1, 2)
K = K.transpose(1, 2)
V = V.transpose(1, 2)
# 4. 计算注意力分数:Q * K^T / sqrt(d_k)
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
# 5. 应用掩码(如果有)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
# 6. 应用softmax得到注意力权重
attention_weights = F.softmax(scores, dim=-1)
# 7. 应用注意力权重到V值
output = torch.matmul(attention_weights, V)
# 8. 转置回原始形状并合并多头
output = output.transpose(1, 2).contiguous().view(
batch_size, seq_len, self.d_model
)
# 9. 最终线性变换
output = self.W_o(output)
return output2. 位置编码实现
class PositionalEncoding(nn.Module):
"""
位置编码实现 - 为Transformer模型添加位置信息
参数:
d_model: 模型维度(特征维度)
max_len: 最大序列长度
"""
def __init__(self, d_model, max_len=100):
super().__init__()
# 创建位置编码矩阵:形状为(max_len, d_model)
pe = torch.zeros(max_len, d_model)
# 创建位置索引:0到max_len-1,形状为(max_len, 1)
position = torch.arange(0, max_len).unsqueeze(1)
# 计算除数项:用于生成不同频率的正弦和余弦波
# 公式:div_term = exp(-2i * log(10000) / d_model) 其中i从0到d_model/2-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)
# 注册为buffer(不参与梯度计算,但会保存到模型状态)
# 调整形状为(1, max_len, d_model)以便广播
self.register_buffer('pe', pe.unsqueeze(0).transpose(0, 1))
def forward(self, x):
"""
前向传播:将位置编码添加到输入张量
参数:
x: 输入张量,形状为(batch_size, seq_len, d_model)
返回:
添加了位置编码的张量,形状与输入相同
"""
# 将位置编码添加到输入中
# 使用切片确保只取需要的长度(seq_len <= max_len)
x = x + self.pe[:x.size(0), :]
return x🎮 实践项目:英法机器翻译
项目特点
- 数据准备: 构建英法平行语料库
- 词汇表构建: 处理特殊标记(
, , , ) - 模型训练: 完整的编码器-解码器训练流程
- 推理生成: 使用贪心搜索或束搜索生成翻译
关键实现细节
- 序列填充: 处理不同长度的序列
- 掩码机制: 防止关注填充位置和未来信息
- 损失计算: 忽略填充标记的交叉熵损失
- 梯度裁剪: 防止梯度爆炸
📊 性能评估
评估指标
- BLEU分数: 衡量翻译质量的常用指标
- 困惑度: 评估语言模型性能
- 人工评估: 最可靠的评估方式
优化技巧
- 学习率调度: 使用warmup和衰减策略
- 标签平滑: 防止模型过度自信
- 梯度累积: 在有限内存下训练大模型
- 模型集成: 结合多个模型的预测
🔬 技术深度解析
注意力权重的可视化
通过可视化注意力权重,可以理解模型如何关注输入序列:
def visualize_attention(src_sentence, tgt_sentence, attention_weights):
"""可视化注意力权重矩阵"""
fig, ax = plt.subplots(figsize=(10, 8))
im = ax.imshow(attention_weights, cmap='viridis')
# 设置坐标轴标签
ax.set_xticks(range(len(src_sentence)))
ax.set_yticks(range(len(tgt_sentence)))
ax.set_xticklabels(src_sentence, rotation=45)
ax.set_yticklabels(tgt_sentence)
plt.colorbar(im)
plt.title('Attention Weights')
plt.tight_layout()
plt.show()Transformer的变体
- BERT: 仅使用编码器的预训练模型
- GPT: 仅使用解码器的生成模型
- T5: 统一的文本到文本转换模型
- Vision Transformer: 将Transformer应用于图像
🚀 实际应用场景
自然语言处理
- 机器翻译
- 文本摘要
- 问答系统
- 情感分析
其他领域
- 代码生成
- 蛋白质结构预测
- 时间序列预测
- 多模态学习
💡 学习建议
循序渐进的学习路径
- 基础理解: 先理解注意力机制的基本概念
- 代码实现: 手动实现核心组件
- 项目实践: 完成完整的翻译项目
- 深入优化: 尝试不同的超参数和技巧
调试技巧
- 检查维度: 确保所有张量维度匹配
- 验证注意力: 可视化注意力权重检查合理性
- 梯度检查: 使用梯度检查确保反向传播正确
- 过拟合测试: 在小数据集上测试过拟合能力
📈 进阶学习方向
理论研究
- 注意力机制的数学基础
- Transformer的复杂度分析
- 位置编码的理论研究
工程优化
- 模型压缩和加速
- 分布式训练
- 推理优化
应用扩展
- 多语言模型
- 领域自适应
- 少样本学习
🎯 本章总结
Transformer架构通过自注意力机制彻底改变了序列建模的方式,其并行计算能力和强大的表示学习能力使其成为现代深度学习的基石。掌握Transformer不仅对NLP任务至关重要,也为理解其他领域的深度学习模型奠定了基础。
关键收获:
- ✅ 理解了注意力机制的核心原理
- ✅ 掌握了Transformer架构的各个组件
- ✅ 实现了完整的机器翻译系统
- ✅ 学会了模型调试和优化技巧
- ✅ 了解了Transformer的实际应用场景
在下一章中,我们将探索生成对抗网络(GAN),学习如何让AI创造全新的内容!