Pytorch_python|神经网络的基本组成之卷积层(Conv Layer)&& 利用 Pytorch 搭建神经网络 && 空洞卷积对比普通卷积

Pytorch_python|神经网络的基本组成之卷积层(Conv Layer)&& 利用 Pytorch 搭建神经网络 && 空洞卷积对比普通卷积
文章图片

图1 卷积网络中的 layers
目录
(1)卷积层
(2)本文章设计的函数列表,以及函数的参数解读
(3)代码示例
(4)空洞卷积
(1)卷积层 卷积本是分析数学中的一种运算,在深度学习中使用的卷积运算通常是离散的。作为卷积神经网络中最基础的组成部分,卷积的本质是用卷积核的参数来提取数据的特征,通过矩阵点乘运算与求和运算来得到结果。
如图2 所示为一个基本二维卷积的运算过程,公式为y=ωx+b。这 里的特征图(x)大小为1×5×5,即输入通道数为1,卷积核(ω)的大小为3×3,偏置(b)为1,为保证输出维度和输入特征维度一致,还需要有填充(padding),这里使用zero-padding,即用0来填充。
Pytorch_python|神经网络的基本组成之卷积层(Conv Layer)&& 利用 Pytorch 搭建神经网络 && 空洞卷积对比普通卷积
文章图片

图2 卷积计算的基本过程

卷积核参数与对应位置像素逐位相乘后累加作为一次计算结果。以 图2 左上角为例,其计算过程为 1×0+0×0+1×0+0×0+1×1+0×8+1×0+0×6+1×7+1=9,然后在特征图上进行滑动,即可得到所有的计算结果。
Pytorch_python|神经网络的基本组成之卷积层(Conv Layer)&& 利用 Pytorch 搭建神经网络 && 空洞卷积对比普通卷积
文章图片

图3 卷积计算过程(GIF)
(2)本文章设计的函数列表,以及函数的参数解读

import torch.nn as nn
  1. nn.Conv1d# 一维卷积
  2. nn.Conv1d# 一维卷积
  3. nn.Conv1d# 一维卷积
  4. nn.ConvTranspose1d# 一维的解卷积操作
  5. nn.ConvTranspose1d# 二维的解卷积操作
  6. nn.ConvTranspose1d# 三维的解卷积操作
torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
  • # # 函数的参数解读 # #
  • # # Parameters:
  • # in_channels(int) – 输入信号的通道
  • #out_channels(int) – 卷积产生的通道
  • #kerner_size(int or tuple) - 卷积核的尺寸
  • # stride(int or tuple, optional) - 卷积步长
  • # padding (int or tuple, optional)- 输入的每一条边补充 0 的层数
  • # dilation(int or tuple, `optional``) – 卷积核元素之间的间距
  • # groups(int, optional) – 从输入通道到输出通道的阻塞连接数
  • # bias(bool, optional) - 如果 bias=True,添加偏置
  • # # groups: 控制输入和输出之间的连接: group=1,输出是所有的输入的卷积;group=2,此时相当于有 # 并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。
(3)代码示例
import torch import torch.nn as nn from torch import autograd# ========================================================= # # 对由多个输入平面组成的输入信号应用一维卷积。 # torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, m = nn.Conv1d(in_channels=16, out_channels=33, kernel_size=(3, 3), stride=(1, 1)) print(m) input = autograd.Variable(torch.randn(20, 16, 50, 50)) output = m(input) print(output.size())# ========================================================= # # torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, # groups=1, bias=True) m = nn.Conv2d(16, 33, (3, 3), (1, 1)) m1 = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2)) m2 = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1)) input = autograd.Variable(torch.randn(20, 16, 50, 100)) output = m(input) output1 = m1(input) output2 = m2(input) print(output.size()) print(output1.size()) print(output2.size())# ========================================================= # # torch.nn.Conv3d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, # groups=1, bias=True) m = nn.Conv3d(16, 33, (3, 5, 2), stride=(2, 1, 1), padding=(4, 2, 0)) input = autograd.Variable(torch.randn(20, 16, 10, 50, 100)) output = m(input) print(output.size())# ========================================================= # # torch.nn.ConvTranspose1d(in_channels, out_channels, kernel_size, stride=1, padding=0, # output_padding=0, groups=1, bias=True) # 1 维的解卷积操作(transposed convolution operator,注意该视觉操作可视作解卷积操作,但并不是真正的解卷积操作) # 该模块可以看作是 Conv1d 相对于其输入的梯度,有时(但不正确地)被称为解卷积操作。 m = nn.ConvTranspose1d(in_channels=33, out_channels=16, kernel_size=(3, 3), stride=(1, 1)) print(m) input = autograd.Variable(torch.randn(20, 33, 48, 48))# 对应着Conv1d的操作 out = m(input) print(out.size())# ========================================================= # # torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, # output_padding=0, groups=1, bias=True) m = nn.ConvTranspose2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2)) input = autograd.Variable(torch.randn(20, 16, 50, 100)) output = m(input) print(output.size())# ========================================================= # # torch.nn.ConvTranspose3d m = nn.ConvTranspose3d(16, 33, (3, 5, 2), stride=(2, 1, 1), padding=(4, 2, 0)) input = autograd.Variable(torch.randn(10, 16, 30, 30, 30)) out = m(input) print(out.size())

>>>output

Conv1d(16, 33, kernel_size=(3, 3), stride=(1, 1))
torch.Size([20, 33, 48, 48])
torch.Size([20, 33, 48, 98])
torch.Size([20, 33, 28, 100])
torch.Size([20, 33, 26, 100])
torch.Size([20, 33, 8, 50, 99])
ConvTranspose1d(33, 16, kernel_size=(3, 3), stride=(1, 1))
torch.Size([20, 16, 50, 50])
torch.Size([20, 33, 93, 100])
torch.Size([10, 33, 53, 30, 31])
阅读以上内容后,空洞卷积就很好理解了
(4)空洞卷积空洞卷积最初是为解决图像分割的问题而提出的。常见的图像分割算法通常使用池化层来增大感受野,同时也缩小了特征图尺寸,然后再 利用上采样还原图像尺寸。特征图缩小再放大的过程造成了精度上的损失,因此需要有一种操作可以在增加感受野的同时保持特征图的尺寸不变,从而替代池化与上采样操作,在这种需求下,空洞卷积就诞生了。
在近几年的物体检测发展中,空洞卷积也发挥了重要的作用。因为虽然物体检测不要求逐像素地检测,但是保持特征图的尺寸较大,对于小物体的检测及物体的定位来说也是至关重要的。
空洞卷积,顾名思义就是卷积核中间带有一些洞,跳过一些元素进行卷积。在此以3×3卷积为例,其中,
  • 图4 a是普通的卷积过程,在卷积核紧密排列在特征图上滑动计算,
  • 图4 b代表了空洞数为2的空洞卷积,可以看到,在特征图上每2行或者2列选取元素与卷积核卷积。
  • 图4 c代表了空洞数为3的空洞卷积。
Pytorch_python|神经网络的基本组成之卷积层(Conv Layer)&& 利用 Pytorch 搭建神经网络 && 空洞卷积对比普通卷积
文章图片

图4 空洞卷积和普通卷积的对比
在代码实现时,空洞卷积有一个额外的超参数dilation rate,表示空洞数,普通卷积dilation rate默认为1,图4 中的b与c的dilation rate分别为2与3。

  • 普通卷积:conv1 = nn.Conv2d(3, 256, 3, stride=1, padding=1, dilation=1)
  • 空洞卷积:conv2 = nn.Conv2d(3, 256, 3, stride=1, padding=1, dilation=2)
在图4 中,同样的一个3×3卷积,却可以起到5×5、7×7等卷积的效果。可以看出,空洞卷积在不增加参数量的前提下,增大了感受野。 假设空洞卷积的卷积核大小为K,空洞数为d,则其等效卷积核大小 K1 计算如式(3-12)所示。
K1 = K + (K - 1) × (d - 1)
在计算感受野时,只需要将原来的卷积核大小 K 更换为 K1 即可。
空洞卷积的优点显而易见,在不引入额外参数的前提下可以任意扩大感受野,同时保持特征图的分辨率不变。这一点在分割与检测任务中十分有用,感受野的扩大可以检测大物体,而特征图分辨率不变使得物体定位更加精准。
当然,空洞卷积也有自己的一些缺陷,主要表现在以下3个方面:
  1. 网格效应(Gridding Effect):由于空洞卷积是一种稀疏的采样方 式,当多个空洞卷积叠加时,有些像素根本没有被利用到,会损失信息的连续性与相关性,进而影响分割、检测等要求较高的任务。
  2. 远距离的信息没有相关性:空洞卷积采取了稀疏的采样方式,导致远距离卷积得到的结果之间缺乏相关性,进而影响分类的结果。
  3. 不同尺度物体的关系:大的dilation rate对于大物体分割与检测有利,但是对于小物体则有弊无利,如何处理好多尺度问题的检测,是空洞卷积设计的重点。
对于上述问题,有多篇文章提出了不同的解决方法,典型的有图森未来提出的HDC(Hybrid Dilated Convolution)结构。该结构的设计准 则是堆叠卷积的dilation rate不能有大于1的公约数,同时将dilation rate设 置为类似于 [1,2,5,1,2,5] 这样的锯齿类结构。此外各dilation rate之间还需要满足一个数学公式,这样可以尽可能地覆盖所有空洞,以解决网格效应与远距离信息的相关性问题,具体细节可参考相关资料。
>>>本文参考:Pytoch官方文件 &&《深度学习之PyTorch物体检测实战》
【Pytorch_python|神经网络的基本组成之卷积层(Conv Layer)&& 利用 Pytorch 搭建神经网络 && 空洞卷积对比普通卷积】>>>如有疑问,欢迎评论区一起探讨

    推荐阅读