1180 字
6 分钟
Self Attention 计算
Self Attention 计算
1. Scaled Dot-Product Attention 公式
四步拆解:
2. 逐步数值实例
以 3 个词、 为例,手动走完全过程。
Step 0:输入
假设经过线性投影后,Q、K、V 矩阵为:
Step 1:计算 (相似度矩阵)
矩阵中第 行第 列 = 词 的 Q 与词 的 K 的点积 = 它们的相关度。
Step 2:缩放
为什么要除以 ?当 较大时,Q 和 K 的点积结果方差约为 ,值会很大。大值送入 Softmax 后会产生接近 one-hot 的分布(一个接近 1,其余接近 0),梯度几乎为零——训练卡住。除以 将方差稳定在 1 附近。
数学证明:若 ,则 的方差 = ,除以 后方差 = 1。
Step 3:Softmax 归一化
对每一行做 Softmax(每个词对所有词的注意力权重归一化为概率分布):
完整的注意力权重矩阵 :
Step 4:加权求和
解读:词 1 和词 2 均匀关注所有词(权重相等),词 3 更偏向自己(权重 0.476 > 0.262)。
3. 矩阵维度全链路
输入 X: (T, d_model) 例如 (3, 512) ↓ × W_QQ: (T, d_k) 例如 (3, 64) ↓ × W_KK: (T, d_k) 例如 (3, 64) ↓ × W_VV: (T, d_v) 例如 (3, 64)
Q × K^T: (T, T) 例如 (3, 3) —— 注意力分数矩阵÷ √d_k → Softmax:A: (T, T) 注意力权重矩阵
A × V: (T, d_v) 例如 (3, 64) —— 输出计算复杂度的矩阵乘法复杂度为 。当序列长度 很大时,这是 Transformer 的主要瓶颈。这也是为什么 LLM 的上下文窗口有限制(如 4K、8K、128K),以及为什么有很多研究致力于”高效注意力”(Flash Attention、稀疏注意力等)。
4. 完整代码实现
import subprocesssubprocess.check_call(["pip", "install", "numpy"])import numpy as np
def softmax(x, axis=-1): """数值稳定的 Softmax""" e_x = np.exp(x - np.max(x, axis=axis, keepdims=True)) return e_x / np.sum(e_x, axis=axis, keepdims=True)
def scaled_dot_product_attention(Q, K, V, mask=None): """Scaled Dot-Product Attention
Args: Q: (T_q, d_k) K: (T_k, d_k) V: (T_k, d_v) mask: (T_q, T_k) 可选掩码矩阵
Returns: output: (T_q, d_v) 注意力输出 weights: (T_q, T_k) 注意力权重(可视化用) """ d_k = Q.shape[-1]
# Step 1: Q × K^T scores = Q @ K.T # (T_q, T_k)
# Step 2: 缩放 scores = scores / np.sqrt(d_k)
# 可选: 掩码(将被掩盖位置设为 -inf,Softmax 后变 0) if mask is not None: scores = np.where(mask == 0, -1e9, scores)
# Step 3: Softmax weights = softmax(scores, axis=-1) # (T_q, T_k)
# Step 4: 加权求和 output = weights @ V # (T_q, d_v)
return output, weights
# ========== 用文中的例子验证 ==========Q = np.array([[1,0,1,0], [0,1,0,1], [1,1,0,0]], dtype=float)K = np.array([[0,1,1,0], [1,0,0,1], [1,1,0,0]], dtype=float)V = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]], dtype=float)
output, weights = scaled_dot_product_attention(Q, K, V)
print("注意力权重矩阵:")print(weights.round(3))print("\n输出:")print(output.round(2))5. 注意力可视化
注意力权重矩阵 是理解模型行为的利器。每一行是一个词的”注意力分布”:
猫 坐在 垫子 上猫 [0.10 0.05 0.80 0.05] ← 猫最关注"垫子"坐在 [0.40 0.05 0.45 0.10] ← 坐在关注"猫"和"垫子"垫子 [0.05 0.10 0.15 0.70] ← 垫子最关注"上"上 [0.08 0.72 0.15 0.05] ← 上最关注"坐在"实际使用在 PyTorch 中训练后,可以导出 weights 矩阵用 matplotlib.imshow 或 seaborn.heatmap 可视化。这在调试模型(看它是否学到了合理的语义关联)和论文展示中非常常用。
相关笔记
- 理解 Self Attention — 上一篇:直觉与概念
- 多头注意力 — 下一篇:从单头扩展到多头