11.1 自注意力的算法本质
核心洞察:自注意力是一种"可学习的相似度检索"——每个位置生成查询,检索所有位置的信息,加权聚合结果。这不是神经网络的"魔法",而是检索算法的泛化。
11.1.1 开篇直觉:从检索开始
图书馆类比:
场景:在图书馆找一本书
传统检索(Ch3 哈希表):
步骤1:查目录——书名 → 书架位置
步骤2:精确匹配——书名完全一致
复杂度:O(1) 或 O(log n)
Transformer检索:
步骤1:描述需求——"我想找关于算法优化的书"
步骤2:相似度匹配——找到最相关的多本书
步骤3:聚合信息——综合多本书的内容
复杂度:O(n)(需扫描所有书)
关键区别:
┌────────────────────────────────────────────┐
│ 传统检索:精确匹配,单一结果 │
│ Transformer:相似度匹配,融合多个结果 │
│ │
│ 传统检索:查找函数固定(哈希函数) │
│ Transformer:查找函数可学习(Q/K投影) │
└────────────────────────────────────────────┘直觉先于公式:
在引入数学公式前,先建立三个直觉概念:
| 概念 | 直觉解释 | 图书馆类比 |
|---|---|---|
| Query (Q) | "我要找什么?" | 你的阅读需求描述 |
| Key (K) | "这本书有什么特征?" | 书的分类标签 |
| Value (V) | "书的具体内容" | 书的实际内容 |
11.1.2 问题驱动:长文本理解的困境
War Story:第1000词依赖第1词
场景:
任务:阅读一篇10000字的技术文章,回答问题:
"文章开头的假设,如何影响结尾的结论?"传统方法失败:
方案1:关键词提取
找到"假设"、"结论"、"影响"
问题:无法理解假设与结论的逻辑关系
方案2:RNN/LSTM顺序处理
逐词处理,每个词依赖前一个词的隐藏状态
问题:信息经过10000次传递,"稀释"严重
第1000词几乎看不到第1词的信息
方案3:Transformer自注意力
每个词都能"看到"所有词
第1000词可以直接关注第1词
成本:每个词对所有词计算注意力 → O(n²)关键问题:
自注意力如何解决长距离依赖?代价是什么?
11.1.3 核心概念:自注意力的算法定义
算法定义(而非数学定义)
从算法视角,自注意力可以定义为:
问题:序列信息聚合
输入:序列 X = [x₁, x₂, ..., xₙ]
输出:聚合序列 Y = [y₁, y₂, ..., yₙ]
自注意力算法:
For each position i:
1. 生成查询:Query_i = f(x_i) // "我要聚合什么信息?"
2. 遍历所有位置j:
生成键:Key_j = g(x_j) // "j位置有什么信息?"
生成值:Value_j = h(x_j) // "j位置的实际内容"
计算相似度:Score_{i,j} = similarity(Query_i, Key_j)
3. 归一化权重:
Attention_{i,j} = softmax(Score_{i,*})[j]
4. 加权聚合:
y_i = Σ_j Attention_{i,j} × Value_j与传统检索的对照:
| 传统检索(Ch3) | 自注意力 | 关系 |
|---|---|---|
| 查找表索引 | Key向量 | Key是"可检索的签名" |
| 查询条件 | Query向量 | Query编码"查找请求" |
| 返回值 | Value向量 | Value是"检索结果内容" |
| 匹配度判断 | QK内积 | 内积=相似度度量 |
| 精确选择 | softmax权重 | 软选择(加权融合) |
软检索 vs 硬检索:
硬检索(Ch3 查找表):
if exact_match(key, query):
return value
else:
return null
特点:要么匹配,要么不匹配(二值)
软检索(Transformer):
weights = softmax(query × keys^T) // 所有位置的权重
return weighted_sum(weights, values) // 融合所有相关位置
特点:程度匹配(连续权重),融合多个结果11.1.4 多头注意力:并行检索策略
算法直觉:
类比:数据库的多索引查询
单索引查询:
用一个索引查找 → 单一检索策略
多索引查询:
索引1:按时间查找
索引2:按类别查找
索引3:按重要性查找
合并多个索引的结果 → 更全面的检索
多头注意力:
头1:关注语法结构(主语-谓语关系)
头2:关注语义相似(同义词关系)
头3:关注位置关系(相邻词关系)
...
合并多个头的结果 → 多角度信息聚合算法定义:
MultiHead(Q, K, V) = Concat(head₁, head₂, ..., head_h) × W^O
其中每个头独立计算:
head_i = Attention(Q × W_i^Q, K × W_i^K, V × W_i^V)
关键洞察:
每个头学习不同的检索策略
这是"集成检索"的算法化
类似于多路径探索(Ch4 图算法对照)11.1.5 与前章节关联
对照表:自注意力 vs Ch3 查找
| 维度 | Ch3 哈希查找 | 11.1 自注意力 | 关联强度 |
|---|---|---|---|
| 查找对象 | 键(固定) | Key向量(学习) | 强 |
| 查找请求 | 查询条件 | Query向量(学习) | 强 |
| 匹配方式 | 精确匹配 | 相似度匹配 | 强 |
| 返回结果 | 单一值 | 加权融合值 | 强 |
| 复杂度 | O(1)或O(log n) | O(n) | 中 |
| 查找函数 | 固定(哈希) | 可学习(投影) | 强 |
知识卡片 C11-01:自注意力=软检索
概念:自注意力是将检索算法"软化"的范式
核心转变:
硬检索 → 软检索
精确匹配 → 相似度匹配
单一返回 → 加权聚合
固定查找函数 → 可学习查找函数
算法本质:
检索 + 聚合 算法化
关联:
Z-查找表(Ch3)
Z-信息聚合11.1.6 可视化建议
1. 注意力矩阵热图
python
def visualize_attention(attention_matrix, tokens):
"""
可视化注意力权重
attention_matrix: [n, n] 注意力权重矩阵
tokens: [n] token列表
"""
plt.figure(figsize=(10, 8))
sns.heatmap(attention_matrix,
xticklabels=tokens,
yticklabels=tokens,
cmap='Blues',
annot=True,
fmt='.2f')
plt.title("Self-Attention Matrix")
plt.xlabel("Key Positions")
plt.ylabel("Query Positions")
plt.show()
# 示例:句子 "The cat sat on the mat"
tokens = ["The", "cat", "sat", "on", "the", "mat"]
# 预期注意力模式:
# "sat" 关注 "cat"(主谓关系)
# "mat" 关注 "the" 和 "sat"(修饰关系)热图解读要点:
- 对角线:自关注(token关注自己)
- 块状模式:短语内关注
- 长距离亮点:跨句子依赖
2. Q/K/V 向量可视化
颜色编码:
Query向量:红色
Key向量:蓝色
Value向量:绿色
可视化方式:
每个token的三种向量并排展示
内积高的位置用连线标注3. 多头注意力对比
展示8个头的注意力矩阵:
头1-2:语法关系(主谓、修饰)
头3-4:语义相似(同义词)
头5-6:位置关系(相邻token)
头7-8:全局关系(句子级别)11.1.7 代码示例
简化自注意力实现
python
import numpy as np
def self_attention(X, W_Q, W_K, W_V):
"""
简化自注意力实现(教学版)
X: [n, d] 输入序列,n个token,每个d维
W_Q, W_K, W_V: [d, d] 投影矩阵
"""
n, d = X.shape
# 步骤1:投影(生成Query/Key/Value)
Q = X @ W_Q # [n, d]
K = X @ W_K # [n, d]
V = X @ W_V # [n, d]
# 步骤2:计算相似度分数
scores = Q @ K.T # [n, n]
scores = scores / np.sqrt(d) # 缩放防止梯度消失
# 步骤3:softmax归一化
# 每行(每个Query位置)的权重归一化
attention = np.zeros_like(scores)
for i in range(n):
exp_scores = np.exp(scores[i] - np.max(scores[i])) # 防止溢出
attention[i] = exp_scores / np.sum(exp_scores)
# 步骤4:加权聚合
Y = attention @ V # [n, d]
return Y, attention
# 测试示例
def test_attention():
# 简化输入:4个token,每个2维
X = np.array([
[1.0, 0.0], # token 0: "The"
[0.0, 1.0], # token 1: "cat"
[1.0, 1.0], # token 2: "sat"
[0.5, 0.5] # token 3: "mat"
])
# 简化投影矩阵
W_Q = np.eye(2)
W_K = np.eye(2)
W_V = np.eye(2)
Y, attention = self_attention(X, W_Q, W_K, W_V)
print("注意力矩阵:")
print(attention)
print("\n输出向量:")
print(Y)
return attention
# 运行测试
attention = test_attention()多头注意力实现
python
def multi_head_attention(X, W_Qs, W_Ks, W_Vs, W_O):
"""
多头注意力实现
X: [n, d] 输入序列
W_Qs, W_Ks, W_Vs: [h, d, d] h个头的投影矩阵
W_O: [h*d, d] 输出投影
"""
n, d = X.shape
h = W_Qs.shape[0] # 头数
d_head = d // h # 每个头的维度
heads = []
for i in range(h):
# 每个头独立计算注意力
Q_i = X @ W_Qs[i]
K_i = X @ W_Ks[i]
V_i = X @ W_Vs[i]
Y_i, _ = self_attention(Q_i, K_i, V_i)
heads.append(Y_i)
# 合并所有头
concat = np.concatenate(heads, axis=1) # [n, h*d]
# 输出投影
output = concat @ W_O # [n, d]
return output11.1.8 练习设计
基础理解题(3题)
练习 1:检索对比分析
场景:在100万个文档中检索
方案1:传统倒排索引
方案2:Transformer自注意力
任务:
a) 计算倒排索引查找复杂度
b) 计算自注意力查找复杂度
c) 分析:何时选择哪种方案?
预期答案:
a) O(1) 或 O(log n) —— 精确匹配
b) O(n²) —— 需要扫描所有文档
c)
倒排索引:关键词精确查找,大规模检索
自注意力:语义相似查找,小规模深度融合练习 2:Q/K/V直觉预测
句子:"The cat sat on the mat"
任务:
a) 预测Query="sat"时,Key="cat"和"mat"的注意力分数
b) 解释为什么"sat"应该关注"cat"
c) 绘制注意力矩阵的热图(手绘或代码)
预期分析:
a) "sat"关注"cat"(主谓关系)分数高
"sat"关注"mat"(修饰关系)分数中
b) 主谓关系是语法核心,语义上最重要
c) 热图应显示"sat-row"对"cat-col"高亮练习 3:多头注意力功能推测
模型有8个注意力头
任务:
a) 猜测每个头学习什么检索策略?
b) 分析多头相比单头的优势?
c) 设计实验验证不同头的功能差异?
预期分析:
a)
头1-2:语法关系(主谓、修饰)
头3-4:语义相似(同义词)
头5-6:位置关系(相邻token)
头7-8:全局关系(句子级别)
b) 优势:
多角度信息聚合
避免单一检索策略的盲点
类似多索引查询
c) 验证方法:
单独屏蔽某个头,观察性能变化
可视化不同头的注意力模式方法应用题(2题)
练习 4:注意力矩阵分析
给定一个注意力矩阵 [n, n]
任务:
a) 计算注意力熵(衡量集中度)
b) 识别"关注焦点"位置
c) 检测异常模式(如全关注某位置)
算法设计:
def analyze_attention(attention):
# 计算每个位置的注意力熵
entropy = []
for i in range(n):
p = attention[i]
e = -np.sum(p * np.log(p + 1e-10))
entropy.append(e)
# 熵低 = 集中,熵高 = 分散
# 识别焦点:熵最低的位置
# 检测异常:某列权重异常高
column_weights = np.sum(attention, axis=0)
# 如果某列权重 >> 其他,标记异常练习 5:相似度函数设计
传统:点积相似度 score = Q × K^T
任务:
a) 设计其他相似度函数(如余弦相似度)
b) 分析优劣
c) 何时使用哪种?
方案对比:
┌─────────────────────────────────────────────┐
│ 点积相似度: │
│ score = Q × K^T │
│ 优点:简单,可学习 │
│ 缺点:受向量长度影响 │
│ │
│ 余弦相似度: │
│ score = (Q × K^T) / (|Q| × |K|) │
│ 优点:不受长度影响,纯方向 │
│ 缺点:额外归一化成本 │
│ │
│ 加权相似度: │
│ score = Q × A × K^T (A是可学习矩阵) │
│ 优点:更灵活 │
│ 缺点:参数更多 │
└─────────────────────────────────────────────┘
选择建议:
点积:默认选择,简单高效
余弦:需要消除长度影响时
加权:需要更精细匹配时LLM协同题(1题)
练习 6:注意力可视化审查
让LLM生成注意力矩阵可视化代码
任务:
a) 提供可视化需求描述
b) 让LLM生成代码
c) 审查代码质量
审查要点:
┌─────────────────────────────────────────────┐
│ 可视化准确性: │
│ 矩阵索引是否正确? │
│ softmax归一化是否正确? │
│ │
│ 大规模处理: │
│ 是否支持大矩阵(1000×1000)? │
│ 是否有内存优化? │
│ │
│ 交互性设计: │
│ 是否支持点击查看详情? │
│ 是否支持对比不同层/头? │
└─────────────────────────────────────────────┘
提交审查报告,包括:
代码正确性分析
可读性评估
改进建议11.1.9 设计反思
教学要点总结
| 要点 | 教学策略 |
|---|---|
| 直觉先行 | 图书馆类比,Q/K/V直觉解释在公式前 |
| 算法视角 | 从"软检索"定义,而非神经网络视角 |
| 问题驱动 | War Story展示长距离依赖问题 |
| 跨章节关联 | 对照Ch3查找,强化检索视角 |
| 可视化 | 热图直观展示注意力模式 |
常见误解澄清
| 误解 | 澄清 |
|---|---|
| "注意力是神经网络魔法" | 注意力是检索算法的泛化 |
| "Q/K/V是神秘概念" | Q=查询,K=键,V=值,检索三要素 |
| "注意力矩阵没用" | 可揭示模型关注模式,诊断问题 |
| "多头只是参数增加" | 多头是并行检索策略,类似多索引 |
知识卡片清单
| 编号 | 卡片标题 | 本节位置 |
|---|---|---|
| C11-01 | 自注意力=软检索 | 11.1.3 |
| C11-02 | 注意力矩阵热图 | 11.1.6 |
| C11-03 | 多头注意力=并行检索 | 11.1.4 |
下一节:11.2 计算成本与缓存策略 —— O(n²)瓶颈如何优化?