要了解转置卷积,先要了解 卷积 的矩阵运算形式,因为转置是矩阵转置的意思。通常我们对卷积的认识就是按照卷积的定义来的,输入信号和卷积核依次匹配过去。如果是 4x4 的输入,卷积 Kernel 为 3x3, 没有 Padding / Stride, 则输出为 2x2,如下图所示。
但是,事实上,按照卷积的定义来实现卷积运算的话是非常低效的。在神经网络中,其实是用矩阵运算的形式实现的。那么卷积可以用矩阵运算的形式来实现吗?当然可以。卷积是线性运算,线性运算都可以写作矩阵乘法的形式。具体来看,
- 输入信号 4 x 4 的矩阵可展开为 16 维向量,记作
$x$ - 输出矩阵可展开为 4 维向量,记作
$y$ - 卷积运算可以表示为
$y = Cx$ -
$C$ 是 3 x 3 的卷积核转化来的稀疏矩阵,大小为 16 x 4,即输入信号长度 x 输出信号长度。输出信号的每一个数值,都是输入信号 与 同输入信号相同大小的卷积权重矩阵 点乘后的结果。卷积权重矩阵大小不是 3 x 3 的卷积核大小,而是同输入信号大小相同。不同的输出信号上的点,只是权重在 卷积权重矩阵 上平移。这里平移的顺序是先从左到右,到最右后再换下一行(先从左向右,再从上到下)。$C$ 具体的元素如下所示。可以看到,这个矩阵其实是以第一行为标准,然后依次向右循环移位,这个矩阵叫作 Toeplitz matrix。注意,输出信号并不是 column-first 拉成的向量,而是 row-first 拉成的向量。 - 上面的 case 是没有 padding,stride = 1 的情形,先来看有 padding 会怎么样?padding 的话,相当于改变输入信号,如果 padding = 2,那么 4 x 4 的输入信号就变成 8 x 8 的矩阵,填充的都是 0。相应的,卷积权重矩阵也要变成 8 x 8 = 81,而真正非 0 的权重系数还是只有 3 x 3 = 9,因此 padding 会让 卷积权重矩阵 变得更大,也更加 sparse。
- Stride = 2 会怎么样?卷积权重矩阵这个 Toeplitz matrix 中的行向量平移不再是逐个平移了,对于从左向右的平移,变成一次平移两个;如果换行从上到下,则要多平移一行。
至此,我们来小结一下,卷积核 c
,卷积运算 y = x * c
可以写作 y = C x
的矩阵形式,这里 C
的具体取值由 c
给定。
- 输入转为列矩阵
- weight 通过 toeplitz 规律转化为矩阵
- 相乘后的列矩阵再排回结果;
虽然很多时候 Transposed Convolution 也会被叫作 反卷积,但是其实这么叫是错误的。真正的反卷积的意思是,给定卷积的输出信号,还原出未知的输入信号。这里“反”卷积的反的意思并不在意还原输入信号,“反”的意思在于正常卷积 output 的 size 小于等于 input 的 size,而转置卷积(“反”卷积)通常是 output 的 size 大于等于 input 的 size,这个反就是尺寸大小变化相反的意思。事实上,转置卷积只能还原 shape 大小,不能还原 value.
重新写完后再来理解“反”卷积,我感觉这个反还可以理解成 输入信号 和 卷积核 交换,正常是 卷积核 移动分别点乘输入信号;反卷积可以是 卷积核不动,输入信号变成新的卷积核来移动,输入信号 和 卷积核 的角色刚好相反,对调了一下。
“反”卷积这个错误用法的来源:第一篇用 deconv 表示 transposed convolution 的是 ZF 的 Visualizing and Understanding Convolutional Neural Networks,论文提出了 ZFNet,而且是 ImageNet 2013 年的 winner,比较有影响力,之后大家就错误的用下去了 from https://www.zhihu.com/question/43609045/answer/135914409
Convolution arithmetic tutorial 这里有很详细的大小关系叙述。
那么通过卷积,怎么可以让小尺寸的 input 怎么变成大尺寸的 output?先看一下下面两幅图像的例子。 看下面 Transposed convolution 的动画,和上面 Convolution 的动画其实没有什么区别,都是 灰色的卷积核在移动,蓝色以及虚线的pad为输入,依次点积后就得到绿色的输出。两幅 GIF 唯一的差别就是正常 Convolution 是输入比较大,输出比较小,而 Transposed convolution 是输入比较小,输出比较大,怎么实现这种由小变大呢?
![]() |
![]() |
---|---|
No padding, no strides, transposed | No padding, strides, transposed |
我们从普通卷积的角度来看,由上图可见,可以通过 Padding 和对 input 插 0 来实现。
以前面那副画为例,那就是从 2 x 2 的输入 Pad 成 6 x 6 的矩阵,那相应的 3 x 3 的卷积核变成 16 x 36 的卷积权重矩阵。
- 输入信号 2 x 2 => 6 x 6
- 3 x 3 的卷积核 => 16 x 36 卷积权重矩阵
- 输出信号 4 x 4 => 16 x 1: 16 x 1 = (16 x 36) x (36 x 1)
以后面那副画为例,那就是从 2 x 2 的输入通过 Pad 和 插 0 变成 7 x 7 的矩阵,那相应的 3 x 3 的卷积核变成 25 x 49 的卷积权重矩阵,这种方式实现的计算效率太低了,因为需要 pad 太多 0 了。
- 输入信号 2 x 2 => 7 x 7
- 3 x 3 的卷积核 => 25 x 49 卷积权重矩阵
- 输出信号 5 x 5 => 25 x 1: 25 x 1 = (25 x 49) x (49 x 1)
下图,我们把左图卷积出来的前 3 个输出 x1, x2, x3 是怎么得到的单独画出来,如下图所示。
那有上述图片过程的更简洁的描述方式吗?有,就是
要变大多一些,主要靠插 0(也就是 stride)。
那这种卷积 也可以用 矩阵乘法形式实现吗?
当然可以用矩阵乘法形式实现,形式恰恰是 x = C^T y
,可以看一下下面 D.T
的前几行是不是刚好对应着上面图像里的权重。因此,转置卷积这个名字,更确切的说法应该是卷积核对应矩阵的转置,所以转置 transposed 这个词应该是这么来的。但事实上,D
是可以学习的,不一定一定就是卷积的转置。y = Cx
,C 为 4 x 16,希望有一个新的矩阵 D,形状为 16 x 4,使得 x = Dy
,事实上 D 只是和 C 转置的形状大小一样,并不是说 D 就是 C 的转置本身。除非 C 是正交矩阵,才可能 x = C^T y = C^T C x
,但 C 不可能是正交矩阵,所以 D 肯定不是 C^T
,只是两者形状一样而已。
>>> C
Matrix([
[w_00, w_01, w_02, 0, w_10, w_11, w_12, 0, w_20, w_21, w_22, 0, 0, 0, 0, 0],
[ 0, w_00, w_01, w_02, 0, w_10, w_11, w_12, 0, w_20, w_21, w_22, 0, 0, 0, 0],
[ 0, 0, 0, 0, w_00, w_01, w_02, 0, w_10, w_11, w_12, 0, w_20, w_21, w_22, 0],
[ 0, 0, 0, 0, 0, w_00, w_01, w_02, 0, w_10, w_11, w_12, 0, w_20, w_21, w_22]])
>>> C.T
Matrix([
[w_00, 0, 0, 0],
[w_01, w_00, 0, 0],
[w_02, w_01, 0, 0],
[ 0, w_02, 0, 0],
[w_10, 0, w_00, 0],
[w_11, w_10, w_01, w_00],
[w_12, w_11, w_02, w_01],
[ 0, w_12, 0, w_02],
[w_20, 0, w_10, 0],
[w_21, w_20, w_11, w_10],
[w_22, w_21, w_12, w_11],
[ 0, w_22, 0, w_12],
[ 0, 0, w_20, 0],
[ 0, 0, w_21, w_20],
[ 0, 0, w_22, w_21],
[ 0, 0, 0, w_22]])
因此,不管是从卷积的角度还是矩阵乘法的角度,转置卷积 和 正常卷积 基本没有区别,都是要设置 size,stride,channel,padding 这些,唯一的区别是正常卷积output 的 size 小于等于 input 的 size,而 转置卷积 通常是 output 的 size 大于等于 input 的 size。Transposed Convolution 就是 Convolution 而已,之所以叫 Transposed,就是形状是转置而已。
对于正向卷积,给定输入信号大小 I,卷积核大小为 K,Pad 大小为 P,Stride 大小为 S,则输出信号的大小 O = Ceil([(I + 2P) - (K-1)] / S),发现一个更好的公式是
如果正常卷积的 Stride S = 1, Pad P = 0, 卷积核大小为 K,那么转置卷积的卷积核大小 K' = K, S' = S, P' = K - 1,如果转置卷积的输入大小是 I',那么转置卷积的输出的大小 O' = I' + 2(K - 1) - (K' - 1)
- 如果
$(O' + 2P' - K') % S' = 0$ ,则$O' = S' (I' - 1) - 2P' + K'$ - 如果
$(O' + 2P' - K') % S' \neq 0$ ,则$O' = S' (I' - 1) - 2P' + K' + (O' + 2P' - K') % S'$ 通用公式:
公式关系为正向卷积 s=1,p=0 那么转置卷积的 k'=k,s'=s,p'=k-1, 转置卷积的输出尺寸
转置卷积 和 UpSampling 都可以把小的 Input 变成大的 Output,那么两者有什么区别呢?哪个更好呢?
- UpSampling 可以看作是 Pooling 的反向操作,就是采用 Nearest Neighbor interpolation 来进行放大、Resize,说白了就是复制行和列的数据来扩充 feature map 的大小,并不通过学习。
- 转置卷积(反卷积)就是卷积,想要把小的 Input 变成大的 Output,做法是通过对输入隔行补 0、pading 等方式,实现输出尺寸增大。转置卷积里面的 filter 都是可以学出来的,本质上是一种 Learnable Upsampling。
也有说法把 UpSampling 囊括为 Resize 和 转置卷积 这两类,这样的话 UpSampling 就是一个更大的概念了。
需要注意的是,FCN 中并没有使用转置卷积,FCN 是用的 UpSampling,具体的说是 bilinear interpolation,这也是造成 FCN 不能很好的处理边缘等细节的主要原因。将转置卷积用于图像分割,应该是出自这篇论文:Learning Deconvolution Network for Semantic Segmentation。
其实 resize feature map 到更大的尺寸,然后卷积,以 padding='SAME' 的模式,不比 deconvolution 差
从下图可以看出,转置卷积与 Dilated Conv 都是带洞卷积,只不过 转置卷积 是输入信号带洞,而 Dilated Conv 是卷积核带洞。
![]() |
![]() |
---|---|
No padding, strides, transposed | No padding, no stride, dilation |
convolutional sparse coding 组成的网络也可能叫 deconvolutional network,看文章的时候要搞清楚。一个是用来做 visualizing,一个是用来做 upsampling。