注意力机制让模型"聚焦"于输入的相关部分,是Transformer的核心组件。本课从Bahdanau注意力到自注意力,理解注意力的一切。
人类看图像或读文本时,不会平等关注所有部分——我们"注意"重要的部分。注意力机制让神经网络也拥有这种能力。
Attention(Q, K, V) = softmax(QKᵀ/√dₖ) · V
Q(Query):查询向量——"我在找什么"
K(Key):键向量——"我是什么"
V(Value):值向量——"我的内容"
dₖ:键向量的维度(缩放因子)
eᵢⱼ = vᵀtanh(W₁sᵢ₋₁ + W₂hⱼ)
αᵢⱼ = softmax(eᵢⱼ)
cᵢ = Σⱼ αᵢⱼhⱼ
eᵢⱼ = sᵢ₋₁ᵀ W hⱼ
更高效,无需tanh
Q, K, V都来自同一个输入序列——序列中的每个位置关注其他所有位置。
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
# 注意力机制实现
class ScaledDotProductAttention(nn.Module):
def __init__(self, d_k):
super().__init__()
self.scale = math.sqrt(d_k)
def forward(self, Q, K, V, mask=None):
scores = torch.matmul(Q, K.transpose(-2, -1)) / self.scale
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attn = F.softmax(scores, dim=-1)
context = torch.matmul(attn, V)
return context, attn
# 测试
d_model = 64
n_head = 4
d_k = d_model // n_head
Q = torch.randn(2, n_head, 10, d_k)
K = torch.randn(2, n_head, 10, d_k)
V = torch.randn(2, n_head, 10, d_k)
attn = ScaledDotProductAttention(d_k)
context, attn_weights = attn(Q, K, V)
print(f"注意力输出: {list(context.shape)}")
print(f"注意力权重: {list(attn_weights.shape)}")
print(f"注意力权重和: {attn_weights[0, 0, 0, :].sum().item():.4f}")
# 不同注意力类型
print("\n=== 注意力类型对比 ===")
seq_len = 10
# 自注意力:Q=K=V
X = torch.randn(2, seq_len, d_model)
W_q = nn.Linear(d_model, d_model)
W_k = nn.Linear(d_model, d_model)
W_v = nn.Linear(d_model, d_model)
Q_self = W_q(X).view(2, n_head, seq_len, d_k)
K_self = W_k(X).view(2, n_head, seq_len, d_k)
V_self = W_v(X).view(2, n_head, seq_len, d_k)
context_self, weights_self = attn(Q_self, K_self, V_self)
print(f"自注意力: 输入{list(X.shape)} → 输出{list(context_self.view(2, seq_len, d_model).shape)}")
# 交叉注意力:Q来自解码器,K/V来自编码器
Y = torch.randn(2, seq_len, d_model)
Q_cross = W_q(Y).view(2, n_head, seq_len, d_k)
context_cross, weights_cross = attn(Q_cross, K_self, V_self)
print(f"交叉注意力: Q{list(Y.shape)}, K/V{list(X.shape)} → 输出{list(context_cross.view(2, seq_len, d_model).shape)}")
# 缩放因子的重要性
print("\n=== 缩放因子影响 ===")
for d in [16, 32, 64, 128]:
q = torch.randn(1, 1, 10, d)
k = torch.randn(1, 1, 10, d)
scores_no_scale = (q @ k.transpose(-2, -1))
scores_scaled = scores_no_scale / math.sqrt(d)
print(f"d={d:3d}: 未缩放方差={scores_no_scale.var().item():.2f}, 缩放后={scores_scaled.var().item():.2f}")
本课的核心知识点构成了序列模型学习的重要一环。让我们从更高维度审视这些知识之间的关系:
⚠️ 初学者常犯的错误:
💡 最佳实践:
深度学习是一个整体——每一课的知识都不是孤立的:
💡 继续深入的学习路径:
提取并可视化注意力权重矩阵,理解模型关注了什么。
实现完整的多头注意力,验证不同头学到的模式。
研究线性注意力机制,理解它如何降低计算复杂度。
⚠️ 需要避免的典型错误:
💡 提升模型性能的系统方法:
理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:
建立正确的直觉比记住公式更重要:
1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)
2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的
3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构
4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)
本课作为序列模型阶段的核心内容,与前后课程紧密关联:
| 关联课程 | 关联点 | 协同效应 |
|---|---|---|
| 前序课程 | 提供了本课的基础知识 | 循序渐进的理解 |
| 后续课程 | 本课内容是后续的基础 | 逐步构建能力 |
| 平行课程 | 同一阶段的互补知识 | 多角度深入理解 |
| 实战项目 | 综合运用所有知识 | 理论与实践结合 |
💡 准备面试时,确保能回答以下问题:
掌握本课内容后,可以通过以下方式继续深入:
将本课涉及的关键公式整理如下,方便回顾和记忆。理解每个公式背后的直觉比死记硬背更重要——试着用自然语言解释每个公式在做什么。
| 概念A | 概念B | 关键区别 |
|---|---|---|
| 参数 | 超参数 | 参数通过训练学习,超参数需要手动设定 |
| 训练误差 | 泛化误差 | 训练误差衡量拟合程度,泛化误差衡量预测能力 |
| 偏差 | 方差 | 偏差是系统性误差,方差是随机波动 |
| L1正则 | L2正则 | L1产生稀疏解,L2产生平滑解 |
| BatchNorm | LayerNorm | BN沿batch维度,LN沿特征维度 |
以下是本课核心概念的标准PyTorch实现模板,可以直接用于实际项目:
💡 调试建议:当结果不符合预期时,先检查数据,再检查损失函数,最后检查模型结构。90%的问题出在前两个环节。
注意力机制让模型知道该看哪里——这是突破的开始!