Self-Attention 是什么
Self-Attention 是 Transformer 的核心机制。
它的作用可以先用一句话理解:
让序列中的每个位置,根据当前内容,动态决定应该参考哪些其他位置。经典的 Attention 公式是:
这个公式可以先粗略拆成三步:
- :计算每个位置和其他位置的匹配程度。
- :把匹配分数变成注意力权重。
- 乘以 :按照注意力权重读取并汇总信息。
后面的小节会逐步解释这里的 Q、K、V、softmax 和因果 mask。
在 Decoder-only 模型里,它通常还会加上因果限制:只能看自己和前面的 token,不能看未来 token。
为什么需要 Self-Attention
假设输入是:
一只 / 蓝色 / 怪兽 / 走进 / 森林如果只看每个 token 自己:
一只:数量
蓝色:颜色
怪兽:角色或生物
走进:动作
森林:地点模型很难理解它们合起来是:
一只蓝色怪兽走进森林Self-Attention 的作用,就是让每个位置可以参考前文中相关的 token。比如“怪兽”可以参考“一只”和“蓝色”,“走进”可以参考“怪兽”,“森林”可以参考“走进”和前面的主角,从而形成更完整的上下文状态。
输入输出形状
假设输入有 10 个 token,每个 token 是 128 维向量:
输入:10×128Self-Attention 输出通常仍然是:
输出:10×128形状不变,但每一行向量都可能融合了其他位置的信息。
在 Decoder-only 模型中,第 (i) 个位置只能融合第 (0) 到第 (i) 个位置的信息。
Q、K、V 是什么
Self-Attention 会先把每个输入向量变成三种向量:
Q:Query,表示“我想找什么信息”
K:Key,表示“我这里有什么信息可被匹配”
V:Value,表示“如果别人关注我,我实际提供什么内容”可以用一个查资料的比喻:
Query:我提出的问题
Key:每份资料的标签
Value:资料的正文内容某个位置会拿自己的 Query 去和其他位置的 Key 比较,看哪些位置更相关,然后再按相关程度读取那些位置的 Value。
Q/K/V 如何得到
Q、K、V 通常由输入矩阵 (X) 通过线性变换得到:
其中:
X:输入向量矩阵
W_Q, W_K, W_V:模型训练出来的权重矩阵如果输入是 ,并且 Q/K/V 维度也是 128,那么:
X:10×128
Q:10×128
K:10×128
V:10×128真实模型里常常会把这些向量拆成多个 head,也就是 Multi-Head Attention。这里先只看单个 head 的直觉。
注意力分数:Query 和 Key 的匹配程度
有了 Q 和 K 后,模型会计算每个位置对其他位置的关注程度。
常见做法是点积:
含义是:
第 i 个位置的 Query
和第 j 个位置的 Key
有多匹配所有位置两两比较后,会得到一个分数矩阵:
10×10比如第 10 行表示:
第 10 个位置分别想关注第 1 到第 10 个位置的程度。用“一只 / 蓝色 / 怪兽 / 走进 / 森林”做一个例子,分数矩阵可能长这样:
| Query \ Key | 一只 | 蓝色 | 怪兽 | 走进 | 森林 |
|---|---|---|---|---|---|
| 一只 | 2.4 | -0.6 | 0.2 | -1.1 | -0.8 |
| 蓝色 | 0.8 | 2.7 | 1.9 | -0.4 | 0.1 |
| 怪兽 | 1.4 | 3.1 | 3.5 | 0.2 | 0.4 |
| 走进 | 0.5 | 0.7 | 3.4 | 2.9 | 1.8 |
| 森林 | 0.2 | 0.6 | 2.3 | 3.6 | 3.0 |
这里每一行表示“当前位置想关注其他位置的程度”。
比如最后一行是 Query: 森林,它表示“森林”这个位置在看前文时,对几个位置的匹配分数:
和“一只”的匹配分数:0.2
和“蓝色”的匹配分数:0.6
和“怪兽”的匹配分数:2.3
和“走进”的匹配分数:3.6
和“森林”自己的匹配分数:3.0分数越高,说明当前位置越倾向于从那个位置读取信息。这个例子里,“森林”比较关注“走进”和自己,也会关注“怪兽”,因为“谁走进了森林”对当前位置很重要。
缩放和 softmax
实际计算中,attention 分数通常会除以一个缩放因子:
其中 (d_k) 是 Key 向量的维度。
缩放的目的是让分数范围更稳定,避免点积结果太大。
然后模型会对分数做 softmax:
softmax 会把分数变成概率一样的权重。
比如某个位置可能得到:
关注“怪兽”:25%
关注“走进”:45%
关注“森林”:30%这些权重表示:当前位置应该从哪些 token 那里读更多信息。
加权求和:读取 Value
得到 attention weights 后,模型会用这些权重去加权求和 Value:
直观理解:
更相关的位置,Value 占比更高。
不相关的位置,Value 占比更低。对于“一只 / 蓝色 / 怪兽 / 走进 / 森林”来说,“森林”这个位置可能会比较关注:
怪兽
走进
森林于是它的新向量会融合这些位置的 Value 信息,形成更完整的“怪兽走进森林”这一上下文状态。
因果 Mask:不能看未来
Decoder-only 模型生成文本时,不能偷看未来 token。
如果输入是:
t0, t1, t2, t3第 2 个位置只能看:
t0, t1, t2不能看:
t3这通常通过 causal mask 实现。
可以把允许看的范围写成:
第 0 个位置:t0
第 1 个位置:t0, t1
第 2 个位置:t0, t1, t2
第 3 个位置:t0, t1, t2, t3继续用“一只 / 蓝色 / 怪兽 / 走进 / 森林”的例子,因果 mask 可以画成这样:
| Query \ Key | 一只 | 蓝色 | 怪兽 | 走进 | 森林 |
|---|---|---|---|---|---|
| 一只 | 1 | 0 | 0 | 0 | 0 |
| 蓝色 | 1 | 1 | 0 | 0 | 0 |
| 怪兽 | 1 | 1 | 1 | 0 | 0 |
| 走进 | 1 | 1 | 1 | 1 | 0 |
| 森林 | 1 | 1 | 1 | 1 | 1 |
也就是说:
1 表示可以看。
0 表示不能看。
“一只”这个位置只能看自己。
“蓝色”这个位置可以看“一只”和自己。
“怪兽”这个位置可以看“一只”“蓝色”和自己。
“走进”这个位置可以看“一只”“蓝色”“怪兽”和自己。
“森林”这个位置可以看前面全部 token 和自己。需要注意:这个 1/0 表格只是表示“可见性”。在真正计算 attention 时,通常不是简单把不能看的分数改成 0,而是把不能看的位置改成 -inf 或一个非常小的数,比如 -1e9。
原因是后面要经过 softmax。如果把不能看的位置设成 0,它仍然可能分到概率:
scores = [2.0, 0.0, 0.0]
softmax(scores) ≈ [0.79, 0.10, 0.10]这两个 0 分位置仍然有注意力权重。
如果把不能看的位置设成 -inf:
scores = [2.0, -inf, -inf]
softmax(scores) = [1.0, 0.0, 0.0]这样不能看的位置经过 softmax 后,注意力权重才会真正变成 0。
这样模型训练和推理时都符合“从左到右生成”的规则。
小结
Self-Attention 的流程可以概括为:
输入向量
-> 生成 Q / K / V
-> 用 Q 和 K 计算匹配分数
-> softmax 得到注意力权重
-> 用权重加权求和 V
-> 输出融合上下文后的向量它的核心价值是:
让每个位置动态选择应该参考哪些 token。在 Decoder-only 模型中,它还会受到因果 mask 限制,只能看自己和前面的 token。