反向传播算法详解:四步推导加一次手算,把前馈神经网络的梯度讲透
2026/9/8 22:31:26 网站建设 项目流程

反向传播算法详解:四步推导加一次手算,把前馈神经网络的梯度讲透

【免费下载链接】nndl邱锡鹏《神经网络与深度学习》第二版与通识版:电子书、章节目录、学习资源与勘误。项目地址: https://gitcode.com/GitHub_Trending/nn/nndl

训练前馈神经网络时,如果一次预测"考砸了",反向传播就是那套复盘流程:把损失拆成责任,一层一层追到每个参数头上,然后按各自的责任大小调整。本文结合邱锡鹏《神经网络与深度学习》仓库 nndl 的配套资源,带你走完这个过程。读完你会收获:

  • 不看公式也能说清的误差分摊直觉
  • 输出层、隐藏层误差项与参数梯度的完整推导
  • 一个 1-2-1 小网络的逐步手算
  • 梯度消失/爆炸、学习率问题的排查清单

先建立直觉:误差是怎么分摊的

把一次前向计算想象成一场考试:输出层交卷,分数(损失)出来了。现在要复盘,做法不是重做整张卷子,而是沿计算链从后往前追责

  • 输出层的误差,直接由"预测值 − 真实值"衡量;
  • 往前一层走一步,上游的误差要乘以该层与下游之间连接权重的转置,再乘上本层激活函数的导数——前者回答"我通过哪条连线影响了下游",后者回答"我当时对输入的敏感度有多高";
  • 每个权重拿到的误差份额,就是上游误差和本层输出的乘积。

追责完毕,每个参数就知道该往哪个方向、迈多大步去更新:

参数 ← 参数 − 学习率 × 该参数的梯度

记住这个"转置传误差、导数调敏感度、外积算份额"的三步口诀,下面的公式就只是把它写成符号而已。

数学拆解:从损失到参数梯度

符号约定与损失函数

第 l 层(输入层记为 0 层)的计算是:

z⁽ˡ⁾ = W⁽ˡ⁾a⁽ˡ⁻¹⁾ + b⁽ˡ⁾ a⁽ˡ⁾ = σ(z⁽ˡ⁾)

z 是加权求和后的输入,a 是过完激活函数 σ 的输出。训练用均方误差衡量差距:

L = ½ ||t − a⁽ᴸ⁾||²

我们的目标只有一个:把 ∂L/∂W⁽ˡ⁾ 和 ∂L/∂b⁽ˡ⁾ 算出来。直接对整条链求导会指数级膨胀,反向传播的聪明之处在于引入中间量误差项 δ⁽ˡ⁾ = ∂L/∂z⁽ˡ⁾,让梯度像接力一样逐层传递。

输出层误差怎么算

最后一层的误差项一步到位:

δ⁽ᴸ⁾ = (a⁽ᴸ⁾ − t) ⊙ σ′(z⁽ᴸ⁾)

白话说:预测超真实值多少,再乘以输出激活函数在该点的斜率。⊙ 表示逐元素相乘,σ′ 是激活函数的导数。

隐藏层误差如何回传

对任意隐藏层 l,误差项由上一层"倒推"回来:

δ⁽ˡ⁾ = (W⁽ˡ⁺¹⁾)ᵀ δ⁽ˡ⁺¹⁾ ⊙ σ′(z⁽ˡ⁾)

白话说:下游的误差先沿连接权重转置传回,再按本层激活函数的敏感度缩放。注意这里只用到相邻两层的信息,所以整个网络 O(层数) 次矩阵乘法就扫完了。

参数梯度怎么算

拿到各层 δ,权重和偏置的梯度就是两行式子:

∂L/∂W⁽ˡ⁾ = δ⁽ˡ⁾ (a⁽ˡ⁻¹⁾)ᵀ ∂L/∂b⁽ˡ⁾ = δ⁽ˡ⁾

白话说:一个权重的责任 = 下游传回的误差 × 本层的输出;偏置没有"输入"可分摊,直接等于误差本身。

手算走一遍:一个 1-2-1 的网络

网络结构:1 个输入、2 个隐藏神经元、1 个输出,隐藏层用 Sigmoid(Sigmoid 把任意实数压到 0~1 之间,它的导数是 a(1−a)),输出层不加激活函数(回归任务常用)。损失取 L = ½(t − a³)²。

取一组好算的数据:x = 0.5,目标 t = 1;参数 w1 = 0.4,w2 = −0.2,w3 = 0.6,b = 0.1。

前向传播:

步骤计算结果
隐藏层输入z¹ = [w1x, w2x] = [0.4×0.5, −0.2×0.5][0.2, −0.1]
隐藏层输出a¹ = σ(z¹)[0.55, 0.475]
输出层z² = 0.6×0.55 + (−0.2)×0.475 + 0.10.225
损失L = ½(1 − 0.225)²≈ 0.300

反向传播:

  1. 输出层误差(输出层是线性,σ′ ≡ 1):δ² = a³ − t = 0.225 − 1 =−0.775
  2. 隐藏层误差:先传回再缩放。δ¹ = [0.6, −0.2]ᵀ × (−0.775) ⊙ [0.55×0.45, 0.475×0.525] ≈ [−0.153, 0.089] ⊙ [0.2475, 0.2494] ≈[−0.038, 0.022]
  3. 参数梯度:
    • ∂L/∂w3 = δ² × a1¹ ≈ −0.775 × 0.55 =−0.426
    • ∂L/∂b = δ² =−0.775
    • ∂L/∂w1 = δ¹[0] × x ≈ −0.038 × 0.5 =−0.019
    • ∂L/∂w2 = δ¹[1] × x ≈ 0.022 × 0.5 =0.011

看一个细节:预测偏低,输出层的误差是负的,所以 w3 和 b 的梯度都指向"把输出调大"的方向——符号和直觉对上了。👍

代码要点:20 行以内抓住关键

反向传播最该先验证的不是"能不能跑",而是"梯度算没算错"。一个通用的数值梯度检验:

import numpy as np def gradcheck(loss_fn, params, eps=1e-6): num, ana = np.zeros_like(params), params.grad # ana 为你的反向传播结果 for i in np.ndindex(params.shape): old = params[i] params[i] = old + eps lp = loss_fn(params) params[i] = old - eps lm = loss_fn(params) params[i] = old num[i] = (lp - lm) / (2 * eps) return np.allclose(num, ana, rtol=1e-4)

几条实用建议:

  • 梯度检验只在小数据上做:数值法每个参数要跑两次前向,参数一多就慢得受不了;
  • 激活导数优先用输出表示,如 Sigmoid 写a * (1 - a),避免重复指数运算;
  • 向量化 + mini-batch:把一批样本写成矩阵一次算完,别写 Python for 循环;
  • 在 PyTorch 里反向传播就是loss.backward()一行,但它算的还是上面这套 δ——理解推导后,你才能看懂 autograd 出错时的方向问题。

避坑清单:训练出问题的排查表

现象常见成因对策
梯度消失:浅层参数几乎不动Sigmoid 导数最大只有 0.25,连乘后层层缩水换 ReLU(负区间为 0 的分段线性函数)、用合适的初始化、加残差连接
梯度爆炸:损失变 NaN权重初始化过大或网络过深,连乘指数增长梯度裁剪、Xavier/He 初始化、换自适应优化器
收敛震荡不下降学习率过大,一步迈过谷底学习率衰减、Adam 类自适应方法
收敛极慢学习率过小,或误差项本身太小预热后提学习率、检查初始化尺度

资源直达:去哪儿继续学

  • 本书第二版入口(章节目录、习题与勘误):nndl-v2/index.md
  • 书中关键概念的可视化动图(按章节组织):viz/index.md
  • 配套实战教程《案例与实践》,含 PyTorch 可运行案例:nndl-practice/index.md
  • 四本书的阅读路径与选读建议:reading-path/index.md

进阶文献两篇:

  • Rumelhart, Hinton, Williams (1986),Learning representations by back-propagating errors——反向传播的经典论文,推导和本文同构;
  • Glorot & Bengio (2010),Understanding the difficulty of training deep feedforward neural networks——解释为什么初始化尺度决定训练成败,与上面避坑表直接相关。

写在最后

📌 框架再好用,梯度出错时也得靠你自己的推导去定位。拿一支笔把上面的 1-2-1 例子重新算一遍,比读完三遍公式更有用。下一步如果想继续深挖,可以看卷积网络里权值共享如何改写梯度公式——那是反向传播原理的另一个经典应用。

【免费下载链接】nndl邱锡鹏《神经网络与深度学习》第二版与通识版:电子书、章节目录、学习资源与勘误。项目地址: https://gitcode.com/GitHub_Trending/nn/nndl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询