参考:
https://pytorch.org/docs/master/generated/torch.nn.Conv2d.html#torch.nn.Conv2d
https://zhuanlan.zhihu.com/p/251068800
https://www.cnblogs.com/mrlonely2018/p/13946730.html
https://www.jb51.net/article/177739.htm
https://www.jianshu.com/p/a6da4ad8e8e7
https://blog.csdn.net/HappinessSourceL/article/details/106207022
目录:
1 官方文档
2 函数原型及参数介绍
函数原型:
class torch.nn.Conv2d(in_channels,
out_channels,
kernel_size,
stride=1,
padding=0,
dilation=1,
groups=1,
bias=True)
函数作用:定义一个卷积核,方便后面进行二维卷积操作
我们会发现一个现象:pytorch的conv2d没有要求输入卷积核的信息,设置卷积核的权重可以在后面采用:
conv_zeros = torch.nn.Conv2d(1,1,1,1,padding=1,padding_mode='zeros',bias=False)
conv_zeros.weight = torch.nn.Parameter(torch.ones(1,1,1,1))
Parameters:
参数 | 数据类型 | ||
---|---|---|---|
in_channels | int | Number of channels in the input image | 输入图像通道数 |
out_channels | int | Number of channels produced by the convolution | 卷积产生的通道数 |
kernel_size | (int or tuple) | Size of the convolving kernel | 卷积核尺寸,可以设为1个int型数或者一个(int, int)型的元组。例如(2,3)是高2宽3卷积核 |
stride | (int or tuple, optional) | Stride of the convolution. Default: 1 | 卷积步长,默认为1。可以设为1个int型数或者一个(int, int)型的元组。 |
padding | (int or tuple, optional) | Zero-padding added to both sides of the input. Default: 0 | 填充操作,控制padding_mode的数目。简言之,就是决定图像边沿填充的方式 |
padding_mode | (string, optional) | optional) ‘zeros’, ‘reflect’, ‘replicate’ or ‘circular’. Default: ‘zeros’ | padding模式,默认为Zero-padding 。 |
dilation | (int or tuple, optional) | Spacing between kernel elements. Default: 1 | 扩张操作:控制kernel点(卷积核点)的间距,默认值:1。 |
groups | (int, optional) | Number of blocked connections from input channels to output channels. Default: 1 | group参数的作用是控制分组卷积,默认不分组,为1组。输入图像通道数 |
bias | (bool, optional) | If True, adds a learnable bias to the output. Default: True | 为真,则在输出中添加一个可学习的偏差。默认:True。 |
3 关于二维卷积:
首先我们介绍以下基本的卷积运算:
可以看出,上图的卷积运算实质上就是两个相同尺寸的矩阵进行了一次矩阵内积运算。
实际上,一次卷积操作要经过很多次上述的矩阵内积运算,最终也会获得一个新的矩阵。
(1)单通道卷积
下图可以看作是一次常规的卷积操作:
这是用一个卷积核在一个二维图像上进行卷积操作后得到一个二维的特征映射。
可以看出卷积核尺寸为3*3kernel_size=3
,卷积步长为1stride=1
。
输入通道in_channels
和输出通道out_channels
不得而知,假设都为1。
关于padding
后面介绍,暂定为padding=0
代码:
nn.Conv2d(in_channels=1,out_channels=1,kernel_size=3,stride=1,padding=0)
(2)多通道卷积
若是出现in_channels>1
,out_channels=1
,则可以参考下图的卷积运算:
卷积过程如下,每一个通道的像素值与对应的卷积核通道的数值进行卷积,因此每一个通道会对应一个输出卷积结果,三个卷积结果对应位置累加求和,得到最终的卷积结果(这里卷积输出结果通道只有1个,因为卷积核只有1个。卷积多输出通道下面会继续讲到)。
可以这么理解:最终得到的卷积结果是原始图像各个通道上的综合信息结果。
代码:
nn.Conv2d(in_channels=3,out_channels=1,kernel_size=3,stride=1,padding=0)
上述过程中,每一个卷积核的通道数量,必须要求与输入通道数量一致,因为要对每一个通道的像素值要进行卷积运算,所以每一个卷积核的通道数量必须要与输入通道数量保持一致
(3)多通道输入,多通道输出
代码:
nn.Conv2d(in_channels=3,out_channels=3,kernel_size=1,stride=1,padding=0)
4 卷积操作后图形尺寸的变化公式:
input_size: 输入图形的尺寸
output_size: 输出图形的尺寸
kernel_size: 卷积核的尺寸
stride: 步长
padding: 边缘扩充
output_size = ((input_size - dilation*(kernel_size-1) + 2* padding)-1)/stride + 1
注:整除
常见举例:
input_size=(512,512)
conv2d(in_channels,out_channels,kernel_size=3,stride=2, padding=1)
# 求output_size:
output_size = (input_size - kernel_size + 2* padding)/stride + 1
output = (512 - 3 +2)/2+1 = 255+1 = 256
input_size=(128,128)
conv2d(in_channels,out_channels,kernel_size=3,stride=1, padding=1)
# 求output_size:
output_size = (input_size - kernel_size + 2* padding)/stride + 1
output = (128 - 3 +2)/1+1 = 127+1 = 128
5 关于参数 padding与padding_mode:
(1)padding的理解:
padding参数作用:控制zero-padding的数目。
用容易理解的语言来说:padding就是在图像四周加格子。
原因:如果不对图像边缘进行填充,卷积核将无法到达图像边缘的像素,而且卷积前后图像的尺寸也会发生变化,这会造成许多麻烦。
因此现在各大深度学习框架的卷积层实现上基本都配备了padding操作,以保证图像输入输出前后的尺寸大小不变。
例如,若卷积核大小为3x3,那么就应该设定padding=1,即填充1层边缘像素;若卷积核大小为7x7,那么就应该设定padding=3,填充3层边缘像素;也就是padding大小一般设定为核大小的一半。在pytorch的卷积层定义中,默认的padding为零填充。
(2) PyTorch Conv2d中的padding_mode四种填充模式解析
padding,即边缘填充,可以分为四类:零填充,常数填充,镜像填充,重复填充。
padding_mode
参数,可选项有4种:
(1) zeros
,代表零填充。padding_mode
默认选项为zeros
即在矩阵的高、宽两个维度上用0进行填充,填充时将在一个维度的两边都进行填充:
torch.nn.Conv2d(1,1,1,1,padding=1,padding_mode='zeros',bias=False)
(2) reflect
,代表镜像填充。
reflect
是以矩阵边缘为对称轴,将矩阵中的元素对称的填充到最外围。
torch.nn.Conv2d(1,1,1,1,padding=1,padding_mode='reflect',bias=False)
(3) replicate
,代表重复填充。
对图像或者张量的边缘进行重复填充,就是说直接用边缘的像素值来填充。示例如下:
torch.nn.Conv2d(1,1,1,1,padding=1,padding_mode='replicate',bias=False)
(4) circular
,代表循环填充。
示例如下:
# 定义一个1*1卷积,设置填充模式为'circular',在高和宽维度上两边各填充1个单位
In [64]: conv_reflect = torch.nn.Conv2d(1,1,1,1,padding=1,padding_mode='circular',bias=False)
# 将卷积核的权重设置为1,这样可使卷积后的输出即为填充后的输入矩阵
In [65]: conv_reflect.weight = torch.nn.Parameter(torch.ones(1,1,1,1))
# 进行卷积计算,并输出结果
In [66]: conv_circular(x)
Out[66]:
tensor([[[[16., 13., 14., 15., 16., 13.],
[ 4., 1., 2., 3., 4., 1.],
[ 8., 5., 6., 7., 8., 5.],
[12., 9., 10., 11., 12., 9.],
[16., 13., 14., 15., 16., 13.],
[ 4., 1., 2., 3., 4., 1.]]]], grad_fn=<ThnnConv2DBackward>)
6 关于参数dilation:
dilation:控制kernel点(卷积核点)的间距,默认值:1。
(1)dilation=0的话,效果如图:
蓝色(下面)为输入,绿色(上面)为输出,卷积核为3*3,此时为密集连接。
(2)dilation=1,那么效果如图:
称为扩张卷积(也叫空洞卷积)
优点:
这样单次计算时覆盖的面积(即感受域)由dilation=0时的3*3=9
变为了dilation=1时的5*5=25
。
在增加了感受域的同时却没有增加计算量,保留了更多的细节信息,对图像还原的精度有明显的提升。
7 关于参数groups——分组卷积:
Group Convolution顾名思义,则是对输入feature map进行分组,然后每组分别卷积。
当group=1时,该卷积层需要6611=36个参数,即需要6个61*1的卷积核
计算时就是6H_inW_in的输入整个乘以一个611的卷积核,得到输出的一个channel的值,即1H_outW_out。这样经过6次与6个卷积核计算就能够得到6H_outW_out的结果了
如果将group=3时,卷积核大小为torch.Size([6, 2, 1, 1]),即6个211的卷积核,只需要需要621*1=12个参数
那么每组计算就只被in_channels/groups=2个channels的卷积核计算,当然这也会将输入分为三份大小为2H_inW_in的小输入,分别与211大小的卷积核进行三次运算,然后将得到的3个2H_outW_out的小输出concat起来得到最后的6H_outW_out输出
在实际实验中,同样的网络结构下,这种分组的卷积效果是好于未分组的卷积的效果的。
为什么要设置groups参数,有什么优点?
为了在GPU上并行计算:
标准卷积的计算如下图:
举个例子,假设有一个3×3大小的卷积层,其输入通道为16、输出通道为32。
那么一般的操作就是用32个3×3的卷积核来分别同输入数据卷积,这样每个卷积核需要3×3×16个参数,得到的输出是只有一个通道的数据。之所以会得到一通道的数据,是因为刚开始3×3×16的卷积核的每个通道会在输入数据的每个对应通道上做卷积,然后叠加每一个通道对应位置的值,使之变成了单通道,那么32个卷积核一共需要(3×3×16)×32 =4068个参数。
分组卷积的计算:
9 标准卷积的详细计算图可表示为:
10 案例代码:
>>> # With square kernels and equal stride
>>> m = nn.Conv2d(16, 33, 3, stride=2)
>>> # non-square kernels and unequal stride and with padding
>>> m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
>>> # non-square kernels and unequal stride and with padding and dilation
>>> m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
>>> input = torch.randn(20, 16, 50, 100)
>>> output = m(input)