Backpropagation
反向传播通过神经网络反向应用链式法则,只需一次遍历即可计算出每个权重的梯度。本文介绍相关数学原理、计算成本和代码。
反向传播是「误差的反向传播」的简称,是一种算法,用于计算神经网络中的每个权重对预测误差的贡献。它从损失函数出发,逐层向输入方向应用链式法则,并重复利用每个中间结果,因此一次反向遍历就能算出每个权重的梯度,无论模型有数千个参数还是数十亿个参数。随后,优化器会使用这些梯度更新权重,因此反向传播是几乎所有深度学习模型的学习步骤。Rumelhart、Hinton 和 Williams 在 1986 年发表于 Nature 的论文《通过反向传播误差来学习表征》中,推动了反向传播在神经网络中的普及。
反向传播的工作原理#
对一个批次进行训练需要两次遍历。前向传播将输入依次送入每一层,存储中间激活值,并最终计算损失。反向传播从损失开始,以相反顺序遍历各层。每一层都会接收损失相对于其输出的梯度,也就是上游梯度,再将该梯度与本层的局部导数相乘,得到两个结果:一是供优化器使用并保留的本层权重梯度,二是损失相对于本层输入的梯度,该梯度会成为下一低层的上游梯度。
由于每一层只需要其上游梯度和已存储的激活值,因此不会重复计算导数。在自动微分中,反向传播是沿神经网络计算图进行的反向模式微分;PyTorch会在前向传播时构建该图,并在调用 loss.backward() 时反向遍历该图。对于一个有两个权重的网络,可以将 PyTorch 的 autograd结果与手动展开的链式法则进行对照验证:
import torch
# A two-weight network: prediction = w2 * tanh(w1 * x)
x, target = torch.tensor(1.5), torch.tensor(0.5)
w1 = torch.tensor(0.8, requires_grad=True)
w2 = torch.tensor(-1.2, requires_grad=True)
# Forward pass, keeping the hidden activation h for the backward pass
h = torch.tanh(w1 * x)
loss = (w2 * h - target) ** 2
# Backward pass: autograd applies the chain rule from the loss back to each weight
loss.backward()
# The same chain rule by hand, reusing the upstream gradient dL/dy for both weights
dL_dy = 2 * (w2 * h - target)
dL_dw2 = dL_dy * h
dL_dw1 = dL_dy * w2 * (1 - h**2) * x
print(f"autograd: dL/dw1 = {w1.grad:.4f}, dL/dw2 = {w2.grad:.4f}")
print(f"by hand: dL/dw1 = {dL_dw1:.4f}, dL/dw2 = {dL_dw2:.4f}")两行都会打印 dL/dw1 = 1.6475, dL/dw2 = -2.5016。手写版本只计算一次 dL_dy,并将其用于两个权重;autograd 处理数十亿个权重的网络时,也会以同样的方式重复利用结果。时间反向传播 (BPTT) 是将同一算法应用于沿时间步展开的循环神经网络。
反向传播与优化器的区别#
反向传播只计算梯度,不会更改任何权重。优化算法(例如随机梯度下降 (SGD)或 Adam)会将这些梯度转换为权重更新,并通过学习率进行缩放。PyTorch 会将每次反向传播的结果累加到权重上已存储的梯度中,因此一次优化器更新可以合并多个批次的结果:
Ultralytics YOLO 训练会使用梯度累积达到 64 张图像的名义批次大小(nbs=64):预热后,batch=16 运行会对每个批次进行反向传播,但每四个批次才更新一次优化器,并在此之前将累积梯度裁剪到范数 10。
梯度消失与梯度爆炸#
反向传播会逐层将局部导数相乘,因此梯度大小会随网络深度呈几何级数缩小或增大。当大多数因子小于 1 时,例如饱和的 sigmoid 单元,最早的层几乎接收不到信号,这就是梯度消失问题。当大多数因子大于 1 时,梯度爆炸问题会导致更新幅度巨大,并使 NaN 损失值暴涨。ReLU 系列激活函数、经过缩放的权重初始化、批归一化,以及残差网络中的跳跃连接,能让梯度幅值在数百层中保持稳定。
计算与内存开销#
局部导数所需的每个激活值都必须从前向传播开始一直保存在内存中,直到反向传播处理到它,因此训练内存会随批次大小、输入分辨率和网络深度增加,训练所需内存也远高于推理。梯度检查点只保存部分激活值,并在反向传播期间重新计算其余部分,以额外计算换取更低的内存占用;混合精度则将大多数激活值保存在 16 位格式中,所占空间只有 32 位浮点数的一半。在计算方面,反向传播的开销约为前向传播的两倍,因为每层都要计算权重和输入的梯度。这就是为什么缩放定律估算每个训练 token、每个参数约需 6 次浮点运算:前向传播需要 2 次,反向传播需要 4 次。










