在计算机视觉领域, 往往我们输入的图像都很大,使用全连接网络的话,计算的代价较高. 另外图像也很难保留原有的特征,导致图像处理的准确率不高.
卷积神经网络(Convolutional Neural Network)是含有卷积层的神经网络. 卷积层的作用就是用来自动学习、提取图像的特征.
CNN网络主要有三部分构成:卷积层、池化层和全连接层构成,其中卷积层负责提取图像中的局部特征;池化层用来大幅降低参数量级(降维);全连接层类似人工神经网络的部分,用来输出想要的结果。
1. 卷积计算
- input 表示输入的图像
- filter 表示卷积核, 也叫做滤波器
- input 经过 filter 的得到输出为最右侧的图像,该图叫做特征图
那么, 它是如何进行计算的呢?卷积运算本质上就是在滤波器和输入数据的局部区域间做点积。
左上角的点计算方法:
按照上面的计算方法可以得到最终的特征图为:
2. Padding
通过上面的卷积计算过程,我们发现最终的特征图比原始图像小很多,如果想要保持经过卷积后的图像大小不变, 可以在原图周围添加 padding 来实现.
3. Stride
按照步长为1来移动卷积核,计算特征图如下所示:
如果我们把 Stride 增大为2,也是可以提取特征图的,如下图所示:
4. 多通道卷积计算
实际中的图像都是多个通道组成的,我们怎么计算卷积呢?
计算方法如下:
- 当输入有多个通道(Channel), 例如 RGB 三个通道, 此时要求卷积核需要拥有相同的通道数.
- 每个卷积核通道与对应的输入图像的各个通道进行卷积.
- 将每个通道的卷积结果按位相加得到最终的特征图.
如下图所示:
5. 多卷积核卷积计算
上面的例子里我们只使用一个卷积核进行特征提取, 实际对图像进行特征提取时, 我们需要使用多个卷积核进行特征提取. 这个多个卷积核可以理解为从不同到的视角、不同的角度对图像特征进行提取.
那么, 当使用多个卷积核时, 应该怎么进行特征提取呢?
6. 特征图大小
输出特征图的大小与以下参数息息相关:
- size: 卷积核/过滤器大小,一般会选择为奇数,比如有 1*1, 3*3, 5*5*
- Padding: 零填充的方式
- Stride: 步长
那计算方法如下图所示:
- 输入图像大小: W x W
- 卷积核大小: F x F
- Stride: S
- Padding: P
- 输出图像大小: N x N
以下图为例:
- 图像大小: 5 x 5
- 卷积核大小: 3 x 3
- Stride: 1
- Padding: 1
- (5 – 3 + 2) / 1 + 1 = 5, 即得到的特征图大小为: 5 x 5
7. PyTorch 卷积层 API
图像中主要用 Conv2D 卷积层,接下来了解下它的使用。示例代码:
import torch.nn as nn import torch import torch.nn.functional as F # 1. nn.Conv2d 参数解释 def test01(): # Conv2d 表示卷积层, 由 out_channels 个卷积核组成,每个卷积核有 in_channels 个通道 conv2d = nn.Conv2d(in_channels=3, out_channels=5, kernel_size=2) # 卷积核权重参数: # 5: 表示需要 5 个卷积核, 输出几个通道 out_channels 对应就有几个卷积核 # 3: 输入的图像有 in_channels=3 个,那么对应每个卷积核就有 3 个通道 # [2, 2]: 表示卷积核的 kernel size # torch.Size([5, 3, 2, 2]) print(conv2d.weight.shape) # 卷积核偏置参数: # 5: 表示共有 5 个卷积核对应 5 个偏置 # torch.Size([5]) print(conv2d.bias.shape) # 卷积时候向右、向下的步长分别为 1 # (1, 1) print(conv2d.stride) # 卷积时候向右、向下的填充分别为 0 # (0, 0) print(conv2d.padding) # 卷积时候向右、向下的扩张为 1,可以增加范围 # (1, 1) print(conv2d.dilation) # 2. nn.Conv2d 计算过程 def test02(): torch.manual_seed(0) inputs = torch.randint(0, 10, size=[2, 3, 3, 3]).float() print(inputs) print('-' * 60) # 设置卷积核权重参数 weight = torch.ones(size=[5, 3, 2, 2]).float() # 设置卷积核偏置参数 bias = torch.zeros(size=[5,]).float() output = F.conv2d(inputs, weight=weight, bias=bias) print(output) if __name__ == '__main__': test02()