python神经网络编程入门(三十一)——自注意力(Self-Attention):Q、K、V 三件套
2026/8/9 20:01:11 网站建设 项目流程

📍路标:本篇位于《从零构建 Transformer》系列第 2/16 章 · 基石篇
系列主线:注意力思想 → 自注意力 → 多头 → 位置编码 → 编解码架构 → 手撕实现 → 预训练模型 → 实战微调 → 知识收官
进度:▸基石篇(1-5·本篇)▸ 架构篇(6-10) ▸ 预训练篇(11-13) ▸ 实战篇(14-16)

上一篇(三十):Transformer 从 RNN 到注意力:模型为什么需要"瞄一眼"


引言:上一章问了"为什么",这一章回答"怎么瞄"

上一章我们把 RNN 的老底掀开:串行读、单一记忆,导致越读越忘。然后提出了治本思路——不"传"而"看",让模型回头给全句每个词打分加权。我们还用"我 love 猫"的小例子,手工指定了一个查询向量,跑出了 0.457 的最大权重落在最相关的词上。

但那个例子有个"作弊味":查询向量是我们手工拍的,不是模型自己算的。这章要做的,就是把这块"作弊"补上——让模型自己从输入里把 Q、K、V 三个向量算出来。这才是自注意力真正的样子,也是"词和词互相打探消息"的完整机制。

🎯本章目标

  1. 用生活类比彻底搞懂 Q(查询)、K(键)、V(值)三者的分工;
  2. 吃透关键公式:Q、K、V 是同一个输入乘三个权重矩阵得到的;
  3. 手写完整self_attention,一步步把每个词的上下文表示算出来;
  4. 看懂为什么"权重矩阵一变,模型爱看谁就变"——注意力是可学习的。

一、三件套:查询、键、值

先给 Q、K、V 一个最朴素的生活类比——查图书馆找书

  • Q(Query,查询):心里"现在要找什么"。比如"我要找讲深度学习的书"——这就是查询。
  • K(Key,键):每本书的"标签"。比如《Attention Is All You Need》的标签是"深度学习",《小王子》的标签是"童话"。
  • V(Value,值):每本书的"正文内容"。这才是真正要拿走的东西。

找书的过程就是:拿 Q(要什么)去和每本书的 K(标签)比对,越匹配的权重越高,然后按权重把对应的 V(内容)取出来加权合并。标签越像"要的",这本书的内容在心里的分量越大。

把这三者对应到自注意力里,一句话就能连起来:

自注意力三件套:Q 问、K 答、V 给Q 查询 Query我要看什么K 键 Key我是什么标签V 值 Value我提供什么内容打分取内容Q 和 K 配对打分 → softmax 成权重 → 加权取 V标签越像"我想要的",对应的内容分量越大
  • Q 负责"问":每个词都提出自己的问题(我要了解句子里哪些词);
  • K 负责"答身份":每个词都亮出自己的标签(我是谁、和什么有关);
  • V 负责"给内容":每个词都准备好自己的实质内容(我到底说了什么)。

于是"词和词互相对话"就具象成:每个词用自己的 Q 去匹配所有词的 K,得到一组权重,再按权重把所有词的 V 加权求和。这就是自注意力的完整流程。


二、核心公式:Q、K、V 从哪来?

关键问题来了:上面三个向量,到底怎么得到?答案极其简洁——都是同一个输入X XX分别乘三个可学习的权重矩阵

Q = X W q , K = X W k , V = X W v Q = X\,W_q, \qquad K = X\,W_k, \qquad V = X\,W_vQ=XWq,K=XWk,V=XWv

其中X XX是所有词的向量拼成的矩阵,W q , W k , W v W_q, W_k, W_vWq,Wk,Wv是三个不同的权重矩阵。注意:Q、K、V 来自同一个输入X XX,只是被三个不同的"滤镜"(权重矩阵)各滤了一遍,提取出三种不同角色。这就是为什么它叫"注意力"(Self-Attention)——注意力作用在"自己和自己"上,每个词都跟全句(包括自己)发生关系。

把这条公式画成一张数据流向图,一眼看清X XX变出三个分身:

同一个输入 X,三个权重矩阵,变出 Q / K / VX(3, 4) 所有词向量Q = X·Wq(3, 3) 查询K = X·Wk(3, 3) 键V = X·Wv(3, 3) 值Wq (4×3)Wk (4×3)Wv (4×3)同一个 X 分身成 Q/K/V,三个权重矩阵都是可学习的

为什么是"同一个X XX"?因为这正是"自"(Self)的含义:不是去外部找参考,而是每个词都在观察整句话里的其他词,包括它自己。Q、K、V 都从这句话本身提取,所以叫"自注意力"。


三、手撕自注意力:一步步把矩阵算出来

理论讲完了,直接上手。用小例子"我 love 猫"(3 个词,每个词 4 维向量),一步步把 Q/K/V 算出来,再算出注意力权重和最终的上下文表示。

第一步:准备输入X XX

把 3 个词编码成 4 维向量,拼成矩阵X ∈ R 3 × 4 X \in \mathbb{R}^{3\times4}XR3×4

importnumpyasnp X=np.array([[1.0,0.0,0.0,0.0],# 我[0.0,1.0,0.0,0.0],# love[0.0,0.0,1.0,0.2],# 猫])print("X.shape =",X.shape)# (3, 4):3 个词,每个 4 维
第二步:定义三个可学习权重矩阵

W q , W k , W v W_q,W_k,W_vWq,Wk,Wv都是4 × 3 4\times34×3(把 4 维输入降成 3 维的"查询/键/值"空间)。这里用随机初始化的值来演示(真实训练时它们会被梯度更新):

np.random.seed(42)Wq=np.random.randn(4,3)*0.3Wk=np.random.randn(4,3)*0.3Wv=np.random.randn(4,3)*0.3print("Wq.shape =",Wq.shape)# (4, 3)
第三步:算出 Q、K、V

一行矩阵乘法:

Q=X @ Wq# (3, 3)K=X @ Wk# (3, 3)V=X @ Wv# (3, 3)

真实数值(每个词一行):

Q(每个词一个"查询"向量): 我: [ 0.149 -0.041 0.194] love: [ 0.457 -0.07 -0.07 ] 猫: [ 0.506 0.202 -0.169] K(每个词一个"键"向量): 我: [ 0.073 -0.574 -0.517] love: [-0.169 -0.304 0.094] 猫: [-0.286 -0.42 0.354] V(每个词一个"值"向量): 我: [-0.163 0.033 -0.345] love: [ 0.113 -0.18 -0.088] 猫: [-0.244 0.605 -0.077]

到这里,Q、K、V 三件套就齐了。注意它们不是我们手工指定的,而是X XX乘权重矩阵自动变出来的——这正是上一章"作弊"被补上的地方。

第四步:用 Q 去匹配 K,算相似度打分

每个词用自己的 Q 去点乘所有词的 K,得到Q K ⊤ QK^\topQK相似度矩阵(越大越相关):

d_k=K.shape[1]# 3scores=Q @ K.T# (3, 3) 打分矩阵

真实数值:

列: 我 love 猫 我: -0.066 +0.006 +0.044 love: +0.110 -0.062 -0.126 猫: +0.008 -0.163 -0.290

i ii行第j jj列,就是"第i ii个词"对"第j jj个词"的关注程度。比如第一行"我"的三列都很小且接近,说明在当前这组随机权重下,"我"对三个词都一视同仁(还没训练,注意力还没分化)。

第五步:softmax 把打分变成权重

对打分矩阵每一行做 softmax,让每行之和等于 1,得到注意力权重矩阵:

defsoftmax_rows(M):e=np.exp(M)returne/e.sum(axis=1,keepdims=True)weights=softmax_rows(scores)

真实数值(每行和为 1):

我 看全句权重: [0.313 0.337 0.35 ] love 看全句权重: [0.38 0.32 0.3 ] 猫 看全句权重: [0.387 0.326 0.287]

每一行都是"这个词如何分配自己 100% 的注意力给全句"。当前这组权重下大家分配得比较平均——因为权重还没被训练,等学出合适的W q , W k W_q,W_kWq,Wk,各行就会"分化"出明确的关注偏好。

第六步:按权重取 V,加权求和

最后,用权重矩阵去加权求和所有 V,得到每个词的上下文表示

out=weights @ V# (3, 3)

真实数值:

我 的上下文表示: [-0.099 0.161 -0.165] love 的上下文表示: [-0.099 0.137 -0.182] 猫 的上下文表示: [-0.096 0.128 -0.184]

这个输出就是自注意力的结果:每个词的向量,此时已经"掺入"了全句相关词的信息。比如"我"的上下文表示,不再是孤零零的[1,0,0,0],而是综合了 love、猫的信息后的一份新表示——这就是"词和词互相打探消息"的最终产物。


四、封装成完整函数

把上面六步收进一个函数,就是标准的自注意力(这一章先不除以d k \sqrt{d_k}dk,那是下一章《缩放点积注意力》的主角,后面会补上):

defself_attention(X,Wq,Wk,Wv):Q,K,V=X @ Wq,X @ Wk,X @ Wv scores=Q @ K.T# 打分e=np.exp(scores)# softmaxweights=e/e.sum(axis=1,keepdims=True)returnweights @ V,weights# 输出 + 权重out,attn=self_attention(X,Wq,Wk,Wv)print("输出 shape =",out.shape)# (3, 3) 与输入行数一致

验证要点:输入X XX( 3 , 4 ) (3,4)(3,4),输出是( 3 , 3 ) (3,3)(3,3)——行数不变(3 个词还是 3 个词),只是每个词的向量维度从 4 变成了 3(进入"注意力空间")。这符合直觉:自注意力不改变词的个数,只改写每个词的表示。


五、注意力是可学习的:权重一变,爱看谁就变

很多初学者背下公式却不懂"可学习"到底什么意思。用一个直观实验讲透:Q 由W q W_qWq决定,而W q W_qWq是训练学出来的——所以"模型爱看谁"是学出来的,不是写死的

实验:把"我"这个词的查询向量,强行调成"我"自己的键向量(并放大 5 倍放大差异),模拟"我特别想把注意力放在自己身上":

Q_self=Q.copy()Q_self[0,:]=K[0,:]*5.0# 让"我"的查询贴近"我"的键scores_self=Q_self @ K.T w_self=softmax_rows(scores_self)print("我 的注意力:",w_self[0].round(3))

真实结果:

改查询前 我 的注意力: [0.313 0.337 0.35 ] 改查询后 我 的注意力: [0.695 0.169 0.136]

看!我只是把"我"的查询向量调成偏向"我自己",它这一行的注意力瞬间从"平均分配"变成高度集中在自己身上(0.695)。这说明:查询向量决定了"这个词爱看谁",而查询向量是W q W_qWq变出来的,W q W_qWq是训练学出来的——一层套一层,最终就是"注意力权重是模型学出来的偏好"。

翻译成大白话:训练时,模型为了让预测更准,会不断调整W q , W k W_q,W_kWq,Wk,让每个词"学会"去关注对自己真正有用的词。比如理解"猫"时,模型可能学会重点看"可爱";理解"他"时,学会重点看上文的某个名词。这一切,都在W q , W k W_q,W_kWq,Wk的更新里自动完成。


六、常见坑与自查

  • 误以为 Q/K/V 是三个不同的输入:它们是同一个X XX乘三个不同的权重矩阵得到的三个"分身"。搞混这个,后面多头注意力必乱。
  • W q , W k , W v W_q,W_k,W_vWq,Wk,Wv当成固定值:它们是可学习参数,训练时通过反向传播更新。随机初始化时注意力很平均,训练后才会分化。
  • 形状搞错:输入( S , E ) (S, E)(S,E),乘以( E , d k ) (E, d_k)(E,dk)的权重,得到( S , d k ) (S, d_k)(S,dk)。Q、K、V 的维度都是d k d_kdk(本章三者维度相同)。S SS是词数,自注意力输出词数不变。
  • softmax 方向搞反:对每一行(每个查询词)做 softmax,让"一个词对全句的注意力加起来等于 1",不是对列。
  • 把打分和权重混为一谈:打分是Q K ⊤ QK^\topQK的原始相似度(可正可负),权重是打过 softmax、非负且行和为 1 的值。只有权重才用于加权求和。

小结

这一章把上一章的"注意力思想"变成了"自注意力的完整机制":

  • 三件套:Q 问(我要看什么)、K 答(我是什么标签)、V 给(我提供什么内容),用查图书馆类比记牢分工;
  • 核心公式Q = X W q , K = X W k , V = X W v Q=XW_q,\ K=XW_k,\ V=XW_vQ=XWq,K=XWk,V=XWv——三个分身都来自同一个输入X XX,这就是"自"的含义;
  • 完整流程X → Q / K / V → Q K ⊤ → softmax → weighted V X \to Q/K/V \to QK^\top \to \text{softmax} \to \text{weighted}\,VXQ/K/VQKsoftmaxweightedV,手撕成 6 步,每个词的上下文表示都算出了真实数值;
  • 可学习:查询向量由W q W_qWq变出,W q W_qWq由训练更新——模型"爱看谁"是学出来的,实验里改一下查询向量,"我"的注意力就从 0.337 跳到 0.695。

到这一步,我们已经有了"让词和词互相对话"的完整骨架。但还有三个细节等着补:当前打分没经过缩放,维度大时容易让 softmax 饱和(第 3 章);单头注意力只能关注一种关系,不够(第 4 章);注意力不分先后顺序,得给词补上位置(第 5 章)。下一章,先解决第一个——缩放点积注意力。


下一篇(三十二):缩放点积注意力——打分、归一、加权

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询