loss.backward() 到底在干什么?一篇讲透计算图与反向传播 loss.backward() 到底在干什么一篇讲透计算图与反向传播整理说明本文基于 B 站视频【第05讲《计算图与反向传播梯度如何流动》】公开信息、课程逐字稿与配套资料进行原创整理。不是逐字转写而是把核心概念、手算流程、代码练习和排错方法整理成科研小白可以照着学的教程。视频来源B站 Ai学术叫叫兽视频链接https://www.bilibili.com/video/BV1y5Ko6DEcR/视频时长约 19 分 28 秒很多同学第一次看到 PyTorch 里的这行代码loss.backward()第一反应通常是这是不是一个“魔法按钮”明明我们只看到一个 Loss点一下backward()模型里成千上万个参数突然就都有了梯度。哪个权重要变大哪个偏置要变小每个参数该改多少好像模型自己都知道了。但它不是魔法。反向传播的本质其实很朴素用计算图记录计算路线再用链式法则沿着路线反向追责。这一讲只解决一个核心问题模型预测错了以后最终错误是怎么分摊到前面每一个参数上的如果你能把这个问题讲清楚后面学深层神经网络、CNN、YOLO 训练、Loss 曲线、梯度消失和梯度爆炸就不会只是在背术语。文末也给大家整理了复盘清单和资料领取话术。PPT、讲义和动画资料可以无偿送给大家适合课后反复对着复盘。01 先建立一个画面前向做题反向追责上一讲我们讲过一个最简单的神经元z w·x b a sigmoid(z) L loss(a, y)它的前向过程很像学生做题输入x进来模型用权重w和偏置b算出线性得分z再通过 sigmoid 得到预测概率a最后和真实标签y比较得到 Loss。这一步回答的是模型怎么做预测但训练真正关心的是下一步预测错了以后模型怎么知道每个参数应该怎么改这就像考试总分低了不能只说“考差了”。你还要追问问题在模型里对应什么哪道题扣分最多哪个计算节点对 Loss 影响大是概念错还是计算错是前向输出异常还是梯度传递异常下次该补哪里哪个参数该往什么方向更新是小修小补还是大幅调整梯度大小和学习率共同决定更新步长所以反向传播可以先理解成一句话Loss 是最后的错误结果计算图是路线图链式法则是追责方法梯度是每个参数收到的调整通知。02 六个关键词把反向传播拆成小白能懂的语言反向传播难很多时候不是因为公式本身难而是几个词混在一起了。我们先把 6 个核心概念讲清楚。概念一句话理解小白记法计算图用节点和箭头表示计算依赖关系计算路线图前向传播从输入一路算到预测和 Loss模型先做一遍题局部导数某个节点输出对输入的敏感程度每一小段有多敏感链式法则把路径上的局部导数乘起来一段一段追影响反向传播从 Loss 出发反向计算每个参数梯度从结果往回分责任梯度Loss 对参数的变化率参数该往哪改、改多猛把这几个词连起来就是本讲最重要的一句话计算图记录计算路线前向传播得到 Loss局部导数描述每一小段敏感度链式法则把敏感度串起来反向传播把梯度传回参数优化器再根据梯度更新参数。这句话能顺口说出来反向传播的主线就稳了。03 为什么一定要画计算图很多初学者会问既然最后都是求导为什么不直接背公式因为神经网络不是一个短公式而是一条很长的计算链。以单神经元为例x, w, b → z → a → L每个箭头表示一次依赖节点它从哪来它到哪去x输入特征参与计算zw权重参数参与计算zb偏置参数参与计算zzw·x b送入 sigmoidasigmoid(z)和标签比较算 LossLloss(a, y)反向传播从这里开始计算图的价值不只是“画得好看”而是它能回答训练排错里最关键的三个问题这个变量从哪里来这个变量到哪里去如果 Loss 异常错误信号能不能沿着这条路传回来真实训练里很多问题并不是链式法则错了而是计算图在某一步断了。比如你不小心写了valuetensor.item()或者把参与求导的张量转成了 NumPy 数组再参与计算前面的梯度可能就断了。表面看 Loss 还在实际上参数已经收不到有效梯度。所以对科研小白来说计算图不是可有可无的图示而是训练排错地图。04 手把手手算一遍梯度到底怎么传到 w我们用一个小到能手算的例子z w x b a sigmoid(z) L (a - y)^2前向传播按顺序算用x、w、b算出z把z放进 sigmoid 得到a把a和标签y比较得到L现在问题来了如果 Loss 大了怎么知道w应该怎么改反向传播不会从L一步跳到w它会沿着计算图一段一段问L → a → z → w每一段都问一个“敏感度”追问数学表达人话解释Loss 对预测有多敏感∂L/∂aa变一点Loss 变多少预测对线性得分有多敏感∂a/∂zz变一点sigmoid 输出变多少线性得分对权重有多敏感∂z/∂ww变一点z变多少链式法则就是把三段影响乘起来∂L/∂w ∂L/∂a · ∂a/∂z · ∂z/∂w为了让它更落地我们带一个数字例子x 2 w 0.5 b 0 y 1 z 0.5 × 2 0 1 a sigmoid(1) ≈ 0.731 L (0.731 - 1)^2 ≈ 0.072各段导数∂L/∂a 2(a - y) ≈ -0.538 ∂a/∂z a(1-a) ≈ 0.197 ∂z/∂w x 2所以∂L/∂w ≈ -0.538 × 0.197 × 2 ≈ -0.212这个负号很重要。它表示在当前位置w增大一点Loss 会下降。所以梯度下降更新时w ← w - α∂L/∂w如果学习率α 0.1w_new 0.5 - 0.1 × (-0.212) 0.5212也就是说权重会被稍微调大一点。这就是反向传播最核心的直觉最终错误不是凭空分给参数的而是沿着前向计算走过的路反方向追回去的。05 PyTorch 里 backward() 做了什么很多人学反向传播是从 PyTorch 这一套训练代码开始的optimizer.zero_grad()predmodel(x)losscriterion(pred,y)loss.backward()optimizer.step()这一段可以拆成 5 步代码对应机制小白解释optimizer.zero_grad()清空旧梯度先把上次的责任通知清掉pred model(x)前向传播模型做题得到预测loss criterion(pred, y)计算 Loss拿预测和答案对分loss.backward()反向传播沿计算图反向计算梯度optimizer.step()参数更新根据梯度真正改参数注意两个最容易混的点第一backward()只是计算梯度它不会直接更新参数。第二真正改变参数的是optimizer.step()。你可以把训练想象成一次“批改作业”前向传播学生先做题 计算 Loss老师打分 反向传播分析每一步错在哪里 优化器更新根据分析结果调整学习方式06 科研小白实操用 20 行代码看见梯度下面这个最小例子建议你真的跑一遍。代码文件我已经放在本文资料包里code_snippets/01_manual_scalar_backprop.py code_snippets/02_pytorch_autograd_backward.py如果你只想先看 PyTorch 版可以运行python code_snippets/02_pytorch_autograd_backward.py核心代码如下importtorch xtorch.tensor(2.0)ytorch.tensor(1.0)wtorch.tensor(0.5,requires_gradTrue)btorch.tensor(0.0,requires_gradTrue)zw*xb atorch.sigmoid(z)loss(a-y)**2loss.backward()print(z ,z.item())print(a ,a.item())print(loss ,loss.item())print(dL/dw ,w.grad.item())print(dL/db ,b.grad.item())你会看到w.grad和b.grad不再是空的。这说明 PyTorch 已经根据计算图帮你把Loss → a → z → w/b这条链路走完了。建议你做 3 个小实验把w 0.5改成w -2.0看看 Loss 和梯度怎么变。把标签y 1.0改成y 0.0观察梯度方向是否变化。在loss.backward()后加一行print(w.grad)再运行两次理解为什么训练循环里要zero_grad()。小白阶段不要急着改复杂模型。先把这个极简例子跑懂你以后看 CNN 和 YOLO 的训练循环会轻松很多。07 梯度不是误差这是很多人卡住的地方这一讲里最容易混淆的一句话是误差告诉你错了梯度告诉你往哪改。它们不是一回事。对比项误差 / Loss梯度关注什么预测和标签差多少参数变化会怎样影响 Loss作用衡量当前结果好不好指导参数往哪更新例子这次考试扣了 20 分应该重点补哪类题代码位置loss criterion(pred, y)loss.backward()后的param.grad如果只知道 Loss 大你只知道模型错了。但你不知道是哪个参数影响更大应该增大还是减小每个参数应该改大步还是小步这些信息都来自梯度。所以调模型时不要只盯着 Loss 曲线也要学会检查梯度forname,pinmodel.named_parameters():ifp.gradisnotNone:print(name,p.grad.abs().mean().item())如果很多层梯度接近 0可能存在梯度消失、计算图断裂或学习信号太弱。如果梯度特别大Loss 还来回震荡可能存在梯度爆炸或学习率过大。08 梯度消失和梯度爆炸为什么会出现深层网络里梯度要经过很多层才能传回前面的参数。每经过一层都会乘上一个局部导数。如果很多局部导数都小于 10.5 × 0.5 × 0.5 × 0.5 × ... → 越乘越小梯度就可能越来越小这叫梯度消失。如果很多局部导数都大于 12 × 2 × 2 × 2 × ... → 越乘越大梯度就可能越来越大这叫梯度爆炸。这就是为什么后面学深层神经网络时要讨论激活函数怎么选参数怎么初始化学习率怎么设置是否需要归一化是否需要残差连接它们不是“高级装饰”而是在帮助梯度稳定流动。09 真实训练排错按这 5 步检查很多科研小白一遇到训练异常就马上想换模型、换优化器、换更大的网络。先别急。真实项目里大量问题都出在数据、shape、标签和计算链路上。建议按下面 5 步排查步骤你要问什么常见问题1. 输入是什么进入模型的是图片、张量还是特征通道顺序错、归一化错、batch 维丢失2. 输出是什么输出是概率、logits、Loss 还是指标把 logits 当概率、Loss 对象搞错3. shape 对吗预测和标签形状是否匹配[B, C]和[B]搞混4. 梯度通吗param.grad是否存在、是否为 0计算图断裂、忘记requires_grad5. 参数更新了吗optimizer.step()后参数是否变化忘记 step、学习率为 0、梯度被清空最重要的是不要一上来就换模型。先确认输入、输出、中间变量、梯度、参数更新这条链路是通的。10 结合图像任务理解错误会传回前面的卷积核放到图像模型里反向传播就更有画面感了。比如一个猫狗分类模型把猫预测成了狗。Loss 变大以后错误信号不会只停在最后一层。它会沿着计算图一路往回传分类输出 → 分类层权重 → 高级特征 → 中级特征 → 低级卷积核前面的卷积核并不是天生会看边缘、纹理和形状。它们一开始通常是随机初始化的。之所以后来能学出边缘、纹理、局部结构是因为每一次预测错误都会通过反向传播把调整信号传回来。这也解释了为什么标签质量非常重要。如果标签错了Loss 给出的方向就会偏反向传播会非常认真地把这个错误方向传给参数。模型不是故意学错是你给它的学习信号错了。所以做科研项目时永远记住三件事数据是否正确标签是否可靠梯度是否能稳定传回前面的层11 常见误区别再这样理解反向传播误区正确理解反向传播是模型自己想明白了它是链式法则在计算图上的高效应用梯度就是误差本身误差描述错多少梯度描述往哪改有 Loss 就一定能训练有 Loss 只能衡量错误不代表梯度稳定层数越深一定越好深层网络表达力更强但梯度更难稳定传递backward()会自动更新参数backward()算梯度optimizer.step()才更新参数Loss 不降就换模型先查数据、shape、标签、梯度和学习率如果你刚入门最该背的不是一堆公式而是这句话前向传播让模型知道自己错了多少反向传播让每个参数知道自己该怎么改。12 随堂自测3 道题判断你是否真的懂了建议停 30 秒自己答一下。题 1画出单神经元计算图请画出z w·x b a sigmoid(z) L loss(a, y)参考答案x, w, b → z → a → L前向从左往右算反向从右往左传梯度。题 2为什么链式法则适合多层网络因为多层网络本质上是很多函数一层一层嵌套起来。前面参数不是直接影响 Loss而是通过很多中间节点间接影响 Loss。链式法则可以把每一小段的局部影响乘起来得到最前面参数对最终 Loss 的总影响。题 3误差和梯度有什么区别误差描述预测结果和标签差了多少。梯度描述某个参数变化会怎样影响误差。一句话误差告诉你错了梯度告诉你往哪改。13 课后按这个顺序学别乱如果你是科研小白建议按下面 4 步复盘先看第 3 页核心定义表把计算图、前向传播、局部导数、链式法则、反向传播、梯度讲顺。再看第 5 页机制流程图复述“前向计算 → 保存中间量 → 从 Loss 开始 → 局部反传 → 累积梯度 → 更新参数”。然后手算第 6 页例子用zwxb、asigmoid(z)、L(a-y)^2推一遍∂L/∂w。最后跑本文代码观察loss.backward()后w.grad怎么出现。如果你能做到这 4 件事这一讲就过关了。14 一句话总结本讲可以压缩成一条训练链路计算图记录路线 前向传播得到 Loss 局部导数描述每一小段敏感度 链式法则把敏感度串起来 反向传播把梯度传回参数 优化器根据梯度更新参数再压缩成一句人话前向是数据流反向是梯度流前向产生错误反向分摊责任。下一讲会进入向量化和 Batch当样本很多、参数很多时如何用矩阵一次性高效计算。如果你想系统学习这套“从深度学习到 YOLO26”的课程可以关注我。第05讲的 PPT、讲义、动画和本文代码练习都可以无偿送给大家。建议收藏本文课后对着图和代码再走一遍反向传播就不再是黑盒了。