前向传播与反向传播到底在做什么?

发布时间:2026/6/29 5:54:10
前向传播与反向传播到底在做什么? 训练神经网络时我们经常看到下面几行代码logits model(x) loss loss_fn(logits, y) loss.backward() optimizer.step()它们看起来只是四次函数调用背后却完成了一次完整的学习先根据现有参数作出预测再计算错误把错误逐层传回每个参数最后调整参数。可以先把这条主线记成四个词预测 → 算错 → 追责 → 更新前向传播用当前参数作一次预测前向传播是数据从输入端流向输出端的过程。假设一个网络由两层全连接层组成输入特征 x → Linear(2, 4) → ReLU → Linear(4, 1) → 预测值每一层读取上一层的输出用自己的权重和偏置完成计算再把结果交给下一层。执行prediction model(x)就是在进行一次前向传播。此时模型只是用当前参数回答问题参数还没有发生变化。前向传播会保留计算过程中必要的关系。例如某个输出由哪些输入、权重和中间结果得到。这些关系会组成后续反向传播需要的计算图。损失函数把预测变成一个错误分数模型给出预测后需要和真实答案比较loss loss_fn(prediction, target)损失函数把整批样本的预测误差汇总成一个标量。这个数不只是用来显示训练效果它还是反向传播的起点。如果没有损失就不知道模型要朝什么方向调整。损失越小通常说明当前预测越接近训练目标。反向传播计算每个参数对错误负多少责任调用loss.backward()并不会直接修改参数。它做的是计算梯度。梯度回答的问题是某个参数稍微增大一点损失会增大还是减小变化有多明显神经网络有很多层损失位于网络末端。反向传播会从损失开始沿着前向传播留下的计算关系反方向逐层计算损失 → 输出层参数的梯度 → 隐藏层参数的梯度 → 更前面各层参数的梯度这背后的数学工具是链式法则。可以把它理解成一次逐级追责最终结果出了问题就从最后一步开始向前判断每一步对结果造成了多大影响。反向传播结束后梯度通常保存在参数的.grad中for name, parameter in model.named_parameters(): print(name, parameter.grad)优化器更新真正修改参数知道每个参数的梯度以后优化器才会执行更新optimizer.step()最简单的更新可以写成新参数 旧参数 - 学习率 × 梯度梯度给出调整方向学习率控制每次调整的幅度。loss.backward()负责算梯度optimizer.step()负责改参数两者不能混为一谈。用最小 PyTorch 代码跑通一次更新下面的例子用两个输入特征预测一个连续数值import torch from torch import nn ​ torch.manual_seed(7) ​ model nn.Sequential( nn.Linear(2, 4), nn.ReLU(), nn.Linear(4, 1), ) ​ x torch.tensor([ [1.0, 2.0], [2.0, 1.0], [3.0, 4.0], ]) y torch.tensor([[3.0], [3.0], [7.0]]) ​ loss_fn nn.MSELoss() optimizer torch.optim.SGD(model.parameters(), lr0.01) ​ optimizer.zero_grad() # 清空上一次留下的梯度 prediction model(x) # 前向传播作出预测 loss loss_fn(prediction, y) loss.backward() # 反向传播计算梯度 optimizer.step() # 根据梯度更新参数 ​ print(loss:, loss.item())这段代码只完成一次参数更新。真正训练时会对许多 batch 重复同一套顺序。为什么要先清空梯度PyTorch 默认会累加梯度。如果连续调用两次backward()第二次计算的梯度会加到第一次的结果上。普通训练循环通常需要在每次更新前执行optimizer.zero_grad()否则当前 batch 和之前 batch 的梯度会混在一起更新幅度也会偏离预期。梯度累加确实有用途但应该是明确设计的训练策略而不是忘记清空造成的意外。三个高频误区1. 以为backward()会更新参数backward()只计算并保存梯度。没有optimizer.step()模型参数不会改变。2. 把训练顺序写乱常规顺序应该是zero_grad → forward → loss → backward → step先step()再backward()优化器就拿不到这一次预测产生的梯度。3. 在验证和推理时仍然记录梯度验证阶段不需要反向传播通常应写成model.eval() with torch.no_grad(): prediction model(x)这样可以减少不必要的计算与内存占用。技术图把关键链路画清楚可运行实验观察一次完整参数更新前向传播产生预测和 loss反向传播只负责计算梯度优化器才会修改参数。下面打印更新前后的权重。import torch from torch import nn ​ torch.manual_seed(0) model nn.Linear(1, 1) optimizer torch.optim.SGD(model.parameters(), lr0.1) x, y torch.tensor([[2.0]]), torch.tensor([[5.0]]) print(f更新前 weight{model.weight.item():.4f}) optimizer.zero_grad() pred model(x) loss nn.MSELoss()(pred, y) loss.backward() print(floss{loss.item():.4f} grad{model.weight.grad.item():.4f}) optimizer.step() print(f更新后 weight{model.weight.item():.4f})运行结果更新前 weight-0.0075 loss20.0572 grad-17.9141 更新后 weight1.7839backward()后梯度已经存在但权重仍未改变执行optimizer.step()后权重才沿降低损失的方向更新。常见误区loss.backward()会自动更新参数。它只计算并累积梯度。每轮可以省略zero_grad()。默认梯度会累积除非你明确需要梯度累加。动手练习在backward()后、step()前再次打印权重验证它尚未改变。这一课先记住什么一次训练更新可以拆成四个职责清楚的步骤前向传播根据当前参数产生预测损失函数衡量预测与答案的差距反向传播计算每个参数的梯度优化器根据梯度真正修改参数。下一课继续进入 PyTorch 的自动微分与计算图解释loss.backward()为什么能自动找到并计算这些梯度。本文首发于「去你想去的地方」 https://bestsdz.xyz/posts/forward-and-backpropagation/完整学习路线、视频版和后续更新请访问原文。