卷积神经网络中的卷积层系统地对输入应用滤波器,并创建输出特征图。
虽然卷积层非常简单,但它能够取得复杂而令人印象深刻的结果。然而,要直观地理解滤波器形状如何影响输出特征图的形状,以及填充(padding)和步幅(stride)等相关配置超参数应如何配置,可能具有挑战性。
在本教程中,您将直观地了解卷积神经网络中滤波器大小、填充的必要性以及步幅。
完成本教程后,您将了解:
- 滤波器大小或核大小如何影响输出特征图的形状。
- 滤波器大小如何在特征图中产生边界效应,以及如何通过填充来克服它。
- 滤波器在输入图像上的步幅如何用于对输出特征图的大小进行下采样。
通过我的新书《深度学习计算机视觉》开启您的项目,其中包括逐步教程和所有示例的 Python 源代码文件。
让我们开始吧。

卷积神经网络中的填充(Padding)和步幅(Stride)简介
图片由Red~Star提供,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- 卷积层
- 边界效应问题
- 滤波器大小(核大小)的影响
- 通过填充(Padding)解决边界效应问题
- 通过步幅(Stride)对输入进行下采样
想通过深度学习实现计算机视觉成果吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
卷积层
在卷积神经网络中,卷积层负责系统地将一个或多个滤波器应用于输入。
滤波器与输入图像的乘法运算产生一个单一的输出。输入通常是三维图像(例如,行、列和通道),相应地,滤波器也是三维的,具有相同数量的通道,但比输入图像的行和列更少。因此,滤波器被重复地应用于输入图像的每个部分,从而产生一个二维的激活输出图,称为特征图。
Keras 提供了一个名为 Conv2D 的卷积层实现。
它要求您以行(高度)、列(宽度)和通道(深度)或 [行,列,通道] 的形式指定输入图像的预期形状。
滤波器包含在层训练过程中必须学习的权重。滤波器权重代表滤波器将检测到的结构或特征,激活的强度表示特征被检测到的程度。
该层要求指定滤波器的数量和滤波器的形状。
我们可以通过一个小的例子来演示这一点。在这个例子中,我们定义了一个具有一个通道的单个输入图像或样本,它是一个 8x8 像素的正方形,所有值为 0,中心有一个两像素宽的垂直线。
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),并带有一个具有 3x3 像素形状的单个滤波器的隐藏卷积层。
1 2 3 4 5 |
# 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), input_shape=(8, 8, 1))) # 总结模型 model.summary() |
滤波器在模型初始化时被随机初始化。我们将覆盖随机权重,并硬编码我们自己的 3x3 滤波器,该滤波器将检测垂直线。
也就是说,当它检测到垂直线时,滤波器将强烈激活;当它不检测到时,则弱激活。我们期望通过将此滤波器应用于输入图像,输出特征图将显示垂直线已被检测到。
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) |
结果是一个四维输出,包含一个批次、给定数量的行和列以及一个滤波器,即 [批次,行,列,滤波器]。
我们可以打印单个特征图中的激活,以确认该线已被检测到。
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), 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])]) |
运行该示例首先总结了模型的结构。
值得注意的是,单个隐藏卷积层将接收 8x8 像素的输入图像,并生成尺寸为 6x6 的特征图。我们将在下一节中探讨原因。
我们还可以看到该层有 10 个参数,即滤波器(3x3)的 9 个权重和一个偏差项的 1 个权重。
最后,打印出特征图。我们可以通过查看 6x6 矩阵中的数字来确认手动指定的滤波器确实检测到了输入图像中间的垂直线。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= 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] |
边界效应问题
在上一节中,我们定义了一个高度为三个像素、宽度为三个像素(行、列)的单个滤波器。
我们看到,将 3x3 滤波器(在 Keras 中称为核大小)应用于 8x8 输入图像后,会得到一个 6x6 大小的特征图。
也就是说,具有 64 像素的输入图像被缩小为具有 36 像素的特征图。那另外 28 个像素去哪了?
滤波器被系统地应用于输入图像。它从图像的左上角开始,然后从左向右每次移动一个像素列,直到滤波器的边缘到达图像的边缘。
对于应用于 8x8 输入图像的 3x3 像素滤波器,我们可以看到它只能应用六次,导致输出特征图的宽度为六。
例如,让我们逐一处理输入图像(左侧)的六个补丁与滤波器(右侧)的点积(“.”运算符)
1 2 3 |
0, 0, 0 0, 1, 0 0, 0, 0 . 0, 1, 0 = 0 0, 0, 0 0, 1, 0 |
向右移动一个像素
1 2 3 |
0, 0, 1 0, 1, 0 0, 0, 1 . 0, 1, 0 = 0 0, 0, 1 0, 1, 0 |
向右移动一个像素
1 2 3 |
0, 1, 1 0, 1, 0 0, 1, 1 . 0, 1, 0 = 3 0, 1, 1 0, 1, 0 |
向右移动一个像素
1 2 3 |
1, 1, 0 0, 1, 0 1, 1, 0 . 0, 1, 0 = 3 1, 1, 0 0, 1, 0 |
向右移动一个像素
1 2 3 |
1, 0, 0 0, 1, 0 1, 0, 0 . 0, 1, 0 = 0 1, 0, 0 0, 1, 0 |
向右移动一个像素
1 2 3 |
0, 0, 0 0, 1, 0 0, 0, 0 . 0, 1, 0 = 0 0, 0, 0 0, 1, 0 |
这就得到了输出特征图的第一行和每一列
1 |
0.0, 0.0, 3.0, 3.0, 0.0, 0.0 |
输入到特征图大小的缩减被称为边界效应。它是由滤波器与图像边界的交互作用引起的。
对于大型图像和小型滤波器来说,这通常不是问题,但对于小型图像来说,这可能成为一个问题。一旦堆叠了多个卷积层,它也可能成为一个问题。
例如,下面是更新后的相同模型,具有两个堆叠的卷积层。
这意味着将 3x3 滤波器应用于 8x8 输入图像,从而得到一个 6x6 的特征图,如上一节所述。然后将 3x3 滤波器应用于 6x6 的特征图。
1 2 3 4 5 6 7 8 9 |
# 堆叠卷积层的示例 from keras.models import Sequential from keras.layers import Conv2D # 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), input_shape=(8, 8, 1))) model.add(Conv2D(1, (3,3))) # 总结模型 model.summary() |
运行示例总结了每一层输出的形状。
我们可以看到,将滤波器应用于第一层的特征图输出,反过来又产生了更小的 4x4 特征图。
随着我们开发具有数十或数百层的非常深的卷积神经网络模型,这可能会成为一个问题。我们的特征图将简单地耗尽可操作的数据。
1 2 3 4 5 6 7 8 9 10 11 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 6, 6, 1) 10 _________________________________________________________________ conv2d_2 (Conv2D) (None, 4, 4, 1) 10 ================================================================= 总参数:20 可训练参数:20 不可训练参数: 0 _________________________________________________________________ |
滤波器大小(核大小)的影响
不同大小的滤波器将在输入图像中检测不同大小的特征,从而产生不同大小的特征图。
通常使用 3x3 大小的滤波器,对于更大的输入图像,也可以使用 5x5 甚至 7x7 大小的滤波器。
例如,下面是一个模型示例,其中包含一个更新为使用 5x5 像素滤波器大小的单个滤波器。
1 2 3 4 5 6 7 8 |
# 卷积层的示例 from keras.models import Sequential from keras.layers import Conv2D # 创建模型 model = Sequential() model.add(Conv2D(1, (5,5), input_shape=(8, 8, 1))) # 总结模型 model.summary() |
运行示例表明,5x5 滤波器只能应用于 8x8 输入图像 4 次,从而产生 4x4 的特征图输出。
1 2 3 4 5 6 7 8 9 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 4, 4, 1) 26 ================================================================= 总参数:26 可训练参数:26 不可训练参数: 0 _________________________________________________________________ |
为了进一步理解滤波器大小与输出特征图之间的关系,可以考虑两种极端情况。
第一种情况是滤波器大小为 1x1 像素。
1 2 3 4 5 6 7 8 |
# 卷积层的示例 from keras.models import Sequential from keras.layers import Conv2D # 创建模型 model = Sequential() model.add(Conv2D(1, (1,1), input_shape=(8, 8, 1))) # 总结模型 model.summary() |
运行示例表明,输出特征图的尺寸与输入相同,具体为 8x8。这是因为滤波器只有一个权重(和一个偏差)。
1 2 3 4 5 6 7 8 9 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 8, 8, 1) 2 ================================================================= 总参数:2 可训练参数:2 不可训练参数: 0 _________________________________________________________________ |
另一个极端是滤波器与输入大小相同,在此例中为 8x8 像素。
1 2 3 4 5 6 7 8 |
# 卷积层的示例 from keras.models import Sequential from keras.layers import Conv2D # 创建模型 model = Sequential() model.add(Conv2D(1, (8,8), input_shape=(8, 8, 1))) # 总结模型 model.summary() |
运行示例,我们可以看到,正如您所期望的,输入图像中的每个像素都有一个权重(64个权重加上偏差的1个),并且输出是一个单像素的特征图。
1 2 3 4 5 6 7 8 9 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 1, 1, 1) 65 ================================================================= 总参数:65 可训练参数:65 不可训练参数: 0 _________________________________________________________________ |
现在我们已经熟悉了滤波器大小对生成特征图大小的影响,接下来我们来看看如何避免像素损失。
通过填充(Padding)解决边界效应问题
默认情况下,滤波器从图像的左侧开始,滤波器的左侧边缘位于图像的最左侧像素上。然后,滤波器每次向右移动一列像素,直到滤波器的右侧边缘位于图像的最右侧像素上。
将滤波器应用于图像的另一种方法是确保图像中的每个像素都有机会位于滤波器的中心。
默认情况下,情况并非如此,因为输入图像边缘的像素只会暴露在滤波器的边缘。通过将滤波器从图像框外开始,可以使图像边缘的像素有更多机会与滤波器交互,更多机会让滤波器检测到特征,进而产生与输入图像形状相同的输出特征图。
例如,在将 3x3 滤波器应用于 8x8 输入图像的情况下,我们可以在图像周围添加一个一像素的边框。这实际上创建了一个 10x10 的输入图像。当应用 3x3 滤波器时,它会产生一个 8x8 的特征图。添加的像素值可以为零,当应用滤波器时,零值在点积运算中没有影响。
1 2 3 |
x, x, x 0, 1, 0 x, 0, 0 . 0, 1, 0 = 0 x, 0, 0 0, 1, 0 |
在图像边缘添加像素被称为填充(padding)。
在 Keras 中,这通过 Conv2D 层的“padding”参数指定,其默认值为“valid”(无填充)。这意味着滤波器仅以有效方式应用于输入。
“padding”值为“same”时,会计算并向输入图像(或特征图)添加所需的填充,以确保输出具有与输入相同的形状。
下面的示例为我们演示的卷积层添加了填充。
1 2 3 4 5 6 7 8 |
# 带填充的卷积层示例 from keras.models import Sequential from keras.layers import Conv2D # 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), padding='same', input_shape=(8, 8, 1))) # 总结模型 model.summary() |
运行该示例表明,输出特征图的形状与输入图像相同:填充达到了预期效果。
1 2 3 4 5 6 7 8 9 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 8, 8, 1) 10 ================================================================= 总参数:10 可训练参数:10 不可训练参数: 0 _________________________________________________________________ |
添加填充允许开发非常深的模型,从而使特征图不会逐渐消失。
下面的示例通过三个堆叠的卷积层演示了这一点。
1 2 3 4 5 6 7 8 9 10 |
# 带有填充的深度 CNN 示例 from keras.models import Sequential from keras.layers import Conv2D # 创建模型 model = Sequential() model.add(Conv2D(1, (3,3), padding='same', input_shape=(8, 8, 1))) model.add(Conv2D(1, (3,3), padding='same')) model.add(Conv2D(1, (3,3), padding='same')) # 总结模型 model.summary() |
运行示例,我们可以看到,在添加填充之后,即使深达三层,输出特征图的形状仍保持在 8x8 不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 8, 8, 1) 10 _________________________________________________________________ conv2d_2 (Conv2D) (None, 8, 8, 1) 10 _________________________________________________________________ conv2d_3 (Conv2D) (None, 8, 8, 1) 10 ================================================================= 总参数:30 可训练参数:30 不可训练参数: 0 _________________________________________________________________ |
通过步幅(Stride)对输入进行下采样
滤波器在图像上从左到右、从上到下移动,水平移动时每次改变一个像素列,垂直移动时每次改变一个像素行。
滤波器应用于输入图像之间移动的量称为步幅(stride),它在高度和宽度维度上几乎总是对称的。
默认步幅或二维步幅为(1,1),用于高度和宽度移动,并在需要时执行。此默认值在大多数情况下效果良好。
步幅可以改变,这会影响滤波器如何应用于图像,进而影响所得特征图的大小。
例如,步幅可以更改为 (2,2)。这样做的效果是,在创建特征图时,滤波器每次水平移动会向右移动两个像素,每次垂直移动会向下移动两个像素。
我们可以通过一个示例来演示这一点,使用 8x8 图像(左侧)与垂直线滤波器(右侧)进行点积(“.”运算符),步幅为两个像素
1 2 3 |
0, 0, 0 0, 1, 0 0, 0, 0 . 0, 1, 0 = 0 0, 0, 0 0, 1, 0 |
向右移动两个像素
1 2 3 |
0, 1, 1 0, 1, 0 0, 1, 1 . 0, 1, 0 = 3 0, 1, 1 0, 1, 0 |
向右移动两个像素
1 2 3 |
1, 0, 0 0, 1, 0 1, 0, 0 . 0, 1, 0 = 0 1, 0, 0 0, 1, 0 |
我们可以看到,对于步幅为二的 8x8 输入图像,3x3 滤波器只有三种有效应用方式。在垂直维度上也是如此。
这样做的好处是,通过这种方式应用滤波器,正常的特征图输出(6x6)会进行下采样,使每个维度的尺寸减半(3x3),从而使像素数量减少四分之一(36个像素减少到9个)。
在 Keras 中,步幅可以在 `Conv2D` 层通过 `stride` 参数指定,并以高度和宽度的元组形式给出。
该示例演示了将我们的手动垂直线滤波器应用于具有两步幅的卷积层的 8x8 输入图像。
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 |
# 步幅为 2 的垂直线滤波器示例 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), strides=(2, 2), 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])]) |
运行该示例,从模型的摘要中我们可以看到输出特征图的形状将是 3x3。
将手工制作的滤波器应用于输入图像并打印生成的激活特征图,我们可以看到,确实,滤波器仍然检测到垂直线,并且可以用更少的信息来表示这一发现。
在某些情况下,如果对模型中使用的滤波器或模型架构有更深入的了解,可以对生成的特征图进行一些压缩,那么下采样可能是可取的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_1 (Conv2D) (None, 3, 3, 1) 10 ================================================================= 总参数:10 可训练参数:10 不可训练参数: 0 _________________________________________________________________ [0.0, 3.0, 0.0] [0.0, 3.0, 0.0] [0.0, 3.0, 0.0] |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
文章
书籍
- 第9章:卷积网络,深度学习,2016年。
- 第5章:深度学习计算机视觉,Python 深度学习,2017年。
API
总结
在本教程中,您直观地了解了卷积神经网络中滤波器大小、填充的必要性以及步幅。
具体来说,你学到了:
- 滤波器大小或核大小如何影响输出特征图的形状。
- 滤波器大小如何在特征图中产生边界效应,以及如何通过填充来克服它。
- 滤波器在输入图像上的步幅如何用于对输出特征图的大小进行下采样。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
你好,假设我使用堆叠滤波器。滤波器内的数字会相同吗?例如
model.add(Conv2D(1, (3,3), padding=’same’, input_shape=(8, 8, 1)))
model.add(Conv2D(1, (3,3), padding=’same’))
model.add(Conv2D(1, (3,3), padding=’same’))
第二行和第三行中的滤波器是什么样的?滤波器是否与第一行中的值相同?一般来说,了解如何构建滤波器会很有帮助。
每个滤波器在初始化时都将具有不同的随机数,并且在训练后将具有不同的表示形式——将检测不同的特征。
“例如,步幅可以改为 (2,2)。这样做会使滤波器在创建特征图时,每次水平移动向左移动两个像素,每次垂直移动向下移动两个像素。”
更正:“例如,步幅可以改为 (2,2)。这样做会使滤波器在创建特征图时,每次水平移动向右移动两个像素,每次垂直移动向下移动两个像素。”
谢谢,已修正。
是否有特定的公式可以根据输入大小 (n*n)、填充大小 (p) 和步幅 (s) 计算特征图的大小?
是的,也许可以查阅这份文档
https://arxiv.org/abs/1603.07285
不错,详细的教程。谢谢。
只是“点积”这个术语听起来有点奇怪,它不恰当且容易误导。
谢谢。
此处了解更多关于矩阵数学的信息
https://machinelearning.org.cn/introduction-matrices-machine-learning/
非常感谢,Jason。在我的 TF 课程的额外阅读中偶然发现了您的帖子。它真的帮助我理解了卷积滤波器背后的直觉和数学原理。
谢谢。
谢谢,很高兴听到这个。
请注意,在步幅示例中,如果垂直线太细,您可能会完全错过它。例如,如果单个输入行可以表示为
[0,0,0,0,1,0,0,0]
那么步幅卷积滤波器将直接跳过它,因为它将在第 1、3 和 5 列停止。请注意第 4 列具有垂直线段。
是的,有可能。这只是一个虚构的例子。
谢谢 Jason,
这篇博文在详细解释零填充和步幅以及它们如何工作方面非常有帮助……您的所有文章都总是在更深入地理解事物方面非常有帮助……如果能在这篇博文中也添加计算特征图大小的公式就更好了……
谢谢!
好建议。
解释得很棒
谢谢 Jason!
谢谢。希望你喜欢。
感谢您提供如此精彩的信息,希望您能通过分享此类概念来帮助我们摆脱困境。
感谢这篇精彩的文章。
我正在做一个使用图像作为输入的深度学习作业。
我已经完成了 mnist 和 ImageNet 的教程,但它们的输入图像非常小。
由于实际输入图像很大,我们需要对其进行缩放或卷积。
我想选择卷积,但我不知道如何确定滤波器的大小。
有没有关于滤波器应该占原始图像的百分比,或者它应该是 3x3 并具有更多层的讨论?
增加滤波器的大小似乎可以加快降维速度,但似乎也会丢失一些信息。
如果减小滤波器的大小,我们可能能够保持信息量,但降维速度会更慢,处理时间会更长。
我认为这些是权衡取舍,但有没有合适的方法来处理这个问题?
你好,小川!你可能会觉得下面的内容很有趣
https://machinelearning.org.cn/how-to-visualize-filters-and-feature-maps-in-convolutional-neural-networks/
感谢您的评论。
我以前读过您关于 CNN 可视化的文章。
我以前在 mnist 数字上做过这个。
就像给数字做马赛克一样。
当您能够将它们识别为数字时,您会真正感受到 CNN 的效果,即使马赛克处理减少了图像中的信息量。
文章中讨论的图像是一只鸟,所以直到第三个区块,您才能勉强将其识别为一只鸟,但这让您很好地了解了 CNN 正在观察图像的哪个部分以及以何种粒度进行观察。
现在,詹姆斯。
我正在尝试弄清楚如何处理输入图像的大小。
您提到的文章似乎将图像缩放为 224x224 以适应 VGG16 的输入层。
作为起点,我实际上尝试了类似的方法。
在这里,我认为最好尝试卷积而不是缩放,以相同的数据提高准确性。
那么,有什么指示我应该尝试什么滤波器大小(和层)扩展吗?
你觉得呢?对此有很多讨论吗?
或者我没有解释清楚?
希望得到一些评论。
对于这段代码
inp = tf.zeros([64, 64, 3])
inp = tf.expand_dims(inp, axis=0)
inp = tf.keras.layers.Conv2D(16, (2, 2), (2, 2), padding=’same’)(inp)
print(inp.shape)
(1, 32, 32, 16)
当我将核大小更改为 (3, 3) 或 (5, 5) 时,inp 的形状不会改变。你能解释一下吗?
因为您使用的是“相同”填充。输出中的每个“像素”只是以不同(使用更大的核)但仍然相同的步幅等方式计算。