在之前的博客中,已经在理论层面上介绍过转置卷积,但一直没有在代码中真正应用过,因为目前在图像分割领域中的上采样操作通常直接用双线性插值来做了。最近探索AutoEncoder
,在解码器中要用到转置卷积,涉及到了编码,发现pytorch的实际操作以及参数设置上并没有那么简单,因此写下本文记录一下。
关于什么是转置卷积,参照我上节给出的博客,这里就不过多叙述了。直接上pytorch实操:
conv = nn.Conv2d(3, 8, 3, stride=2, padding=1)
Dconv = nn.ConvTranspose2d(8, 3, 3, stride=2, padding=1)
x = torch.randn(1, 3, 5, 5)
feature = conv(x)
print(feature.shape)
# out : torch.Size([1, 8, 3, 3])
y = Dconv(feature)
print(y.shape)
# out : torch.Size([1, 3, 5, 5])
首先传入一个尺寸为[1,3,5,5]
的特征图[N, C, H, W]
,首先利用8 x kernel=3x3x3,stride=2, padding=1
的二维卷积核进行卷积运算,很容易计算得到输出特征图的尺寸为[1,8,3,3]
,尺寸缩小了两倍。
进行卷积后的特征图尺寸计算公式为(以H方向为例):
H ′ = ⌊ H − K h + 2 p s ⌋ + 1 H^{'} = \lfloor\frac{H - K_{h} + 2p}{s}\rfloor + 1 H′=⌊sH−Kh+2p⌋+1
其中,H为原始长度,Kh为卷积核长度,p为padding大小,s为步长
现在想通过转置卷积来执行上采样操作,其中nn.ConvTranspose2d
官方参数如下:
详见:https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose2d.html?highlight=convtranspose2d#torch.nn.ConvTranspose2d
其中根据官方的使用demo,我们大致能够理解如果进行操作,如之前的代码所示,除了in_channels
和out_channels
互换一下,其他参数都与Conv2d
设置一致。经过转置卷积运算得到尺寸为[1,3,5,5]
输出特征图,达到了恢复分辨率的效果。
那么,nn.ConvTranspose2d
这些参数到底有什么含义呢?以及如何计算输出尺寸的呢?
这里只介绍部分关键参数
① 首先in_channels
,out_channels
就是输入通道数以及期望的输出通道数,通常是与对应的下采样Conv
相反;
② kernel
, stride
,padding
通常与对应的下采样Conv
保持一致,下面讲一下如何根据这些参数计算输出特征图尺寸:
如果对转置卷积参数熟悉的话,首先我们在特征图中间插值0
,每两个元素之间插stride-1
个0
值;然后真实的padding
与输入的会有所区别,真正做的padding
为kernel-padding-1
,真实的卷积核kernel
与输入参数一致,真实的stride
不管输入参数是什么,永远为1:
因此,此刻的特征图尺寸为(以H方向为例
):
H ′ = ( H + ( s t r i d e − 1 ) ∗ ( H − 1 ) − k h + 2 ∗ ( k h − p a d d i n g − 1 ) / 1 + 1 H^{'} = (H+(stride-1)*(H-1) - k_h + 2*(k_h - padding - 1)/1 + 1 H′=(H+(stride−1)∗(H−1)−kh+2∗(kh−padding−1)/1+1。
③ output_padding
参数?
通常我们将参数按demo的设置就已经可以得到与原图分辨率一致的上采样结果了,但有些情况下,不同分辨率的输入经过卷积能够得到同一分辨率的输出。比如,上一节给出的实例代码,我们输入6x6
的卷积也能得到3x3
尺寸的特征输出。
conv = nn.Conv2d(3, 8, 3, stride=2, padding=1)
Dconv = nn.ConvTranspose2d(8, 3, 3, stride=2, padding=1)
x = torch.randn(1, 3, 6, 6)
feature = conv(x)
print(feature.shape)
# out : torch.Size([1, 8, 3, 3])
y = Dconv(feature)
print(y.shape)
# out : torch.Size([1, 3, 5, 5])
但在利用转置卷积进行恢复时,只得到了5x5
的特征,这个时候output_padding
参数就起作用了,先看官方的解释:
However, when stride > 1
, Conv2d maps multiple input shapes to the same output shape. output_padding
is provided to resolve this ambiguity by effectively increasing the calculated output shape on one side. Note that output_padding
is only used to find output shape, but does not actually add zero-padding to output.
已经很清楚了,该参数就是用于调整输出分辨率的大小的,注意:不是再输出上插值喔!比如按照计算规则,我们只能得到5x5
的输出,要得到6x6
的输出就需要将output_padding
设为1。
conv = nn.Conv2d(3, 8, 3, stride=2, padding=1)
Dconv = nn.ConvTranspose2d(8, 3, 3, stride=2, padding=1, output_padding=1)
x = torch.randn(1, 3, 6, 6)
feature = conv(x)
y = Dconv(feature)
print(y.shape)
# out : [1,3,6,6]
[1] 官方文档
https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose2d.html?highlight=convtranspose2d#torch.nn.ConvTranspose2d
[2] ConvTranspose2d原理,深度网络如何进行上采样?
https://blog.csdn.net/qq_27261889/article/details/86304061