卷积层在卷积神经网络中总结输入图像中特征的存在。
卷积层输出的一个问题是它们对输入中特征的位置很敏感。解决此敏感性的一种方法是对特征图进行下采样。这会使由此产生的下采样特征图对图像中特征位置的变化更具鲁棒性,这在技术上称为“局部平移不变性”。
池化层通过对特征图的块中特征的存在进行总结,提供了一种下采样特征图的方法。两种常见的池化方法是平均池化和最大池化,它们分别总结了特征的平均存在和特征的最激活存在。
在本教程中,您将了解池化操作的工作原理以及如何在卷积神经网络中实现它。
完成本教程后,您将了解:
- 需要池化来对特征图中的特征检测进行下采样。
- 如何在卷积神经网络中计算和实现平均池化和最大池化。
- 如何在卷积神经网络中使用全局池化。
开启您的项目,阅读我的新书《计算机视觉深度学习》,其中包含分步教程和所有示例的Python源代码文件。
让我们开始吧。

卷积神经网络池化层简介
照片由 Nicholas A. Tonelli 拍摄,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- 池化
- 检测垂直线
- 平均池化层
- 最大池化层
- 全局池化层
想通过深度学习实现计算机视觉成果吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
池化层
卷积神经网络中的卷积层系统地将学习到的滤波器应用于输入图像,以创建总结输入中特征存在的特征图。
卷积层被证明非常有效,并且在深度模型中堆叠卷积层允许靠近输入的层学习低级特征(例如线条),而模型中更深的层则学习更高级别或更抽象的特征,例如形状或特定对象。
卷积层特征图输出的一个限制是它们会记录特征在输入中的精确位置。这意味着特征在输入图像中的位置发生微小移动会导致特征图不同。这可能发生在重新裁剪、旋转、偏移和其他对输入图像的微小更改中。
信号处理中解决此问题的一种常见方法称为下采样。这是创建输入信号的较低分辨率版本,该版本仍包含大的或重要的结构元素,而没有可能对任务不太有用的精细细节。
可以通过更改图像上的卷积步长,使用卷积层来实现下采样。一种更鲁棒且常见的方法是使用池化层。
池化层是添加到卷积层之后的新层。具体来说,是在应用了非线性(例如 ReLU)到卷积层输出的特征图之后;例如,模型中的层可能如下所示:
- 输入图像
- 卷积层
- 非线性
- 池化层
在卷积层之后添加池化层是卷积神经网络中用于排序层的一种常见模式,该模式可以在给定模型中重复一次或多次。
池化层独立地作用于每个特征图,以创建一组新的、数量相同的池化特征图。
池化涉及选择一个池化操作,类似于应用于特征图的滤波器。池化操作或滤波器的大小小于特征图的大小;具体来说,它几乎总是2×2像素,步长为2像素。
这意味着池化层将始终将每个特征图的大小减小2倍,例如,每个维度减半,从而使每个特征图中的像素或值数量减少到四分之一。例如,应用于6×6特征图(36个像素)的池化层将生成一个3×3(9个像素)的输出池化特征图。
池化操作是指定的,而不是学习的。在池化操作中使用的两个常见函数是
- 平均池化:计算特征图上每个块的平均值。
- 最大池化(或 Max Pooling):计算特征图每个块的最大值。
使用池化层并创建下采样或池化特征图的结果是输入中检测到的特征的汇总版本。它们很有用,因为卷积层在输入中检测到的特征位置的微小变化将导致具有相同位置特征的池化特征图。池化添加的此功能称为模型的局部平移不变性。
在所有情况下,池化都有助于使表示近似于输入的小平移不变。平移不变性意味着如果我们稍微平移输入,大多数池化输出的值不会改变。
— 第 342 页,深度学习,2016。
现在我们熟悉了池化层的需求和好处,让我们看一些具体的例子。
检测垂直线
在查看池化层的示例及其效果之前,让我们开发一个输入图像和卷积层的简化示例,稍后我们可以在其中添加和评估池化层。
在此示例中,我们定义了一个输入图像或样本,它有一个通道,是一个8像素×8像素的正方形,所有像素值均为0,中心有一个宽度为2像素的垂直线。
1 2 3 4 5 6 7 8 9 10 11 |
# 定义输入数据 data = [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0]] data = asarray(data) data = data.reshape(1, 8, 8, 1) |
接下来,我们可以定义一个模型,该模型接受形状为 (8, 8, 1) 的输入样本,并具有一个隐藏的卷积层,该层带有形状为 3 像素×3 像素的单个滤波器。
然后,将 ReLU(整流线性单元)激活函数应用于特征图中的每个值。这是一种简单有效的非线性函数,在这种情况下不会更改特征图中的值,但之所以存在,是因为我们将稍后添加后续的池化层,并且池化是在应用于特征图的非线性之后添加的,例如,这是最佳实践。
1 2 3 4 5 |
# 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), activation='relu', input_shape=(8, 8, 1))) # 总结模型 model.summary() |
滤波器在模型初始化时使用随机权重进行初始化。
取而代之的是,我们将硬编码我们自己的 3×3 滤波器来检测垂直线。也就是说,当滤波器检测到垂直线时会强激活,当未检测到时会弱激活。我们期望通过将此滤波器应用于输入图像,输出特征图将显示已检测到垂直线。
1 2 3 4 5 6 7 |
# 定义垂直线检测器 detector = [[[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]]] weights = [asarray(detector), asarray([0.0])] # 将权重存储在模型中 model.set_weights(weights) |
接下来,我们可以通过调用模型的predict()函数将滤波器应用于我们的输入图像。
1 2 |
# 将滤波器应用于输入数据 yhat = model.predict(data) |
结果是四维输出,包含一个批次、给定数量的行和列以及一个滤波器,即 [batch, rows, columns, filters]。我们可以打印单个特征图中的激活值,以确认已检测到线条。
1 2 3 4 |
# 枚举行 for r in range(yhat.shape[1]): # 打印行中的每一列 print([yhat[0,r,c,0] for c in range(yhat.shape[2])]) |
将所有这些联系在一起,完整的示例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# 使用卷积层检测垂直线的示例 from numpy import asarray from keras.models import Sequential from keras.layers import Conv2D # 定义输入数据 data = [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0]] data = asarray(data) data = data.reshape(1, 8, 8, 1) # 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), activation='relu', input_shape=(8, 8, 1))) # 总结模型 model.summary() # 定义垂直线检测器 detector = [[[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]]] weights = [asarray(detector), asarray([0.0])] # 将权重存储在模型中 model.set_weights(weights) # 将滤波器应用于输入数据 yhat = model.predict(data) # 枚举行 for r in range(yhat.shape[1]): # 打印行中的每一列 print([yhat[0,r,c,0] for c in range(yhat.shape[2])]) |
运行该示例首先会总结模型的结构。
值得注意的是,单个隐藏的卷积层将接收 8×8 像素的输入图像,并生成一个尺寸为 6×6 的特征图。
我们还可以看到该层有 10 个参数:即滤波器的 9 个权重(3×3)和 1 个偏置权重。
最后,打印出单个特征图。
从审查 6×6 矩阵中的数字可以看出,我们手动指定的滤波器确实检测到了我们输入图像中间的垂直线。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 6, 6, 1) 10 ================================================================= 总参数:10 可训练参数:10 不可训练参数: 0 _________________________________________________________________ [0.0, 0.0, 3.0, 3.0, 0.0, 0.0] [0.0, 0.0, 3.0, 3.0, 0.0, 0.0] [0.0, 0.0, 3.0, 3.0, 0.0, 0.0] [0.0, 0.0, 3.0, 3.0, 0.0, 0.0] [0.0, 0.0, 3.0, 3.0, 0.0, 0.0] [0.0, 0.0, 3.0, 3.0, 0.0, 0.0] |
现在我们可以看看一些常见的池化方法以及它们如何影响输出特征图。
平均池化层
在二维特征图上,池化通常以 2×2 的块在特征图上应用,步长为 (2,2)。
平均池化涉及计算特征图每个块的平均值。这意味着特征图的每个 2×2 正方形将被下采样到该正方形中的平均值。
例如,上一节中线条检测器卷积滤波器的输出是一个 6×6 的特征图。我们可以手动查看将平均池化操作应用于该特征图的第一行。
用于池化的第一行(前两行和六列)输出特征图如下:
1 2 |
[0.0, 0.0, 3.0, 3.0, 0.0, 0.0] [0.0, 0.0, 3.0, 3.0, 0.0, 0.0] |
第一个池化操作如下应用:
1 2 |
average(0.0, 0.0) = 0.0 0.0, 0.0 |
给定步长为 2,操作沿左侧移动两个列,并计算平均值:
1 2 |
average(3.0, 3.0) = 3.0 3.0, 3.0 |
再次,操作沿左侧移动两个列,并计算平均值:
1 2 |
average(0.0, 0.0) = 0.0 0.0, 0.0 |
这就是第一行池化操作。结果是平均池化操作的第一行:
1 |
[0.0, 3.0, 0.0] |
给定 (2,2) 的步长,操作随后向下移动两行,然后移回到第一列,并继续此过程。
由于下采样操作将每个维度减半,我们预计应用于 6×6 特征图的池化输出将是一个新的 3×3 特征图。考虑到特征图输入的水平对称性,我们预计每行将具有相同的平均池化值。因此,我们预计上一节中检测到的线条特征图的平均池化结果将如下所示:
1 2 3 |
[0.0, 3.0, 0.0] [0.0, 3.0, 0.0] [0.0, 3.0, 0.0] |
我们可以通过更新上一节的示例来使用平均池化来验证这一点。
在 Keras 中,可以通过使用AveragePooling2D层来实现此目的。该层的默认池化大小(例如,类似于核大小或滤波器大小)为 (2,2),默认步长为None,在这种情况下,它将池化大小用作步长,即 (2,2)。
1 2 3 4 |
# 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), activation='relu', input_shape=(8, 8, 1))) model.add(AveragePooling2D()) |
完整的平均池化示例列示如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# 平均池化示例 from numpy import asarray from keras.models import Sequential 从 keras.layers 导入 Conv2D from keras.layers import AveragePooling2D # 定义输入数据 data = [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0]] data = asarray(data) data = data.reshape(1, 8, 8, 1) # 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), activation='relu', input_shape=(8, 8, 1))) model.add(AveragePooling2D()) # 总结模型 model.summary() # 定义垂直线检测器 detector = [[[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]]] weights = [asarray(detector), asarray([0.0])] # 将权重存储在模型中 model.set_weights(weights) # 将滤波器应用于输入数据 yhat = model.predict(data) # 枚举行 for r in range(yhat.shape[1]): # 打印行中的每一列 print([yhat[0,r,c,0] for c in range(yhat.shape[2])]) |
运行示例首先总结了模型。
从模型摘要中我们可以看到,池化层的输入将是一个形状为 (6,6) 的单个特征图,并且平均池化层的输出将是一个每个维度减半的单个特征图,形状为 (3,3)。
应用平均池化会生成一个新特征图,该特征图仍然可以检测到线条,尽管是以下采样的方式,这与我们从手动计算操作中预期的完全一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 6, 6, 1) 10 _________________________________________________________________ average_pooling2d_1 (Average (None, 3, 3, 1) 0 ================================================================= 总参数:10 可训练参数:10 不可训练参数: 0 _________________________________________________________________ [0.0, 3.0, 0.0] [0.0, 3.0, 0.0] [0.0, 3.0, 0.0] |
平均池化效果很好,尽管最大池化更常用。
最大池化层
最大池化(或 Max Pooling)是一种池化操作,它计算每个特征图中每个块的最大值或最大值。
结果是下采样或池化后的特征图,它们突出显示了块中特征的最存在,而不是像平均池化那样突出特征的平均存在。对于图像分类等计算机视觉任务,这已被证明比平均池化效果更好。
简而言之,原因是特征倾向于对特征图不同图块上的某些模式或概念的空间存在进行编码(因此,称为特征图),并且查看不同特征的最大存在比查看它们的平均存在更有信息量。
— 第 129 页,Python 深度学习,2017。
我们可以通过再次将最大池化操作应用于线条检测器卷积操作的输出特征图,并手动计算池化特征图的第一行来具体化最大池化操作。
用于池化的第一行(前两行和六列)输出特征图如下:
1 2 |
[0.0, 0.0, 3.0, 3.0, 0.0, 0.0] [0.0, 0.0, 3.0, 3.0, 0.0, 0.0] |
第一个最大池化操作如下应用:
1 2 |
max(0.0, 0.0) = 0.0 0.0, 0.0 |
给定步长为 2,操作沿左侧移动两个列,并计算最大值:
1 2 |
max(3.0, 3.0) = 3.0 3.0, 3.0 |
再次,操作沿左侧移动两个列,并计算最大值:
1 2 |
max(0.0, 0.0) = 0.0 0.0, 0.0 |
这就是第一行池化操作。
结果是最大池化操作的第一行:
1 |
[0.0, 3.0, 0.0] |
同样,考虑到用于池化的特征图的水平对称性,我们预计池化特征图将如下所示:
1 2 3 |
[0.0, 3.0, 0.0] [0.0, 3.0, 0.0] [0.0, 3.0, 0.0] |
碰巧的是,所选的线条检测器图像和特征图在使用平均池化和最大池化进行下采样时产生相同的输出。
可以通过添加 Keras API 提供的MaxPooling2D层来将最大池化操作添加到工作示例中。
1 2 3 4 |
# 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), activation='relu', input_shape=(8, 8, 1))) model.add(MaxPooling2D()) |
带有最大池化的垂直线条检测的完整示例列示如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# 最大池化示例 from numpy import asarray from keras.models import Sequential 从 keras.layers 导入 Conv2D from keras.layers import MaxPooling2D # 定义输入数据 data = [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0]] data = asarray(data) data = data.reshape(1, 8, 8, 1) # 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), activation='relu', input_shape=(8, 8, 1))) model.add(MaxPooling2D()) # 总结模型 model.summary() # 定义垂直线检测器 detector = [[[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]]] weights = [asarray(detector), asarray([0.0])] # 将权重存储在模型中 model.set_weights(weights) # 将滤波器应用于输入数据 yhat = model.predict(data) # 枚举行 for r in range(yhat.shape[1]): # 打印行中的每一列 print([yhat[0,r,c,0] for c in range(yhat.shape[2])]) |
运行示例首先总结了模型。
正如我们现在所期望的,我们可以看到最大池化层的输出将是一个每个维度减半的单个特征图,形状为 (3,3)。
应用最大池化会生成一个新特征图,该特征图仍然可以检测到线条,尽管是以一种下采样的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 6, 6, 1) 10 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 3, 3, 1) 0 ================================================================= 总参数:10 可训练参数:10 不可训练参数: 0 _________________________________________________________________ [0.0, 3.0, 0.0] [0.0, 3.0, 0.0] [0.0, 3.0, 0.0] |
全局池化层
还有另一种有时使用的池化类型,称为全局池化。
全局池化不如下采样输入特征图的块,而是将整个特征图下采样为一个值。这等同于将池化大小设置为输入特征图的大小。
全局池化可以在模型中使用,以极大地总结图像中特征的存在。它有时也用在模型中,作为使用全连接层将特征图转换为模型输出预测的替代方案。
全局平均池化和全局最大池化都可以通过 Keras 的GlobalAveragePooling2D和GlobalMaxPooling2D类分别支持。
例如,我们可以将全局最大池化添加到用于垂直线条检测的卷积模型中。
1 2 3 4 |
# 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), activation='relu', input_shape=(8, 8, 1))) model.add(GlobalMaxPooling2D()) |
结果将是一个单一值,该值将总结输入图像中垂直线的最强激活或存在。
完整的代码列表如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# 使用全局最大池化的示例 from numpy import asarray from keras.models import Sequential 从 keras.layers 导入 Conv2D from keras.layers import GlobalMaxPooling2D # 定义输入数据 data = [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0]] data = asarray(data) data = data.reshape(1, 8, 8, 1) # 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), activation='relu', input_shape=(8, 8, 1))) model.add(GlobalMaxPooling2D()) # 总结模型 model.summary() # # 定义一个垂直线检测器 detector = [[[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]], [[[0]],[[1]],[[0]]]] weights = [asarray(detector), asarray([0.0])] # 将权重存储在模型中 model.set_weights(weights) # 将滤波器应用于输入数据 yhat = model.predict(data) # 枚举行 print(yhat) |
运行该示例首先会总结模型。
正如预期的那样,我们可以看到全局池化层的输出是一个汇总了单个特征图中特征存在的单一值。
接下来,打印模型的输出,显示全局最大池化对特征图的影响,打印单个最大激活值。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 6, 6, 1) 10 _________________________________________________________________ global_max_pooling2d_1 (Glob (None, 1) 0 ================================================================= 总参数:10 可训练参数:10 不可训练参数: 0 _________________________________________________________________ [[3.]] |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
文章
书籍
- 第9章:卷积网络,深度学习,2016年。
- 第5章:深度学习计算机视觉,Python 深度学习,2017年。
API
总结
在本教程中,您了解了池化操作的工作原理以及如何在卷积神经网络中实现它。
具体来说,你学到了:
- 需要池化来对特征图中的特征检测进行下采样。
- 如何在卷积神经网络中计算和实现平均池化和最大池化。
- 如何在卷积神经网络中使用全局池化。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
谢谢。一篇有趣的读物。
谢谢。
我不明白全局池化在编码结果方面是如何工作的。请帮助
具体哪个部分不明白?
我关注的是结果。它如何给我们一个单一的值?
平均池化之所以给出一个单一的输出,是因为它计算了输入的平均值。
我们可以在卷积层中使用哪些算法?
您可以在本教程中了解卷积层的工作原理。
https://machinelearning.org.cn/convolutional-layers-for-deep-learning-neural-networks/
您好,您能否帮助我理解 SVM 的训练阶段,当我进行二分类时?
从这里开始
https://machinelearning.org.cn/support-vector-machines-for-machine-learning/
很棒的文章,非常感谢您的写作。创建您示例的细微变体,使平均池化和最大池化产生不同的结果,可能会有所帮助 :).
伟大的建议,谢谢 Justin。
情况 1:如果我们应用平均池化,那么它是否需要放置所有 FC 层,然后是 Softmax?
情况 2:如果我们应用平均池化,那么它是否需要将结果向量直接馈送到 Softmax?
情况 3:序列看起来正确吗…特征图 – 平均池化 – Softmax?或特征图 – 平均池化 – FC 层 – Softmax?
情况 3:我们能否说平均池化的服务可以通过 GAP 实现?
情况 4:如果是多 CNN,我们如何将特征图连接到平均池化?
不确定是否同意,它们都是选项,不是要求。
您具体指的是什么?
我正在为分类/识别任务而努力。
那么,放置所有上述操作的正确顺序是什么?
因为,GAP 研究文章中提到,当使用它时,就不需要
使用 FC 层。那么平均池化层的情况是什么?
(1):如果我们想使用 CNN 进行图像(分类/识别任务),我们可以使用
在平均池化层之后直接使用 Softmax 分类器(跳过全连接层)?
(2):或者对于任何输入图像的分类/识别,我们可以在
平均池化层之后放置 FC 层,然后是 Softmax?
最后一个问题是,对于图像分类/识别,当
使用多个 CNN 从图像中提取特征,
选项 1:平均池化层或 GAP
选项 2:平均池化层 + Softmax?
选项 3:平均池化层 + FC 层 + Softmax?
选项 4:特征图 + GAP?
选项 5:特征图 + GAP + FC 层 + Softmax?
我之所以详细询问,是因为我阅读了多个来源,但并不清楚确切的正确程序是什么,而且在我阅读之后,我认为平均池化和 GAP 可以提供相同的服务。
没有最好的方法。没有规定,模型也各不相同,最好根据您的具体数据集进行实验,看看哪种方法效果最好。
您可以在全局池化后使用 softmax 或密集层,或者只使用密集层而不使用全局池化,或者许多其他组合。
最好参考一些表现良好的模型(如 vgg、resnet、inception)的架构,并在您的模型中尝试它们提出的架构,看看效果如何。或者获取灵感。
很棒的文章!!!
谢谢,很高兴它帮到了你!
我们可以使用随机森林进行池化吗?
不行。
感谢您清晰的定义和很好的例子。
关于在 CNN 模型(全连接层之前,例如 resnet)的末尾使用全局池化,有几个问题:
您认为在特征提取的最后一层使用全局平均池化与全局最大池化相比,其优缺点是什么(有没有 max 更受青睐的情况)?
切换两者时,它如何影响学习率和权重正则化等超参数?(因为最大值不像平均值那样将梯度传递给所有特征?)
您写道:“全局池化可用于模型中,以积极地总结图像中特征的存在。有时也用于模型中 **作为替代** 全连接层,以从特征图过渡到模型的输出预测。”
是不是更准确地说,(通常在 cnn 领域)全局池化有时会 **在** 完全连接 (fc) 层 **之前** 添加(即,作为补充),在从特征图到模型的输出预测的过渡中(两者都为特征提供全局关注并减少 fc 层的计算)?
为了让全局池化取代最后一个 fc 层,您需要先将通道数与类别数进行匹配(例如,使用 1x1 卷积?),这会更耗费计算资源,并且与在最终全局池化层之后添加 fc 层(例如,在常见的 cnn 模型中所做的)的操作有所不同。这种情况真的会发生吗?
谢谢。
也许可以事后推导差异。我建议两者都测试一下,并用结果来指导您。
不,全局池化是用来替代全连接层的——它们用作输出层。检查一些经典模型以确认。
是的,它会输出一个向量。
谢谢,这是对池化的非常好的解释。由于有示例,因此非常易读且信息丰富。
谢谢,很高兴它有帮助。
关于池化层的下面这句话是什么意思?
“这意味着特征在输入图像中的位置发生微小移动会导致特征图不同。
“
这意味着,对我们眼睛来说看起来相同的微小差异图像,对模型来说却看起来非常不同。
那么,它如何识别一张图片中包含狗但不在中心呢?这意味着狗的特征在输入图像中的位置发生巨大移动,对模型来说看起来会非常不同。
是的,CNN 架构的一个特性是它对输入中特征的位置具有不变性,例如,如果模型知道什么是狗,那么狗可以出现在几乎任何位置并仍然被正确检测到(在一定限度内)。
是的,我明白了。我的问题是 CNN 如何对输入中的特征位置不变?通过池化层,只能解决输入中轻微差异的问题(正如您上面提到的)。那么这种位置上的巨大差异(从中心到角落)是如何解决的?我们有其他类型的层来做这个吗?
卷积层和池化层堆叠在一起可以共同实现特征不变性。
也许我不明白你的问题。
你好 Jason,我正在通过迁移学习训练卷积神经网络。我想找到每个卷积层的类间标准差的均值,以确定要冻结的最佳卷积层。有什么帮助吗?
我不确定我是否理解,抱歉。也许把你的问题发到 stackoverflow 上?
嗨 Jason
好文章!
CNN 中卷积后需要池化操作的原因是人们常问的问题之一。您强调的使图像检测器平移不变这一点非常重要。
有没有方法使检测器也具有旋转不变性?
谢谢
谢谢。
是的,用旋转过的图像版本进行训练。这称为数据增强。
你好,
感谢您这篇精彩的文章。
我有一个疑问。我正在构建自己的 CNN,并且我正在使用最大池化。我从解释中理解了前向传播。
我想知道反向传播,我们保存最大值的索引,并为该索引插入‘1’。但对于您展示的示例,它所有的值都相同。例如,第一个 2x2 单元格中的‘0’。那么,我们是为所有的零都插入‘1’,还是随机插入一个‘0’?同样,如果我们有一个 2x2 单元格,其所有值都相同(0.9)。那么我们是为所有相同的‘0.9’值都插入‘1’,还是随机插入?
再次感谢您的帖子
谢谢。
抱歉,我不完全理解您的问题。您能重新表述一下吗?
你好,
谢谢你的回复。
抱歉造成混淆。我想问的是您提到的最大池化示例的反向传播。
[0.0, 0.0, 3.0, 3.0, 0.0, 0.0]
[0.0, 0.0, 3.0, 3.0, 0.0, 0.0]
上述矩阵的前向传播是:
[0.0, 3.0, 0.0]
那么,矩阵的导数(即在前向传播中选择的最大值处为‘1’)是多少?
但是,如果池化用的 2x2 矩阵的所有值都相同
它是随机选择的‘3.0’值之一,即最大值,为‘1’吗?
[0.0, 0.0, 1.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
或者
‘1’为所有最大值
[0.0, 0.0, 1.0, 1.0, 0.0, 0.0]
[0.0, 0.0, 1.0, 1.0, 0.0, 0.0]
***另外,我假设所有零的导数都是‘0’(不确定)
再次感谢
Chinmay
池化层没有权重,例如它们不参与学习。
感谢您的回复。我对此感到困惑,因为我阅读了一些 CNN 的帖子,说我们需要保存池化后选择的最大值的索引号。该帖子没有正确说明保存索引值的用途,所以我假设它们在反向传播期间被使用。
感谢您为我解惑。
很有趣,但如果你只使用一个八乘八像素的图像并展示输出,那会更简单、更有用。对于已经很抽象的概念来说,这太抽象了。
感谢您的建议。
先生,这次教程非常棒,但我有一个疑问。
在教程的开头,您说“这意味着特征在输入图像中的位置发生微小移动会导致特征图不同”。为什么我们会在乎它是一个不同的特征图,因为它仍然具有与之前相同的特征,只是位置不同了?
例如:想象一下,我们有一个检测“嘴唇”的卷积核,我们在嘴唇的图像上进行了训练,在所有图像中,嘴唇都出现在图像的中心。训练后,我们会有一个可以检测嘴唇的卷积核。现在,如果我们展示一张嘴唇出现在右顶角的图像,它仍然会做得很好,因为它是一个检测嘴唇的卷积核。
所以,即使特征在特征图中的位置发生变化,CNN 也应该做得很好。
那么,为什么我们会在乎它是一个不同的特征图,当它仍然包含相同的特征,只是位置不同时?
谢谢!
好问题。我们在乎是因为模型会提取不同的特征——导致数据不一致,而实际上它是应该一致的。这使得学习更加困难,并且模型性能更差。
谢谢你的回复。
“不同的特征”是指对于一个与未更改图像略有不同的图像,模型会提取不同的特征集吗?
是的,同一图像的旋转版本可能意味着提取不同的特征。
如果我们使用池化,我们可能会在特征提取中实现一些旋转不变性。
啊,我明白了。谢谢。您真是一位机器学习大师。
不,只是一个普通人。
Jason,您好!感谢您提供的所有教程!我是数据科学领域的新手,并且是自学,所以您的帖子对我来说非常非常有用。
不过,我有一个问题。您是否推荐在 CNN 中不使用池化层的情况?还是它们是“有总比没有好”的那种东西?
不,不是真的。如果您不确定您的模型,请比较有和没有这些层的性能,然后使用结果最好的方法。
你好,
我正在努力理解,并想多了解一下像 yolo 这样的 CNN 是如何工作的,我大致理解了卷积部分——换句话说,检测和分类,但我不太明白这些网络是如何通过绘制边框来标记已检测到的对象的。
这可能要复杂得多 😉 但也许您可以给我指个方向。
也许从这里开始
https://machinelearning.org.cn/object-recognition-with-deep-learning/
先生,您的书是用
TensorFlow 2.0
写的和更新的吗……?因为我计划购买《深度学习》。请回复,先生。????
是的,这是一个常见问题,我在这里回答了
https://machinelearning.org.cn/faq/single-faq/do-you-support-tensorflow-2
您好 Jason,
我想知道对于灰度图像,在一些 ResBlocks 之后,全局求和池化还是全局最大池化更好?
谢谢,
Hosein
也许可以在您的特定数据集上进行测试和比较。
我想问的是,池化是否不影响反向传播(导数)计算?
我指的是池化层。
是的,它改变了网络该点的数据流结构。
非常感谢 Jason。您的文章对于获得清晰的直觉非常有帮助。任何时候我需要理解机器学习中的一个概念,我都会先在您的文章中搜索。
谢谢。
在“检测垂直线”的代码中,data.reshape(1, 8, 8, 1) 有 4 个参数。我理解 (8, 8, 1) 表示 8 像素 x 8 像素和 1 个通道,但另一个‘1’的目的是什么?
附注:请对我宽容点,我是 AI 新手 :)。
它是 1 个样本,例如一张图像。我们通常在批处理中使用许多图像。这样效率更高。
如何在 PyTorch 中执行全局求和池化(使用和不使用 view() 函数)。谢谢!
改用平均池化。它与带有恒定缩放因子的求和池化相同。
为什么最大池化更好?
不总是更好。它只是在计算机视觉任务中通过实验发现效果更好。
你好
您能给我平均池化的 MATLAB 代码吗?
你好 H... 我没有 Octave 或 Matlab 的教程。
我相信 Octave 和 Matlab 是在学术环境中学习机器学习算法如何工作的优秀平台。
我认为它们不适合在工业中进行应用机器学习,这也是我网站的重点。