大家好,又见面了,我是你们的朋友全栈君。
本文是论文《A guide to convolution arithmetic for deep learning》的阅读笔记及思考,对于文章中浅尝则止的部分进行了深入的分析,如步长大于 1 的卷积与池化之间是否存在一种等价的关系,用矩阵的形式表示CNN的前向传播和反向传播。
以 CNN 为代表的卷积神经网络在图像的相关领域得到了较为长足的发展。在 CNN 中卷积实际分类两大类,一种是卷积,另一种是转置卷积(transposed convolutional ),或者称为分数步长卷积( fractionally strided convolutional layers),亦或者是反卷积(deconvolution)。
虽然在一些文章中将反卷积与转置卷积认为是等价的,但是 [1] 中的作者表示他们反对使用反卷积来表示转置卷积,他们的依据是在数学中反卷积被定义为卷积的逆,这实际上与转置卷积是不相同的。所以在下面的内容,我都是用转置卷积这个名词。
1.CNN中的卷积
因为本文主要介绍卷积与转置卷积之间的关系,所以对于卷积相关的知识并没有过多的介绍,如果想详细了解卷积的相关基础知识课参考我的博文《卷积神经网络入门详解》。在这里只是概括性的讲述一些知识点,方便叙述卷积与转置卷积之间的关系。
1.1 卷积与相关滤波间的差别
卷积运算与相关滤波之间的差别主要在于,相关滤波直接在核所对应的区域进行相乘再相加,而卷积需要先将卷积核旋转 180 度在进行相乘和相加。所以我们平时所接触的卷积实际上不是卷积,而是一种相关滤波。但是当核是可学习的时候,卷积和相关滤波是可以互换的,所以在 CNN 中我们认为两者是等价的,具体原理及计算过程可以参考《图像的复原与重建(4):图像处理中滤波(filtering)与卷积(convolution)的区别》
1.2 卷积输出尺寸计算
卷积输出尺寸计算公式如下,假设输出数据体在空间上的尺寸 W 2 × H 2 × D 2 W_2 times H_2 times D_2 W2×H2×D2 可以通过输入数据体尺寸 W 1 × H 1 × D 1 W_1 times H_1 times D_1 W1×H1×D1,卷积层中神经元的感受野尺寸(F),步长(S),滤波器数量(K)和零填充的数量(P)计算输出出来。
W 2 = ( W 1 − F 2 P ) / S 1 H 2 = ( H 1 − F 2 P ) / S 1 D 2 = K begin{matrix} W_2=(W_1-F 2P)/S 1 \ H_2=(H_1-F 2P)/S 1 \ D_2=K \ end{matrix} W2=(W1−F 2P)/S 1H2=(H1−F 2P)/S 1D2=K
一般说来,当步长S=1时,零填充的值是P=(F-1)/2,这样就能保证输入和输出数据体有相同的空间尺寸。
但是这里有一点需要注意,对于某一个 W 1 W_1 W1,如果 W 1 − F 2 P W_1-F 2P W1−F 2P 满足是 s 的整数倍,那么这时将输入增大到 W 3 = W 1 a , a ∈ ( 0 , 1 , … , s − 1 ) W_3=W_1 a,ain (0,1,…,s-1) W3=W1 a,a∈(0,1,…,s−1),那么都会输出与原来一样大小的 feature map。这个虽然对卷积的影响并不是很大,但是会使得转置卷积的计算变得复杂。
1.3 s>1 的卷积
假设现在输入的特征图 (input feature map) 的大小是 5 × 5 × 1 5times5times1 5×5×1,使用一个大小为 3 × 3 3times3 3×3 的卷积核进行步长为 s = 2 s=2 s=2 的卷积,将会得到如下图中左侧的结果
其中蓝色的部分是输入,绿色的部分是输出,所以经过卷积后会产生一个 2 × 2 × 1 2times2times1 2×2×1 的 output feature map。步长为 1 的卷积很好理解,对于步长为2的卷积我们可以以内核的增量为 1 ,只有 s = 2 s=2 s=2 的对应部分才会被保留。如上图的右侧所示,用一种比较好理解的方式就是,步长为 2 的卷积,可以理解为在步长为 1 的卷积输出的 feature 上(上图右半部分绿色整体)进行 s = 2 s=2 s=2 的采样(未画叉的部分为采样部分),因此可以将它也认为是下采样的一种。
1.4 s>1 的卷积与 pooling 之间的关系
在上面那节我们了解到,步长大于1的卷积可以理解为在步长为1的卷积的 feature 上进行下采样的过程;然而我们又知道 pooling 也是一种下采样的方法,所以两者之间到底有什么关系呢?如下图所示
假设现在输入的特征图 (input feature map) 的大小是 7 × 7 × 1 7times7times1 7×7×1,使用一个大小为 3 × 3 3times3 3×3 的卷积核进行步长为 s = 2 s=2 s=2 的卷积。如上图所示,中间绿色的部分是对原始图像进行步长为 1 的卷积得到的结果。对于 s=2 的卷积,相当于在步长为1的卷积的输出结果上(绿色的部分)每隔一个点采样一次,我们可以想想绿色的那部分,被若干个红色的框不重复的覆盖,然后每次采样都是在左上角的位置进行采样。所以 s>1 的卷积可以认为是一种在步长为 1 的卷积的 output feature map 上进行等间距采样的过程。
在上图中下半部分,我们也可以想想绿色的那部分,被若干个红色的框不重复的覆盖,但这次不是采样左上角的位置,而是这个红色矩形框中最大值的位置。所以都是下采样,但是 maxpooling 不会是那种等间距的采样。
另一个值得思考的问题是,两者虽然都是下采样,但是输出的 feature 的大小却不相同。这是因为对于奇数大小边长的 feature 没有办法进行步长为 2 下采样,所以在这里我们假设它输出的是一个 2 × 2 2times2 2×2 大小的 feature。一个很自然的想法,有没有可能让步长为 2 的卷积的输出结果和步长为1的卷积再经过 maxpooling 的输出结果,尺寸相等?很遗憾,在卷积核大小不变的情况下是不可能的。 具体原因如下
我们假设 input feature map 的大小为 W × W Wtimes W W×W,卷积核的大小为 K 1 × K 1 K_1times K_1 K1×K1,padding 的大小为 P 1 P_1 P1,则 S 1 = 2 S_1=2 S1=2 的卷积输出尺寸如下所示 W 1 = ( W − K 1 2 P 1 ) / 2 1 W_1=(W -K_1 2P_1)/2 1 W1=(W−K1 2P1)/2 1 对于卷积核大小为 K 2 × K 2 K_2times K_2 K2×K2,padding 的大小为 P 2 P_2 P2,则 S 2 = 1 S_2=1 S2=1 的卷积,再经过 maxpooling 的结果为 W 2 = ( W − K 2 2 P 2 1 ) / 2 W_2=(W -K_2 2P_2 1)/2 W2=(W−K2 2P2 1)/2 如果另 W 1 = W 2 W_1=W_2 W1=W2,则会得到如下的关系式 K 2 − K 1 1 = 2 ( P 2 − P 1 ) K_2-K_1 1=2(P_2-P_1) K2−K1 1=2(P2−P1) 如果令 K 2 = K 1 K_2=K_1 K2=K1,则 2 ( P 2 − P 1 ) = 1 2(P_2-P_1)=1 2(P2−P1)=1,这明显是不可能的;如果令 P 2 = P 1 P_2=P_1 P2=P1,则 K 2 = K 1 − 1 K_2=K_1-1 K2=K1−1,这就意味着在两个卷积核中有一个卷积核是偶数,但是偶数的卷积核在实际应用中很少见到。只有当卷积核的大小和 pading 的大小均不相等时,才会得到的相等大小的输出。
2. 转置卷积
转置卷积常常用于自编码器中的解码器部分,或者将 feature 映射到高维。
2.1 将卷积转化为矩阵相乘
我们可以将图像的卷积过程转变成矩阵相乘的过程,比如说下面的这个卷积的过程
上图中最左边的是一个卷积核,中间的是一个原始的卷积过程,蓝色部分 (44) 是输入 input feature map ,而绿色部分 (22) 是输出 output feature map 部分,其中深蓝色对应一个卷积核的大小 (3*3) ,上面的卷积的过程共经历 4 次卷积。
假如我们将原始图像按照上图中最右边所标注的顺序展开为列向量,并记为向量 X X X ,将得到的向量式的 feature map 作为输出并记为 Y Y Y,则卷积的过程可以表示如下的这种形式 Y = C X Y=CX Y=CX 其中的 C C C 如下所示
以矩阵 C C C 的第一行为例,其实非零权重的位置与上图左右边标注的位置是一样的;同理第二行与第二次卷积运算是一样的……。经过上面的运算我们会得到一个 4 × 1 4times1 4×1 大小的向量,将这个向量按照展开图像的反向顺序,重构成为一个矩阵,即可得到卷积所对应的输出。
这样,卷积的过程就转变为了一个稀疏的权重矩阵与一个图像向量相乘的过程。
2.2 卷积的前向传播和反向传播
因为通过上面 2.1 的知识卷积可以表示为如上的矩阵相乘的形式,关于卷积的求导很容易通过下面这种方式进行,这里主要引用了[2] 的求导过程,十分感谢
所以我们可以知道,通过卷积核可以定义两个矩阵,一个是对应前向传播的矩阵 C C C ,另一个是对应反向传播的矩阵 C T C^T CT 。
2.3 转置卷积
转置卷积是通过交换前向传播与反向传播得到的。还有一种说法是核定义了卷积,但是它定的是直接卷积还是转置卷积是由前向和反向的计算方式得到的。所以定义分别乘以 C C C 和 C T C^T CT 来计算前向传播和反向传播的是直接卷积(也就是常说的卷积),使用 C T C^T CT 和 C C C 来计算前向传播和反向传播的是转置卷积。
总可以使用直接卷积来模拟转置卷积,但是由于要在行列之间补零,所以执行的效率会低。通过后面的分析,我们也可以认为,转置卷积实际上就是卷积。
2.4 转置卷积的计算
由 1.2 部分已经知道,卷积神经网络的 out feature map 可以通过下面的等式进行计算 W 2 = ( W 1 − F 2 P ) / S 1 W_2=(W_1-F 2P)/S 1 W2=(W1−F 2P)/S 1 这里我们只考虑一条边,因为另外一条边是一样的道理。转置卷积的计算其实很简单,实际上就是求上面那个等式中的 W 1 W_1 W1,很容易得到如下的结果 W 1 = S ( W 2 − 1 ) − 2 P F W_1=S(W_2-1)-2P F W1=S(W2−1)−2P F
分析上面的表达式我们可以看到, W 2 W_2 W2 的形式与 W 1 W_1 W1 的形式是相同的,当 S S S 为原来的倒数且 F − 2 P = 1 F-2P=1 F−2P=1。这也对应上了上面的结论,总可以使用直接卷积来模拟转置卷积。
下面我们具体举几个例子来说明转置卷积的计算过程。
如上图所示,第一行是卷积的过程,第二行是一个反卷积的过程。卷积和转置卷积是一对相关的概念,转置卷积嘛,你总得告诉我你针对谁是转置卷积啊。在上图中,卷积过程是在一个 4 × 4 4times4 4×4 大小,padding =0 的 feature map 上使用一个 3 × 3 3times3 3×3 大小的卷积核进行 s = 1 s=1 s=1 的卷积计算,输出的 output feature map 大小为 2 × 2 2times2 2×2 。
这个时候,根据 W 1 = S ( W 2 − 1 ) − 2 P F = 1 × ( 2 − 1 ) − 2 × 0 3 = 4 W_1=S(W_2-1)-2P F=1times(2-1)-2times0 3=4 W1=S(W2−1)−2P F=1×(2−1)−2×0 3=4 可知输出的 output feature map 的大小为 4 × 4 4times4 4×4。但是需要注意的是这里根据 W 1 = S ( W 2 − 1 ) − 2 P F W_1=S(W_2-1)-2P F W1=S(W2−1)−2P F 计算只是输出 feature map 的大小。如果将转置卷积以卷积的形式实现出来,那么它与上图中第二行是相对应的,卷积核与步长与原来相等,步长为原来的倒数,但是的 padding 值需要通过下面这种方式计算 P T = F − P − 1 P^T=F-P-1 PT=F−P−1 其中 P T P^T PT 是转置卷积中 padding 的大小, F F F 是直接卷积中卷积核的大小, P P P 是直接卷积中 padding 的大小。
另外我们可以以一种较为直觉的角度去理解卷积和反卷积。在上图中的第一行,我们看到代表输入的蓝色矩阵中的 1 实际上只对计算代表输出的绿色矩阵中的 1 有贡献,对绿色矩阵中的其他值并没有贡献。因为直接卷积的输入是转置卷积的输出,转置卷积的输出是直接卷积的输入,所以第二行绿色矩阵中的 1 实际是与第一行蓝色矩阵中的 1 相对应的,而第一行蓝色矩阵中的 1 有只影响第一行绿色矩阵中的 1,所以在计算转置卷积的过程中,绿色矩阵中 1 的值只与蓝色矩阵中 1 的值有关,其他的位置都补零了;我们可以相同的思想思考剩下的结果。
我们看到一个有趣的现象,valid卷积 (0 padding ,s=1) 的转置卷积实际上是一个 fully 卷积 (F-1 padding , s=1)的过程。而且我们可以看到 same 卷积的转置卷积实际上还是一个 same 卷积,如下图所示
而且 fully 卷积 (F-1 padding , s=1) 的转置卷积实际上是一个 valid 卷积 (0 padding ,s=1) 的过程,如下图所示
2.5 步长小于 1 的转置卷积
由于转置卷积的步长是直接卷积的倒数,因此当直接卷积的步长 s>1 的时候,那么转置卷积的步长就会是分数,这也是转置卷积又称为分数步长卷积的原因。在 2.4 的例子中,我们所处理的都是直接卷积步长为1 的例子,所以可以认为直接卷积与转置卷积的步长相等。当转置卷积的步长小于1的时候,我们可以通过下面的例子有一个直接的了解
如上图是一个输入 feature map 为 5 × 5 5times5 5×5,卷积核大小为 3 × 3 3times3 3×3,步长 s = 2 s=2 s=2 的直接卷积的转置卷积,此时的转置卷积的输入是在 2 × 2 2times2 2×2 的矩阵间进行插孔得到的。首先计算此时转置卷积输出的大小,我们发现与之前的计算方法是一样的 W 1 = S ( W 2 − 1 ) − 2 P F = 2 × ( 2 − 1 ) − 2 × 0 3 = 5 W_1=S(W_2-1)-2P F=2times(2-1)-2times0 3=5 W1=S(W2−1)−2P F=2×(2−1)−2×0 3=5 果然通过之前推导出的公式计算出了与上图相同的结果,这时我们计算下转置卷积中 padding 的大小 P T = F − P − 1 = 3 − 0 − 1 = 2 P^T=F-P-1=3-0-1=2 PT=F−P−1=3−0−1=2 很明显 padding 的计算结果也是符合上面的公式要求的。之后就是最关键的部分了,如何体现出步长是分数步长。在原始的卷积中插入数字 0,这使得内核以比单位步幅的速度移动慢,具体的在输入的每两个元素之间插入 s − 1 s-1 s−1 个 0。所以此时转置卷积的输入尺寸大小由原来的 W 2 W_2 W2 变为 W 2 ( W 2 − 1 ) ( s − 1 ) W_2 (W_2-1)(s-1) W2 (W2−1)(s−1)。
记得在 1.2 节曾经提到过”对于某一个 W 1 W_1 W1,如果 W 1 − F 2 P W_1-F 2P W1−F 2P 满足是 s 的整数倍,那么这时将输入增大到 W 3 = W 1 a , a ∈ ( 0 , 1 , … , s − 1 ) W_3=W_1 a,ain (0,1,…,s-1) W3=W1 a,a∈(0,1,…,s−1),那么都会输出与原来一样大小的 feature map。这个虽然对卷积的影响并不是很大,但是会使得转置卷积的计算变得复杂。”现在我们就来考虑一下这样的问题会复杂在哪里?
虽然在直接卷积的过程中,将输入增大到 W 3 = W 1 a , a ∈ ( 0 , 1 , … , s − 1 ) W_3=W_1 a,ain (0,1,…,s-1) W3=W1 a,a∈(0,1,…,s−1),那么都会输出与原来一样大小的 feature map。但是转置卷积的过程肯定是希望获得与直接卷积输入一样大的 feature map,那么应该如何做呢?
输出相同的大小,是因为在原图中有一部分的输入没有经过卷积计算,直接忽略掉了,所以我们就求出这部分的大小是多少,然后直接加到转置卷积的 output feature map 上就好了,具体计算如下 a = ( W 1 − K 2 P ) m o d ( s ) a = (W_1-K 2P)mod(s)%s a=(W1−K 2P)mod(s) 这个很好理解,就是在直接卷积中有多少是没有被卷积而遗留下来的。所以这个时候转置卷积的输出也变为了 W 1 = S ( W 2 − 1 ) − 2 P F a W_1=S(W_2-1)-2P F a W1=S(W2−1)−2P F a 下面的图像将直观地解释这一过程
可以看到在最右侧和最下面补了一圈 0,就是上面计算转置卷积输出中的 a。
参考
[1] Dumoulin V, Visin F. A guide to convolution arithmetic for deep learning[J]. 2016. [2] ustc_lijia CSDN 博客 《反卷积,转置卷积》
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/151922.html原文链接:https://javaforall.cn