前两篇我们反复提到一个词:注意力机制。它让每个词去看所有其他词、它让 Transformer 能并行计算、它是整个大模型大厦的基石。但「看」到底是怎么看的?这篇我们把这个「看」的过程彻底拆开。
核心直觉:加权求和
注意力机制的本质,就四个字:加权求和。
想象你在一个房间里,周围有十个人在说话。你想听清楚其中一个人在说什么,你不是把所有人说的话平均一下——你会聚焦在目标那个人身上,其他声音自动被你「调低音量」。
注意力机制做的事完全一样:对序列中每个位置,根据它和其他位置的相关性,给不同位置分配不同的权重,然后把这些位置的信息按权重加起来。
从词向量开始
在进入注意力计算之前,输入文本已经变成了数字——每个 token 被映射成一个向量。假设我们有三个词:「我」「爱」「AI」,每个词用一个 4 维向量表示:
我 → [0.1, 0.3, 0.7, 0.2] 爱 → [0.5, 0.1, 0.9, 0.4] AI → [0.8, 0.6, 0.2, 0.9]
这就是输入矩阵 X,形状是 (3, 4)——3 个 token,每个 4 维。
Q、K、V 从哪来
每个 token 的向量不直接参与注意力计算。而是先通过三个不同的线性变换(其实就是矩阵乘法),生成三个新向量:
- Query(查询)——当前 token 想问什么
- Key(键)——当前 token 有什么可被关注的
- Value(值)——当前 token 要传递什么信息
这三个向量是通过三个不同的权重矩阵 Wq、Wk、Wv 乘以输入向量得到的:
Q = X × Wq K = X × Wk V = X × Wv
Wq、Wk、Wv 都是可训练的参数——在训练过程中,模型自己学会什么样的 Query 能匹配什么样的 Key,什么样的 Value 值得传递。
注意力分数的计算
有了 Q、K、V 之后,注意力计算分四步:
第一步:计算相似度
用第一个词的 Query 去点乘(dot product)所有词的 Key:
score(我, 我) = Q[我] · K[我] = 0.8 score(我, 爱) = Q[我] · K[爱] = 1.2 score(我, AI) = Q[我] · K[AI] = 0.3
点积的结果越大,说明 Query 和 Key 越相似,两个词越相关。
第二步:缩放
如果向量维度很大,点积的结果会很大,导致下一步 Softmax 的输出极端化(一个接近 1,其他接近 0)。所以除以向量维度的平方根:
score = score / √d_k
这一步叫 Scaled Dot-Product Attention 中的「Scaled」。
第三步:Softmax 归一化
把所有分数变成 0 到 1 之间、总和为 1 的概率分布:
attention_weights = softmax(scaled_scores)
比如结果可能是:
[我和我: 0.3, 我和爱: 0.55, 我和AI: 0.15]
这说明「我」和「爱」的关联最强——在「我爱AI」这句话里,「爱」确实和「我」的关系最大。
第四步:加权求和
用这些权重去乘对应的 Value 向量,然后加起来:
output[我] = 0.3 × V[我] + 0.55 × V[爱] + 0.15 × V[AI]
这就是「我」这个位置在「看」了所有词之后的最终表示。它包含了「我」自己的信息,但更重要的是,它把「爱」的信息以 0.55 的权重融合进来了。
一句话总结自注意力
自注意力就是:每个位置,通过 Query 去问所有位置「你跟我有多相关」,然后用相关度作为权重,把所有位置的信息融合起来。
这个操作对序列中的每个位置同时做一次,所以可以完全并行。这也是 Transformer 相比 RNN 最大的优势。
矩阵形式
实际实现中,不是写循环一个个算,而是直接用矩阵乘法一次算完:
Attention(Q, K, V) = softmax(Q × K^T / √d_k) × V
这个公式是整个 Transformer 的核心。你会在每一篇相关论文里看到它。
直观理解
自注意力机制真正优雅的地方在于:它没有给模型预设任何「应该关注什么」的规则。模型完全从数据中学习——在翻译任务中,它学会把主语和动词关联在一起;在代码任务中,它学会把变量名和它的使用位置关联在一起。所有关联模式都是训练出来的,不是人写死的。
下一篇讲多头注意力(Multi-Head Attention)——既然一个注意力头能看到一种模式,为什么不用多个头同时看不同类型的模式?
