pytorch中的Tensor通道排列顺序是:[batch, channel, height, width]

我们常用的卷积(Conv2d)在pytorch中对应的函数是:

torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')

其中:

  • in_channels参数代表输入特征矩阵的深度即channel,比如输入一张RGB彩色图像,那in_channels=3
  • out_channels参数代表卷积核的个数,使用n个卷积核输出的特征矩阵深度即channel就是n
  • kernel_size参数代表卷积核的尺寸,输入可以是int类型如3代表卷积核的height=width=3,也可以是tuple类型如(3, 5)代表卷积核的height=3,width=5
  • stride参数代表卷积核的步距默认为1,和kernel_size一样输入可以是int类型,也可以是tuple类型
  • padding参数代表在输入特征矩阵四周补零的情况默认为0,同样输入可以为int型如1代表上下方向各补一行0元素,左右方向各补一列0像素(即补一圈0),如果输入为tuple型如(2, 1)代表在上方补两行下方补一行,左边补两列,右边补一列。
  • bias参数表示是否使用偏置默认使用
  • dilation、groups是高阶用法这里不做讲解,如有需要可以参看官方文档

在卷积操作过程中,我们知道矩阵经卷积操作后的尺寸由以下几个因数决定:

  • 输入图片大小 W×W
  • Filter大小 F×F
  • 步长 S
  • padding的像素数 P

经卷积后的矩阵尺寸大小计算公式为:pytorch 3d卷积 pytorch 卷积函数_卷积

但在实际应用中,有时会出现N为非整数的情况(例如在alexnet,googlenet网络的第一层输出),再例如输入的矩阵 H=W=5,卷积核的F=2,S=2,Padding=1。经计算我们得到的N =(5 - 2 + 21)/ 2 +1 = 3.5 此时在Pytorch中是如何处理呢,先直接告诉你结论:在卷积过程中会直接将最后一行以及最后一列给忽略掉,以保证N为整数,此时N = (5 - 2 + 21 - 1)/ 2 + 1 = 3,接下来我们来看个简单的实例:

  • (1)首先使用torch中的随机函数生成一个batch_size为1,channel为1,高和宽都等于5的矩阵
  • (2)接着我们定义一个卷积核,input_size=1, output_size=1, kernel_size=2, stride=2,padding=1
  • (3)然后我们使用该卷积核对我们生成的随机矩阵进行卷积操作
  • (4)打印各参数的数值
import torch.nn as nn
import torch
 
 
im = torch.randn(1, 1, 5, 5)
c = nn.Conv2d(1, 1, kernel_size=2, stride=2, padding=1)
output = c(im)
 
print(im)
print(output)
print(list(c.parameters()))

通过计算我们知道输出矩阵尺寸应该为pytorch 3d卷积 pytorch 卷积函数_卷积_02

但实际的打印信息如下:

pytorch 3d卷积 pytorch 卷积函数_ide_03

通过分析,我们可以知道真正的输出矩阵尺寸是3x3,那内部具体是如何操作的呢,

(1)首先进行padding的填充,size:7 x 7

[0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
[0.0000,  0.3633,  1.1750, -0.1432, -0.9463,  1.0776,  0.0000],
[0.0000, -1.1750,  0.0361,  0.6624, -0.4544,  0.6980,  0.0000],
[0.0000, -0.8161, -1.2988, -0.0263,  1.7814,  0.4467,  0.0000],
[0.0000,  0.7315,  0.2855, -0.4346,  0.3127, -0.7801,  0.0000],
[0.0000, -0.7387,  1.4969, -1.1641, -0.1837, -0.3165,  0.0000],
[0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000]

(2)通过计算发现输出为非整数,为了得到整数,将最后一行以及最后一列删除掉,size:6 x 6

[0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
[0.0000,  0.3633,  1.1750, -0.1432, -0.9463,  1.0776],
[0.0000, -1.1750,  0.0361,  0.6624, -0.4544,  0.6980],
[0.0000, -0.8161, -1.2988, -0.0263,  1.7814,  0.4467],
[0.0000,  0.7315,  0.2855, -0.4346,  0.3127, -0.7801],
[0.0000, -0.7387,  1.4969, -1.1641, -0.1837, -0.3165]

(3)接着使用卷积核进行卷积操作,就能得到我们的输出矩阵,需要注意的是pytorch中的卷积默认是带有bias的,所以计算卷积后需要加上bias偏量。例如输出的第一个值的计算过程如下:\

[0.0000,  0.0000],        [-0.0114,  -0.3502],
                    卷积                        加上  [-0.1196]
[0.0000,  0.3633]         [-0.1244, 0.4637]
 
# 即
(0*(-0.0114)+ 0*(-0.3502)+ 0*(-0.1244)+(0.3633)*(0.4637))+(-0.1196)= 0.0489

我们的计算结果与pytorch的输出相同,我们只计算了其中一个值,其他的值也一样:

tensor([[[[ 0.0489, -0.3322,  0.4978],
          [-0.0865, -0.2026, -0.3733],
          [-0.7183, -0.6966,  0.0262]]]], grad_fn=<ThnnConv2DBackward>)

通过我们的实验可以发现,在pytorch的卷积过程中,当通过pytorch 3d卷积 pytorch 卷积函数_卷积计算式得到的输出尺寸非整数时,会通过删除多余的行和列来保证卷积的输出尺寸为整数。