Skip to content

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 output

11.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²)瓶颈如何优化?

新时代的算法课程