自注意力机制到底是怎么算的?——Q/K/V 与加权求和

前两篇我们反复提到一个词:注意力机制。它让每个词去看所有其他词、它让 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)——既然一个注意力头能看到一种模式,为什么不用多个头同时看不同类型的模式?