Transformer拆解06:Self-Attention,一句话里的词怎样互相看见

我是老计。前面三篇把Embedding、QKV和缩放点积公式分别拆开了。现在把它们装起来,回答注意力机制最核心的问题:同一条序列里的每个位置,怎样根据自己的需要,从其他位置取回信息?
这就是Self-Attention,自注意力。
Self表示查询、键和值来自同一组序列表示。它不表示每个位置只看自己,不表示Q、K、V数值相同,也不表示位置之间的关注关系一定对称。
本篇用一个三位置小矩阵走完整个前向过程。数字是人为构造的教学示例,不代表训练后某个真实语言模型一定产生相同权重。
一,Self-Attention的输入和输出
设输入序列经过Embedding和位置表示后为:
text
X:[B, S, d_model]
自注意力用同一份X生成:
text
Q = XW_Q
K = XW_K
V = XW_V
然后计算:
text
A = Softmax(QK^T / sqrt(d_k))
Y = AV
如果不拆多头,形状是:
text
Q、K:[B, S, d_k]
V:[B, S, d_v]
A:[B, S, S]
Y:[B, S, d_v]
矩阵 A 的每一行对应一个查询位置,每一列对应一个被查询的键位置。第 i 行表示位置 i 如何在所有允许位置之间分配注意力。

二,手算一个三位置例子
为了让数字可追踪,取3个位置、每个输入3维,并投影到2维Q、K、V:
text
X = [[1,0,1],
[0,1,1],
[1,1,0]]
投影矩阵选为固定小矩阵。完整NumPy代码如下:
python
import numpy as np
np.set_printoptions(precision=4, suppress=True)
X = np.array([[1., 0., 1.],
[0., 1., 1.],
[1., 1., 0.]])
WQ = np.array([[1., 0.], [0., 1.], [1., 1.]])
WK = np.array([[1., 1.], [1., 0.], [0., 1.]])
WV = np.array([[1., 0.], [0., 1.], [1., -1.]])
Q, K, V = X @ WQ, X @ WK, X @ WV
scores = Q @ K.T / np.sqrt(Q.shape[-1])
stable = scores - scores.max(axis=-1, keepdims=True)
A = np.exp(stable)
A /= A.sum(axis=-1, keepdims=True)
Y = A @ V
print("attention:\n", A)
print("row sums:", A.sum(axis=-1))
print("output:\n", Y)
运行环境:Python 3,NumPy 1.26.4,CPU。实际输出:
text
attention:
[[0.284 0.14 0.576 ]
[0.576 0.14 0.284 ]
[0.4011 0.1978 0.4011]]
row sums: [1. 1. 1.]
output:
[[ 1.284 0.292 ]
[ 1.576 -0.292 ]
[ 1.4011 0. ]]
第一行到底在说什么
第一行 [0.284, 0.14, 0.576] 属于第一个查询位置。它表示第一个位置从三个V来源取信息时,分别使用约0.284、0.14、0.576的系数。
因此第一个输出为:
text
Y_1 = 0.284 V_1 + 0.14 V_2 + 0.576 V_3
它不是选中某一个位置后复制,而是做软加权汇总。第二行和第三行分别属于第二、第三个查询,彼此独立归一化。
每行和为1不代表每列和为1
Softmax沿键位置轴做,因此每一行和为1。列和没有必须等于1的约束。多个查询可以同时高度关注同一个键,也可能几乎没人关注某个键。
这个方向不能反。如果沿查询轴归一化,矩阵可能出现列和为1,但语义变成每个键在不同查询之间分配份额,不是标准自注意力的计算。
三,一个Token怎样"看见"上下文
假设序列是"服务发生严重故障"。某一层中,"故障"位置的Q可以与所有允许位置的K计算分数;"严重"的K若与它兼容,权重会较高;最终"故障"位置把"严重"等位置的V信息混入自己的新表示。
这只是直觉描述。真实模型中的Token切分、投影维度和头功能不会如此整齐,也不能仅凭某个注意力头的高权重,断言模型形成了完整语法关系。
表示是逐层更新的
第1层的QKV来自输入表示,第2层来自第1层输出。一个位置在前一层汇总上下文后,下一层用更新后的表示再次生成QKV。因此多层Transformer不是反复使用一张固定注意力图,而是逐层重新判断关系。
残差连接还会把子层输入沿主路径保留下来。最终表示既包含注意力汇总,也包含原表示和前馈网络加工,不能只用一张A矩阵解释全部输出。
注意力矩阵描述的是某一层、某一头、某次前向中V怎样被加权,不是整台模型的完整思考过程。
四,没有位置表示时,顺序从哪里来
纯自注意力只根据输入内容做相同的线性投影和位置间匹配。如果把输入位置整体重新排列,输出会按同样顺序重新排列。这叫排列等变性。
设排列矩阵为P,忽略Mask:
text
Attention(PX) = P Attention(X)
直觉是:交换输入行,会同步交换Q、K、V的行,关系矩阵的行列一起重排,最后输出也跟着重排。模型知道"这些元素怎样互相关联",却没有额外依据区分原先谁在第1位。
用NumPy验证排列等变
python
import numpy as np
rng = np.random.default_rng(67)
X = rng.normal(size=(4, 3))
WQ = rng.normal(size=(3, 2))
WK = rng.normal(size=(3, 2))
WV = rng.normal(size=(3, 2))
def attention(x):
q, k, v = x @ WQ, x @ WK, x @ WV
scores = q @ k.T / np.sqrt(q.shape[-1])
scores -= scores.max(axis=-1, keepdims=True)
weights = np.exp(scores)
weights /= weights.sum(axis=-1, keepdims=True)
return weights @ v
perm = np.array([2, 0, 3, 1])
y = attention(X)
y_perm = attention(X[perm])
print("max permutation error:", np.max(np.abs(y_perm - y[perm])))
print("output shape:", y.shape)
实际输出:
text
max permutation error: 1.1102230246251565e-16
output shape: (4, 2)
误差处在浮点舍入量级,说明输出只是跟随输入排列。加入绝对位置编码后,若只排列Token而不同时排列位置表示,等变关系会被打破;RoPE则通过QK关系注入相对位置信息。第09篇会细讲。

五,双向自注意力、因果自注意力与Padding
Self-Attention只说明QKV同源,不决定哪些位置允许互相看见。可见范围由Mask和任务定义。
Encoder式双向自注意力
没有因果限制时,每个有效查询可以读取整条输入的有效位置,包括左边和右边。BERT类Encoder常用这种结构,适合理解完整输入。
"可以看见"不等于"权重一定很高"。Mask只规定合法连接,具体权重仍由QK分数决定。
Decoder式因果自注意力
GPT类Decoder要求位置 t 只能读取不晚于 t 的位置。未来位置在Softmax前被Mask成负无穷。训练时整段输入可并行送入,但每行可见范围不同;推理时未来Token尚不存在,仍需逐步生成。
Padding Mask
批次中较短序列补齐后,真实查询不应从Padding键位置取信息。Padding Mask通常遮挡键列。是否还要处理Padding查询行和损失位置,要结合框架实现和任务,不能只看注意力权重一层。
这三种情况可以同时出现。例如Decoder批次既需要Causal Mask防未来,又需要Padding Mask忽略补齐键。
六,自注意力矩阵为什么不必对称
虽然QKV同源,W_Q 和 W_K 通常不同,因此:
text
q_i k_j^T generally differs from q_j k_i^T
即使未缩放分数偶然对称,逐行Softmax的归一化分母也可能不同,最终权重不保证对称。
语言关系本来也常有方向。"它"关注前面的实体来确定指代,不要求那个实体以同样强度反向关注"它"。
此外,Causal Mask会显式打破双向连接,未来位置对当前不可见,而后面位置可以看前面,更不可能对称。
七,自注意力的计算与存储成本
标准全局自注意力需要构造或等价计算 S×S 关系。单头分数张量形状为 [B, S, S],多头为 [B, H, S, S]。
序列长度翻倍,关系元素数量变为4倍。训练时还可能保存权重或中间量供反向传播,显存压力明显。现代融合内核可以减少中间数据写回,但标准全局注意力的两两关系算术规模仍是二次的。
自回归推理时,每个新Token只产生一个新查询,对历史所有键做匹配,单步关系数量随历史长度线性增长;生成整个长度的累计工作仍会增长。KV Cache避免重复计算历史K/V,却不取消当前查询读取全部历史键值的需要。
八,从关系图理解可见范围
可以把自注意力看成一张有向图:每个Token位置是节点,允许关注的方向是一条边,注意力权重是边上的动态系数。
双向图、因果图和Padding删边
- 双向Encoder在有效位置之间通常接近全连接有向图;
- 因果Decoder只保留从当前位置指向自身和过去的边;
- Padding Mask删除所有有效查询指向Padding键的边;
- 局部注意力只保留窗口内边;
- 稀疏注意力按规则保留部分远距离边。
这个视角能把两个概念分开:Mask决定边存不存在,QK和Softmax决定现有边权重多大。 Mask是硬约束,权重是输入相关的软选择。
如果一条边被Mask,不管QK分数原本多高,它都不应参与汇总;如果边被允许,也不代表模型一定关注它,权重仍可能接近0。
多层如何扩大信息传播
在全局自注意力中,一层就允许任意位置直接交换信息。局部或稀疏注意力中,一层只能沿有限边传播,但堆叠多层后,信息可经过中间节点到达更远位置。
因此讨论"上下文窗口"时,要区分理论可见边、层数传播路径和模型实际学到的有效依赖。一个位置在Mask上可见,不等于模型一定充分利用;不可见则是计算图上明确无法直接读取。

九,注意力输出还不是Block最终输出
单头Self-Attention得到 Y=AV 后,完整Transformer还会经历多头拼接、输出投影、Dropout、残差连接、归一化和前馈网络。不能把Y直接等同于这一层最终表示。
残差为什么影响解释
假设注意力子层输出为 F(X),残差路径会形成类似:
text
X + F(X)
即使某个位置的注意力几乎不从自身V取信息,原始位置表示仍可通过残差主路径保留。于是"注意力权重里自身很低"不等于模型丢掉了自身内容。
同样,某个来源V权重高,只说明它对注意力分支贡献大。最终表示还包含残差与后续非线性加工。
批次和头各自独立归一化
带多头后权重形状为 [B,H,S,S]。每个批次、每个头、每个查询行分别做Softmax:
- 不同样本之间不共享一张权重表;
- 不同头之间不在同一次Softmax里竞争;
- 一个头关注局部、另一个头关注远处是允许的;
- 拼接后还要由输出投影混合各头信息。
第07篇会严格追踪这条形状链。
十,注意力矩阵是一张输入相关的混合表
Softmax后的注意力矩阵每行非负且和为1,可以看成一张行随机矩阵。左乘V时,每个输出位置都从允许的V行中做一次凸组合。
但这不等于整个Transformer只能"平均"已有信息。原因包括:
- Q、K、V本身是学习到的线性投影,V已经改变了表示空间;
- 多个头分别混合不同V子空间,拼接后再经过输出投影;
- 残差把原表示加入结果;
- 前馈网络提供逐位置非线性变换;
- 多层堆叠会反复生成新的QKV和新的混合矩阵。
因此,单头的一次 AV 是加权平均,整台Transformer不是一个简单平均器。
注意力权重随输入变化
卷积核权重训练后固定,而注意力混合系数A会随当前输入变化。参数矩阵WQ、WK训练后固定,但不同句子产生不同Q和K,进而产生不同A。这种"参数固定、路由动态"的特点,是注意力能按内容选择信息的重要来源。
当然,动态不等于没有结构约束。Mask规定哪些边不能出现,位置表示影响位置关系,头维度和参数决定可表达空间,训练数据决定模型最终学到什么。
自注意力不是把所有词无差别混在一起,而是为每个输入、每层、每头动态生成一张受Mask约束的信息混合表。
十一,常见误区
误区一:Self表示只关注自己
Self表示QKV同源。没有Mask时,每个位置通常可以关注序列内所有位置。
误区二:Self表示Q等于K等于V
三者使用不同投影矩阵,数值通常不同。
误区三:注意力矩阵一定对称
QK角色、参数和逐行Softmax都不要求对称,因果Mask还会进一步制造方向性。
误区四:权重最高的位置就是唯一答案
输出通常是所有允许V的软加权和,多层残差和前馈还会继续加工。
误区五:自注意力天然知道顺序
纯内容自注意力具有排列等变性,需要位置编码、RoPE或其他机制注入顺序。
小结
Self-Attention让同一序列的每个位置生成Q、K、V。每个查询与所有允许键计算分数,沿键轴Softmax,再加权汇总V。注意力矩阵每行属于一个查询、行和为1,列和不受约束。
Self只表示QKV同源,不表示三者相同、只看自己或矩阵对称。没有位置表示和Mask时,自注意力对输入排列具有等变性;双向、因果和Padding可见范围由不同Mask决定。
排列等变也不等于模型不能处理集合。若任务本来不关心顺序,等变结构反而合理;只有任务语义依赖先后时,才必须引入位置或其他顺序信号。
下一篇把一个头扩展为多个头,严格追踪拆头、转置、并行注意力、拼接和输出投影的每一步形状。
延伸阅读
- Vaswani等,《Attention Is All You Need》,自注意力章节:arxiv.org/abs/1706.03762
- PyTorch官方文档,scaled_dot_product_attention:pytorch.org/docs/stable/generated/torch.nn.functional.scaled_dot_product_attention.html
- The Annotated Transformer:nlp.seas.harvard.edu/annotated-transformer
(本文为技术学习与经验分享,代码用于理解机制。框架与实现可能随版本变化,具体用法请以官方最新文档为准。)
版权声明:本文版权归作者老计个人所有。欢迎个人学习阅读,未经授权,禁止转载、摘编、搬运或用于任何商业用途,违者将追究责任。转载请联系作者Jich。