查看全课大纲(第 37 / 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的误差反向传播_池化层
池化层的误差反向传播
小象实战讲义 · 基于深度学习的计算机视觉
在上一节中,我们学习了卷积层误差反向传播的原理。本节我们将聚焦于卷积神经网络(CNN)中的另一个核心组件——池化层,探讨其误差反向传播的机制。理解池化层的反向传播,是掌握整个CNN训练流程的关键一环,它决定了梯度如何从池化层流向前一层的卷积层,从而完成权重的更新。
💡 核心导读
- 池化层的作用与位置:回顾池化层(下采样)在CNN中的经典位置,通常位于卷积层和ReLU激活函数之后。
- 误差反向传播的通用公式:复习全连接网络误差反向传播的矩阵形式,理解其如何为池化层的反向传播提供理论基础。
- 池化层反向传播的核心思想:池化层的反向传播本质上是其前向传播(下采样)的逆过程,即上采样(Upsampling)。
- 两种池化方式的具体实现:分别推导平均池化(Mean Pooling) 和最大池化(Max Pooling) 的反向传播具体操作。
- 完整的反向传播流程:强调在完成上采样后,必须乘以前一卷积层激活函数的导数,以得到完整的卷积层残差。
回顾:误差反向传播的符号与公式
在深入池化层之前,我们先统一并复习一下神经网络误差反向传播中使用的符号和基本公式。这些是理解后续内容的基础。
- :表示第 层的输出。特别地, 代表网络的原始输入。
- :表示第 层的总输入,即未经过激活函数前的加权和。
- :代表非线性激活函数(如Sigmoid、ReLU等)。因此,第 层的输出可以表示为 。
- , :分别代表第 层的权重矩阵和偏置向量。第 层的总输入由前一层的输出计算得来:。
- :代表损失函数(Cost Function)。
- :代表第 层的残差(误差敏感项),定义为损失函数对第 层总输入的偏导:。
对于全连接层,误差反向传播的核心公式(从第 层传播到第 层)为: 其中, 表示矩阵的对应位置元素相乘(Hadamard积)。这个公式可以解读为:第 层的残差,等于第 层的残差经过权重矩阵 的转置进行“反向加权”后,再乘以第 层激活函数的导数。
CNN中的经典顺序:卷积、激活、池化
在卷积神经网络中,各层的排列顺序有其设计逻辑。最经典和常见的模式是: 卷积层(Convolution) → 激活函数(ReLU) → 池化层(Pooling)
为什么是这个顺序?让我们结合前面的公式来理解。
- 卷积操作对应 :卷积层的计算本质上就是使用卷积核(权重 )对输入特征图进行加权求和并加上偏置,这正好对应了 的计算过程。
- 激活函数紧随其后:得到 后,立即应用非线性激活函数 (如ReLU),得到该层的输出 。这是引入网络非线性的关键步骤。
- 池化层进行下采样:对激活后的特征图 进行池化(如最大池化或平均池化),实现特征融合和降维,得到池化层的输出,作为下一层的输入。
因此,在经典结构中,ReLU激活函数总是紧跟在卷积层之后。当然,学术界也存在其他变体,例如把池化提到 ReLU 之前——其动机是计算效率:既然 2×2 最大池化会丢弃 3/4 的响应,不如先池化、再对保留下来的值做激活,从而省掉被丢弃部分的 ReLU 计算。这是一种完全可行的方案,但本课程仍围绕最经典、最广泛使用的“卷积 → ReLU → 池化”结构讲解。
明确了这一点,我们就能理解池化层在反向传播中所处的位置:池化层(第 层)的前一层(第 层)是一个带有激活函数的卷积层。这一点至关重要,因为根据反向传播公式,计算 时,需要用到 ,即卷积层激活函数的导数。
池化层误差反向传播的原理
现在,我们正式进入池化层的误差反向传播。假设已知池化层(第 层)的残差 ,我们的目标是计算其前一卷积层(第 层)的残差 。
回顾全连接层的公式:。 对于池化层,它没有需要学习的权重参数 。那么,公式中的 部分该如何理解?
核心思想:上采样(Upsampling) 池化层的前向传播是一个下采样(Downsampling) 过程,例如从4x4的特征图中选取区域,输出一个2x2的特征图。其反向传播,自然就是这个下采样过程的逆过程,即上采样。我们可以将上采样的操作,类比为全连接层公式中“乘以权重矩阵转置”这一步骤在池化层语境下的具体实现。
因此,池化层反向传播的公式可以写作: 这里, 函数根据池化方式(平均池化或最大池化)有不同的具体实现。接下来,我们通过具体的例子来详细说明。
平均池化(Mean Pooling)的反向传播
假设卷积层(层)的输出 是一个4x4的矩阵,经过2x2、步长为2的平均池化后,得到池化层(层)2x2的输出。 同时,假定我们通过后续网络计算,得到了池化层的残差 为:
我们的任务是计算 。
前向传播回顾:平均池化将输入中每个2x2区域的值取平均,作为输出中的一个值。为便于理解,可以想象前向时输入左上角2x2区域的四个值经平均得到输出的某个值 A;反向传播时,与这个输出位置对应的残差 要按同样的2x2区域平均分配回去。
反向传播步骤:
上采样:将 中的每个误差值,平均分配给前向传播中对应池化区域的所有输入位置。因为前向是“取平均”,反向就应该是“平均分配”。
- 将 分配给输入左上角2x2区域的四个位置,每个位置分得 。
- 同理, 分配给右上角四个位置,每个得 。
- 分配给左下角四个位置,每个得 。
- 分配给右下角四个位置,每个得 。 这样,我们得到了一个4x4的矩阵,这是上采样的结果: 关键:这样分配保证了误差的总量在传播过程中保持不变。前向传播是4个数合为1个数,反向传播是1个误差拆成4份,总和不变。
乘以激活函数导数:得到上采样结果后,还需要乘以卷积层(层)激活函数的导数 。假设我们已知 (卷积层的总输入),并且使用ReLU激活函数,那么 就是一个由0和1组成的矩阵(ReLU的导数在输入>0时为1,<=0时为0)。
- 假设 是一个4x4的全1矩阵(即所有神经元在前向时均被激活),那么 就等于上面的
upsample(\delta^l)。 - 如果 中某些位置为0,则对应位置的梯度也会被置零,表示该神经元在前向传播中对误差没有贡献。
- 假设 是一个4x4的全1矩阵(即所有神经元在前向时均被激活),那么 就等于上面的
下面的代码演示了平均池化反向传播中上采样的计算过程:
import numpy as np
# 假设池化层残差 delta_l
delta_l = np.array([[1, 2],
[3, 4]], dtype=np.float32)
print("池化层残差 delta_l:")
print(delta_l)
print()
# 平均池化的反向传播上采样
pool_size = 2 # 池化窗口大小 2x2
# 初始化一个4x4的零矩阵用于存放上采样结果
upsampled = np.zeros((4, 4), dtype=np.float32)
# 将 delta_l 中的每个值平均分配到对应的 2x2 区域
for i in range(delta_l.shape[0]):
for j in range(delta_l.shape[1]):
# 计算对应输入区域的起始位置
start_i, start_j = i * pool_size, j * pool_size
# 将 delta_l[i, j] 的平均值填入对应区域
upsampled[start_i:start_i+pool_size, start_j:start_j+pool_size] = delta_l[i, j] / (pool_size * pool_size)
print("平均池化上采样结果 upsampled(delta_l):")
print(upsampled)池化层残差 delta_l:
[[1. 2.]
[3. 4.]]
平均池化上采样结果 upsampled(delta_l):
[[0.25 0.25 0.5 0.5 ]
[0.25 0.25 0.5 0.5 ]
[0.75 0.75 1. 1. ]
[0.75 0.75 1. 1. ]]最大池化(Max Pooling)的反向传播
最大池化的反向传播逻辑与平均池化不同,因为它在前向传播时不是进行线性平均,而是执行了一个“选择最大值”的操作。
核心思想:路由(Routing) 在最大池化的前向传播中,我们记录了每个池化区域中最大值所在的位置。在反向传播时,误差只传播到前向传播中产生最大值的那个位置,其他位置的梯度为0。这就像把误差“路由”回当初做出贡献的神经元。
继续使用之前的例子,卷积层输出为4x4,池化层残差 仍为 。 假设在前向传播的Max Pooling过程中,我们记录了以下信息(称为最大索引):
- 输出(0,0)的值,来源于输入(0,1)位置的值(假设它是左上角2x2区域的最大值)。
- 输出(0,1)的值,来源于输入(1,2)位置的值。
- 输出(1,0)的值,来源于输入(2,0)位置的值。
- 输出(1,1)的值,来源于输入(3,3)位置的值。
反向传播步骤:
上采样:初始化一个4x4的零矩阵。将 中的每个误差值,直接放置到其对应前向传播中最大值来源的输入位置上。
- 放到输入位置(0,1)。
- 放到输入位置(1,2)。
- 放到输入位置(2,0)。
- 放到输入位置(3,3)。 得到上采样结果:
乘以激活函数导数:与平均池化相同,将上采样结果与卷积层的激活函数导数 逐元素相乘,得到最终的 。
下面的代码演示了最大池化反向传播中基于最大索引的上采样:
import numpy as np
# 假设池化层残差 delta_l
delta_l = np.array([[1, 2],
[3, 4]], dtype=np.float32)
print("池化层残差 delta_l:")
print(delta_l)
print()
# 假设在前向传播时记录的最大值索引 (相对于4x4输入矩阵的展平坐标或二维坐标)
# 这里我们用二维坐标表示:每个池化区域最大值在输入中的位置 (行, 列)
# 对应关系: delta_l[0,0] -> 输入位置(0,1), delta_l[0,1] -> (1,2), delta_l[1,0] -> (2,0), delta_l[1,1] -> (3,3)
max_indices = [(0, 1), (1, 2), (2, 0), (3, 3)]
# 最大池化的反向传播上采样
upsampled = np.zeros((4, 4), dtype=np.float32)
# 将 delta_l 展平以便按顺序遍历
delta_flat = delta_l.flatten()
for idx, (max_i, max_j) in enumerate(max_indices):
upsampled[max_i, max_j] = delta_flat[idx]
print("最大池化上采样结果 upsampled(delta_l):")
print(upsampled)池化层残差 delta_l:
[[1. 2.]
[3. 4.]]
最大池化上采样结果 upsampled(delta_l):
[[0. 1. 0. 0.]
[0. 0. 2. 0.]
[3. 0. 0. 0.]
[0. 0. 0. 4.]]完整流程与注意事项
将上述过程整合,池化层误差反向传播的完整步骤如下:
- 根据池化类型进行上采样:
- 平均池化:将残差值平均分配到前向传播对应的池化区域。
- 最大池化:将残差值直接放置到前向传播记录的最大值位置,其他位置置零。
- 与激活函数导数相乘:将上采样得到的结果,与池化层前一层的卷积层的激活函数导数 进行逐元素相乘(Hadamard积)。
- 这一步至关重要,它确保了梯度流经了网络中的所有非线性变换。
- 如果使用ReLU, 是一个由0和1组成的掩码,它会“关闭”那些在前向传播中未被激活的神经元的梯度流。
特别提醒:平均池化在当代CNN中依然有重要应用,例如在全局平均池化(Global Average Pooling) 中,它被用来替代全连接层,将最后一个卷积层的每个特征图直接池化为一个标量,极大地减少了参数数量并有助于防止过拟合。
📝 动手练一练
- 概念辨析:在CNN的经典结构中,为什么ReLU激活函数通常紧跟在卷积层之后,而不是放在池化层之后?从误差反向传播公式的角度尝试解释。
👉 点击查看参考答案
从误差反向传播公式 可以看出,计算前一层的残差需要用到该层激活函数的导数 。
需要先澄清:顺序并不是数学上的强制要求。若把 ReLU 放在池化之后,池化层反向时前一层确实是纯卷积(),但 ReLU 的导数会在下一步(从 ReLU 层传回池化层)补上,梯度同样正确。经典顺序“卷积 → ReLU → 池化”更多是工程惯例:先做非线性映射,再在激活后的特征上挑选最强响应,实现直观、计算也简单;把池化提前的变体则是为了省掉被丢弃部分的激活计算。真正关键的只有一点:无论激活函数放在哪,反向传播时都要在它所在的位置乘上对应的 。
- 手算练习:假设一个4x4的卷积层输出 经过2x2(步长为2)的最大池化得到2x2的 。已知池化层残差 ,且前向传播时记录的最大值索引分别为:来自输入(0,0),来自输入(0,2),来自输入(2,0),来自输入(2,2)。假设卷积层使用ReLU且前向传播时所有 。请计算卷积层的残差 。
👉 点击查看参考答案
步骤1: 最大池化上采样 根据最大索引,将 的值路由到对应位置:
- -> 位置(0,0)
- -> 位置(0,2)
- -> 位置(2,0)
- -> 位置(2,2) 其他位置为0。因此,4x4 的上采样结果为:
步骤2: 乘以激活函数导数 题目假设所有 ,对于ReLU,其导数 (对于所有位置)。因此, 是一个4x4的全1矩阵。
所以,卷积层的残差 就是上面的4x4矩阵。
本章小结
本节我们深入探讨了卷积神经网络中池化层的误差反向传播机制。我们首先复习了反向传播的通用矩阵公式,然后明确了CNN中卷积、激活、池化的经典顺序及其原因。核心在于,池化层的反向传播是其前向传播(下采样)的逆过程,即上采样。
- 平均池化的反向传播采用平均分配的策略,将误差均匀地回溯到前向池化区域的所有输入单元,保持了误差总量的守恒。
- 最大池化的反向传播采用路由策略,利用前向传播时记录的最大值位置,将误差只传回给那个对输出贡献最大的输入单元,其他单元梯度为零。
无论哪种方式,在完成上采样后,都必须与前一卷积层激活函数的导数相乘,才能得到该卷积层完整的残差,从而继续向更早的层传播误差,最终更新网络权重。
今天,最大池化仍很常见,但不少现代网络改用步长为 2 的卷积来做下采样(把下采样也变成可学习参数),分类头则普遍用全局平均池化替代全连接层。无论前端怎么变,本节的逆向规则不变:平均池化按面积均分,最大池化按前向记录的索引路由,最后再乘激活导数。
📋 行动清单
- 在纸上推导一次平均池化和最大池化在简单案例(如4x4到2x2)下的完整前向与反向传播过程,确保理解每一步。
- 思考:如果池化层的步长(stride)小于池化窗口大小(即存在重叠池化),上述反向传播过程需要如何调整?
- 尝试使用NumPy编写一个函数,同时支持平均池化和最大池化的前向传播(并记录最大索引)以及反向传播。
—— 小象教研组