训练1000轮损失不降?反向传播手算一遍就懂了
【免费下载链接】nndl邱锡鹏《神经网络与深度学习》第二版与通识版:电子书、章节目录、学习资源与勘误。项目地址: https://gitcode.com/GitHub_Trending/nn/nndl
训练跑了 1000 轮,损失却纹丝不动,模型一点没学——十有八九,问题出在反向传播(backpropagation)上:梯度要么算错,要么层层传递后归零。这篇写给深度学习新手,也适合回头补基础的工程师:不依赖框架,从"参数到底怎么更新"倒着推,把每一步在算什么讲清楚。读完你能解决:
- 看懂反向传播传递的到底是什么:误差项 error term 如何从输出层一路回到输入层
- 会手动推导误差项公式,不再只会背结论
- 能用一个最小的 2-2-1 网络,亲手算出完整梯度
- 分清手写循环、向量化、自动微分三条实现路线各自适合什么场景
🧠 先建立直觉:反向传播像流水线追责
把一次前向传播(forward pass)想成一条工厂流水线:原料进(输入层)、中间加工(隐藏层)、成品出厂(输出层)。现在成品检验不合格,质检员不会把整条线推倒重来,而是拿着"这批差了多少"沿流水线往回问——最后一道工序担几成责?它又把几成"甩锅"给上一道?每个环节被追到的责任记下来,最后才知道谁该改多少、改多狠。
这个"被追到的责任",就是误差项;质检"往回问"的动作,就是反向传播。落到数学上:我们要让损失 L 最小,参数的更新方向由梯度(gradient)∂L/∂w 决定,而反向传播正是用链式法则(chain rule)把这个梯度从输出层往输入层一层层累乘出来。它优化的是"每一步都在往损失更小的地方走"这件事本身:
图:损失要最小化,反向传播给出的梯度就是下山方向;上图对比了几种优化器沿下降方向的轨迹。
直觉有了,接下来别急着记公式——我们倒过来,从"参数该怎么更新"这个终点往回推。
🔁 倒着推导误差项:从参数更新量反推
终点很明确:我们最终要的是每个参数的更新量
Δw = -η · ∂L/∂w # η 是学习率 learning rate所以要算 ∂L/∂w。为什么中途要引入"误差项" δ 这个量?因为权重梯度恰好等于 δ 与上一层激活的外积,只要先拿到 δ,∂L/∂W 一步就出来——δ 是把"重复出现的乘积"提前打包好的中间结果。
为什么需要输出层的 δ?它把"损失对最后一层输入的敏感程度"一次算清,供后面逐层复用:
δ^(L) = (a^(L) - Y) ⊙ σ'(z^(L))为什么需要往回传的 δ?因为前一层对损失的贡献 = 后一层传回的责任 × 连接权重 × 本地激活导数,这条递推就是反向传播的"追责规则":
δ^(l) = (W^(l+1)ᵀ δ^(l+1)) ⊙ σ'(z^(l))为什么拿到 δ 就够?有了它,权重和偏置的梯度直接落地,不需要再对损失求一次复杂偏导:
∂L/∂W^(l) = δ^(l) (a^(l-1))ᵀ ∂L/∂b^(l) = δ^(l)三行公式闭环了。下面用一个能手算的小网络验证它真的对得上。
✏️ 手算走一遍反向传播:最小2-2-1网络
固定参数,输入X=[1,0],标签Y=1,激活全用 sigmoid,损失用均方误差。参数取W¹=[[0.5,1.0],[1.0,0.5]]、b¹=[1.0,-1.0]、W²=[0.5,-0.5]、b²=0。按"检查项 → 数值 → 来源"过一遍:
| 检查项 | 数值 | 来源说明 |
|---|---|---|
| 隐藏层输入 z¹ | [1.5, 0.0] | W¹X+b¹,前向第一层 |
| 隐藏层输出 a¹ | [0.8176, 0.5] | σ(z¹) |
| 输出层 z² / a² | 0.1588 / 0.5396 | W²a¹+b²再 σ |
| 损失 L | 0.106 | ½(Y-a²)² |
| 输出层 δ² | -0.1144 | (a²-Y)·σ'(z²),σ'(z²)=0.2484 |
| 隐藏层 σ'(z¹) | [0.1491, 0.25] | a¹(1-a¹) |
| 隐藏层 δ¹ | [-0.0085, 0.0143] | (W²ᵀδ²)⊙σ'(z¹) |
| ∂L/∂W² / ∂L/∂b² | [-0.0935,-0.0572] / -0.1144 | δ²a¹ᵀ/δ² |
| ∂L/∂W¹ / ∂L/∂b¹ | [[-0.0085,0],[0.0143,0]] / [-0.0085,0.0143] | δ¹Xᵀ/δ¹ |
注意 δ¹ 第二项是正的:因为W²第二维权重为负,"甩锅"回传后符号被翻转,再乘上激活导数。手算一遍,链式法则的符号与量级就都踏实了。
算对了,落到代码上无非是把这些外积写成矩阵乘法。
⚙️ 工程落地:手写、向量化与自动微分怎么选
| 路线 | 代码量 | 速度 | 调试透明度 | 适合谁 |
|---|---|---|---|---|
| 手写循环 | 多 | 慢 | 最高,每步可打印 | 学习原理、定位错误 |
| 向量化(NumPy) | 中 | 快 | 高,矩阵维度可控 | 生产前的原型、教学 |
| 自动微分(PyTorch) | 少 | 最快 | 黑盒,靠梯度检验兜底 | 实际训练大模型 |
核心逻辑其实就几行,前向存下中间量,反向按上面公式回填:
import numpy as np sig = lambda z: 1/(1+np.exp(-z)) z1 = W1@x + b1; a1 = sig(z1) # 前向 z2 = W2@a1 + b2; a2 = sig(z2) # 前向 d2 = (a2 - y) * a2*(1-a2) # 输出层误差项 dW2 = np.outer(d2, a1); db2 = d2 d1 = (W2*d2) * a1*(1-a1) # 隐藏层误差项(含权重回传) dW1 = np.outer(d1, x); db1 = d1写完别急着跑,先核对几个高频坑。
❓ 反向传播高频疑问:梯度消失与数值核对
问:为什么必须从后往前算,不能各层独立求梯度?答:链式法则是连乘关系,后一层的 δ 是前一层求 ∂L/∂W 的输入。先算输出层并复用这个中间量,能把重复的乘积只算一次;逐参数暴力求导会把它算成千上万遍。
问:换交叉熵损失,输出层公式会变吗?答:会简化。MSE+sigmoid 时 δ² 带一个 σ' 因子;改用交叉熵+sigmoid,对数损失求导产生的 1/σ' 会把 σ' 抵消掉,δ² 直接退化成(a²-Y),少乘一项、数值也更稳。
问:手算和框架的梯度对不上,先查哪?答:先做数值梯度检验,对每个 w 用(f(w+ε)-f(w-ε))/2ε逼近,和反向值比对,相对误差 <1e-5 才算对。常见坑:漏加偏置、激活导数写错、batch 维度没乘进去。
问:梯度消失到底消失的是什么?答:消失的是"传给更浅层的信号"。σ' 最大 0.25,多层连乘因子小于 1,δ 越往前越小,浅层权重梯度趋近 0,等于没更新。ReLU、残差连接、BN 都是为了让这条连乘别一路衰减。
自测题:把上面例子的输出层激活换成 ReLU(此处 z²=0.1588>0,σ'=1),δ² 会变成多少?手算核对一下。想继续:仓库的 可视化资源 汇总了各章动图,优化算法对比 专门看损失面上的优化器轨迹;要动手写代码,案例与实践 第 4 章有可运行的 notebook,教材章节目录 可定位第 4 章前馈神经网络。
【免费下载链接】nndl邱锡鹏《神经网络与深度学习》第二版与通识版:电子书、章节目录、学习资源与勘误。项目地址: https://gitcode.com/GitHub_Trending/nn/nndl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考