31|推导附章:注意力反向传播与可训练检索
目标:能沿计算图手工求导,再用数值实验验证;把模型“怎样学习”与前面“怎样推理”闭合。实验:
attention_gradients.py,提供解析反向和有限差分。
本章目录
沿着对象与状态理解本章
反向推导中每个梯度对象都要对齐前向算子与shape;先看局部变化怎么传回去。
下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。
训练:给每个位置一道“下一项”题
为什么训练看完整句,生成却只能往前走?
训练时答案放在试卷的评分区,所以各位置的题目可以同时准备;mask负责不让模型读到自己的答案。生成时后续Token还未决定,下一题的前缀依赖本题选择。两者共享因果预测目标,但已知信息不同。
输入与适用条件:标签对齐使用5项序列;单位置交叉熵与PAD平均分别是明确独立的小例,不假称那些概率来自对齐图的模型。
1 / 5 · 把输入和目标错开一格
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
完整教学序列为“我/爱/学习/!/EOS”。输入用前四项,目标用后四项。每个输入位置预测的是其后一个真实Token。
输入=[1,2,3,5];目标=[2,3,5,6]默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
用清晰的预测任务,把参数更新与语言序列关系连接。
怎样实现
正确移动标签、限制可见性、屏蔽无效目标、按有效数归一化。
可能的代价与误解
偷看未来会产生漂亮但虚假的训练成绩;训练样本重复或泄漏也会高估泛化。
如何判断是否做对
用极小固定序列验证每个输入位置和目标位置,再验证PAD不影响有效损失。
1. 从单头注意力开始
设Q为m×d,K为n×d,V为n×dv,定义S=QKᵀ/√d,按行softmax得到P,输出O=PV。允许m不等于n,以覆盖缓存中新块查询面对完整历史的形状。设上游损失对输出梯度G为m×dv。
前向可以理解为两个步骤:用Q/K决定每行权重,再按权重混合V。反向同样分为权重路径和内容路径。这个推导来自矩阵微分与softmax导数,不需要把注意力拟人化。[N07]
2. 先对V和P求导
由dO=dP·V+P·dV及标量损失的迹形式,可得:
∂L/∂V = Pᵀ G [n,dv]
∂L/∂P = G Vᵀ [m,n]
第一式表示每个Value从读取它的查询位置累计梯度。第二式表示若改变某个注意力权重,输出沿对应V方向变化会如何影响损失。形状核对是推导的一部分,不应只凭公式记忆。
3. softmax的Jacobian不必显式建立
对一行p,Jacobian是diag(p)−ppᵀ。直接建立n×n矩阵会浪费;设该行上游梯度为g,可写:
ds = p ⊙ (g − Σj pj gj)
把所有行组合:D_S=P⊙(D_P−rowsum(P⊙D_P))。这一步使每行分数梯度和为零,与给整行分数加同一常数不改变softmax的性质一致。
若某位置被固定mask屏蔽,P为零,其对应分数梯度也应为零。全mask行则没有有效概率分布,本书显式拒绝。生产核若采用其他语义,需要在前向与反向保持一致。
4. 再传回Q与K
∂L/∂Q = D_S K / √d [m,d]
∂L/∂K = D_Sᵀ Q / √d [n,d]
注意K与V虽然都来自历史位置,梯度路径不同。Q/K影响选择权重,V影响被聚合内容。若只对V反向而不对Q/K反向,模型可能学到输出内容,却无法正确学习注意力匹配。
Q、K、V若都由同一个X通过不同投影产生,X梯度是三条路径贡献之和;每个投影权重梯度分别由输入转置乘对应输出梯度得到。残差支路还要把上游梯度直接加回。多头实现只是额外的形状重排和各头计算,最后合并处同样需要正确逆变换。
5. RoPE、RMSNorm与门控的反向直觉
RoPE对固定位置是正交线性变换,反向使用旋转矩阵转置,也就是相反角度旋转上游梯度。位置频率若固定,不需要对它优化;若某种架构学习相关参数,则计算图不同,不应一概而论。
RMSNorm设r=sqrt(mean(x²)+ε),y=γ⊙x/r。令g=上游⊙γ,则输入梯度为g/r−x·mean(g⊙x)/r³。这是直接项减去归一化尺度反馈项。把归一化当常数,会丢掉第二项,梯度错误但前向看起来正常。
SwiGLU由乘法门控组成,两个分支都应接收另一分支值乘以上游的梯度。SiLU还需要自身导数。实现时先验证单算子,再组合整层,通常比只看最终训练loss更容易定位。
6. 有限差分与自动求导如何配合
本书以L=sum(O⊙G)构造标量目标,对Q/K/V每个元素分别加减ε,比较数值梯度与解析梯度。小矩阵上代价可接受;大模型则只抽样部分维度或做方向导数检查。
ε过大,截断误差明显;过小,浮点舍入放大。使用float64、适中数值和明确容差更容易发现逻辑错误。配套测试比较非方形mask下的三类梯度,并验证仅首行输出参与损失时未来K/V梯度为零。
自动求导不是不需要验证。错误mask、错误标签或错误维度会让自动求导忠实计算“错误目标”的梯度。有限差分也只能证明相对于当前前向实现的导数,不能证明前向语义正确。因此需要数学不变性、负对照与任务测试共同形成证据。
7. Dense Retriever怎样学习“相关”
双塔检索可对一条查询q、正例d+和多个负例dj计算对比损失:
L = −log exp(s(q,d+)/τ) / Σj exp(s(q,dj)/τ)
它推动正例相对候选更相似,而不是保证绝对距离有统一业务意义。batch内负例提高效率,但可能包含实际上也相关的文档,形成假负例;困难负例能训练细区分,也可能引入标签错误。[N70]
如果训练数据只区分主题相关与不相关,模型可能无法区分同主题的新旧版本、肯定与否定。对代码检索,需要与实际任务相符的正负样本和评价。增加向量维度不会自动补上目标缺陷。
重排模型可以学习更细的查询—文档交互,但其训练标签若偏爱长文或某种格式,也会带来偏差。检索训练与第19章的评测设计必须共同考虑。
8. 这个附章如何使用
先运行现有实验,再故意删掉softmax导数中的行内减项,观察有限差分失败;接着删mask,观察因果梯度测试失败。最后比较PyTorch训练中的自动求导与手工推导,理解两者并不竞争:一个用于高效实现,一个用于验证与理解。
本章达标是能解释某个梯度为什么这样流动,能用实验抓住错导数,并知道实验的逻辑边界。它不等于已经掌握完整分布式训练或GPU反向kernel实现。
用一个方向导数核对矩阵反传是否接对
输入与条件。函数f(Q)=上游G与Attention输出的点积;解析梯度已算得dQ=[0.5615202771,−0.1871734257],方向v=[1,−2]。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1解析方向导数 | dQ·v | 约0.9358671285。 |
| 2有限差分 | 取ε=10^-6,算(f(Q+εv)−f(Q−εv))/(2ε) | 应接近同一数。 |
| 3坐标检查 | 再对Q/K/V每个元素分别扰动 | 能定位转置或softmax雅可比错误。 |
| 4容差 | 比较绝对/相对误差 | 不是要求浮点小数逐字相同。 |
| 5模型边界 | 通过梯度检查只证明这个算子实现 | 不证明训练数据或整个Agent任务正确。 |
把过程连起来。从数学公式到代码,最有价值的示例之一是能用独立数值方法反证错误实现。
边界与反例。示例采用可微且不处于mask/选择变化边界的输入;有限差分在不连续点与低精度下要谨慎。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
让检索向量学习:查询向量真的往哪个方向移动
输入与条件。查询q=[1,0],正例a=[0.8,0.6],负例b=[0,1]。使用点积分数、两候选softmax和正例交叉熵;本步只更新q,不更新a、b。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1先打分 | q·a=0.8,q·b=0 | 正例概率≈0.689974,负例≈0.310026。 |
| 2对分数求导 | 正例梯度p正−1,负例梯度p负 | 约[-0.310026,0.310026]。 |
| 3传回查询 | dq=(p正−1)a+p负b | 得到[-0.248020,0.124010]。 |
| 4更新q | 学习率0.1,q新=q−0.1dq | 得到[1.024802,−0.012401];这一小步增加正负点积分数之差。 |
| 5独立核验 | 分别轻微增减q的每个分量,用损失差除步长 | 有限差分与上面的解析梯度一致。 |
把过程连起来。“训练更好的检索”对应的是明确目标、候选与梯度。不是人工给Embedding某一列命名后再调高该列。
边界与反例。这里是点积相似度,更新后不重新单位化q;若改用cosine或加归一化,必须把对应导数一起传回。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。