正文
如果每个参数都随意改一下,再尝试所有组合,开销会很大。神经网络训练通常利用导数和梯度,计算参数在当前位置发生微小变化时,损失会怎样变化。链式法则则让这种计算能够穿过一层层相接的运算。
先从一个只有一个参数的教学模型开始。假设输入 x 表示资料数量,预测值 ŷ 表示处理时长。我们暂时只用一个乘法:
预测时长 ŷ = w × x
w 是可调整的参数,可以理解为这个极简规则中每份资料对应多少时间。这不是实际 Agent 时延模型,只是为了看清训练过程。假设有一个样本 x = 2,对应目标时长 y = 6,而当前 w = 1,预测时长就是 2。
我们把损失定义成预测与目标之差的平方:
L(w) = (w × 2 - 6)²
当前 w = 1 时,L = (2 - 6)² = 16
平方会把正负误差都变成非负值,也会加重较大误差的影响。这个损失是为例子选定的目标,真实任务需要按问题选合适的损失,并不都使用平方。
现在把 w 从 1 增加一点点。若增加到 1.01,预测为 2.02,损失变成 15.8404,比原来减少 0.1596。损失变化除以参数变化,得到 -15.96。它表示在这段很短的范围内,w 每增加一个单位,损失平均按什么速度变化。
但我们关心的是当前位置附近、变化足够小时的趋势。把变化量 h 缩得越来越小,差商趋近的值,就是导数:
L'(w) = lim[h→0] (L(w+h) - L(w)) / h
lim 表示取极限。不是让程序真的除以零,而是考察 h 接近零时,这个比值趋向哪里。L'(w) 读作“L 对 w 的导数”,也写作 dL/dw。导数是一种局部变化率,曲线上也可以理解为当前位置的切线斜率。MIT:导数作为变化率
对这个例子展开计算,可以得到 L'(w) = 8w - 24。因此 w = 1 时,导数为 -16。负号表示在附近稍微增大 w,损失倾向于下降;如果导数为正,则附近稍微减小 w,损失倾向于下降。导数值和损失值意义不同,不能只看它们偶然相同的数值大小。
有了方向,最简单的梯度下降更新可以写成:
新参数 = 旧参数 - 学习率 × 当前导数
w新 = 1 - 0.1 × (-16) = 2.6
这里的学习率 0.1 是人为设置的步长系数。代回原式,预测变成 5.2,损失变成 0.64。这个具体例子中,一次更新让损失从 16 降到了 0.64。
减去导数的符号很容易让初学者困惑。导数为负,减去一个负数,就会增大参数;导数为正,减去一个正数,就会减小参数。它把当前的局部变化率转换成尝试降低损失的调整方向。
不过,局部方向不保证随便走多远都有效。若把学习率改成 1,新 w 就变成 17,预测为 34,损失反而升到 784。参数跨过了合适的区域。真实训练里还可能遇到随机批次、曲面复杂、数值误差等问题,学习率和优化器需要专门讨论。
梯度为零也要结合位置理解。在这个平方损失中,w = 3 恰好让预测等于目标,导数为零,损失达到最小。但复杂函数的导数为零,还可能处在局部最大点或鞍点,不能只凭“没有一阶变化”就宣布找到最佳模型。一次计算给出的是当前位置的性质,完整训练还需要观察后续表现。
接着看参数不止一个的情况。假设预测规则改为 ŷ = wx + b,除了斜率 w,还有一个可调整的起始偏移 b。我们需要知道:只改变 w、暂时固定 b,会怎样影响损失;只改变 b、暂时固定 w,又会怎样影响损失。
这样的导数叫偏导数,常用 ∂ 表示。把每个可训练参数对应的偏导数按顺序排在一起,就组成梯度,用 ∇L 表示。这里的梯度是一个向量,告诉我们损失对各个参数的局部敏感程度。偏导数与梯度
例如,在 x = 2、y = 6、w = 1、b = 0 时,对 w 的偏导数是 -16,对 b 的偏导数是 -8,梯度可以按参数顺序写成 [-16, -8]。它不是两份错误答案,而是同一个损失分别对两个调整位置的响应。
对可微的标量函数,在通常的欧几里得度量下,梯度指向局部上升最快的方向,负梯度给出相反方向。输入特征的尺度、参数表示和采用的优化方法都会影响实际更新,因此不能只根据某个偏导数大,就认定那个参数在业务上更重要。
神经网络的损失通常不是一个简单的平方,而是很多层运算接在一起。要理解它们怎样求导,就需要链式法则。仍然使用单参数例子,把完整计算拆成三步:
w → z = 2w → e = z - 6 → L = e²
w 先影响预测 z,z 再影响误差 e,e 最后影响损失 L。参数变化要沿着这条关系传过去。链式法则告诉我们,把每一段对应的局部变化率相乘,就能得到整条链上的变化率:
dL/dw = (dL/de) × (de/dz) × (dz/dw)
在 w = 1 的位置,z = 2、e = -4。平方那一步的变化率 dL/de = 2e = -8;减 6 那一步 de/dz = 1;乘 2 那一步 dz/dw = 2。连起来就是 (-8) × 1 × 2 = -16,和前面直接算出的导数一致。复合函数与链式法则
如果一个参数经过多条路径影响最后的损失,需要把各条路径的贡献相加。可以把计算关系画成一个有分支的图:先根据输入和参数往前算中间结果,再从损失出发往回累计导数贡献。反向传播正是高效组织这类计算的重要方法,后面的 07.03 会专门展开。
“往回”指沿计算依赖求导,并不是模型把已经生成的回答逐字倒读。梯度也不是一句“这次错了,请改正”的自然语言反馈,而是根据明确计算关系求出的数值。
在实际训练中,常用自动微分系统记录参与计算的操作,再按照这些操作的求导规则计算梯度。它与用一个很小的数反复扰动所有参数、近似计算差商的数值微分有区别,也不要求先把整个网络展开成一条巨大公式。PyTorch 自动微分的工作方式
自动微分得到梯度后,还要由优化器决定怎样更新参数;两者是相接的环节。得到梯度不等于参数已经更新,损失下降也不等于独立测试表现一定提升。如果训练目标选错,梯度会努力优化那个错误的目标。
导数还有适用边界。像绝对值在零点这样的位置,普通导数并不存在,训练框架可能使用规定的次梯度或其他约定。排序、离散选项、调用外部接口等操作,也不能仅凭“程序连在一起”就自动拥有可用的普通梯度。
这对 Agent 尤其重要:从模型输出工具参数,到外部程序执行,再到用户反馈,未必是一条能够直接反向求导的连续计算链。怎样用行动结果训练策略,需要额外的学习方法;不能把普通工具调用循环直接当作一次神经网络反向传播。
最后,可以用刚才的一次更新检查自己是否理解:损失 16 说明当前偏差,导数 -16 描述附近变化率,学习率 0.1 控制迈多大一步,更新后参数 2.6 才是实际改变的结果。把这几件事分开,模型怎样“根据错误调整数值”就有了可以追踪的过程。下一篇会转向评估:看到一批样本的结果以后,能对更大范围的表现说到什么程度。
当前学习编号:03.08 导数、梯度与链式法则。
下一学习编号:03.09 抽样误差与置信区间。后续还有其他学习编号。
关联分支:输入和参数的数值组织,对应 03.01 向量、矩阵与张量、07.01 神经网络与可训练参数;损失目标对应 04.08 损失函数与优化目标、03.07 熵、交叉熵与 KL 散度;训练过程对应 07.02 前向传播、07.03 反向传播与自动微分、07.04 梯度下降与优化器、07.05 学习率与学习率调度、07.09 梯度裁剪;使用行动结果学习,对应 04.04 强化学习、28.05 长期任务中的信用分配。