PyTorch 中的转置卷积 ConvTranspose2d
现有的关于转置卷积的介绍大多流于表面,并未详细的说明这一操作内部具体的操作流程。由于转置卷积的设计主要是为了对标标准卷积,所以其实现流程与标准卷积基本相反,所以内部的操作逻辑并不直观。其按照卷积的相反逻辑的参数设置方式,这种反逻辑的形式使得我们很难直接从参数的角度去理解。
torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros', device=None, dtype=None)
This module can be seen as the gradient of Conv2d with respect to its input. It is also known as a fractionally-strided convolution or a deconvolution (although it is not an actual deconvolution operation as it does not compute a true inverse of convolution). For more information, see the visualizations here and the Deconvolutional Networks paper.
这里面涉及到了多个参数,包括 in_channels, out_channels, kernel_size, groups=1, bias=True, dilation=1, padding_mode='zeros', device=None, dtype=None
这样的一看就可以理解对的参数,也有一些实际情况和我们想的并不一致的参数 stride=1, padding=0, output_padding=0
。
首先要明确的一点,由于转置卷积可以看做是标准卷积的相反流程(虽然细节处理不同,并非真正的转置),考虑到标准卷积的直观性,所以我们可以从对应的标准卷积的角度去理解转置卷积的参数含义。即反着来看参数的作用。对于相同的参数设置,转置卷积输出和输出的形状,一般与标准卷积的输入和输出一致。
对于 2d 形式,我们分别设置 kernel_size, stride, padding, output_padding
为 :
- 输入形状为 或者为
- 输出形状为 或者为
- 卷积权重形状为
- 卷积偏置形状为
转置卷积的过程
总体而言,结合对应的标准卷积,转置卷积的计算可以拆分为两部分:调整形状和局部聚合。这里的介绍可以结合 A guide to convolution arithmetic for deep learning 中提供的图示进行理解。
调整形状
实际上,转置卷积最难理解的还是这一步。
对输入使用 stride 处理,注意,转置卷积的 stride 并不同于标准卷积,而是在各个输入元素之间插入
而且另外一点需要注意,为了确保对卷积输入输出运算过程形状的对应性,所以 在卷积核滑动之前,须要在输入的四边上进行 padding,这里默认 padding 的值与卷积核的形状有关,即四边的 padding 为 。
这里需要强调的是,不论如何,转置卷积的本质还是卷积,仍然是对输入的局部聚合。所以如果不考虑 padding 和插 0 的情况,输出必然要比输入的尺寸要小。
所以当
- :这是转置卷积的默认情况,此时转置卷积会对输入进行隐式的 padding,如前所述为 。
- :相当于是标准卷积 的时候。在其他参数不变的时候,标准卷积的输出会相对扩大,对应回来,也就是转置卷积的输出应该相对缩小。于是我们可以看到, 实际上起到一个反向调整的作用,即在默认的隐式 padding 上减去转置卷积设置中的 。因而实际的隐式 padding 会变为 。
[!important] 的取值范围
结合上面的推理,进一步可以推测出转置卷积中对
- 首先必须大于等于 0,所以左边界为 0。
- 对于右侧边界,必须要保证一点,即经过默认 padding 调整后的输入,在使用 剪裁后剩下的必须大于等于 ,也就是卷积操作必须有效。所以可以推算出,最大值为 和 。
[!questions] 操作顺序
此处可能有一个问题,究竟是“先将隐式 padding 使用 处理后再卷积?”还是“先基于默认隐式 padding 卷积后再使用
这里有一个额外的参数 也非常重要。在原始文档中提到,这一参数主要的用处是为了保证在 。
对于标准卷积而言,如果 ,则同一种输出形状可以存在多种输入相形状。所以转置卷积通过用户指定的参数来消除这种不确定性,从而明确输出形状的具体尺寸。如果输出形状不合适,可以使用这一参数来进行单边的补齐。注意,这一参数仅是作用于单边,对于 2D 情况,在 H 和 W 轴的末端上补 0。
另外,虽然文档提到“Note that output_padding is only used to find output shape, but does not actually add zero-padding to output.”,但是从实际效果来看,就是 0 的补齐,文档这一句话应该是在强调内部实现并非直接补 0。
局部聚合
使用卷积核在插 0 后的输入上滑动从而获得初步的输出。要注意,这里的 stride 参数并不会影响转置卷积本身卷积核的滑动,可以认为转置卷积核步长始终为 1。
使用标准卷积实现转置卷积
如果单纯使用框架自带的卷积函数,标准卷积只能实现 的转置卷积。而且在使用相同的卷积参数的时候,需要注意的是卷积权重的索引顺序。从 PyTorch 中的转置卷积详解——全网最细 中我们可以知道,如果使用相同的卷积权重,标准卷积与转置卷积的权重索引方式不同,需要进行 .flip(dim=*)
来调整。
典型案例为:
[!important] 标准卷积与转置卷积共用权重需要注意的地方
PyTorch 中标准卷积与转置卷积的权重形状中,输入输出维度恰好相反,所以相同的权重需要进行额外交换轴的操作,即上面代码中 .transpose(0, 1)
处理。
参考链接
- PyTorch 中的转置卷积详解——全网最细
- 官方文档
- A guide to convolution arithmetic for deep learning