反向传播怎么算:手推一个 2-3-1 前馈网络,完整走完梯度计算与踩坑排查
2026/9/8 20:45:44 网站建设 项目流程

反向传播怎么算:手推一个 2-3-1 前馈网络,完整走完梯度计算与踩坑排查

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

loss 停在 0.34 不动,你把学习率调大十倍,它变成 NaN——多半问题出在前馈神经网络的反向传播环节:要么梯度算错,要么算对但传不动。这篇文章用一个能手算的小网,把符号、公式、数字与排查完整走一遍。

先建立直觉:误差为什么得从后往前、逐层"累乘"回传

这一节回答一个问题:凭什么拿最后一层的偏差,就能倒推出每一层参数该背多少责任?

想象群聊里的"传话游戏":A 说一句话,B 听了转述给 C,C 加工后发成最终声明,现在声明被投诉错了。追责只能从下游开始:先算 C 自己发挥了多少,这部分 C 扛;照搬 B 原话的部分,按"C 保留了多少 B 的话"这个比例甩回给 B;B 再按同样的逻辑甩给 A。

对应到神经网络:输出偏差就是那封"投诉信"。每往回传一层,误差都要乘一个系数——一半来自下一层的连接权重,一半来自本层激活函数的导数(该神经元当时"还愿不愿意被调整")。这个逐层累乘的机制有个副作用:系数连乘下去,误差要么缩成零,要么放大成天文数字,这正是后文踩坑指南里两位主角的由来。

手写推导:符号、损失函数与 4 条误差传递式

🧮 这一节把传话游戏翻译成符号。约定:第 l 层的线性输入记 h^(l),激活输出记 a^(l),误差项记 g^(l),a^(0) 就是原始输入 x,φ 为激活函数。

定义与损失函数

h^(l) = W^(l) a^(l-1) + b^(l) a^(l) = φ(h^(l)) L = ½ (y* - a^(L))²

白话:每层做的事就是"加权求和再过一个非线性门";损失是预测与目标 y* 之差的平方除二,整张网络的"总账"。

输出层误差

g^(L) = (a^(L) - y*) · φ'(h^(L))

白话:预测偏差再乘激活函数在该点的斜率;斜率小,说明这一层当时不敏感,该少怪它。

隐藏层误差

g^(l) = (W^(l+1)ᵀ g^(l+1)) ⊙ φ'(h^(l))

白话:下游误差先沿权重矩阵倒着分给本层每个神经元,再逐元素乘本层激活函数的导数(⊙ 即逐元素相乘)——这就是传话游戏里的"保留比例"。

参数梯度

∂L/∂W^(l) = g^(l) (a^(l-1))ᵀ ∂L/∂b^(l) = g^(l)

白话:权重梯度是"本层误差 × 上一层激活"的外积,偏置梯度就是本层误差本身。拿到它们,优化器就能更新参数了。

换一组数字:2-3-1 小网完整算一遍

这一节用具体数值验证公式。网络 2-3-1:输入 2 维、隐藏层 3 个神经元(tanh)、输出 1 维回归。

设定:x = [2, 0.5],目标 y* = 0.8;参数初值:

W^(1) = [[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]],b^(1) = [0.1, -0.2, 0.3] W^(2) = [0.1, -0.2, 0.15],b^(2) = -0.1

前向

h^(1) = W^(1)x + b^(1) = [0.5, 0.45, 1.2] a^(1) = tanh(h^(1)) ≈ [0.4621, 0.4219, 0.8337] h^(2) = W^(2)a^(1) + b^(2) ≈ -0.0131 a^(2) = h^(2)(输出层恒等激活) L = ½(0.8 + 0.0131)² ≈ 0.3306

反向

g^(2) = a^(2) - y* = -0.8131 W^(2)ᵀ g^(2) ≈ [-0.0813, 0.1626, -0.1220] g^(1) = W^(2)ᵀ g^(2) ⊙ (1 - a^(1)²) ≈ [-0.0640, 0.1337, -0.0372]

梯度

∂L/∂W^(2) ≈ [-0.3758, -0.3430, -0.6779],∂L/∂b^(2) = -0.8131 ∂L/∂b^(1) ≈ [-0.0640, 0.1337, -0.0372] ∂L/∂W^(1) ≈ [[-0.1279, -0.0320], [0.2674, 0.0668], [-0.0744, -0.0186]]

留意第三路:输入 1.2 时 tanh 已接近饱和,导数只剩 0.305,这条支路这一轮几乎"不听劝"。

工程落地:先写数值梯度检验,再谈向量化

🔧 这一节给两段最小代码,顺序不能反:先验证正确性,再谈速度。

数值梯度检验怎么写:手写反向传播后,用中心差分独立估一遍梯度来对账——

import numpy as np def numerical_grad(f, p, eps=1e-6): """中心差分:参数 p 每个分量分别 ±eps,估算偏导。""" g = np.zeros_like(p) for i in range(p.size): p_hi, p_lo = p.copy(), p.copy() p_hi[i] += eps p_lo[i] -= eps g[i] = (f(p_hi) - f(p_lo)) / (2 * eps) return g

把"前向 + 损失"打包成 f,与解析梯度逐元素比较,相对误差 |解析-数值| / max(1, |解析|+|数值|) 小于 1e-5 才算过关;维度对不上或误差在 1e-2 量级,先查转置与广播,再看激活函数导数。

向量化与性能优化:确认正确后,把逐样本循环换成矩阵运算——m 个样本排成 n×m 的矩阵,一次矩阵乘法跑完整个 mini-batch。另外两招:激活导数尽量复用前向值(sigmoid 写 a(1-a)、tanh 写 1-a²,少算一遍 exp);大张量保持内存连续,往往比换算法收益更直接。

训练踩坑指南:梯度消失、爆炸与学习率怎么排查

⚠️ 这一节把三类最常见的"loss 不降"按现象→原因→解法拆开。

现象一:深层网络 loss 几乎不降,浅层参数纹丝不动原因:误差每回传一层都要乘"权重 × 激活导数";sigmoid 导数上限仅 0.25,权重若再偏小,连乘十几层后连乘积趋于零,即梯度消失。 解法:换 ReLU 家族、上 BatchNorm 这类层内标准化、加残差跳跃连接(ResNet 的核心思路)给梯度开直通车道;参数初始化按层深缩放(Xavier / He 初始化)也关键。

现象二:loss 变 NaN 或参数爆炸原因:权重初始过大、或梯度沿路径反复累加,误差指数级放大。 解法:梯度裁剪(把梯度范数压到阈值内)、缩小初始权重、检查数据里是否混入极端值。

现象三:学习率调大反而更差,调小又爬得慢原因:学习率决定每次沿梯度走多远——太大就跨过山谷来回震荡,太小则长期原地打转。 解法:用退火策略(分段或指数衰减),或换 Adam、RMSprop 这类自适应优化器;排查时固定结构,把学习率从 1e-1 扫到 1e-4,取最低点附近起步。

资料清单:仓库里能直接用的资源

📚 这一节列出无需离开仓库就能点开的材料。

  • 《神经网络与深度学习》第二版:第 4 章"前馈神经网络"的章节入口、习题与勘误
  • 案例与实践:第 4 章配套的可运行 PyTorch notebook 与 sanity 测试
  • 可视化资源:第 7 章"优化算法对比"等交互演示,直观看损失面上不同优化器的轨迹
  • 阅读路径:不确定从哪本入手时的选书对照
  • 进阶文献(自行检索原文):Rumelhart、Hinton 与 Williams(1986)的反向传播原始论文;Glorot 与 Bengio(2010)关于深层前馈网络初始化难度的分析;Werbos(1990)的多层感知机反向传播工作

推导、数字与排查手段都齐了。合上文档前,用这份清单自查一遍:

  • 不看资料推写出 g^(l) = (W^(l+1)ᵀ g^(l+1)) ⊙ φ'(h^(l)),并解释每一项的含义
  • 手算过至少一个样本的前向 + 反向全过程,且和框架给出的梯度对过账
  • 给自己的实现写过数值梯度检验,相对误差在 1e-5 内
  • 遇到 loss 不动或 NaN,能按"消失 / 爆炸 / 学习率"三种现象分别给出排查动作

哪一步卡住了,就把卡住的那一行贴出来一起拆——反向传播这件事,手算透一遍的价值,远大于跑通十遍。

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

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

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

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

立即咨询