论文:A guide to convolution arithmetic for deep learning
转置卷积(Transposed Convolution)也叫Fractionally-strided Convolution和Deconvolution,但用的最多的是Transposed Convolution。
Deconvolution这个名称是不建议使用的,因为该名称具有一定的误导性(和ZFNet的Deconvolution有歧义)。
主要起到上采样(upsampling)的作用。
对于这个转置卷积,输入是一个2×2的特征图,但是会在它的四周填充一些0元素,卷积核大小也是3×3,输出是一个4×4的特征图 -> 实现了特征图的上采样。
其中, s s s为步长, k k k为kernel size, p p p为padding
O i c o n v / p o o l = O i i n + 2 p i − k i s i + 1 O_i^{\mathrm{conv/pool}} = \frac{O_i^{\mathrm{in}} + 2p_i - k_i}{s_i} + 1 Oiconv/pool=siOiin+2pi−ki+1
O i d i l a t e d c o n v = O i i n + 2 p i − d i × ( k i − 1 ) s i + 1 O_i^{\mathrm{dilated \ conv}} = \frac{O_i^{\mathrm{in}} + 2p_i - d_i \times (k_i-1)}{s_i} + 1 Oidilated conv=siOiin+2pi−di×(ki−1)+1
O i t r a n s c o n v = ( O i i n − 1 ) × s i − 2 × p i + k i O_i^{\mathrm{trans \ conv}} = (O_i^{\mathrm{in}} - 1) \times s_i - 2 \times p_i + k_i Oitrans conv=(Oiin−1)×si−2×pi+ki
O i t r a n s c o n v = ( O i i n − 1 ) × s i − 2 × p i + d i × ( k i − 1 ) + output_padding i + 1 O_i^{\mathrm{trans \ conv}} = (O_i^{\mathrm{in}} - 1) \times s_i - 2 \times p_i + d_i \times (k_i - 1) + \text{output\_padding}_i + 1 Oitrans conv=(Oiin−1)×si−2×pi+di×(ki−1)+output_paddingi+1
通过在PyTorch官方的文档中可以看到,PyTorch内部集成了三种转置卷积:
torch.nn.ConvTranspose1d (Python class, in ConvTranspose1d)
torch.nn.ConvTranspose2d (Python class, in ConvTranspose2d)
torch.nn.ConvTranspose3d (Python class, in ConvTranspose3d)
以torch.nn.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)
在由多个输入平面组成的输入图像上应用 2D 转置卷积算子。
这个模块可以看作是 Conv2d 相对于其输入的梯度。 它也被称为分数步长卷积或反卷积(尽管它不是实际的反卷积操作,因为它不计算真正的卷积逆)。
该模块支持 TensorFloat32。
参数说明:
stride
controls the stride for the cross-correlation.padding
controls the amount of implicit zero padding on both sides for dilation × (kernel_size - 1) - padding number of points. See note below for details.output_padding
controls the additional size added to one side of the output shape. See note below for details. 默认为0dilation
controls the spacing between the kernel points; also known as the à trous algorithm. It is harder to describe, but the link here has a nice visualization of what dilation does.groups
controls the connections between inputs and outputs. in_channels and out_channels must both be divisible by groups
.groups
整除。in_channels
, each input channel is convolved with its own set of filters (of size out_channels in_channels \frac{\text{out\_channels}}{\text{in\_channels}} in_channelsout_channels ). —— 深度卷积bias
如果为True则卷积操作会有一个可学习的偏置,默认为True但在代码中真的是这样计算的吗? —— 并不是,因为这样计算效率低!更加高效的卷积操作如下:
注意:对于现在版本较新的框架如TensorFlow、PyTorch而言,这种计算方法也不再采用了,有更加高效的方法代替。
Q:已知矩阵 C C C和矩阵 O O O,是否可以求出矩阵 I I I?换句话说:卷积是否可逆?
将矩阵两边的右侧同时乘以矩阵 C C C的逆矩阵 C − 1 ∈ R 4 × 16 C^{-1}\in \mathbb{R}^{4\times 16} C−1∈R4×16,那不就可以还原得到矩阵 I I I了吗?
—— 不可以!
A:需要注意的是,一个矩阵存在逆矩阵的条件是:它必须是一个方阵(非方阵没有逆矩阵,只有广义逆矩阵)。而这里的矩阵 C ∈ R 16 × 4 C\in \mathbb{R}^{16 \times 4} C∈R16×4,并不是一个方阵,所以它不存在逆矩阵 -> 无法还原矩阵 I I I。
一般情况下,深度学习中的卷积是不可逆的。
Q:不要求还原原始输入矩阵 I I I,只想得到与输入矩阵相同大小的矩阵 P ∈ R 1 × 16 P\in \mathbb{R}^{1\times 16} P∈R1×16,可以吗?
A:可以。只需要在等号两边的右侧同时乘上矩阵 C C C的转置 C T ∈ R 4 × 16 C^T\in \mathbb{R}^{4\times 16} CT∈R4×16就可以了!运算如下:
O 1 × 4 ⊗ C T = P 1 × 16 O^{1\times 4} \otimes C^T = P^{1\times 16} O1×4⊗CT=P1×16
很明显,矩阵 I I I和矩阵 P P P是不相等的。
对矩阵 P P P进行reshape处理就得到与输入特征图shape相同的特征图。
以上就是转置卷积的运算过程,即通过一个1×4的输入特征图(其实是一个2×2的特征图),得到一个4×4的输出特征图,完成了特征图的上采样。
这也就是为什么转置卷积会被成为Deconvolution,并非空穴来风。
上面的操作是将输入和输出都展平成了一个向量,得到 I I I和 O O O。对于输入特征图的等效矩阵,将每一个等效矩阵也都展平了成一个列向量,并将它们拼接在一起,得到矩阵 C C C。
接下来进行逆向操作:
接下来再拿 C T C^T CT等效矩阵的第一个矩阵与输入特征图 I I I进行 ⊙ \odot ⊙和 ∑ \sum ∑,二者的结果为2,就是刚才我们求的伪·逆卷积的结果 P P P中的第二个值
看右下角,对于输入特征图 I I I先进行最外层的填充0,如果我们拿如图绿色的矩阵与其进行 ⊙ \odot ⊙和 ∑ \sum ∑,二者的结果也为2
依次使用每一个等效矩阵与输入特征图 I I I进行运算
我们发现,我们可以通过绿色矩阵在填充过后的输入特征图 I I I上进行卷积,很轻松地求出伪·逆卷积的结果 P P P中的每一个值。
问题来了:绿色的矩阵怎么得到呢?
我们看一下绿色的卷积和最开始的普通卷积的卷积核有什么区别和联系:
有了绿色卷积核及其数值后,我们在求输入矩阵的伪·逆矩阵的时候就不用那么麻烦了:
直接让经过填充的输入特征图 I I I与上下左右翻转的卷积核进行卷积就可以得到了。
即实现了非常轻松的上采样!因为转置卷积后的矩阵就是输入矩阵的伪·逆矩阵!虽然信息有所丢失,但仍然有一定的联系。
这也就是为什么转置卷积运算的第四步,stride = 1,padding = 0。