A guide to convolution arithmetic for deep learning.(Vincent Dumoulin, Francesco Visin).[https://arxiv.org/abs/1603.07285]
中文:https://zhuanlan.zhihu.com/p/48501100
转置卷积就是用卷积矩阵的转置矩阵进行卷积。
转置卷积可以将与卷积输出形状相同的矩阵变换为与卷积输入相同的矩阵,即完成形状的反向运算,因此也被称为反卷积(Deconvolution)。但是仅仅能恢复形状,不能恢复数据,因此一般不建议叫做反卷积。
卷积常常实现图像size的减少,例如/2,/4等,而反卷积可以实现图像size的增加,例如/0.5,/0.25,因此也被称为分数卷积(Fractionally-strided Convolution)。
由于上述性质,转置卷积常常用于实现上采样功能,也用于反向传播的误差梯度回传。
为了说清楚转置卷积,先看一下卷积。
图示
s=1,p=0为例。
为了少敲点字符,我们把输入和kernel改小点,如下:
卷积可表示为:
将二维输入展开成一维向量(im2col操作),写成矩阵形式:
可见,二维卷积可以看作权重窗在一维向量上滑窗。也可以看作是系数有0的全连接。
定义权重矩阵如下:
卷积运算可以写做:
注意这个式子是不可以逆运算的。也就是说:
,不成立
已知和,是无法求出的,因为4个方程,解不出9个未知数。这也是导致转置卷积不适合称作反卷积或逆卷积的原因。
那么什么情况下可以求出呢?再举例说明
如果恢复出,则等式左边需要有:
则有:
此时,C是正交阵,也即是n阶方阵。则输入与输出维度相同。需要使用1×1conv或pad=same。
即做了不同坐标系的等长变换。
一般都不满足正交,另外也不清楚正交卷积的意义。留待讨论。
右边第一项表示是可以步进几个位置,第二项表示原始的1个位置。
示例如下:
从上图本身是看不出是卷积还是转置卷积的,这两个运算的示意图是一样的。只能说图示有上采样作用,可能是转置卷积。
如上图所示,
1)当作卷积看时,则i=2,k=3,s=1,p=2,o=4。
2)当作转置卷积时,则是i'=2,k=3,s‘=1,p'=2,则对应的直接卷积的p=k-1-p'=0, o'=4,即把转置卷积看作是原来的输入4,输出2的卷积的逆向运算。
转置卷积(transpose convolution)和它的直接卷积(direct convolution)的关系是:
i'=o
o'=i
k'=k
s'=1,总是1
p'=k-1-p,也即p=k-1-p'
以下从不同方面理解转置卷积和推导公式。
这是推导最快的方式。以下是对于可以整除的情况。
卷积公式是:
则转置卷积公式是:
即把卷积的输入输出互换就可以了。
如下图。
直接卷积的参数定义为:i=5,k=3,s=2,p=0,o=2
转置卷积的参数定义为:i'=2,k=3,s'=1,p'=2,o=5
习惯上把输出整理成关于的形式,p',s'只是辅助理解用,在编程过程中是不体现的。
仍然按照卷积公式去计算,则:
输入:,s表示每行插零扩展成s行,表示左起共有几行可以扩展,+1表示最后不能插0的那行
padding:,p'定义没有变,仍然是单侧边界补零的个数。
所以输出是:
关于p'的含义
p'不是设置的,是根据直接卷积的参数k,p算出来的。而且,p'是负数时仍然是有意义的。
p'记录了转置输入(蓝色)的边界与原图(绿色)的有效边界(未padding前的边界)的位置关系。如下所示。
左图: 右图:
原图 i = 4, k = 3, s = 1 and p = 0
转置i' = 2, k' = k, s' = 1 and p' = 2
左图:原图的最大有效边界,就是比转置输入外扩k-1。这是p=0的情况。
右图:当原图有padding时,例如p=1,则说明原图的有效边界在k-1基础上内移p,即内移到了转置输入边界的k-1-1位置,即p'=k-1-p。如果边界仍然按照k-1计算,则转置输出的边界对应到了原图的padding位置,这个位置是不需要计算的,右图的红色线。
p逐渐增大,有效边界继续内移。甚至当p=100,内移100,此时p'成了负值。表示转置输入边沿都是原图pad的0的卷积结果。
需要注意的是,p'是在转置输入图上移动。
其他padding举例:
为了少些字符,同样将输入和kernel写小示意,如下:
代数表示
这里不用im2col展开了,那样很低效。直接使用转置矩阵就可以了。
计算如下:
转置权重矩阵
转置矩阵如下:
由于是卷积运算,所以要先旋转180度再相乘。
公式
满足整除关系时:
通用公式:
比如上采样32倍,一般使用设置kernel_size和stride都是32就可以了:
nn.ConvTranspose2d(in_channels, out_channels, kernel_size=32, stride=32)
此时输出是:
o=s(i-1)+k=32(i-1)+32=32*i
因此正好是32倍。
这个kernel_size没有必要和原图对应,因为有时也不一定真的是在下采样后进行上采样,而是只想做个上采样而已,因此也没有原图这个说法。
上采样32倍,也可以设置kernel_size=64,stride=32:
nn.ConvTranspose2d(in_channels, out_channels, kernel_size=64, stride=32)
此时输出是:
o=s(i-1)+k=32(i-1)+64=32*i+32
此时上采样32倍后多了32,因此需要左右各crop掉16。
根据p'=k-1-p,增大k等效于增大了p',因此输出多了补的零,crop是可解释的。
class torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True)
nn.Conv2d的功能是:对由多个输入平面组成的输入信号进行二维卷积。输入信号的形式为 ,
(1)参数说明:
N:表示batch size(批处理参数)
:表示channel个数
H,W:分别表示特征图的高和宽。
stride(步长):步长,默认为1,可以设为1个int型数或者一个(int, int)型的tuple。
kernel_size:卷积核的宽度和长度,单个整数或由两个整数构成的list/tuple。如为单个整数,则表示在各个空间维度的相同长度。
padding(补0):控制zero-padding的数目,padding是在卷积之前补0。
dilation(扩张):控制kernel点(卷积核点)的间距; 可以在此github地址查看:Dilated convolution animations
groups(卷积核个数):通常来说,卷积个数唯一,但是对某些情况,可以设置范围在1 —— in_channels中数目的卷积核:
(2)图像尺寸:
经过一次卷积之后,生成的图的大小:(original_size - (kernal_size - 1)) / stride
nn.ConvTranspose2d的功能是进行反卷积操作
(1)输入格式:
nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1)
(2)参数的含义:
in_channels(int) – 输入信号的通道数
out_channels(int) – 卷积产生的通道数
kerner_size(int or tuple) - 卷积核的大小
stride(int or tuple,optional) - 卷积步长,即要将输入扩大的倍数。
padding(int or tuple, optional) - 直接卷积的输入的每一条边补充0的层数,高宽都增加2*padding
output_padding(int or tuple, optional) - 输出边补充0的层数,高宽都增加padding
groups(int, optional) – 从输入通道到输出通道的阻塞连接数
bias(bool, optional) - 如果bias=True,添加偏置
dilation(int or tuple, optional) – 卷积核元素之间的间距