跳转至

一:自注意力Self-Attention

来源:http://mp.weixin.qq.com/s?__biz=MzYyNTk3Njg1NA==&mid=2247483713&idx=1&sn=fb7afe9b4a5c916fbc2c58f6519e668b&chksm=f01eb238c7693b2ecc6e215769972ed78e850afcf9f89f3fe2b23bd60937984f6356d813f564#rd

1. 学习定位

self-attention 是 Transformer 架构的核心机制,也是理解现代大语言模型的入口。它的作用是让序列中每个 token 根据当前输入内容,动态读取同一序列中其他 token 的信息,并形成上下文相关的表示。 第一天学习的重点集中在 self-attention 的基本原理,而不是 Transformer 的全部细节。需要掌握的范围包括: - self-attention 的直观含义和计算流程。

  • Query、Key、Value 的角色划分。

  • scaled dot-product attention 的数学公式。

  • attention score、softmax、mask、value 聚合的语义。

  • 单头 attention 和多头 attention 的张量形状。

  • causal mask 与 padding mask 的作用。

  • self-attention 的复杂度、并行性和长上下文瓶颈。

  • 最小 PyTorch 风格实现中的关键细节。

2. 序列表示与上下文依赖

自然语言中的 token 不是孤立存在的。一个 token 的含义经常由上下文决定。同一个词在不同句子中可以表示不同实体、不同语义角色或不同指代关系。模型如果只使用静态 token embedding,就只能得到“词本身”的表示,无法充分表达它在当前句子中的语义。 序列建模的目标之一,是把每个位置的原始 token 表示转换成上下文相关表示。上下文相关表示不仅包含当前 token 的信息,也包含与当前 token 相关的其他位置的信息。 传统 RNN 通过时间步递推建模上下文,长距离信息需要一步步传递。CNN 通过局部卷积捕捉邻域模式,长距离交互通常需要堆叠多层。self-attention 采用另一种方式:直接计算序列中任意两个位置之间的相关性,让信息可以在一层内跨任意距离流动。 核心思想可以概括为:

self-attention = 根据 token 之间的内容相关性,动态分配权重,并对上下文信息做加权聚合。

3. Self-Attention 的整体流程

给定输入序列表示 X,self-attention 的计算过程可以分为六步: - 从输入 X 线性投影得到 QKV

  • QK^T 计算每个 query 对所有 key 的匹配分数。

  • 对匹配分数除以 sqrt(d_k) 做缩放。

  • 根据任务需要加入 attention mask。

  • 沿 key 维度做 softmax,得到 attention weights。

  • 用 attention weights 对 V 加权求和,得到每个位置的新表示。

流程图如下:

X
|
|-- W_Q --> Q
|-- W_K --> K
|-- W_V --> V

QK^T / sqrt(d_k)
|
+ mask
|
softmax
|
attention weights @ V
|
contextual output
在 Transformer 中,self-attention 通常不是单独使用,而是作为 Transformer block 的一个子层,与 residual connection、LayerNorm 或 RMSNorm、MLP/FFN 等模块配合。

4. Query、Key、Value

self-attention 中每个 token 都会生成三种向量:Query、Key、Value。 Query 表示当前位置想要查找的信息。Key 表示当前位置可以被什么查询匹配。Value 表示当前位置在被关注时实际提供的信息内容。 可以用检索系统类比: - Query 类似用户输入的搜索请求。

  • Key 类似文档索引或可检索字段。

  • Value 类似文档正文或返回内容。

某个 token 的 Query 会和所有 token 的 Key 计算匹配分数。匹配分数经过 softmax 后变成权重,这些权重再用于聚合所有 token 的 Value。 Q、K、V 都来自同一个输入 X,这是 self-attention 中 “self” 的含义。它们不是三份不同数据,而是同一份序列表示经过三组不同可学习线性变换得到:

Q = XW_Q
K = XW_K
V = XW_V
使用三套投影的原因是匹配关系和信息内容需要解耦。一个 token 作为 Query 时表达“需要什么”,作为 Key 时表达“如何被匹配”,作为 Value 时表达“要传递什么”。如果直接用同一个向量承担所有角色,模型表达能力会受到限制。

5. Scaled Dot-Product Attention

标准 scaled dot-product attention 公式为:

Attention(Q, K, V) = softmax(QK^T / sqrt(d_k))V
加入 mask 后,常见形式为:

Attention(Q, K, V) = softmax((QK^T / sqrt(d_k)) + mask)V
各部分含义如下: - QK^T:计算 query 与 key 的点积相似度。

  • d_k:key/query 的维度。

  • sqrt(d_k):缩放因子,用于稳定 softmax 输入尺度。

  • softmax:将匹配分数转成非负且归一化的注意力权重。

  • V:被加权聚合的信息内容。

attention 的输出是 value 的加权和,而不是 query 或 key 的加权和。Q 和 K 的主要职责是计算位置间的匹配关系,V 才是被读取和传递的信息载体。

6. 点积相似度

点积用于衡量两个向量的匹配程度。若 query 和 key 在向量空间中方向接近、模长较大,点积值通常更大,表示该 key 对当前 query 更相关。 对一组 query 和 key 使用矩阵乘法可以一次性得到所有位置两两之间的匹配分数。对于长度为 T 的 self-attention,score 矩阵通常是 T x T

      k1 k2 k3 ... kT
q1    .  .  .      .
q2    .  .  .      .
q3    .  .  .      .
...
qT    .  .  .      .
i 行表示第 i 个 token 作为 query 时对所有 key 的匹配分数。第 i 行第 j 列表示第 i 个 token 对第 j 个 token 的关注强度。 这种矩阵化计算方式是 Transformer 易于并行训练的重要原因。

7. 缩放因子 sqrt(d_k)

当 query 和 key 的维度 d_k 较大时,点积值的方差会随维度增大。若每一维近似独立且方差相近,则点积可以看作 d_k 个随机变量的和,其方差大致与 d_k 成正比。 未缩放的点积分数过大时,softmax 会进入饱和区。饱和的 softmax 会产生极端尖锐的分布,最大位置接近 1,其他位置接近 0,导致梯度变小并影响训练稳定性。 除以 sqrt(d_k) 可以把点积分数拉回更稳定的尺度:

scores = QK^T / sqrt(d_k)
缩放因子的本质是数值稳定和优化稳定,不改变 attention 通过相关性聚合信息的核心语义。

img

为了更透彻地理解,图片中是Attention is all you need原文描述:我们推测 d_k 数值偏大时,点积结果幅值会随之变大。简单理解就是,当维度更大的时候,维度越多,相加的随机项数量越多,叠加后总和绝对值更容易冲高,点积数值量级随之膨胀。幅值过大会让 softmax 输入落入梯度极小饱和区,所以除以 sqrt(d_k) 做幅度归一。

8. Softmax 与注意力权重

softmax 将 attention score 转换为注意力权重。权重具有两个重要性质: - 每个权重非负。

  • 对同一个 query,所有 key 位置的权重和为 1。

如果 attention score 的 shape 是:

[B, H, T_q, T_k]
softmax 应沿最后一维 T_k 执行:

weights = softmax(scores, dim=-1)
这表示每个 query 位置都会在所有 key 位置上形成一个注意力分布。 softmax 后的权重再乘以 V

output = weights @ V
输出可以理解为对 value 信息的动态加权平均。不同输入样本、不同位置、不同层和不同 head 都可能产生不同的权重分布。

9. 单头 Attention 的张量形状

设输入 X 的 shape 为:

X: [B, T, d_model]
其中: - B 是 batch size。

  • T 是序列长度。

  • d_model 是模型隐藏维度。

单头 self-attention 中,线性投影参数常写为:

W_Q: [d_model, d_k]
W_K: [d_model, d_k]
W_V: [d_model, d_v]
投影后:

Q = XW_Q: [B, T, d_k]
K = XW_K: [B, T, d_k]
V = XW_V: [B, T, d_v]
计算 attention score:

QK^T: [B, T, T]
softmax 后:

attention weights: [B, T, T]
聚合 value:

output = attention weights @ V: [B, T, d_v]
在 self-attention 中,T_q = T_k = T。在 cross-attention 中,query 和 key/value 可以来自不同序列,因此 T_qT_k 可以不同。

10. Multi-Head Attention

单头 attention 只在一个表示子空间中计算相关性。multi-head attention 将模型维度拆分成多个 head,让模型在多个子空间中并行学习不同类型的关系。 标准公式:

head_i = Attention(QW_i^Q, KW_i^K, VW_i^V)
MultiHead(Q, K, V) = Concat(head_1, ..., head_h)W_O
其中: - h 是 head 数。

  • 每个 head 有独立的 Q/K/V 投影。

  • Concat 将所有 head 的输出拼接。

  • W_O 将拼接后的表示重新映射回 d_model

常见设置为:

d_head = d_model / num_heads
这样多个 head 拼接后仍然回到 d_model 维度,整体计算量与单头完整维度 attention 保持同阶。 示例:

B = 2
T = 4
d_model = 8
num_heads = 2
d_head = 4
shape 变化:

X: [2, 4, 8]
Q/K/V after linear: [2, 4, 8]
reshape: [2, 4, 2, 4]
transpose: [2, 2, 4, 4]  # [B, H, T, d_head]
score: [2, 2, 4, 4]
weights: [2, 2, 4, 4]
head output: [2, 2, 4, 4]
transpose + concat: [2, 4, 8]
output projection: [2, 4, 8]
多头注意力提升了模型表达能力,但 head 数并非越多越好。head 数增加会降低每个 head 的 d_head,若 d_head 过小,单个 head 的表达能力可能不足。实际模型需要在效果、计算效率、显存和硬件实现之间折中。

11. Attention Mask

attention mask 用于控制哪些位置可以被关注。mask 通常在 softmax 前加入 attention score,使被屏蔽的位置在 softmax 后权重为 0。 常见写法:

scores = scores.masked_fill(mask == 0, -inf)
weights = softmax(scores, dim=-1)
使用 -inf 或极大负数的原因是:

exp(-inf) = 0
在过了Softmax的exp后就会变成0,因此被屏蔽位置不会参与 value 聚合。 mask 通常需要能 broadcast 到 attention score 的形状:

scores: [B, H, T_q, T_k]
padding mask 常见形状可以是:

[B, 1, 1, T_k]
causal mask 常见形状可以是:

[1, 1, T_q, T_k]
两类 mask 可以合并使用。

12. Causal Mask

decoder-only 语言模型采用自回归训练目标。每个位置只能依赖自己和之前的 token,不能看到未来 token。causal mask 用来保证这种因果约束。 长度为 5 的 causal mask 允许关注位置如下:

      k1 k2 k3 k4 k5
q1    1  0  0  0  0
q2    1  1  0  0  0
q3    1  1  1  0  0
q4    1  1  1  1  0
q5    1  1  1  1  1
这是一个下三角结构。第 i 行只允许关注 0..i 的 key 位置。 causal mask 避免训练时的信息泄漏。若不加 causal mask,当前位置可以看到未来 token,训练 loss 会不真实地降低,但推理生成时未来 token 不存在,训练和推理条件不一致。 causal mask 不影响训练并行性。训练时仍可以一次性输入完整序列,矩阵乘法仍然并行执行;mask 只是在 attention score 中屏蔽未来位置。

13. Padding Mask

batch 训练通常需要把不同长度的样本 padding 到同一长度。padding token 是补齐用的无效内容,不应该被真实 token 关注,也不应该参与语言模型 loss 或分类 loss。 padding mask 的作用是屏蔽 padding 对应的 key 位置。例如:

真实序列: 我 爱 NLP
padding 后: 我 爱 NLP <pad> <pad>
真实 token 的 query 不应该从 ` 的 value 中读取信息。实现时通常把 padding key 位置对应的 score 置为-inf`。 在 decoder-only LM 中,padding mask 和 causal mask 经常同时存在: - padding mask 屏蔽无效补齐位置。

  • causal mask 屏蔽未来位置。

14. Self-Attention 与位置信息

纯 self-attention 本身不包含顺序信息。若没有位置编码,attention 主要根据 token 内容计算相关性,对输入顺序的区分能力不足。 Transformer 需要显式引入位置相关信息。常见方式包括: - sinusoidal positional encoding

  • learned positional embedding

  • RoPE

  • ALiBi

位置机制让模型可以区分相同 token 集合的不同排列,并学习距离、先后顺序、相对位置等结构信息。 self-attention 负责内容相关的信息聚合,位置编码负责提供顺序结构。二者共同构成 Transformer 序列建模能力的重要基础。

15. 复杂度与并行性

self-attention 的核心计算包括 QK^Tattention weights @ V。 设序列长度为 T,模型维度为 d_model。attention 核心计算复杂度近似为:

O(T^2 d_model)
attention score 矩阵的空间复杂度近似为:

O(T^2)
在 multi-head attention 中,score 的实际形状通常是:

[B, H, T, T]
序列长度翻倍时,T x T score 矩阵的元素数量变为原来的 4 倍。这使 self-attention 在长上下文场景中成本显著增加。 Transformer 的训练并行性来自矩阵化计算。所有位置的 Q/K/V 投影可以并行完成,所有 query-key 匹配也可以通过大矩阵乘法并行完成。相比 RNN 的顺序递推,这种计算模式更适合 GPU/TPU。

16. 与 RNN 和 CNN 的对比

RNN、CNN、self-attention 都可以用于序列建模,但建模方式不同。 RNN 按时间步递推,天然适合流式输入,但序列维度并行性较弱,长距离依赖需要经过多步传播。 CNN 通过局部窗口提取特征,并行性较好,但单层感受野有限。长距离依赖需要堆叠多层、扩大卷积核或使用膨胀卷积。 self-attention 允许任意两个位置在一层内直接交互,长距离路径短,并行性强。主要代价是序列长度二次复杂度。 对比总结:

RNN: 顺序递推,流式友好,并行性弱。
CNN: 局部建模,并行性好,长距离依赖依赖层数扩展。
Self-attention: 全局动态建模,并行性好,长序列成本高。

17. 最小实现参考

下面是 scaled dot-product attention 的 PyTorch 风格伪代码:

import math
import torch
import torch.nn.functional as F

def scaled_dot_product_attention(q, k, v, attn_mask=None, dropout_p=0.0, training=True):
    # q: [B, H, T_q, d_head]
    # k: [B, H, T_k, d_head]
    # v: [B, H, T_k, d_v]
    d_head = q.size(-1)

    scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_head)

    if attn_mask is not None:
        scores = scores.masked_fill(attn_mask == 0, float("-inf"))

    weights = torch.softmax(scores, dim=-1)
    weights = F.dropout(weights, p=dropout_p, training=training)

    out = torch.matmul(weights, v)
    return out, weights
实现要点: - k.transpose(-2, -1) 将 key 的最后两个维度从 [T_k, d_head] 变为 [d_head, T_k]

  • scores 的 shape 是 [B, H, T_q, T_k]

  • softmax 使用 dim=-1,表示每个 query 对所有 key 归一化。

  • mask 必须能 broadcast 到 score 的形状。

  • attention dropout 通常作用在 softmax 后的 attention weights 上。

  • 输出 shape 是 [B, H, T_q, d_v]

18. 数值稳定与调试要点

attention 实现中常见的数值问题包括 NaN、softmax 饱和、mask 语义反转和 shape 错位。 NaN 常见来源: - 某一行 query 对应的所有 key 都被 mask,导致 softmax 输入全为 -inf

  • mixed precision 下使用过大的负数造成溢出或 kernel 不兼容。

  • 忘记除以 sqrt(d_head),score 过大。

  • 上游 Q/K/V 已经出现 inf 或 NaN。

  • 学习率过大或归一化层异常导致激活爆炸。

shape 错误常见来源: - multi-head reshape 后没有 transpose 到 [B, H, T, d_head]

  • K 转置维度错误。

  • mask 维度无法正确 broadcast。

  • softmax 沿错误维度执行。

最小调试策略: - 用很小的 BHT 构造可打印样例。

  • 打印 score、mask、weights 的 shape。

  • 检查 softmax 后每行和是否接近 1。

  • 检查 mask 位置的权重是否接近 0。

  • 与框架内置 scaled_dot_product_attention 在无 dropout 场景下对齐数值。

19. Attention 权重与可解释性

attention weights 表示某一层某个 head 中,每个 query 对不同 value 的聚合权重。它可以作为模型行为分析的线索,但不能直接等同于模型最终决策的因果解释。 Transformer 的最终输出经过多层 attention、MLP、残差连接和归一化。某个 head 的高权重只说明该层该 head 在聚合信息时偏向某个位置,不足以证明该位置对最终预测具有决定性影响。 更严谨的解释需要结合: - token masking 或遮挡实验

  • 反事实替换

  • gradient-based attribution

  • head ablation

  • activation patching

面试中适合表述为:attention 权重有分析价值,但不是完整解释。

20. 第一层知识闭环

self-attention 的知识闭环可以压缩为下面几条:

输入 X 通过三套线性投影得到 Q、K、V。
Q 和 K 的点积产生 token 之间的匹配分数。
分数除以 sqrt(d_k) 后进入 softmax,形成每个 query 对所有 key 的权重分布。
mask 用来屏蔽 padding 或未来位置。
权重乘 V 得到上下文相关表示。
multi-head attention 在多个子空间中并行执行上述过程。
self-attention 的优势是全局交互和并行训练,限制是序列长度二次复杂度。
掌握以上知识后,可以进入Self-Attention题单文章进行自测。

            预览时标签不可点





















    修改于


















<div class="