早先,在学习深度学习基础的时候,在arXiv上发现了一篇很好的文章。最近整理个人电脑的时候,发现了这个文档。独乐乐不如众乐乐,我觉得有可以把它翻译下来。老外写的东西往往十分详细,很多指南外行人都可以看懂。如果你是深度学习相关领域的初学者,请耐心读完这份指南吧,相信在看完之后,大家对深度学习中的卷积操作一定会有更清楚的理解。
感谢Vincent Dumoulin和Francesco Visin两位原作者的贡献,附原文链接:https://arxiv.org/abs/1603.07285
All models are wrong, but some are useful. —— George E. P. Box
深度卷积神经网络(CNNs)一直以来都是深度学习取得巨大进展的核心因素。尽管,CNNs早在上世纪90年代就被用于解决手写字符识别任务;但直到2012,因为一项基于CNN的工作在ImageNet图像分类挑战上达到当时的最佳水准(SOTA),CNNs才在近年来的研究中被广泛关注。
卷积神经网络是机器学习从业者的一样有力工具。然后,初学CNNs是一段令人望而生畏的经历。一个卷积层输出的形状受其输入形状、卷积核的尺寸和数量、零填充(zero padding)以及strides都有关系,并且这些属性都不是随意设置的。 ——这与全连接网络很不相同,全连接网络由于是密集连接,其输出尺寸和输入尺寸是相互独立的。并且,CNNs通常会搭配使用池化层,这使得其相对全连接网络更为复杂。另外,越来越多的工作用到了反卷积(transposed convolutional layers)或称分数步长卷积(fractionally strided convolutional layers),它们和卷积层的关系有着不同程度的解释。
这份指南的主要目的有如下两点:
1. 解释卷积和反卷积的关系;
2. 提供卷积、池化和反卷积操作中输入形状,核尺寸,零填充,strides和输入输出的直观理解。
出于广泛性考虑,这份指南中的讨论均不涉及具体的Theano,Torch,Tensorflow,Caffe等机器学习框架的实现细节。
第一章简要介绍了CNNs的主要组成部分——离散卷积和池化。若想了解更进步一的细节,请参见Goodfellow等人所著的《Deep Learning》一书。
神经网络<结合文文意,作者应该描述的是传统神经网络>的形成基础是仿射变换:输入一个向量,通过一个张量与之相乘得到一个输出(一般,在将结果输入到非线性单元之前会加上一个偏置向量)。这个声音片段可以是任意的形式,一幅图像、一个声音片段、或者是一份无序的特征集合——不论它们是何形式,在进行仿射变换之前,它们的总是可以展开成一个向量来表示。
图像、声音或是类似数据类型都具有以下的重要特征:
当使用仿射变换时,以上的这些属性不会得到充分利用;这些数据的维度被采用相同的方式处理,其间蕴含的拓扑关系也就没有被考虑进去。然而,这些数据中隐藏的结构在处理类似于计算机视觉、语音识别等任务是非常有作用的,我们应该尽量地去保留这些属性。这正式离散卷积所要实现的。
离散卷积是一种能够保留数据结构属性的一种线性变换,它是稀疏的(仅利用部分输入单元得到输出)并且是参数复用的(同样的权重参数被用于与输入的不同部分进行乘法操作)。
图1.1 描述了一个离散卷积的例子。其中,浅蓝色的网格被称为特征图( input feature map)。为了方便绘制,图中仅给出了单层输入特征图,但实际情况中经常会出现多层输入特征图的情况。一个具有特定数值的卷积核沿着特征图滑动。在每个位置上,卷积核与其覆盖区域的输入特征对应位置分别相乘,然后所有相乘结果相加得到这个位置的卷积结果。
<在信号处理领域,相应位置相乘叫做相关,而卷积时卷积核应该与输入的镜像位置相乘;在这里卷积核的方向并没有什么影响,所以无所谓是相应位置还是镜像位置了>
可以利用多个卷积核重复这个过程,以得到所需任意多的输出特征,如图1.3。最后,这些特征的堆叠组合叫做输出特征图(ouput feature maps)。如果输入特征是多维的,卷积核也应该和输入特征图具有相同的维度,这样才可以进行卷积操作——结果特征图的维度是每个卷积操作输出特征维度的总和。
图1.1描述的是2-D卷积的一个例子,但它可以被类推到N-D卷积。举个例子,在3-D卷积中,卷积核会是一个立方体,并且会沿着输入特征图的长宽高三个方向滑动。
一组卷积核可以通过以下方式来定义,(n, m, k_1, … , k_N),其中n表示输出特征图的层数,m为输入特征图的层数,k_j表示卷积核第 j 维的宽度。以下的因素会影响卷积层在 j 维度上输出尺寸o_j:
例如,图1.2描述了3x3的核对5x5输入进行卷积,使用(1,1)的padding和(2,2)的strides。注意,strides可以理解为一种降采样,具体可见图1.4的描述。
图1.3描述了通过利用一个3 x 2 x 3 x 3的卷积核组 w ,将一个二层的输入特征映射为三层的输出特征。以最左边的通路为例,输入特征层1与核w_1,1卷积得到特征层1,输入特征层2与核w_1,2得到特征层2,两个特征层对应位置元素相加的到第一个输出特征图。其他两个通路以此类推,最后将三个特征图堆叠得到最终的输出特征图。
图1.4描述的是stride的两种理解方式。左边是3x3卷积核在5x5输入上,以2为距离间隔滑动,得到2x2输出特征;右边是3x3卷积核在5x5输入上连续滑动,然后再得到的输出的每个维度 上以1为间隔进行采样,得到2x2输出特征。这两种处理方式得到的输出是一摸一样的。
除了卷积操作,池化是CNN中另一种重要的组成结构。池化操作通过,例如求平均或最大值,对特征图的子区域进行聚合,从而实现特征图尺寸的减少。
池化通过在输入中滑动一个窗口,利用特定的池化方式计算或统计窗口内的值。一定程度上,池化操作与卷积操作类似,但是将卷积中乘加的线性操作替换为其他计算方式。图1.5 描述了一个平均池化的例子,图1.6 描述了一个最大值池化的例子。
以下的属性影响池化输出在 j 维度方向上的大小:
图1.5描述的是在5x5输入大小下的3x3的平均池化操作,使用1x1 strides。
图1.6 描述的是在5x5输入大小下的3x3最大值池化操作,使用1x1 strides.
令人愉悦的时,卷积运算在各个维度上是相互独立的,也就是说,j 维度上的核尺寸、stride和零填充的选择只会影响 j 维度上的输出。因此,本章内容将基于以下的简单设定来展开:
这些设定有利于我们将卷积过程可视化,但是读者应该知道以下这些结论以下这些结论均可扩展到N-D的情况。
最简单的一种情况就是卷积核划过输入的每一个位置(即,s = 1,p = 0)。图2.1描述了一个 i = 4, k = 3 的例子。在这种情况下,定义输出尺寸的一种方式是计算卷积核在输入特征图上所有可能放置的位置。考虑宽度的方向:卷积核从输入特征图的最左边开始,以1为步距连续滑动直到达到特征图的最右侧。输出的尺寸应该等于滑动的步数再加上 1 (卷积核的原始位置),如图2.8a。在高度上也是同样的逻辑。
可以给出如下公式,式1:
o = ( i − k ) + 1. o = (i - k) + 1. o=(i−k)+1.
其中,i 和 k 可以是任意值,上式适用情况是 s = 1, p = 0。
针对零填充(仅针对s = 1),让我们考虑它对有效输入的影响:使用 p 个 0 填充会将输入尺寸从 i 变为 i + 2p。一般情况下,式1可以变成如下公式,式2:
o = ( i − k ) + 2 p + 1 o = (i - k) + 2p + 1 o=(i−k)+2p+1
其中,i和k可以是任意值,上式适用情况是 s = 1。图2.2给出了一个i = 5, k = 4,和p = 2的例子。
在实际情况下,两种特定的padding操作因为其特性经常被使用,下面我们进一步讨论。
让卷积层输出尺寸和输入尺寸相同(即,o = i)是一个很吸引人的实现<我是这样理解的——这样我们就可以再卷积层尽量少地损失信息量,从而把网络加深>。能够实现这种效果的padding称为Half padding,或Same padding。以下公式可以描述这种情况下的计算关系,如式3:
o = i + 2 [ k / 2 ] − ( k − 1 ) = i + 2 n − 2 n = i w h e r e , k = 2 n + 1 , n ∈ N o = i + 2[k/2] - (k-1) = i + 2n - 2n = i\\ where, k = 2n + 1, n \in \mathbb{N} o=i+2[k/2]−(k−1)=i+2n−2n=iwhere,k=2n+1,n∈N
其中,i 是任意输入大小,k是奇数,s = 1,padding尺寸为卷积核的1/2取整。图2.3给出了一个i = 5, k = 3的例子,对应的p = 1。
大多数的情况下,卷积操作会降低输入特征的尺寸,但有时我们需要相反的效果,这种操作叫做full padding。在使用full padding的时候,输入特征图上所有可能被卷积核部分覆盖或完全覆盖的位置都被考虑进去<我理解是,从卷积核和输入特征图开始相交,便当做一个中心位置>。这样的效果可以通过设置合适的padding大小来实现,如式4:
o = i + 2 ( k − 1 ) − ( k − 1 ) = i + ( k − 1 ) o = i + 2(k-1)-(k-1)=i+(k-1) o=i+2(k−1)−(k−1)=i+(k−1)
其中,i 和 k 为任意值,padding大小为k - 1,s = 1。图2.4给出了一个i = 5, k = 3的例子,这种情况下p=2。
以上所介绍的所有关系式都是针对strides为1的情况,合并strides不为1的情况需要另一种推理方式。为了帮助理解,让我们从忽略zero padding开始考虑(即,s > 1,p = 0).图2.5描述了一个i = 5, k = 3 和 s = 2的例子。
再一次,输出尺寸可以定义为卷积核在输入特征图上可以放置的位置。以宽度方向为例:卷积核从输入特征图的最左边开始,但是现在它以步距s滑动,直到它打到输入特征图的右侧。输出尺寸也是等于卷积核划动的次数加上1,卷积核的初始位置,如图2.8b。高度方向与宽度方向逻辑一致。这种情况下,运算公司如式5:
o = [ i − k s ] + 1 o = [\frac{i-k}{s}]+1 o=[si−k]+1
上式中计算时,有个实际情况是在卷积过程中,输入特征图可能有些位置没有被卷积核覆盖到,图2.7正好是这样的例子。
卷积操作最一般化的例子是使用零填充,并且strides不为1,这种情况可以用式6计算:
o = [ i + 2 p − k s ] + 1 o = [\frac{i+2p-k}{s}]+1 o=[si+2p−k]+1
其中,i,k,p,s可以是任意值。和前面一样,上式意味着对于不同的输入尺寸,一个卷积操作可以得到同样的输出大小。当 i + 2p - k 是 s 的整数倍,那么对任意的输入尺寸 j = i + a,a 属于 {0, 1, …, s - 1}可产生产生相同的输出尺寸。注意,此种情况 s > 1.
图2.6描述了i = 5,k = 3, s = 2 和 p = 1的一个例子,图2.7描述了i = 6, k = 3, s = 2 和 p = 1的一个例子。有趣的是,尽管这两个例子具有不同的输入尺寸。虽然这不会影响分析卷积操作,但是会是的后面我们要介绍的反卷积的分析变得更为复杂。
在神经网络中,池化层提供输入的平移不变性<当然还一个作用是降低维度,减少后续运算量和参数量,提高模型泛化能力>。最常用的池化操作是最大值池化(max pooling),它将输入特征图划分为与若干个与池化窗大小同的小块,然后通过统计这些小块的最大值得到输出。另一种常用的池化操作是平均池化(mean pooling),思路与最大值池化相同,只不过把统计最大值替换成了计算小块内的平均值。
池化操作和卷积操作的滑动过程时一样的,所以可以沿用之前的公式。由于,池化操作一般不使用padding,所以我们用式7:
o = [ i − k s ] + 1 o = [\frac{i-k}{s}]+1 o=[si−k]+1
其中,i ,k,s是任意值。上式可以用在任意场合的池化情况,o是池化输出。
反卷积,或者说转置卷积的需求来自于,想要实现一种变换,它的运算模型同卷积运算相反,并且能够与卷积运算进行兼容。举个例子, 在某些情况下我们需要利用这种运算构造一个解码器(Decoder)来对卷积的编码器(encoder)进行处理,将卷积特征图进行升维度。
需要再次声明的是,这种运算要比使用全连接层作为解码器来的复杂得多,全连接层只需要将输入和输出尺寸颠倒即可实现特征的升维。然后,既然卷积运算可以归结于一组高效的矩阵运算,那么特征的升维过程也是可以像卷积运算一样从全连接层转变成一种更高效的矩阵运算方式。这种运算方式,我们称之为反卷积。
像卷积运算一样,反卷积运算在不同维度上也是独立的。本章的内容将基于以下简单的设定展开:
同卷积运算一样,以下关于反卷积的结论也可以扩展到N-D的情形。
用图2.1给出的卷积操作举例。如果将输入和输出按照从左到右、从上到下的模式展开成向量,那么卷积操作的可以表示为下图所示的稀疏矩阵C,矩阵中非零的元素就是卷积核的元素w_i,j(其中,i,j分别是元素在卷积核中的行列序号。)
<上图这个矩阵需要怎么理解呢? 结合图2.1,我们可以看到3x3的卷积核在4x4的输入上滑动。将输入展开成向量,对应矩阵的一行。而,这个卷积操作卷积核可能覆盖的位置有4个,就是图2.1的四个图,对应矩阵的四列。这样看,你就清楚这个矩阵表示的是什么了 >
于是,图2.1的卷积操作就可以换种方式理解:将4x4输入展开成16维的向量,然后通过上面的这个4x16的稀疏矩阵C进行线性变换生成一个4维向量,最后把向量reshape成2x2的输出。通过这种表示形式,卷积运算就可以很容易地通过C的转置矩阵进行反向传递;换句话说误差的反向传递过程,就是用误差乘以C^T。这个反向传递的过程采用4-D向量作为输入,生成16-D的输出向量,该向量的连接模式与矩阵C的构造形式兼容。
值得一提的是,卷积核w既可以用于构造C,也可以用于构造C^T。
现在让我们来考虑反过来可以做些什么,即如何将4-D向量映射为16-D的向量,并且能保持图2.1所描述卷积的连接模式。这个过程就是反卷积操作。
反卷积又被成为分数步长卷积,它实现的是调转一个卷积的前向传递和反向传递过程。一种表述方式是——卷积核定义了一个卷积过程,置于它普通卷积还是反卷积取决于其正向、反向传递过程时如何计算的。举例来说,尽管一个卷积核定义了一种卷积运算,其前向传递和反向传递过程分别通过乘以C和CT得到,但它仍可以定义另一个卷积运算,其前向传递和反向传递过程分别通过乘以CT和(CT)T的到。1
最后,需要注意的是,我们也可以用普通卷积实现反卷积的效果<朋友们记得padding吗?>。这样做的缺点是,我们往往需要填充大量的0,这使得计算效率不高。<并且从信息的角度上也不合理,升维比例较大时,填充的0给我们的输出特征带来的有效信息非常有限。>
在前面内容介绍的基础上,本章将进一步针对之前普通卷积的内容介绍其反向传递过程,提出一些共享同一卷积核的普通卷积核反卷积的性质,并且定义等效直接卷积(equivalent direct convolution)。
理解反卷积的最简单的方向,就是将其输入视为某个直接卷积的对于某个原始输入特征的输出结果。然后,可以把反向卷积视为恢复原始输入特征图尺寸2的过程。
考虑,3x3卷积核,4x4输入,无零填充,stride为1的卷积情况(即,k = 3,i = 4,p = 0,s = 1)。如图2.1所描述,该卷积操作得到一个2x2的输出特征。那么其对应的反卷积,使用2x2输入时,将会得到4x4的输出。
另一种得到反卷积结果的方式是采用一个等效直接卷积(这会更加低效),就是利用采用zero padding的方式实现从低维输入到高维输出的普通卷积,如图4.1,就是那个过程。注意,核尺寸和stride尺寸保持一致,但是反卷积的输入已经零填充了3。
一种利用zero padding的理解逻辑是<这里作者只是帮助我们理解,如果要得到目的尺寸的输出,假设用普通卷积,我们应该如何设置零填充。但是实际计算过程并不是采用零填充的形式>,考虑反卷积的连接模式,然后利用其指导设计等价卷积。举个例子,输入特征图的最左上角只会贡献于输出特征图的左上角,输入特征图的最右下角只会贡献于输出特征图的右下角,依次类推<注意观察4.1节的稀疏矩阵C的结构>。为了保持等价卷积的连接模式,我们有必要保证输入特征图的左上角元素只会接触卷积核的左上角元素,即padding尺寸必须等于核尺寸减1。
按照上面的方式处理,我们就有可能确定图像中其他元素的类似观测结果,从而产生以下关系,式8:
o ′ = i ′ + ( k − 1 ) . o' = i' + (k - 1). o′=i′+(k−1).
上式解释:一个卷积操作有s = 1,p = 0和k,则与其具有一致连接模式反卷积<这里的卷积与反卷积具有一致连接模式的意思是,卷积输入、输出分别为该反卷积的输出和输入>具有k’ = k, s’ = s, p’ = k - 1,其输出如上式计算。有趣的是,这个式子与采用stride 1的full padding的计算公式是一样的。
既然无零填充的反卷积相当于对输入进行零填充的普通卷积,那么我们理所当然会猜想:零填充的反卷积是不是相当于对原始输入适用更小尺寸的零填充普通卷积呢? 答案却是如此,如图4.2所示,描述了一个 与i = 5,k = 4,p = 2的卷积所关联反卷积的例子。我们给出如下适用于零填充的反卷积公式,如式 9:
o ′ = i ′ + ( k − 1 ) − 2 p o' = i' + (k-1) -2p o′=i′+(k−1)−2p
其中,s = 1,k‘ = k,s’ = s,p’ = k - p - 1;k,s,p分别为与反卷积具有一致连接模式普通卷积的参数。
通过使用上述的归纳方法,由于half padding的输入输出相同,我们有理由猜测half padding的反卷积的等价普通卷积就是一个half padding卷积。因此,我们可以给出下式,式10:
o ′ = i ′ + ( k − 1 ) − 2 p = i ′ + 2 n − 2 n = i ′ w h e r e , a d i r e c t c o n v o l u t i o n d e s c r i b e d b y k = 2 n + 1 , n ∈ N , s = 1 , p = [ k / 2 ] = n o' = i' + (k-1)-2p=i'+2n-2n=i' \\ where, a\ direct\ convolution\ described\ by\\ k = 2n+1, n \in \mathbb{N}, \ s = 1,\ p = [k/2] = n o′=i′+(k−1)−2p=i′+2n−2n=i′where,a direct convolution described byk=2n+1,n∈N, s=1, p=[k/2]=n
其中,k’ = k, s’ = s, p’ = p, o’是反卷积的输出。图4.3给出了一个 i = 5, k = 3, p = 1的预期具有一致连接模式的half padding反卷积的例子。
同上逻辑,我们给出Full padding卷积对应具有一致连接模式的反卷积计算公式,式11:
o ′ = i ′ + ( k − 1 ) − 2 p = i ′ − ( k − 1 ) o' = i' + (k-1)-2p=i'-(k-1) o′=i′+(k−1)−2p=i′−(k−1)
其中,若一个卷积具有s = 1, p = k - 1, 则与其具有一致连接模式的反卷积的等效卷积具有k’ = k, s’ = s, p’ = 0。图4.4描述了描述了一个i = 5, k = 3, p = 2的卷积对应与其具有一致连接模式的反卷积的例子。
按照之前的推理逻辑,我们可以预料到如果一个反卷积具有s > 1,则其对应一致连接模式的卷积具有s < 1。这个预料是正确的,也是也是为什么我们又称反卷积为分数步长卷积(fractionally strided convolutions)。
图4.5提供了一个i = 5, k = 3, s = 2的普通卷积与其对应一致间接模式的卷积,这可以帮助我们理解:在输入特征图的元素之间插入了一些0,这可以是的卷积核可以按照相对于输入特征图小于1的步距进行滑动4。我们给出如下公式,式12:
o ′ = s ( i ′ − 1 ) + k o' = s(i'-1)+k o′=s(i′−1)+k
其中,与该反卷积具有一致模式的普通卷积具有以下参数,p = 0, k, s, i, 其输入要满足i - k是s的整数倍,那么该反卷积的等效卷积具有如下参数k’ = k, s’ = 1, p’ = k - 1;等效卷积的是i’(对应普通卷积的输出)的每两个相邻元素填充s - 1个零得到的。
当普通卷积的输入 i 具有 i + 2p - k 是 s 的倍数时,可以有式9和式12推出式13:
o ′ = s ( i ′ − 1 ) + k − 2 p . o' = s(i'-1)+k-2p. o′=s(i′−1)+k−2p.
其中,i + 2p - k是s的整数倍,则与该卷积具有一致连接模式的等效卷积具有参数k’ = k, s’ = 1, p’ = k - p - 1,该等效卷积的输入是i’的每个相邻元素之前填充s-1个0得到。图4.6给出了一个i = 5, k = 3, s = 2, p = 1的例子。
当普通卷积的输入不满足i + 2p - k是s的整数倍这个条件时,可以利用一个参数a (0 < a < s -1)来放宽这个约束条件。如式14:
0 ′ = s ( i ′ − 1 ) + a + k − 2 p . 0' = s(i'-1)+a+k-2p. 0′=s(i′−1)+a+k−2p.
上式中,在式13的基础上,a是i + 2p - k除以s的余数。图4.7给出了一个i = 6, k = 3, s = 2, p = 1的例子。
<上图中标黄部分应该是3x3,我认为是原作者的笔误。>
上面这些内容看起来可能比较混乱,小伙伴们应该注意两点:
————————————————————
1. 具有一致连接模式的卷积和反卷积指的是,一个卷积操作将输入为i 输出为o ,则其具有一致连接模式的反卷积输入为 i’ = o, 输出o’ = i;
2. 我在上面翻译的等效(直接)卷积指的是,一个与反卷积输入和输出相同的普通卷积,它可能通过对输入进行padding,元素间填充0,等方式实现。但是反卷积的实际操作并不是这样的,这里都按照等效卷积来描述反卷积的计算公式、绘制图是为了帮助读者理解反卷积输入输出的运算过程。
熟悉深度学习理论的小伙伴们可能会接触到空洞卷积(Dilated convolutions,Atrous convolution)这个概念。这里我们准备介绍空洞卷积的一种基础理解。
空洞卷积通过在卷积核的元素间插入空间对卷积核进行“膨胀”,这个“膨胀率”通过控制一个超参数d实现。空洞卷积的实现方式有多种,但是一般插入的空间是 d = 1,当 d = 1是,它就是一个普通卷积。空洞卷积是一种可以轻易扩大卷积层输出感受野的方式,其比较有效的用法是一层层堆叠的多个空洞卷积层的组合。 举个具体例子,2016年提出的WaveNet基于空洞卷积实现了一个自回归的语音生成模型,该模型可以通过语音序列生成新的语音。
为了理解空洞卷积的计算方式,了解参数 d 对扩充后卷积核大小的影响是比较合适的途径。一个卷积核 k 经过参数 d 进行扩充之后得到的实际核大小为:
k ′ = k + ( k − 1 ) ( d − 1 ) k' = k + (k-1)(d-1) k′=k+(k−1)(d−1)
然后,我们可以利用式6,计算其输出大小,如式15:
o = [ i + 2 p − k − ( k − 1 ) ( d − 1 ) s ] + 1. o = [\frac{i + 2p -k - (k-1)(d-1)}{s}] + 1. o=[si+2p−k−(k−1)(d−1)]+1.
图5.1 给出了一个i = 7, k = 3 和 d = 2的例子。
反卷积运算可以被认为是相对于输入的某个卷积操作的梯度,这通常是反卷积实际的实现方式。 ↩︎
注意反卷积不能保证会恢复原始输入特征图本身,因为它并不是直接卷积的逆运算,而是仅仅将恢复特征图的尺寸,使得输出结果和直接卷积的原始输入特征图宽、高相同。 ↩︎
注意,尽管这相当于应用转置矩阵,但这种可视化以零填充的形式增加了大量的零乘法。这里这样做是为了演示,但效率很低,软件实现通常不会执行无用的零乘法。 ↩︎
和上面一样,实际情况下反卷积不会这么做,但是这可以帮我我们理解反卷积的运算。 ↩︎