查看全课大纲(第 28 / 93 节)
- 1.计算机视觉概述
- 2.计算机视觉应用场景
- 3.困难与挑战
- 4.开源库介绍
- 5.图像显示与存储
- 6.CLAHE
- 7.腐蚀膨胀与开闭运算
- 8.滤波与边缘填充
- 9.各种滤波
- 10.图像金字塔
- 11.傅里叶变换
- 12.短时傅里叶变换与小波变换
- 13.颜色特征与颜色直方图
- 14.几何特征_边缘
- 15.几何特征-角点与斑点
- 16.局部特征-SIFT特征描述子
- 17.局部特征-SURF特征描述子
- 18.局部特征-ORB特征描述子
- 19.其他特征描述子
- 20.传统图像分割技术
- 21.GraphCut
- 22.GrabCut
- 23.人脸检测的传统方法
- 24.行人检测的传统方法HOG+SVM
- 25.行人检测DPM
- 26.人工神经网络的架构
- 27.目标函数与梯度下降
- 28.误差反向传播
- 29.可手算的神经网络
- 30.改进目标函数与克服梯度消散
- 31.神经网络之传统神经网络与深度网络的差异
- 32.各种梯度下降算法
- 33.如何选择合适的梯度下降算法
- 34.避免过适应
- 35.CNN的架构
- 36.CNN的误差反向传播_卷积层
- 37.CNN的误差反向传播_池化层
- 38.分类所用的数据集
- 39.Alexnet详解
- 40.VGG详解
- 41.GoogLeNet_v1
- 42.BatchNormalization
- 43.GoogLeNet_v3v4
- 44.ResNet
- 45.ResNeXt
- 46.CNN设计准则
- 47.图像检索与推土机距离
- 48.基于颜色相似性
- 49.基于形状相似性
- 50.基于纹理相似性
- 51.基于局部特征相似性
- 52.DL的二值哈希编码
- 53.高维空间中检索KDTree
- 54.高维空间中检索LSH
- 55.目标识别任务综述
- 56.RCNN
- 57.SPP-Net
- 58.Fast R-CNN
- 59.Faster R-CNN
- 60.R-CNN系列模型回顾
- 61.R-FCN的设计思路
- 62.R-FCN的实现
- 63.YOLOv1
- 64.YOLOv2_9000
- 65.YOLOv3
- 66.图像分割应用场景与数据集
- 67.上采样的实现方式
- 68.FCN网络架构的由来
- 69.FCN构建细节
- 70.DeepLabv1
- 71.CRF
- 72.DeepLabv2
- 73.DeepLabv3
- 74.特定的医学影像分割任务
- 75.U-Net
- 76.3D U-Net
- 77.V-Net
- 78.DenseNet
- 79.病理切片分析任务及数据集
- 80.病理切片分析
- 81.图说任务及其传统处理方法
- 82.深度语言模型与RNN
- 83.LSTM及其变种
- 84.NIC模型
- 85.SAT模型
- 86.图说的性能指标
- 87.判别模型与生成模型
- 88.KL散度与JS散度
- 89.VAE
- 90.GAN的基本原理
- 91.DCGAN与CGAN
- 92.Wasserstein GAN
- 93.SRGAN
误差反向传播
小象实战讲义 · 基于深度学习的计算机视觉
在上一节我们学习了梯度下降法,它是优化神经网络权重的核心思想。然而,对于一个由多层神经元构成的复杂网络,如何高效地计算目标函数对每一层权重的梯度,是训练能否成功的关键。本节将深入讲解神经网络训练的基石——误差反向传播(Backpropagation, BP)算法,它将告诉我们如何将输出层的误差,一层层地“反向”传播回网络深处,从而计算出所有参数的梯度。
💡 核心导读
- 链式法则:理解误差反向传播的数学核心,即如何通过中间变量逐层求导。
- 输出层梯度:学习如何计算目标函数对隐藏层与输出层之间权重的偏导数。
- 隐藏层梯度:掌握如何将误差进一步反向传播,计算输入层与隐藏层之间权重的偏导数。
- 统一公式:总结误差反向传播的通用迭代公式,理解其“残差”与“输入”相乘的本质。
- 算法意义:认识BP算法在神经网络发展史上的奠基性作用。
梯度下降与链式法则回顾
在开始推导之前,我们需要明确目标。神经网络的训练目标是最小化一个目标函数(或称损失函数)。对于分类问题,一个常见的目标函数是均方误差(Mean Squared Error, MSE)。假设网络有 c 个输出单元,对于单个样本,期望输出为 t = (t1, t2, ..., tc),网络实际输出为 z = (z1, z2, ..., zc),则目标函数 J 定义为:
其中,w 代表网络中所有的权重参数。我们的任务就是找到一组 w,使得 J(w) 最小。
梯度下降法告诉我们,要找到最小值,需要沿着目标函数梯度的反方向(即下降最快的方向)更新参数。参数 w 在第 m+1 轮的更新公式为:
其中,η 是学习率(步长),∂J/∂w 是目标函数对权重的梯度(偏导数向量)。问题的核心就转化为:如何高效地计算这个梯度 ∂J/∂w?
对于一个多层网络,J 与深层权重 w 的关系是嵌套了多层函数的复杂关系。直接求导非常困难。这时就需要用到微积分中的链式法则。
链式法则指出,如果 y 是 u 的函数,而 u 又是 x 的函数,即 y = f(u), u = g(x),那么 y 对 x 的导数可以通过中间变量 u 来传递:
在神经网络中,我们将大量使用链式法则,通过引入每一层神经元的加权输入和激活输出作为中间变量,将复杂的全局梯度分解为一系列简单的局部梯度相乘。
网络架构与符号定义
为了清晰地推导,我们以一个典型的三层前馈神经网络为例,它包括输入层、一个隐藏层和输出层。
- 输入层:有
d个神经元,输入向量记为x = (x1, x2, ..., xd)。输入层神经元没有计算功能,仅传递信号。 - 隐藏层:有
H个神经元。第j个隐藏层神经元的加权输入(或称净输入)记为net_j,其输出记为y_j。net_j = Σ_{i=1}^{d} w_{ji} * x_i(w_{ji}是输入层第i个神经元到隐藏层第j个神经元的权重)y_j = f(net_j)(f是激活函数,如 sigmoid)
- 输出层:有
c个神经元。第k个输出层神经元的加权输入记为net_k,其输出记为z_k。net_k = Σ_{j=1}^{H} w_{kj} * y_j(w_{kj}是隐藏层第j个神经元到输出层第k个神经元的权重)z_k = f(net_k)
我们的目标是计算目标函数 J 对两类权重 w_{kj} 和 w_{ji} 的偏导数 ∂J/∂w_{kj} 和 ∂J/∂w_{ji}。
输出层权重的梯度计算
首先计算离误差更近的权重 w_{kj}(隐藏层到输出层)的梯度。
根据链式法则,我们可以引入中间变量 net_k(输出层第 k 个神经元的加权输入):
第一步:计算 ∂net_k / ∂w_{kj} 根据 net_k 的定义:net_k = w_{k1}y_1 + w_{k2}y_2 + ... + w_{kj}y_j + ...。当对特定的 w_{kj} 求偏导时,只有包含 w_{kj} 的项起作用,其他项视为常数,导数为0。因此: 这一项非常直观:权重 w_{kj} 的系数就是来自隐藏层第 j 个神经元的输出 y_j。
第二步:计算 ∂J / ∂net_k J 与 net_k 没有直接关系,需要再引入中间变量 z_k(输出层第 k 个神经元的输出):
计算
∂J / ∂z_k: 根据J = 1/2 * Σ (t_k - z_k)^2,对z_k求偏导(注意求和号中只有第k项与z_k有关): 这里的负号源于(t_k - z_k)的平方求导。计算
∂z_k / ∂net_k: 根据z_k = f(net_k),这就是激活函数f在net_k处的导数:
因此,∂J / ∂net_k = -(t_k - z_k) * f'(net_k)。
第三步:合并结果 将第一、二步的结果代入链式法则:
为了简化表达式,我们定义一个非常重要的量——残差(δ,delta),对于输出层第 k 个神经元: 注意, 取 的方向,因此下面梯度式带负号;更新时用 自然体现。于是,梯度可以简洁地写为:
物理意义:输出层权重 w_{kj} 的梯度,等于输出层神经元 k 的残差 δ_k 与来自隐藏层神经元 j 的输入 y_j 的乘积的负值。δ_k 包含了该神经元的输出误差 (t_k - z_k) 和激活函数在该点的变化率 f'(net_k)。
隐藏层权重的梯度计算
接下来计算更靠内的权重 w_{ji}(输入层到隐藏层)的梯度。这需要将误差从输出层进一步反向传播到隐藏层。
同样使用链式法则,并引入隐藏层神经元 j 的加权输入 net_j 作为中间变量:
第一步:计算 ∂net_j / ∂w_{ji} 与输出层情况类似,根据 net_j = Σ_i w_{ji} x_i,对特定的 w_{ji} 求偏导,结果是其系数 x_i:
第二步:计算 ∂J / ∂net_j 这是关键且稍复杂的一步。J 与 net_j 没有直接联系,我们引入隐藏层神经元 j 的输出 y_j 作为中间变量:
计算
∂y_j / ∂net_j: 根据y_j = f(net_j),这就是隐藏层激活函数的导数:计算
∂J / ∂y_j: 这是难点。隐藏层神经元j的输出y_j会馈送到所有输出层神经元(k=1 到 c)。因此,y_j的变化会通过所有与之相连的权重w_{kj}影响所有输出z_k,进而影响总误差J。必须考虑所有路径的影响。 这个求和体现了“全连接”的特性。∂J/∂net_k我们在计算输出层梯度时已经求过了,它就是-δ_k(注意,δ_k本身不带负号,定义为δ_k = (t_k - z_k)f'(net_k),因此∂J/∂net_k = -δ_k)。∂net_k/∂y_j根据net_k的定义很简单:net_k = Σ_j w_{kj} y_j,所以∂net_k/∂y_j = w_{kj}。
因此:
第三步:合并结果 将各部分组合起来:
同样,我们为隐藏层神经元 j 定义一个残差 δ_j: 注意,这里 δ_j 的定义与输出层 δ_k 不同。输出层的 δ_k 直接依赖于输出误差 (t_k - z_k);而隐藏层的 δ_j 依赖于下一层(输出层)所有神经元的残差 δ_k 的加权和,权重正是连接这两层的权重 w_{kj}。
于是,隐藏层权重的梯度可以简洁地写为:
物理意义:隐藏层权重 w_{ji} 的梯度,等于隐藏层神经元 j 的残差 δ_j 与来自输入层神经元 i 的输入 x_i 的乘积的负值。δ_j 不是直接由最终误差计算,而是由后一层所有神经元的残差反向传播、加权求和后再乘以本层激活函数的导数得到。
误差反向传播的通用公式
总结上面的推导,我们可以得到误差反向传播算法的统一表述。
对于网络中的任意一层(记当前层为 l),假设它接收前一层(l-1层)的输入 a^{(l-1)},计算加权和 z^{(l)} = W^{(l)} a^{(l-1)},然后通过激活函数得到输出 a^{(l)} = f(z^{(l)})。
- 前向传播:计算每一层的
z^{(l)}和a^{(l)},直到得到最终输出a^{(L)}(L为输出层)。 - 计算输出层残差: 其中
⊙表示逐元素相乘(Hadamard积)。 - 反向传播残差:对于
l = L-1, L-2, ..., 2(即从后往前逐层计算): 这里(W^{(l+1)})^T δ^{(l+1)}正是下一层残差的加权和。 - 计算梯度:得到各层残差后,权重
W^{(l)}的梯度为: (这里 的定义已把“目标减预测”的方向含进去,故梯度带负号;若把残差定义为 ,负号就要反过来,两者只能取其一)。 - 更新权重:使用梯度下降法更新权重:
这个算法清晰地展示了“误差反向传播”的过程:误差信号 δ 从输出层开始,沿着网络反向传播,每经过一层,就利用该层的激活函数导数和来自后一层的权重矩阵进行转换。一旦计算出某一层的残差 δ,该层权重的梯度就简单地等于负的残差与前一层输出的外积(负号由 的定义带来)。
BP算法的意义与影响
误差反向传播算法因 Rumelhart、Hinton 和 Williams 在 1986 年发表的论文《Learning representations by back-propagating errors》而广为人知(其思想更早可追溯到 Werbos 等人在 1970 年代的工作)。这篇论文让 BP 从数学技巧变成了可操作的训练方法,解决了多层神经网络训练的核心难题,使得利用梯度下降法训练深度网络成为可能。
- 高效性:BP算法利用链式法则,把“求所有参数的梯度”从“每个参数各做一次前向传播”的平方级代价,降到与一次前向传播同量级(代价线性于网络的边数),使得训练大规模网络变得可行。
- 奠基性:它是现代深度学习几乎所有训练流程的基础。后续出现的各种优化器(如SGD、Adam)、正则化技术、乃至卷积神经网络(CNN)和循环神经网络(RNN)的训练,都建立在BP算出的梯度之上。
- 局限性:标准的BP算法也存在一些挑战,如梯度消失/爆炸问题(在深层网络中,梯度在反向传播时可能指数级缩小或增大)、对初始值和学习率敏感、容易陷入局部极小值等。这些挑战也催生了ReLU激活函数、残差连接(ResNet)、批量归一化(BatchNorm)等一系列重要的深度学习技术。
📝 动手练一练
- 概念辨析:在误差反向传播中,输出层神经元的残差
δ_k和隐藏层神经元的残差δ_j在计算公式上有何本质区别?这反映了网络训练的什么逻辑?
👉 点击查看参考答案
输出层残差 δ_k:直接由该神经元的目标输出与实际输出的误差 (t_k - z_k) 乘以激活函数在当前位置的导数 f'(net_k) 得到。它衡量的是该输出节点本身的“错误程度”。
隐藏层残差 δ_j:不由最终输出误差直接计算,而是由后一层所有神经元的残差 δ_k 反向传播回来,经过权重 w_{kj} 加权求和后,再乘以本层激活函数的导数 f'(net_j) 得到。即 δ_j = f'(net_j) * Σ_k (δ_k * w_{kj})。
反映的逻辑:这体现了“责任分配”的思想。输出层的误差是明确的,可以直接计算每个输出节点的责任(δ_k)。对于隐藏层节点,我们无法直接知道它“应该”输出多少才是对的,但我们可以根据它所连接的下一层节点的责任(δ_k)以及连接强度(w_{kj}) 来反推它的责任(δ_j)。连接越强(w_{kj} 越大)的下一层节点,其责任对当前隐藏层节点的影响就越大。这就像团队工作:最终成果(输出)的偏差,会逐级回溯到每个成员(神经元)身上,成员的责任大小取决于他/她对出错的下一级成员的影响程度。
- 手算练习:考虑一个极简网络:输入层1个节点(
x=0.5),隐藏层1个节点(权重w_ji=0.8,偏置b_j=0.1,激活函数f(x)=sigmoid(x)),输出层1个节点(权重w_kj=1.5,偏置b_k=0.2,激活函数同上)。给定目标输出t=0.9。 a) 请进行前向传播,计算网络实际输出z。 b) 假设使用MSE损失,计算输出层残差δ_k(提示:sigmoid导数f'(x)=f(x)*(1-f(x)))。 c) 计算隐藏层残差δ_j。 d) 根据BP公式,计算损失J对权重w_kj和w_ji的梯度(学习率η=0.1)。
👉 点击查看参考答案
a) 前向传播
- 隐藏层加权输入:
net_j = w_ji * x + b_j = 0.8*0.5 + 0.1 = 0.5 - 隐藏层输出:
y_j = sigmoid(net_j) = 1/(1+e^{-0.5}) ≈ 0.6225 - 输出层加权输入:
net_k = w_kj * y_j + b_k = 1.5*0.6225 + 0.2 ≈ 1.13375 - 网络输出:
z = sigmoid(net_k) = 1/(1+e^{-1.13375}) ≈ 0.7565
b) 输出层残差 δ_k 输出误差:(t - z) = 0.9 - 0.7565 = 0.1435 sigmoid在 net_k 处的导数:f'(net_k) = z*(1-z) = 0.7565*(1-0.7565) ≈ 0.1842 δ_k = (t - z) * f'(net_k) = 0.1435 * 0.1842 ≈ 0.02643
c) 隐藏层残差 δ_j 首先需要 f'(net_j) = y_j*(1-y_j) = 0.6225*(1-0.6225) ≈ 0.2350 δ_j = f'(net_j) * (δ_k * w_kj) = 0.2350 * (0.02643 * 1.5) ≈ 0.2350 * 0.039645 ≈ 0.00932
d) 计算梯度
对
w_kj的梯度:∂J/∂w_kj = -δ_k * y_j = -0.02643 * 0.6225 ≈ -0.01645更新量(带学习率):Δw_kj = -η * ∂J/∂w_kj = -0.1 * (-0.01645) = 0.001645更新后:w_kj_new ≈ 1.5 + 0.001645 = 1.501645对
w_ji的梯度:∂J/∂w_ji = -δ_j * x = -0.00932 * 0.5 = -0.00466更新量:Δw_ji = -η * ∂J/∂w_ji = -0.1 * (-0.00466) = 0.000466更新后:w_ji_new ≈ 0.8 + 0.000466 = 0.800466
可以看到,经过一次BP更新,两个权重都得到了微调,目的是使下一次前向传播的输出 z 更接近目标值 0.9。
本章小结
本节我们深入推导了神经网络的核心训练算法——误差反向传播(BP)。我们从梯度下降的目标出发,利用链式法则,一步步推导出输出层和隐藏层权重的梯度计算公式,并最终将其统一为简洁的“残差 × 输入”的形式。BP算法的精髓在于它高效地实现了误差信号从网络输出端到输入端的反向流动,使得深度网络的端到端训练成为可能。
今天你几乎不需要手写这套公式:PyTorch、TensorFlow 的自动微分(autograd)会在前向传播时记录计算图,反向时自动套用本节这条残差递推。但正因为框架把它藏了起来,理解 δ 的来源、知道梯度为什么会消失,才更能帮你判断训练异常到底出在哪一层。
📋 行动清单
- 理解链式法则的应用:确保能清晰说出在计算
∂J/∂w_{kj}和∂J/∂w_{ji}时,分别引入了哪些中间变量,以及每一步求导的物理意义。 - 掌握残差的定义与传播:能够区分输出层残差
δ_k和隐藏层残差δ_j的计算公式,并理解δ_j是δ_k的加权和这一反向传播的本质。 - 尝试手算推导:参照动手练一练中的极简网络,在纸上完整地演练一遍前向传播、误差计算、反向传播和权重更新的全过程,加深对公式的理解。
—— 小象教研组