Q 和 K 怎么算匹不匹配?为什么偏偏用点积?
本课一问:Q 和 K 怎么算匹不匹配?为什么偏偏用点积?
这一课先不碰:分数怎么变成权重、为什么要除以 √d(第 3 课);谁能看谁(第 4 课)。
从已有出发
上一课结束时,每个 token 都有了自己的 q 和 k。现在需要一个函数 f(q, k),吃进两个向量、吐出一个数:越大表示「这个 k 越符合这个 q 要找的」。对「给了」来说,要对 5 个 token 各算一次;对整句来说,是 5 × 5 = 25 次。在锚点模型的 256K 上下文里,一个头就是 262,144² 次。所以 f 除了「算得对」,还得「算得快、不占地方」。
先自己设计:能想到的几种打分
- 欧氏距离:−|q − k|²,越近越匹配。
- 余弦:q·k ÷ (|q||k|),只看方向。
- 再过一个小网络:把 q、k 各投影一次、相加、过 tanh、再和一个向量 v 点积。这是注意力最早的形态,叫加性注意力(Bahdanau 等 2014 年用在机器翻译里) 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条原文把它作为「软搜索源句相关位置」的对齐模型提出,效果由翻译实验支持 出处Bahdanau et al. 2014 ↗需联网 。它最灵活:小网络想学什么函数都行。
最灵活的那个先上场。
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
点积把 5 个 q 摞成矩阵 Q(每行一个 token),5 个 k 摞成 K,一次矩阵乘 Q·KT 就得到全部 5 × 5 个分数,中间不产生额外张量。矩阵乘是 GPU 上被优化得最狠的运算。
「理论复杂度差不多,但点积在实践中快得多、省空间得多,因为它可以用高度优化的矩阵乘实现」——这是 Transformer 原文选它的理由 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条原文:similar in theoretical complexity, dot-product attention is much faster and more space-efficient in practice。这是工程实测的说法 出处Vaswani et al. 2017 §3.2.1 ↗ 。
改进到点积:它到底在算什么
点积 q·k = q1k1 + q2k2 + … + qdkd,逐维相乘再相加。它等于 |q| × |k| × cos θ 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条余弦定理 / 内积定义,任何维数都成立 出处待补 :
- 方向越一致(θ 越小),分数越大;方向相反,分数为负。这就是「匹配」的几何含义。
- 长度也算在里面。和余弦比,点积多了 |q||k|:一个 q 越长,它对所有 k 的分数都被同比放大,下一课会看到这会让 softmax 更「坚决」;一个 k 越长,它越容易被所有人注意到。
- 和负距离平方只差两项:−|q − k|² = 2·q·k − |q|² − |k|²。对同一个 q,|q|² 是常数,差别只在 −|k|²——距离会惩罚长的 k,点积不会。
下面把一个分数彻底拆开。先用对话样本的 2 维例子手算三个数,再切到教学世界看 4 维。
试三件事:① 在 2 维例子里核对 [2, 3]·[2, 2] = 2×2 + 3×2 = 10,再看「汽车」为什么是 −4(方向几乎相反);② 切到教学世界,选「给了」对「苹果」,看 4 个乘积里哪一项贡献最大;③ 在「三种匹配度」表里找排名不一致的地方,想一想是 |k| 的哪一项造成的。
从一行到整个矩阵。上面算的是一个 q 对一个 k。真实计算把全部 q 摞成 Q、全部 k 摞成 K,一次矩阵乘得到 T×T 的分数矩阵:第 i 行第 j 列 = 第 i 个 token 的 q 点乘第 j 个 token 的 k。下面是锚点模型一个头的真实形状:
| 维度 | 含义 | 真实规模 | 小规模 |
|---|---|---|---|
| T | 序列长度(贯穿例句) | 5 | 5 |
| dh | 每头维数 head_dim | 256 | 4 |
⚠ 常见误解
「点积是唯一正确的选择。」不是。距离、余弦、加性网络都能用来打分,数学上没有哪条定理规定必须是点积。点积胜出靠的是三件事叠加:几何含义清楚、能写成一次矩阵乘、实验效果不差。现代模型还在往回找余弦的好处:锚点 Qwen3.8 对每个头的 q、k 先做一次 RMSNorm(q_norm、k_norm,各 256 维),把长度钉在固定尺度上,点积就接近「余弦 × 一个学出来的尺度」 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条QK-Norm 的动机是控制注意力 logit 的尺度、稳定训练;它让点积主要反映方向。是否更好由实验决定,C8 讲 出处待补 。
注意力的演进,你在这里
① 训练时:一个 token 怎么从别的 token 那里取信息
- 解决了能读任意长的序列,参数与长度无关;手里只带一个固定大小的状态 h。卡在ht 要等 ht−1 算完:T = 4,096 时每层 4,096 步不能并行,第 1 个 token 的信息要经过 4,095 次更新才到最后;翻译时整句还得挤进一个定长向量——Bahdanau 2014 说的瓶颈。展开岔路 B1
- 解决了解码器每一步回头给源句的每个位置打分、加权取回,不再只靠那一个定长向量:score = vT·tanh(W1q + W2k)。卡在每一对都要单独过 tanh,交不给矩阵乘单元;朴素实现要先落一个 T×T×da 的中间张量:T = 4,096、da = 256 时单头单层 8.0 GiB(BF16),点积只留 T×T 个分数,32 MiB。它还长在 RNN 翻译模型里,串行没解决。展开第 2 课
- 解决了直接比 embedding 时「给了」最像的是它自己(余弦 1.000),小明、苹果、小王排在最后;投影成 Q、K 再点积,它自己排到最后、前三名是苹果、小王、小明,全部配对一次矩阵乘 QKT;取回用单独的 V——两个人名的 K 余弦 0.995,取回 K 分不清是谁。卡在不缩放:锚点每头 256 维,点积标准差 √256 = 16,同一行分差十几二十是常态(e20 ≈ 4.9 亿),softmax 几乎独占,梯度传不回 WQ、WK。
- 解决了softmax 把分数变成正的、和为 1 的权重,只看分差(按占比分,对话样本的 10、3、−4 会给「汽车」−44.4%);除以 √dk = 16,点积方差回到 1。卡在训练时整句一次喂进去:不加掩码,「给了」一行有 32.5% 的注意力直接落在它要预测的答案「小王」上,loss 低得虚假。展开第 3 课
- 解决了旋转后 q·k 只依赖距离 j − i;V 不参与打分,不转(转了会把绝对位置混进取回的内容)。锚点每头 256 维里只转 64 维。卡在旋转矩阵随位置变,夹在展开矩阵和缓存之间就没法事先并进 Q:MLA 因此把 RoPE 单独拆出 64 维另存(DeepSeek-V2 config,第 10 课)。
- 解决了标准 softmax 逼每行分完 100%:「苹果」一行 3 个分数只差零点几(0.04、0.36、0.00),照样分成 30% / 41% / 29%;分母加可学习的 sink logit,或在输出上乘门,这个头就能「这次不取」——锚点开着输出门(attn_output_gate = true)。卡在出口的效果只有实验证据:Gated Attention 原文说门控「缓解」sink,不是消除;DeepSeek-V4 没给 sink logit 的单独消融。展开岔路 B4
② 生成时:K、V 怎么越存越少
- 解决了缓存直接除以头数:锚点形状下每 token 16 KiB,262,144 上下文 4 GiB。卡在质量会掉、训练可能不稳:GQA 论文表 1(T5-XXL,七个任务平均)MHA 47.2 → MQA 46.6。展开第 9 课
- 解决了24 个 Q 头分 4 组、每组 6 个共用 1 个 KV 头(组数 = KV 头数):每 token 64 KiB、262,144 上下文 16 GiB;T5-XXL 上 GQA-8 平均 47.1、推理 0.28 s,质量接近 MHA(47.2、1.51 s),速度接近 MQA(0.24 s)。卡在再减 KV 头就回到 MQA:DeepSeek LLM 67B 已是 GQA-8,每 token 仍要 389,120 B(每头一份的 MHA 是 3,112,960 B),1M token 单条请求 380 GiB。展开第 9 课
- 解决了不存 K、V,每层每 token 只存 512 维潜向量 + 64 维 RoPE = 576 个数,用时再展开:DeepSeek-V2(60 层)每 token 69,120 B、V3(61 层)70,272 B;相对 67B 按 BF16 降 82.2%,官方「−93.3%」还含约 6 bit 的 KV 量化。卡在仍随长度线性增长,打分仍是 T²:GLM-5.3 同是 576 个数、78 层,每 token 89,856 B,原生 1,048,576 上下文时单条请求只算潜向量就要 87.75 GiB。展开第 10 课
- 解决了换掉「按长度存」本身:锚点 64 层里 48 层不存 KV,只存固定大小的状态(FP32 共 144 MiB),与上下文长度无关(这些层是什么、怎么更新,C11 第 4 课揭晓);DeepSeek-V4.1 叠加压缩、稀疏与低精度,官方报告全局 KV 890 B/token。卡在固定状态装不下任意多对键值,锚点仍留 16 层全注意力;稀疏注意力每步只读挑出来的一部分,挑本身也有代价——各自省了什么、没省什么,C11 讲。
证据三问
同一个结论「用点积打分」,三层证据各说各的:
- 数学性质:q·k = |q||k|cos θ,方向越近越大 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条内积的几何定义 出处待补 ;全部配对等于一次矩阵乘 Q·Kᵀ 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条矩阵乘的定义:(QKᵀ)_ij = q_i·k_j 出处待补 。
- 效率:与加性注意力复杂度相近,但实践中快得多、省显存得多 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条工程实测;上面的计数器给出了显存差距的量级 出处Vaswani et al. 2017 §3.2.1 ↗ 。
- 效果:原文引用的对比实验说,dk 小时两者差不多,dk 大时不缩放的点积不如加性注意力 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条只在当时的翻译模型上比较过 出处Vaswani et al. 2017 §3.2.1 引 Britz et al. 2017 ↗ ——这正是下一课「除以 √d」的由来。
一句话
点积 = 方向对齐 × 长度;T 个 token 的全部配对,一次矩阵乘就算完。
下一个缺口
「给了」这一行的分数是 1.84、−1.04、2.2、−1.2、2.04(教学世界,除以 √4 之后)。有正有负、没有上界,不能直接当「关注多少」用。怎么把它变成一组像样的权重?→ 第 3 课:分数怎么变成「关注多少」的权重?
你可能会问
这一课还没有收到真实提问;你的提问会记到 05-问答记录/C3-L2.md。
🔍 余弦只看方向,不是更「公平」吗?为什么不用它?
「公平」在这里不一定是好事。点积里的长度是模型可以利用的自由度:q 越长,这一行的分数整体越大,softmax 越尖锐——相当于 token 自己决定「这次要不要坚决地只看一个」。纯余弦把这个自由度拿掉了,分数被夹在 [−1, 1] 里,softmax 后很难变尖,只能再乘一个温度把它放大。QK-Norm 走的是中间路线:先归一化防止长度失控,再用可学习的权重把尺度还给模型。
🔍 加性注意力现在还有人用吗?
本课程涉及的锚点与前沿模型(Qwen3.8、GLM-5.3、DeepSeek-V3)的全注意力都是缩放点积:MLA 也是,只是 K 由潜向量展开(第 10 课)。加性注意力留在 RNN 翻译模型的时代;它的思想——「让解码器每一步回头软搜索相关位置」——被整个继承了下来。