大家好,又见面了,我是你们的朋友全栈君。
首先说下pytorch中的Tensor通道排列顺序是:[batch, channel, height, width]
我们常用的卷积(Conv2d)在pytorch中对应的函数是:
代码语言:javascript复制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) 代表在上方补两行下方补两行,左边补一列,右边补一列。可见下图,padding[0]是在H高度方向两侧填充的,padding[1]是在W宽度方向两侧填充的:
注意:如果要实现更灵活的padding方式,可使用nn.ZeroPad2d方法。
bias参数表示是否使用偏置(默认使用)
dilation、groups是高阶用法这里不做讲解,如有需要可以参看官方文档
在卷积操作过程中,我们知道矩阵经卷积操作后的尺寸由以下几个因数决定:
- 输入图片大小 W×W
- Filter大小 F×F
- 步长 S
- padding的像素数 P
经卷积后的矩阵尺寸大小计算公式为:
N = (W − F 2P ) / S 1
但在实际应用中,有时会出现N为非整数的情况(例如在alexnet,googlenet网络的第一层输出),再例如输入的矩阵 H=W=5,卷积核的F=2,S=2,Padding=1。经计算我们得到的N =(5 – 2 2*1)/ 2 1 = 3.5 此时在Pytorch中是如何处理呢,先直接告诉你结论:在卷积过程中会直接将最后一行以及最后一列给忽略掉,以保证N为整数,此时N = (5 – 2 2*1 – 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)打印各参数的数值
代码语言:javascript复制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()))
通过计算我们知道输出矩阵尺寸应该为N =(5 – 2 2*1)/ 2 1 = 3.5,
但实际的打印信息如下:
代码语言:javascript复制# im
tensor([[[[-0.2146, 0.3375, 2.7877, 0.2052, -0.4651],
[-0.2261, 0.0116, -0.6255, 1.2523, -1.0565],
[-1.9227, -0.2575, -0.7725, 0.5658, 0.0717],
[ 0.8153, -1.3656, -0.1844, 0.1573, -0.2235],
[ 0.0184, -0.0475, 0.2359, 0.0127, 2.0665]]]])
# output
tensor([[[[-0.0467, -1.1766, -0.0450],
[ 0.5063, 0.1971, -1.0401],
[-0.0748, 0.4769, -0.8986]]]], grad_fn=<ThnnConv2DBackward>)
# conv2d:parameters
[Parameter containing:
tensor([[[[-0.4872, 0.0604],
[-0.3968, -0.3317]]]], requires_grad=True), Parameter containing:
tensor([-0.1179], requires_grad=True)]
通过分析,我们可以知道真正的输出矩阵尺寸是3×3,那内部具体是如何操作的呢,
(1)首先进行padding的填充,size:7 x 7
代码语言:javascript复制[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[0.0000, -0.2146, 0.3375, 2.7877, 0.2052, -0.4651, 0.0000],
[0.0000, -0.2261, 0.0116, -0.6255, 1.2523, -1.0565, 0.0000],
[0.0000, -1.9227, -0.2575, -0.7725, 0.5658, 0.0717, 0.0000],
[0.0000, 0.8153, -1.3656, -0.1844, 0.1573, -0.2235, 0.0000],
[0.0000, 0.0184, -0.0475, 0.2359, 0.0127, 2.0665, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]
(2)通过计算发现输出为非整数,为了得到整数,将最后一行以及最后一列删除掉,size:6 x 6
代码语言:javascript复制[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[0.0000, -0.2146, 0.3375, 2.7877, 0.2052, -0.4651],
[0.0000, -0.2261, 0.0116, -0.6255, 1.2523, -1.0565],
[0.0000, -1.9227, -0.2575, -0.7725, 0.5658, 0.0717],
[0.0000, 0.8153, -1.3656, -0.1844, 0.1573, -0.2235],
[0.0000, 0.0184, -0.0475, 0.2359, 0.0127, 2.0665]
(3)接着使用卷积核进行卷积操作,就能得到我们的输出矩阵,需要注意的是pytorch中的卷积默认是带有bias的,所以计算卷积后需要加上bias偏量。例如输出的第一个值的计算过程如下:
代码语言:javascript复制[0.0000, 0.0000], [-0.4872, 0.0604],
卷积 加上 [-0.1179]
[0.0000, -0.2146] [-0.3968, -0.3317]
# 即
(0*(-0.4872) 0*(0.0604) 0*(-0.3968) (-0.2146)*(-0.3317)) (-0.1179)= -0.0467
我们的计算结果与pytorch的输出相同,我们只计算了其中一个值,其他的值也一样:
代码语言:javascript复制# output
tensor([[[[-0.0467, -1.1766, -0.0450],
[ 0.5063, 0.1971, -1.0401],
[-0.0748, 0.4769, -0.8986]]]], grad_fn=<ThnnConv2DBackward>)
通过我们的实验可以发现,在pytorch的卷积过程中,当通过N = (W − F 2P ) / S 1计算式得到的输出尺寸非整数时,会通过删除多余的行和列来保证卷积的输出尺寸为整数。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/140101.html原文链接:https://javaforall.cn