4. Query、Key、Value 概念详解
聊到注意力机制,有三个绕不开的概念:Query、Key、Value。说实话,我刚接触 Transformer 那会儿,就觉得这三个词太抽象了。什么“查询”、“键”、“值”,听着像数据库,又像字典查询。
但其实,它们的核心逻辑特别朴素。你想想看,我们去图书馆找书,是什么流程?你脑子里想的是“深度学习入门”这个念头——这就是 Query。你去索引柜里翻卡片,每个卡片上写着书名和位置——这就是 Key。你找到对应书架,抽出那本书——这就是 Value。
注意力机制的 QKV,干的完全是同一件事。
4.1 从数据库检索理解 QKV
我们先用最直观的类比把这三个角色说清楚。
| 角色 | 比喻 | 数学含义 |
|---|---|---|
| Query (Q) | 你脑子里要找的“主题” | 当前 token 发出的“我想找什么” |
| Key (K) | 图书索引卡上的“标签” | 每个 token 对外展示的“我是什么” |
| Value (V) | 书架上那本“书”的内容 | 每个 token 携带的“实际信息” |
这里面有个关键点:Query 和 Key 是用来算相似度的,Value 才是最后被加权求和的那个。我在项目中遇到过不少人,代码里把 Q 和 K 算清楚了,但最后拿 Q 去加权 V——那就全错了。
4.2 Q、K、V 的来源与维度
在实际的 Transformer 里,Q、K、V 都来自同一份输入。假设输入序列有 n 个 token,每个 token 的向量维度是 d_model。那我们会用三个权重矩阵去投影:
输入序列 X: shape [n, d_model]
W_Q: shape [d_model, d_k]
W_K: shape [d_model, d_k]
W_V: shape [d_model, d_v]
Q = X @ W_Q → [n, d_k]
K = X @ W_K → [n, d_k]
V = X @ W_V → [n, d_v]
为什么 Q 和 K 的投影维度都是 d_k?因为要算点积,维度必须相等。V 的维度 d_v 可以和 d_k 不同。不过在我实际调过的模型里,大部分场景下 d_k = d_v = d_model // num_heads,也就是每个注意力头的维度。
4.3 它们如何协作产生注意力
好,现在三个角色都到位了。它们怎么协作?流程分两步:
- 计算注意力权重: Q 和所有 K 做点积,再 Softmax 归一化。
- 加权聚合 V: 用上一步的权重去加权求和 V。
用公式表示就是:
Attention(Q, K, V) = softmax(Q @ K^T / sqrt(d_k)) @ V
其中:
Q @ K^T → [n, n] 相似度矩阵
/ sqrt(d_k) → 缩放,防止点积值过大
softmax(·) → 按行归一化,得到权重
@ V → 加权和,输出 [n, d_v]
我建议你把 Q @ K^T 这一步单独拿出来看。它的第 i 行第 j 列,就是第 i 个词(作为 Query)对第 j 个词(作为 Key)的匹配分数。这个矩阵,说白了就是整个注意力机制的灵魂。我调试模型时,80% 的问题都是从这里看出来的。
4.4 多头注意力中的 QKV
实际用的 Transformer 很少只做一个注意力头。多头注意力(Multi-Head Attention)把 Q、K、V 切分成 h 个头,每个头独立做注意力,最后再拼起来。
假设 h = 8:
Q: [n, d_model] → 拆成 8 个头,每个头 [n, d_k]
K: [n, d_model] → 拆成 8 个头,每个头 [n, d_k]
V: [n, d_model] → 拆成 8 个头,每个头 [n, d_v]
每个头独立计算 Attention_i
输出拼回 [n, h * d_v] → 再经过 W_O 投影回 [n, d_model]
这里有一个常见的误解:很多人以为每个头要学不同的 W_Q、W_K、W_V。实际上,只有一个大的 W_Q,只是把输出切开了而已。但 PyTorch 的 nn.MultiheadAttention 内部实现确实会为每个头维护独立的权重——这是为了并行计算效率做的优化,逻辑上没有本质区别。
4.5 一张图理清 QKV 全流程
下面这张图把 QKV 从输入到注意力输出的完整路径画出来了。我把它放在手边,每次写 attention 层之前瞄一眼。
4.6 实际项目中的一个避坑案例
我之前调过一个翻译模型,BLEU 值始终上不去。排查了梯度、学习率、数据预处理,都没问题。最后我把注意力权重矩阵可视化出来一看——好家伙,Q 和 K 的内积值全集中在 0 附近,Softmax 之后几乎成了均匀分布,模型根本没学到注意力。
原因很简单:初始化时 d_k 设得特别大,但没有做缩放(/ sqrt(d_k)),点积值爆炸到 Softmax 的饱和区,梯度全消失了。所以,缩放因子从来不是一个“可选项”,它是保证训练稳定的必要条件。
- Q 是“问”,K 是“索引”,V 是“内容”。
- Q 和 K 算权重(点积 + Softmax),V 被加权求和。
- 三个向量都来自输入投影,但维度可以不同(d_k 和 d_v)。
- 多头注意力只是把 QKV 拆成多份并行计算。
- 永远别忘记缩放 d_k。