F11|Attention逐步计算:先算相关分数,再混合内容
从具体对象与小例子开始,理解计算过程与适用条件。
本章目录
Q/K/V:先算从哪读,再算读进多少
Query、Key、Value各自是什么数,为什么分三条路径?
计算某个位置的新表示时,它是接收者;所有可见位置都是潜在信息提供者。Query与Key决定各提供者的读取份额,Value提供实际被混合的数。可以借“提问—匹配—取内容”记住分工,但Q不是自然语言问题,K不是数据库主键,V不是正确答案。
输入与适用条件:沿用F11正文的独立两位置算例:X=[[1,1],[1,−1]]。本图省略偏置、RoPE、归一化和多头。所有Q/K/V和输出都由配套程序计算。
1 / 5 · 同一输入,三条参数路径
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
先不打分。两行输入分别代表“我”和“爱”;Wq、Wk、Wv是三张乘法系数表。它们不是另外三份文字。
本步状态由图形与说明共同给出。默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
让匹配特征与被传递内容分开学习,给模型不同的变换能力。
怎样实现
先分别投影每行,再跨位置打分、屏蔽、softmax、混合V。
可能的代价与误解
“注意力最大”不直接证明模型最终回答的因果原因;若只有一个可见Key,softmax永远为1,不能用来展示竞争。
如何判断是否做对
核对六行投影、两项分数、份额和输出;改变Wv不应在本层直接改变由Q/K算出的份额。
它想解决什么问题
一个Token先由Embedding表换成一排数,但这还不够。“苹果很好吃”和“苹果发布了电脑”需要结合不同上下文。注意力(Attention)是一段数值运算:为当前位置计算它应从哪些可读位置取得多少信息,再按这些份额混合信息。 它不是模型的眼睛,也不是另外三个会说话的智能体。
先抓住两件事:匹配时用什么来打分;打完分后实际拿什么来混合。 Query、Key、Value就是在这两件事里承担不同职责的数字。下面先给它们的来源,再算完整过程。来源:注意力原论文的机制说明。
进入注意力之前,X到底是什么
这里开始一个独立的两位置小模型,只处理“我/爱”两个教学Token。为了能逐格计算,把每个Token的表示宽度设为2,不沿用F05的四维Embedding表。从本节这张表直到输出,始终使用下面同一份输入与参数,不再切换数字。
| 序列位置(从0开始) | 本例Token | 当前输入向量 | 这排数从哪里来 |
|---|---|---|---|
| 0 | 我 | x₀=[1,1] |
本例给定的小模型Embedding表示 |
| 1 | 爱 | x₁=[1,-1] |
本例给定的小模型Embedding表示 |
把两行摆在一起,给整张表起名为大写 X:
X = [[1, 1], 第0行对应“我”
[1,-1]] 第1行对应“爱”
形状:[2,2] = [Token位置数, 每个位置的特征数]
X不是整句中文,也不是Token编号表,而是本次注意力计算收到的数值表示。小写x₀只取X的第0行;x₁取第1行。方括号里的1和-1是人为指定的教学特征,没有“一定表示主语或动词”的含义,更不是二进制位。
本例省略归一化、位置旋转、偏置、多头与残差,只观察一个注意力头。真实网络中,注意力收到的X也可能是上一层输出或归一化后的表示;不能把每一层的X都当成最初的Embedding。那些额外步骤在F12—F16及系统篇逐步接入。
W是什么:先认识“乘法系数表”,再认识三个名字
F07里,[1,2] × [[3,4],[5,6]] = [13,16]:输出的第一个数是1×3+2×5,第二个是1×4+2×6。右边那张表里的3、4、5、6,决定了输入数字怎样被重新组合。它是一张权重矩阵,也就是乘法系数表,通常用字母W表示。
W是规则里的参数,X是这次送来的数据。 同一张W可以处理很多不同X;改X会改变本次结果,但正常推理不会因此改写W。神经网络里把“每行输入乘一张参数表,得到一排新特征”称为线性变换或线性投影。它不一定降低维度,也不是把三维东西投影到墙上。
Attention准备了三套这样的参数:
| 参数名字 | 中文含义 | 用它算什么 | 它自身是什么 |
|---|---|---|---|
Wq,也写W_Q |
查询投影权重矩阵 | 从X算出Query表示Q | 一张数值系数表 |
Wk,也写W_K |
键投影权重矩阵 | 从X算出Key表示K | 另一张数值系数表 |
Wv,也写W_V |
值投影权重矩阵 | 从X算出Value表示V | 第三张数值系数表 |
下标q、k、v只是说明“这张W负责哪一路”。Wq不是W乘q;W_Q中的下划线也不是一个计算动作。后面公式XWq才表示矩阵乘法,Python中通常写作X @ Wq。
这些系数从哪里来? 训练前,程序按初始化方法设置它们;训练中根据预测误差调整;使用训练好的模型时,从模型权重文件加载已经得到的数值。它们不是对当前提问临时随机生成,也不是程序员给每个词手写的语法规则。这里的三张表是为演示手算而设定,不声称经过语言训练。来源:线性层的参数与计算约定。
Query、Key、Value各做哪一件事
现在把名字与对象连起来:
| 名字 | 是什么对象 | 在当前计算中的职责 | 不是什么 |
|---|---|---|---|
| Query,查询向量q | 当前接收信息的位置生成的一排数 | 分别与各位置的Key比较,为“读多少”打分 | 不是用户输入的问句,也不是搜索字符串 |
| Key,键向量k | 每个可读位置生成的一排数 | 接受Query的比较,为这个位置提供匹配特征 | 不是密钥,也不是从文字里直接摘出的关键词 |
| Value,值向量v | 每个可读位置生成的另一排数 | 在份额确定后,提供真正参与加权求和的内容 | 不是标准答案,也不是最终输出Token |
同一个位置可以同时当接收者和信息提供者。例如计算“爱”位置的新表示时,用“爱”的Query去比较“我”的Key和“爱”的Key,再混合“我”的Value与“爱”的Value。
小写q、k、v是一行;大写Q、K、V是把所有位置各自的行摆成的表。 在本节里每张表有2行,每行2个数。它们不是三份独立原文,而是同一份X经过不同参数变换得到的三份中间表示。
因此,三条计算是:
Q = X @ Wq 输入表示 × 查询参数表 → 所有位置的查询表示
K = X @ Wk 输入表示 × 键参数表 → 所有位置的匹配表示
V = X @ Wv 输入表示 × 值参数表 → 所有位置的内容表示
“分成三套参数”不意味着数学上不允许它们恰好相等。某些教学特例会设成相同矩阵;有些研究结构也会共享参数。但一般的分开设计允许三条路径学到不同变换。数值是否相同、是否共享,必须看该例子或模型的具体设定。
用同一份X,把三张参数表完整乘一次
本节始终使用下面三张2行2列的系数表,且不加偏置:
Wq = [[ 0.5, 0.5], Wk = [[0.5, 0.5], Wv = [[5, 10],
[-0.5, 0.5]] [0.5,-0.5]] [5,-10]]
先取“我”的输入x₀=[1,1]。每个输出格子都是输入的一行与参数表的一列做点积:
q₀ = x₀Wq = [1×0.5+1×(-0.5), 1×0.5+1×0.5] = [0,1]
k₀ = x₀Wk = [1×0.5+1×0.5, 1×0.5+1×(-0.5)] = [1,0]
v₀ = x₀Wv = [1×5+1×5, 1×10+1×(-10)] = [10,0]
再取“爱”的输入x₁=[1,-1],仍然使用相同的三张参数表,没有为“爱”换另一组W:
q₁ = x₁Wq = [1×0.5+(-1)×(-0.5), 1×0.5+(-1)×0.5] = [1,0]
k₁ = x₁Wk = [1×0.5+(-1)×0.5, 1×0.5+(-1)×(-0.5)] = [0,1]
v₁ = x₁Wv = [1×5+(-1)×5, 1×10+(-1)×(-10)] = [0,20]
于是,整个X变成三张结果表:
| 位置 | 原输入X的一行 | 算出的Q的一行 | 算出的K的一行 | 算出的V的一行 |
|---|---|---|---|---|
| 我,0 | [1,1] |
[0,1] |
[1,0] |
[10,0] |
| 爱,1 | [1,-1] |
[1,0] |
[0,1] |
[0,20] |
到这里还没有混合不同Token。 三个投影都是分别加工每一行;真正跨位置的比较和混合发生在下一步。Q/K/V各行的数字是由上面的X和W算出的,不是凭空给出的新输入。
对“爱”这个位置,从打分一直算到新表示
本节使用因果读取规则:当前位置能读自己和前面的位置。现在计算序列第1号位置“爱”,所以它能读第0号“我”和自己。取出的对象是:
当前接收者:爱,q₁=[1,0]
可读位置0:我,k₀=[1,0],v₀=[10,0]
可读位置1:爱,k₁=[0,1],v₁=[0,20]
第一步:Query和每一个Key打分。
对“我”的原始分数:q₁·k₀ = 1×1+0×0 = 1
对“爱”的原始分数:q₁·k₁ = 1×0+0×1 = 0
原始分数:[1,0],顺序仍是[我,爱]
第二步:把分数除以Query/Key的宽度的平方根。 宽度记作d_k,这里是2;√2≈1.414214。这叫缩放,不是再修改W:
缩放分数:[1/√2, 0/√2] ≈ [0.7071067812,0]
第三步:沿可读位置做softmax,得到读取份额。 算法与F09相同,但这是当前Q/K得到的分数,不是F09词表候选的[2,1,0]。这里候选对象是两个输入位置,不是三个待生成Token。
减最大值: [0, -0.7071067812]
取指数: [1, 0.4930686914]
指数之和: 1.4930686914
除以总和: [0.6697615493, 0.3302384507]
来自“我”的份额 来自“爱”的份额
这些份额也是“权重”,但和Wq里的参数权重不是同一种东西。Wq等是已经加载的模型参数;这里的两个份额由本次输入计算而来,随输入改变。
第四步:用份额混合Value的对应格子。
输出第0格 = 0.6697615493×10 + 0.3302384507×0
≈ 6.6976154933
输出第1格 = 0.6697615493×0 + 0.3302384507×20
≈ 6.6047690135
“爱”位置的注意力输出 o₁ ≈ [6.6976154933,6.6047690135]
这排数是**“爱”位置的新中间表示**。它不是随机抽中“我”,不是选出一个Value,也不是生成了一句回答。Attention在这里做确定的加权求和,不执行F09演示的随机抽样。
把一行变成所有行,公式就出现了
现在仍然使用同一X和同一组三张参数表,同时看两个接收位置:
Q = [[0,1], K = [[1,0], V = [[10,0],
[1,0]] [0,1]] [0,20]]
QKᵀ = [[0,1], 行:接收信息的位置(Query)
[1,0]] 列:提供信息的位置(Key)
上标ᵀ表示转置:让K的原来每一行成为一列,以便一次计算所有“Query行与Key行”的点积。它不是序列长度T,也不是K的某次方。
第0号“我”不能读未来的“爱”。给禁止的分数位置加负无穷,记为−∞,它的指数按极限取0。给允许的位置加0。把这种加性掩码表命名为M:
M = [[0,−∞], 第0行只能读“我”
[0, 0]] 第1行能读“我”和“爱”
S = QKᵀ/√2 + M = [[0,−∞],
[0.7071067812,0]]
A = 每行softmax(S) ≈ [[1,0],
[0.6697615493,0.3302384507]]
O = AV ≈ [[10,0],
[6.6976154933,6.6047690135]]
S是分数表,A是读取份额表,O是混合后的输出表。它们都是本次计算结果,不是另外加载的模型参数。第0行只有一个合法位置,份额是1,输出自然就是自己的Value。
所有动作合起来,才写成这条简式:
Q=XWq,K=XWk,V=XWv
O=softmax(QKᵀ/√d_k + M)V
| 符号 | 对象身份 | 本例形状 | 本次推理中如何获得 |
|---|---|---|---|
| X | 本次输入表示 | [2,2] |
上一步提供 |
| Wq/Wk/Wv | 三张投影参数表 | 各[2,2] |
本例预设;实际加载训练好的参数 |
| Q/K/V | 三套中间表示 | 各[2,2] |
X分别乘对应的W |
| M | 可读位置约束 | [2,2] |
根据序列、位置和读取规则构造 |
| S | 每行对每列的分数 | [2,2] |
Q与K点积、缩放、加M |
| A | 每行的读取份额 | [2,2] |
对S沿Key列做softmax |
| O | 每个接收位置的新表示 | [2,2] |
A乘V |
本例所有形状刚好相同,不能据此认定所有对象可互换。一般单头设有n个查询、m个Key,Q为[n,d_k]、K为[m,d_k]、V为[m,d_v],则分数与份额为[n,m]、输出为[n,d_v]。d_v是每个Value有几个数,不必等于d_k;Query和Key的长度才需要配对做点积。
self并不是“只看自己”
本例Q、K、V都从同一条序列的X产生,所以叫Self-Attention,自注意力。每个位置仍可以读取规则允许的其他位置。“self”说来源,不说只能看自己。
Cross-Attention,交叉注意力则可以让Query来自一份序列、Key和Value来自另一份。例如翻译系统用正在生成的目标侧表示,读取源句编码器提供的表示。这里的两份输入是另一种结构,不能偷偷代入上面的同X公式。
Decoder是负责逐步生成后续Token的一类网络结构;Encoder是把给定输入转换成表示的一类结构。这里只认识它们的职责,具体网络结构在系统篇展开。因果读取规则来自生成任务的设计,不是英文字母Q/K/V自带的功能。
为什么需要三套表示,而不是直接拿X比较和混合
若只用原X匹配、也直接混合X,相当于限制匹配特征与被读取内容的变换方式。三套投影让模型可以分别学习“怎样接收”“怎样被匹配”“提供什么内容”。这是一种表达能力与参数量之间的设计选择,不是声称任何任务数学上都必须三张互不相同的表。
可以沿本例做两次明确分开的对照,每次都从本节基准恢复:
| 对照 | 保持不变 | 只改变什么 | 可预测的影响 |
|---|---|---|---|
| 改被读取内容 | X、Wq、Wk | 把Wv整体乘2 | 当前Q/K与读取份额不变,当前O整体乘2 |
| 改匹配规则 | X、Wk、Wv | 修改Wq中的一个系数 | Query可能变化,份额与混合结果也可能变化 |
“份额不变”仅指当前这一次注意力分支;改变输出后,后面的层仍可能跟着变化。不能把局部对照扩大成整模型其他部分永远不受影响。
训练时,误差会沿“O→A与V→Q与K→各张W”的计算依赖传回,调整系数。到F17的梯度与F18的优化器,就会看到怎样算更新量。现在只要先区分:W保存可以学到的规则;Q/K/V保存这些规则作用在本次输入后的结果。
再进入多头、缩放和源码之前
一个头就是一套“Query/Key匹配,再混合Value”的计算路径。多头使用多组这样的路径,并把结果组合;不是模型同时开了多个聊天窗口。各头结果并排后,还可乘另一张参数表Wo,把混合结果转换回主网络需要的宽度。Wo是注意力输出投影,不是最后给词表候选打分的语言模型输出头。
除以√d_k是在控制点积分数的尺度。推导要用方差,F14会先解释这一统计量;它不是softmax的必要定义,也不是改变候选数量。为了逐步学习,本节先把它当作本例固定的缩放规则,再在Q007追问它的理由。
有些库的高级Attention接口把投影前的输入也命名为query/key/value,内部才乘W;另一些底层接口直接接收已经投影后的Q/K/V。同名不代表处于同一计算阶段。 读源码前要看接口是否负责投影,避免自己先乘一次W、库里又乘一次。PyTorch的线性层还把参数存成[输出宽度,输入宽度]并在计算时转置;本书手算采用[输入宽度,输出宽度]右乘约定,两者转置后可以表达同一变换。接口与形状依据。
以上数值使用未舍入结果继续计算;页面小数只作显示。可用本节的参数实验观察中间值,也可运行随包的concepts/verify_concepts.py复算。它验证教学算式与前提,不证明真实模型已学会中文。
从两段内容到一个混合向量,而不是选中一句话
输入与条件。本表核对上面同一条“我/爱”计算链,不另换输入。X=[[1,1],[1,-1]];Wq=[[0.5,0.5],[-0.5,0.5]];Wk=[[0.5,0.5],[0.5,-0.5]];Wv=[[5,10],[5,-10]]。由它们算出q₁=[1,0]、k₀=[1,0]、k₁=[0,1]、v₀=[10,0]、v₁=[0,20];计算“爱”位置时两个位置均可读。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1匹配 | 分别算q与两个Key的点积 | 得到 [1,0]。 |
| 2缩放 | 两个分数都除以√2 | 约 [0.707107,0]。 |
| 3归一化 | 对两个可见位置做softmax | 约 [0.669762,0.330238]。 |
| 4混合第1个特征 | 用两份Value的第1格加权 | 0.6697615493×10+0.3302384507×0≈6.697615。 |
| 5混合第2个特征 | 用两份Value的第2格加权 | 0.6697615493×0+0.3302384507×20≈6.604769。 |
| 6得到输出 | 把两格放一起 | 约 [6.697615,6.604769],不是任何一份原始Value。 |
把过程连起来。匹配分数决定读取份额,Value决定混合什么。只改Value时,读取份额可以完全不变,但输出会变。
边界与反例。本例为单头、两位置、无偏置、无位置旋转的教学注意力,不是训练好的语言模型。所有Q/K/V由同一X和三张参数表实际相乘得到。改变任一参数或输入,应从对应步骤重新计算。
依据与适用范围:[SDPA]。示例输入和场景由本书明确设定。
参数实验:改一格W,观察哪些中间值改变
正文固定使用基准参数。下面是可重置的独立交互对照:只改变Wq的第0行第0列或Wv的第0行第0列,其余X、Wk及参数保持不变。仍采用因果读取规则。
只改变当前Wv时,Q、K与当前份额不变;改变Wq时,Query及份额可能变化。后续网络层不在本实验范围内。显示小数经舍入,内部使用未舍入值。
课后自检
Q与K点积得到的是什么,V负责什么?
查看参考答案收起参考答案
两个Key分数完全相同且均可读,权重是什么?
查看参考答案收起参考答案
Wq与Q到底哪个存的是模型参数?
查看参考答案收起参考答案
本例x₁=[1,-1]怎样产生v₁=[0,20]?
查看参考答案收起参考答案
Q、K的每行宽度必须等于V的每行宽度吗?
查看参考答案收起参考答案
延伸阅读:相关系统问题
02|一次Transformer前向:逐张量、逐算子讲明白03|注意力的深入追问:位置、掩码、多头与反向传播
07|KV Cache、FlashAttention与PagedAttention的内部过程
28|研究深水区与系统综合判断
机制依据:[TR]。本节数值与情景除另注外均为原创教学示例;来源不代替中文讲解。