学术, 机器学习

卷积和池化的作用以及代码实现

卷积(convolution)和池化(pooling)是卷积神经网络(convolutional neural network, CNN)中的两个重要操作。卷积的作用是提取输入数据的局部特征。池化的作用是对卷积后的特征映射进行下采样,降低分辨率,从而减小计算量和参数空间。

一、卷积

想象你在看一张图片,这张图片上有很多小元素,比如线条、圆形等。如果你只是盯着整张图片看,可能很难看清细节,但如果你用一个小窗口在图片上滑动观察,每次只关注窗口内的那一小部分,就能更容易看清里面的元素。卷积的作用是在输入数据(如图像)上滑动一个小滤波器,来自动学习局部特征模式,如边缘、纹理等。通过多层卷积,可以逐步从低级特征组合出更高层次的复杂特征表示。

个人理解:相较于全连接层,卷积层的主要特点可以概括为“先局部,后整体”。其中,局部特征通过训练后的卷积核来体现,尤其是二维卷积核可以很好地提取图像的信息,而全连接层一开始就把数据展平(flattened),因此很难获取到图像所要表达的意思。

卷积公式(二维离散形式):

(f*g)(x, y) = \sum_{m}\sum_{n}f(m, n)g(x-m, y-n)

卷积的二维连续形式:

(f*g)(x, y) = \int_{-\infty}^{\infty} \int_{-\infty}^{\infty} f(u, v) g(x-u, y-v) du dv

卷积的一维连续形式:

(f*g)(x) = \int_{-\infty}^{\infty} f(\tau) g(x-\tau) d\tau

卷积过程的动图:

卷积的 Pytorch 文档:

可能要考虑的参数:

  • kernel_size:卷积核的大小决定了每次卷积操作覆盖输入的区域大小。卷积核的具体数值在训练过程是不断更新的。
  • stride:步幅是指卷积核每次在输入上滑动的步长。默认为 1,即移动一个像素。
  • padding:填充是指在输入的周围添加额外的像素值。填充的目的通常是为了保持输出的大小与输入相同,或者在进行卷积运算时保持边界信息。默认为 0,即不进行填充。
  • dilation:扩张率控制了卷积核中元素之间的间隔。默认为 1,即元素之间没有间隔。

代码示例:

import torch
input_data = torch.randn(1, 1, 28, 28)

conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=1, padding=1)
output_data = conv_layer(input_data)
print("输出数据的形状:", output_data.shape)

conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=1, padding=0)
output_data = conv_layer(input_data)
print("输出数据的形状:", output_data.shape)

运行结果:

输出数据的形状: torch.Size([1, 64, 28, 28])
输出数据的形状: torch.Size([1, 64, 26, 26])

二、池化

池化的方式有最大池化和平均池化等,它们分别取局部区域的最大值和平均值作为输出。其中,最大池化可能会更常用些,因为最大池化是在每个池化窗口中选择最大值作为输出,这样可以保留图像中最显著的特征。

池化过程的动图(以最大池化为例):

最大池化的 Pytorch 文档:

代码示例:

import torch
input_data = torch.tensor([[7, 3, 5, 2],
                           [8, 7, 1, 6],
                           [4, 9, 3, 9],
                           [0, 8, 4, 5]], dtype=torch.float32).unsqueeze(0).unsqueeze(0)  # 两次 .unsqueeze(0) 分别是添加批次和通道维度

max_pool = torch.nn.MaxPool2d(kernel_size=2, stride=2)
output_data = max_pool(input_data)

print("Input:\n", input_data)
print("Output after max pooling:\n",  output_data)
print('输入数据的形状: ', input_data.shape)
print('输出数据的形状: ', output_data.shape)

运行结果:

Input:
 tensor([[[[7., 3., 5., 2.],
          [8., 7., 1., 6.],
          [4., 9., 3., 9.],
          [0., 8., 4., 5.]]]])
Output after max pooling:
 tensor([[[[8., 6.],
          [9., 9.]]]])
输入数据的形状:  torch.Size([1, 1, 4, 4])
输出数据的形状:  torch.Size([1, 1, 2, 2])

更多阅读:卷积和池化后的数据维度

821 次浏览

【说明:本站主要是个人的一些笔记和代码分享,内容可能会不定期修改。为了使全网显示的始终是最新版本,这里的文章未经同意请勿转载。引用请注明出处:https://www.guanjihuan.com

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code