查看全课大纲(第 36 / 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
CNN的误差反向传播_卷积层
卷积层的误差反向传播
小象实战讲义 · 基于深度学习的计算机视觉
在第 5 章中,我们学习了全连接神经网络的误差反向传播(Backpropagation, BP)算法。然而,卷积神经网络(CNN)的核心是卷积层,其连接方式(局部连接、权重共享)与全连接层截然不同。本节我们将深入探讨卷积层的误差反向传播机制,理解梯度是如何通过卷积层传递到前一层(通常是池化层或另一个卷积层)的,这是理解CNN训练过程的关键一步。
💡 核心导读
- 从全连接到卷积:回顾全连接层BP公式,建立符号体系,理解卷积层可视为一种特殊的、参数共享的连接层。
- 卷积运算的展开:通过将卷积操作展开为全连接形式的矩阵乘法,直观理解卷积层反向传播的数学本质。
- 核心公式推导:推导出卷积层误差反向传播的核心公式:
δ^(l-1) = δ^(l) * rot180(W^(l)) ⊙ σ'(z^(l-1)),其中rot180表示将卷积核旋转180度。 - 多通道处理:理解当卷积层有多个输出通道(特征图)时,前一层某个通道的残差需要汇总所有输出通道的贡献。
- 实例演算:通过一个具体的数值例子,手算验证卷积层反向传播的过程,加深理解。
复习:全连接网络的误差反向传播
在深入卷积层之前,我们先快速回顾一下全连接神经网络中误差反向传播的核心公式,并统一本节将使用的符号体系。
对于一个全连接神经网络,我们定义:
a^(l):第l层的输出(激活值)。a^(0)是网络的原始输入。z^(l):第l层的总输入(加权和),即z^(l) = W^(l) a^(l-1) + b^(l)。σ(·):非线性激活函数(如Sigmoid, ReLU)。C:损失函数(Cost Function)。δ^(l):第l层的残差(误差敏感项),定义为损失函数对当前层总输入的偏导:δ^(l) = ∂C / ∂z^(l)。
符号约定说明:本节采用
δ = ∂C/∂z的标准定义;第 5 章为贴合课件“残差 = 误差 × 激活导数”的写法,把负号放进了权重更新式(δ = (t - z)f',Δw = +ηδy)。两种约定只差一个符号,本质完全一致。这里用∂C/∂z,可以让后续递推公式不带负号。
根据链式法则和全连接网络的结构,我们可以推导出相邻两层残差之间的递推关系(BP算法的核心):
δ^(l-1) = ( (W^(l))^T δ^(l) ) ⊙ σ'(z^(l-1))
公式解读:
(W^(l))^T δ^(l):将更靠近输出层(第l层)的残差δ^(l),乘以当前层权重矩阵W^(l)的转置。这实现了误差从后一层向前一层的“加权反传”。⊙:表示Hadamard积(逐元素相乘)。σ'(z^(l-1)):第l-1层激活函数在其输入z^(l-1)处的导数。- 整个结果就是第
l-1层的残差δ^(l-1)。
这个公式是我们推导卷积层反向传播的基础。在卷积层中,W^(l) 不再是稠密的矩阵,而变成了具有特殊结构(局部连接、权重共享)的卷积核。
卷积层:一种特殊的连接层
为了理解卷积层的反向传播,我们首先需要将卷积操作“翻译”成我们熟悉的全连接语言。
考虑一个简单的例子:一个 3x3 的输入 a^(l-1),使用一个 2x2 的卷积核 W 进行无填充(padding)、步长为1的卷积,得到一个 2x2 的输出 z^(l)。
import numpy as np
# 假设的输入 (l-1层输出)
a_l_minus_1 = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=np.float32)
# 假设的卷积核权重
W = np.array([[0.1, 0.2],
[0.3, 0.4]], dtype=np.float32)
# 手动进行卷积运算 (无padding, stride=1)
z_l = np.zeros((2, 2))
for i in range(2):
for j in range(2):
z_l[i, j] = np.sum(a_l_minus_1[i:i+2, j:j+2] * W)
print("输入 a^(l-1):")
print(a_l_minus_1)
print("\n卷积核 W:")
print(W)
print("\n卷积输出 z^(l):")
print(z_l)输入 a^(l-1):
[[1. 2. 3.]
[4. 5. 6.]
[7. 8. 9.]]
卷积核 W:
[[0.1 0.2]
[0.3 0.4]]
卷积输出 z^(l):
[[3.70000005 4.69999981]
[6.69999981 7.70000076]]这个卷积操作可以等价地展开为一个全连接运算。我们将 3x3 的输入 a^(l-1) 展平为一个 9x1 的列向量,将 2x2 的输出 z^(l) 展平为一个 4x1 的列向量。那么,连接它们的权重矩阵 W_full 将是一个 4x9 的稀疏矩阵。
# 概念上的全连接权重矩阵 W_full (4x9)
# 每一行对应一个输出神经元z_l[i,j],每一列对应一个输入神经元a_l_minus_1[p,q]
# 非零权重的位置由卷积核的滑动决定,且权重值共享。
# 例如,输出 z_l[0,0] = a[0,0]*W[0,0] + a[0,1]*W[0,1] + a[1,0]*W[1,0] + a[1,1]*W[1,1]
# 对应 W_full 的第一行:[W[0,0], W[0,1], 0, W[1,0], W[1,1], 0, 0, 0, 0]
# 输出 z_l[0,1] = a[0,1]*W[0,0] + a[0,2]*W[0,1] + a[1,1]*W[1,0] + a[1,2]*W[1,1]
# 对应 W_full 的第二行:[0, W[0,0], W[0,1], 0, W[1,0], W[1,1], 0, 0, 0]
# 以此类推...从这个展开视图可以清晰地看到卷积层的两个核心特性:
- 局部连接:每个输出神经元只与输入层的局部区域(感受野)相连,而不是全部。
- 权重共享:连接不同输出神经元和其对应感受野的权重是相同的,即卷积核
W的值。
因此,卷积层本质上是一种具有特殊稀疏性和参数共享约束的全连接层。理解了这一点,我们就可以将全连接BP的思想迁移过来。
卷积层误差反向传播公式推导
我们的目标是求第 l-1 层(卷积层的前一层)的残差 δ^(l-1)。已知第 l 层(卷积层)的残差 δ^(l)。
根据链式法则和全连接BP的启示,关键是要计算 ∂z^(l) / ∂a^(l-1)。在全连接中,这就是权重矩阵 W^(l)。在卷积的展开视图中,它对应那个稀疏的、共享权重的矩阵 W_full^(l)。
经过严谨的数学推导(详细过程可参考课程配套PDF),我们可以得到一个非常简洁且直观的结论:
卷积层的误差反向传播公式:
δ^(l-1) = δ^(l) * rot180(W^(l)) ⊙ σ'(z^(l-1))
其中:
*表示卷积操作(注意,不是矩阵乘法)。rot180(W^(l))表示将第l层的卷积核W^(l)旋转180度。⊙是逐元素相乘。σ'(z^(l-1))是第l-1层激活函数的导数。如果l-1层是池化层等无参数层,通常视其激活函数为恒等映射,即σ'(z) = 1。
公式解读:
- 旋转180度:这是“互相关”与严格数学卷积之间的差异造成的。深度学习框架的前向传播通常实现的是互相关(核不翻转,直接滑动相乘);而在反向传播中,要把后一层的残差正确散射回前一层,等价于做一次严格卷积,也就是用旋转180度后的核做互相关。所以这个旋转不是额外技巧,而是把前向的互相关“逆回去”的必然结果。
- 卷积操作:将后一层(
l层)的残差δ^(l)作为“输入”,与旋转后的卷积核进行卷积操作。这实现了误差从后一层特征图向前一层特征图的传播,并且自然地处理了局部连接和权重共享。 - 激活函数导数:与全连接BP一样,需要乘上当前层(
l-1层)激活函数的导数。
一个重要细节:多通道情况 上述公式描述的是单输入通道、单输出通道的情况。在实际CNN中,每一层通常有多个通道(特征图)。
- 假设第
l-1层有P个通道。 - 第
l层(卷积层)有Q个输出通道(即Q个卷积核)。 - 那么,对于第
l-1层的第p个通道的残差δ^(l-1, p),它需要接收来自第l层所有Q个通道的残差的贡献。
因此,多通道下的公式为:
δ^(l-1, p) = ∑_{q=1}^{Q} ( δ^(l, q) * rot180(W^(l, q, p)) ) ⊙ σ'(z^(l-1, p))
其中:
W^(l, q, p)是连接第l-1层第p个通道到第l层第q个通道的卷积核。- 求和
∑表示将所有输出通道q的贡献累加到输入通道p上。
实例演算:从卷积层到池化层的反向传播
让我们通过一个具体的数值例子来验证上述公式。假设网络结构如下:
- 第
l-1层:池化层,输出为单个3x3的特征图a^(l-1)。池化层通常无激活函数,我们设σ'(z^(l-1)) = 1。 - 第
l层:卷积层,有2个卷积核,每个核大小为2x2。因此输出z^(l)是2个2x2的特征图。
已知:
- 两个卷积核的权重:
W^(l, 1) = [[0.1, 0.2], [0.3, 0.4]]W^(l, 2) = [[0.5, 0.6], [0.7, 0.8]](为简化,我们假设输入是单通道;输出有 2 个通道,所以有 2 个卷积核。多输入通道的原理相同,只是还要对输入通道求和,计算更繁琐)。
- 卷积层(第
l层)的残差δ^(l):δ^(l, 1) = [[1, 2], [3, 4]](对应第一个输出特征图)δ^(l, 2) = [[5, 6], [7, 8]](对应第二个输出特征图)
目标:计算池化层(第 l-1 层)的残差 δ^(l-1)。
计算步骤:
- 对每个卷积核进行180度旋转。
- 将每个通道的残差
δ^(l, q)与对应的旋转后的核进行全填充(full padding)卷积。为什么是full padding?因为前向是3x3 → 2x2的 valid 卷积,反向需要把2x2的残差“散射”回3x3,只有 full 卷积的尺寸关系(2+2-1=3)才能还原。 - 将两个通道的卷积结果相加。
- 因为
l-1是池化层,σ' = 1,所以结果就是δ^(l-1)。
import numpy as np
# 已知数据
delta_l1 = np.array([[1, 2],
[3, 4]], dtype=np.float32) # 第l层,第1个通道的残差
delta_l2 = np.array([[5, 6],
[7, 8]], dtype=np.float32) # 第l层,第2个通道的残差
W_l1 = np.array([[0.1, 0.2],
[0.3, 0.4]], dtype=np.float32) # 第1个卷积核
W_l2 = np.array([[0.5, 0.6],
[0.7, 0.8]], dtype=np.float32) # 第2个卷积核
# 1. 旋转卷积核180度
def rot180(kernel):
return np.rot90(kernel, 2)
W_l1_rot = rot180(W_l1)
W_l2_rot = rot180(W_l2)
print("旋转后的卷积核 W_l1_rot:")
print(W_l1_rot)
print("\n旋转后的卷积核 W_l2_rot:")
print(W_l2_rot)
# 2. 进行全填充(full)卷积
# 我们可以通过手动计算或利用信号处理中的“互相关”概念来理解。
# 全填充卷积:在残差图边界外补零,使旋转后核的右下角能与残差图左上角对齐开始计算。
# 对于2x2的核和2x2的输入,全填充输出是3x3。
# 手动计算其中一个元素:delta_l1的左上角元素1,只与W_l1_rot的右下角元素0.1相乘。
# 我们可以用更直观的方式:将残差图用0填充到4x4(上下左右各补一圈0),然后用旋转后的核进行有效(valid)卷积。
def full_convolution(input_map, kernel):
"""实现全填充卷积,输入和核都是2D,输出大小 = input_size + kernel_size -1"""
k_h, k_w = kernel.shape
# 对输入进行填充,上下左右各填充 (k_h-1, k_w-1)
padded_input = np.pad(input_map, ((k_h-1, k_h-1), (k_w-1, k_w-1)), mode='constant')
# 进行有效的互相关(不旋转核,因为我们的kernel已经是rot180后的)
# 注意:这里我们使用互相关,因为我们的kernel已经是旋转后的。
# 对于互相关,我们直接用kernel在padded_input上滑动计算。
output_h = padded_input.shape[0] - k_h + 1
output_w = padded_input.shape[1] - k_w + 1
output = np.zeros((output_h, output_w))
for i in range(output_h):
for j in range(output_w):
output[i, j] = np.sum(padded_input[i:i+k_h, j:j+k_w] * kernel)
return output
# 计算每个通道的贡献
contrib1 = full_convolution(delta_l1, W_l1_rot)
contrib2 = full_convolution(delta_l2, W_l2_rot)
print("\n第1个通道的贡献 contrib1:")
print(contrib1)
print("\n第2个通道的贡献 contrib2:")
print(contrib2)
# 3. 求和得到最终的 δ^(l-1)
delta_l_minus_1 = contrib1 + contrib2
print("\n计算得到的池化层残差 δ^(l-1):")
print(delta_l_minus_1)旋转后的卷积核 W_l1_rot:
[[0.4 0.3]
[0.2 0.1]]
旋转后的卷积核 W_l2_rot:
[[0.8 0.7]
[0.6 0.5]]
第1个通道的贡献 contrib1:
[[0.1 0.40000001 0.40000001]
[0.60000002 2. 1.60000002]
[0.90000004 2.4000001 1.60000002]]
第2个通道的贡献 contrib2:
[[ 2.5 6. 3.60000014]
[ 7. 16.39999962 9.60000038]
[ 4.9000001 11.19999981 6.4000001 ]]
计算得到的池化层残差 δ^(l-1):
[[ 2.6 6.40000001 4.00000015]
[ 7.60000002 18.39999962 11.20000041]
[ 5.80000013 13.5999999 8.00000012]]通过这个实例,我们清晰地看到了:
- 卷积核确实被旋转了180度。
- 反向传播过程是一个卷积操作(这里是全填充卷积)。
- 多通道的贡献被求和。
这个 3x3 的 δ^(l-1) 就是传递到前一层(池化层)的残差,池化层将根据其自身的反向传播规则(例如,平均池化将残差平均分配回原位置)继续向前传递。
📝 动手练一练
- 概念辨析:在全连接层和卷积层的误差反向传播公式中,都需要将后一层的残差与前一层到后一层的权重矩阵进行运算。请问这两个公式中,权重矩阵的处理有什么关键区别?(提示:转置 vs 旋转)
👉 点击查看参考答案
关键区别:
- 全连接层:使用权重矩阵
W^(l)的转置(W^(l))^T。这是因为在全连接中,误差从后一层神经元反传到前一层神经元时,需要乘以连接它们的权重值,而转置操作正好实现了这种对应关系的翻转。 - 卷积层:使用卷积核
W^(l)的180度旋转rot180(W^(l))。这是因为卷积操作本身具有空间局部性和权重共享性。在数学上,为了保持梯度计算的正确性(即与将卷积展开为稀疏矩阵乘法后的梯度一致),必须对卷积核进行旋转。这也是严格数学定义中的卷积(Convolution)与神经网络中常用的互相关(Cross-correlation)操作之间的区别在反向传播中的体现。
- 动手计算:假设一个卷积层,其单个
2x2卷积核为W = [[a, b], [c, d]],该层输出的残差为δ = [[δ11, δ12], [δ21, δ22]]。前一层为ReLU激活函数,且已知在前向传播中,该层所有输入z^(l-1)均大于0。请根据公式δ^(l-1) = δ^(l) * rot180(W^(l)) ⊙ σ'(z^(l-1)),写出计算δ^(l-1)中左上角元素δ^(l-1)_11的具体表达式。
👉 点击查看参考答案
计算过程:
- 旋转卷积核:
rot180(W) = [[d, c], [b, a]]。 - 因为前一层使用ReLU且输入>0,所以
σ'(z^(l-1)) = 1(所有位置)。 - 计算
δ * rot180(W)的左上角元素。这是全填充卷积:在残差图边界外补零,使旋转核的右下角与残差图左上角δ11对齐,此时只有这一对元素重叠,得到输出的第一个元素。- 此时,只有旋转核的右下角
a与残差图的左上角δ11重合。 - 因此,
(δ * rot180(W))_11 = a * δ11。
- 此时,只有旋转核的右下角
- 所以,
δ^(l-1)_11 = a * δ11 * 1 = a * δ11。
表达式:δ^(l-1)_11 = a * δ11
本章小结
本节我们深入剖析了卷积神经网络中卷积层的误差反向传播机制,这是理解CNN如何通过梯度下降进行训练的核心。
- 核心思想迁移:卷积层可视为具有局部连接和权重共享特性的特殊全连接层。因此,其反向传播的核心思想与全连接BP一脉相承——利用链式法则,将后一层的残差通过权重关系传递到前一层。
- 关键公式:我们推导并理解了卷积层反向传播的最终公式:
δ^(l-1) = δ^(l) * rot180(W^(l)) ⊙ σ'(z^(l-1))。特别要注意卷积核需要旋转180度这一关键操作,它源于卷积的严格数学定义。 - 多通道处理:在实际网络中,每一层通常包含多个特征图(通道)。前一层某个通道的残差,由后一层所有通道的残差分别与对应的卷积核进行反向卷积后,再求和得到。
- 连接前后文:本节推导的是从卷积层(第
l层)到其前一层(第l-1层,本例中为池化层)的误差传播。完整的CNN训练需要交替进行卷积层和池化层的反向传播。
📋 行动清单
- 默写并理解卷积层误差反向传播的核心公式,明确每个符号的含义。
- 在纸上复现本节中的数值演算例子,确保能独立完成从
δ^(l)计算δ^(l-1)的过程。 - 思考:如果前一层(
l-1层)也是一个卷积层,那么公式中的σ'(z^(l-1))应该如何计算?(提示:取决于该卷积层使用的激活函数,如ReLU的导数为0或1)。
—— 小象教研组