吴恩达深度学习笔记-9(卷积神经网络)
卷积神经网络
计算机视觉
机器视觉(Computer Vision)是深度学习应用的主要方向之一。一般的CV问题包括以下三类:
- Image Classification
- Object detection
- Neural Style Transfer


比如在一个无人驾驶项目中,你不一定非得识别出图片中的物体是车辆,但你需要计算出其他车辆的位置,以确保自己能够避开它们。
下图展示了一个神经风格转换(Neural Style Transfer)的例子:

比如说有一张图片,想将这张图片转换为另外一种风格。用神经网络将它们融合到一起,描绘出一张新的图片。它的整体轮廓来自于左边,却是右边的风格,最后生成下面这张图片。
使用传统神经网络处理机器视觉的一个主要问题是输入层维度很大。例如一张64x64x3的图片,神经网络输入层的维度为12288。如果图片尺寸较大,例如一张1000x1000x3的图片,神经网络输入层的维度将达到3百万,使得网络权重W非常庞大。这样会造成两个后果,一是神经网络结构复杂,数据量相对不够,容易出现过拟合;二是所需内存、计算量较大。解决这一问题的方法就是使用卷积神经网络(CNN)。
边缘检测例子
卷积运算是卷积神经网络最基本的组成部分,本小节使用边缘检测作为理解这一运算的例子。

在之前的学习当中有了解到在人脸识别例子当中,神经网络可以由浅到深,分别检测出图片的边缘特征、局部特征,最后检测出整体面部轮廓。
这里着重介绍如何检测图片的边缘。
对于图片边缘的检测有这样两类,一个是垂直边缘检测,另一个是水平边缘检测。

看这样一个例子,现在有一个6x6的灰度图。为了检测图像中的垂直边缘,可以选择构造一个3x3的矩阵$$\begin{bmatrix}1 & 0 & -1\\ 1 & 0 & -1\\ 1 & 0 & -1\end{bmatrix}$$,这个矩阵术语称之为过滤器,也或者叫做卷积核。
对这个6×6的图像进行卷积运算,卷积运算用“\(*\)”来表示,用3×3的过滤器对其进行卷积。

(在数学中“\(*\)”就是卷积运算的标准符号,在编程语言中往往表示乘法,python中,卷积用conv_forward()表示;tensorflow中,卷积用tf.nn.conv2d()表示;keras中,卷积用Conv2D()表示。在视频当中表示卷积的时候会特别说明)
这个卷积运算的输出将会是一个4×4的矩阵,你可以将它看成一个4×4的图像。
为了计算第一个元素,在4×4左上角的那个元素,使用3×3的过滤器,将其覆盖在输入图像,如下图所示。然后进行元素乘法(element-wise products)运算,所以\(\begin{bmatrix} 3 \times 1 & 0 \times 0 & 1 \times \left(1 \right) \\\\ 1 \times 1 & 5 \times 0 & 8 \times \left( - 1 \right) \\\\ 2 \times1 & 7 \times 0 & 2 \times \left( - 1 \right) \end{bmatrix} = \begin{bmatrix}3 & 0 & - 1 \\\\ 1 & 0 & - 8 \\\\ 2 & 0 & - 2 \end{bmatrix}\),然后将该矩阵每个元素相加得到最左上角的元素,即\(3+1+2+0+0 +0+(-1)+(-8) +(-2)=-5\)。
为了计算第二个元素,需要将蓝色方块向右移动一步,然后再进行类似的运算,所以是 \(0×1+5×1+7×1+1×0+8×0+2×0+2×(-1)+ 9×(-1)+5×(-1)=-4\)。

重复进行元素乘法,然后加起来。通过这样做得到-10。再将其右移得到-2,接着是2,3。以此类推,这样计算完矩阵中的其他元素。

为什么这个可以做垂直边缘检测?看另外一个例子,有这样一个简单的6x6灰度图。

如果把它当成一个图片,左边那部分看起来是白色的,像素值10是比较亮的像素值,右边像素值比较暗,这里使用灰色来表示0,尽管它也可以被画成黑的。图片里,有一个特别明显的垂直边缘在图像中间,这条垂直线是从黑到白的过渡线,或者从白色到深色。
所以,当你用一个3×3过滤器进行卷积运算的时候,这个3×3的过滤器可视化为下面这个样子,在左边有明亮的像素,然后有一个过渡,0在中间,然后右边是深色的。卷积运算后,你得到的是右边的矩阵。

如果把最右边的矩阵当成图像,它是这个样子。在中间有段亮一点的区域,对应检查到这个6×6图像中间的垂直边缘。
这里的矩阵维度似乎有点不正确,检测到的边缘太粗了。那是因为在这个例子中,图片太小了。如果你用一个1000×1000的图像,而不是6×6的图片,则会很好地检测出图像中的垂直边缘。
更多边缘检测例子
图片边缘有两种渐变方式,一种是由明变暗,另一种是由暗变明。以垂直边缘检测为例,下图展示了两种方式的区别。实际应用中,这两种渐变方式并不影响边缘检测结果,可以对输出图片取绝对值操作,得到同样的结果。

垂直边缘已经检测出来了,那么水平边缘又该如何检测?
考虑对过滤器(卷积核)进行改变,来达到目的。

看一个复杂一点的例子。
左上方和右下方都是亮度为10的点。如果你将它绘成图片,右上角是比较暗的地方,这边都是亮度为0的点,我把这些比较暗的区域都加上阴影。而左上方和右下方都会相对较亮。如果你用这幅图与水平边缘过滤器卷积,就会得到右边这个矩阵。

分析卷积后的矩阵可以得出这样的结论:
这里的30(右边矩阵中绿色方框标记元素)代表了左边这块3×3的区域(左边矩阵绿色方框标记部分),这块区域确实是上边比较亮,而下边比较暗的,所以它在这里发现了一条正边缘。而这里的-30(右边矩阵中紫色方框标记元素)又代表了左边另一块区域(左边矩阵紫色方框标记部分),这块区域确实是底部比较亮,而上边则比较暗,所以在这里它是一条负边。
除了上面提到的这种简单的Vertical、Horizontal过滤器之外,还有其它常用的filters,例如Sobel filter和Scharr filter。这两种过滤器的特点是增加图片中心区域的权重。

在深度学习中,如果我们想检测图片的各种边缘特征,而不仅限于垂直边缘和水平边缘,那么filter的数值一般需要通过模型训练得到,类似于标准神经网络中的权重W一样由梯度下降算法反复迭代求得。CNN的主要目的就是计算出这些filter的数值。确定得到了这些filter后,CNN浅层网络也就实现了对图片所有边缘特征的检测。
Padding
我们在之前视频中看到,如果你用一个3×3的过滤器卷积一个6×6的图像,你最后会得到一个4×4的输出,也就是一个4×4矩阵。那是因为你的3×3过滤器在6×6矩阵中,只可能有4×4种可能的位置。这背后的数学解释是,如果我们有一个\(n×n\)的图像,用\(f×f\)的过滤器做卷积,那么输出的维度就是\((n-f+1)×(n-f+1)\)。在这个例子里是\(6-3+1=4\),因此得到了一个4×4的输出。
这样的话会有两个缺点:
- 卷积运算后,输出图片尺寸缩小
- 原始图片边缘信息对输出贡献得少,输出图片丢失边缘信息
第一个缺点是每次做卷积操作,你的图像就会缩小,从6×6缩小到4×4,你可能做了几次之后,你的图像就会变得很小了,可能会缩小到只有1×1的大小。你可不想让你的图像在每次识别边缘或其他特征时都缩小,这就是第一个缺点。

第二个缺点时,如果你注意角落边缘的像素,这个像素点(绿色阴影标记)只被一个输出所触碰或者使用,因为它位于这个3×3的区域的一角。但如果是在中间的像素点,比如这个(红色方框标记),就会有许多3×3的区域与之重叠。所以那些在角落或者边缘区域的像素点在输出中采用较少,意味着你丢掉了图像边缘位置的许多信息。
为了解决图片缩小的问题,可以使用padding方法,即把原始图片尺寸进行扩展,扩展区域补零,用p来表示每个方向扩展的宽度。

在这个案例中,\(p=1\),因为我们在周围都填充了一个像素点,输出也就变成了\((n+2p-f+1)×(n+2p-f+1)\),所以就变成了\((6+2×1-3+1)×(6+2×1-3+1)=6×6\),和输入的图像一样大。
至于选择填充多少像素,通常有两个选择,分别叫做Valid卷积和Same卷积。
Valid卷积意味着不填充,这样的话,如果你有一个\(n×n\)的图像,用一个\(f×f\)的过滤器卷积,它将会给你一个\((n-f+1)×(n-f+1)\)维的输出。这类似于在上面小节中展示的例子,有一个6×6的图像,通过一个3×3的过滤器,得到一个4×4的输出。
另一个经常被用到的填充方法叫做Same卷积,那意味你填充后,你的输出大小和输入大小是一样的。根据这个公式\(n-f+1\),当你填充\(p\)个像素点,\(n\)就变成了\(n+2p\),最后公式变为\(n+2p-f+1\)。
因此如果你有一个\(n×n\)的图像,用\(p\)个像素填充边缘,输出的大小就是这样的\((n+2p-f+1)×(n+2p-f+1)\)。如果你想让\(n+2p-f+1=n\)的话,使得输出和输入大小相等,如果你用这个等式求解\(p\),那么\(p=(f-1)/2\)。
计算机视觉问题上面,\(f\)通常都是奇数,甚至可能根本就没有人使用偶数的卷积核。
使用奇数可能原因有二:
- 如果\(f\)是一个偶数,那么你只能使用一些不对称填充。只有\(f\)是奇数的情况下,Same卷积才会有自然的填充,我们可以以同样的数量填充四周,而不是左边填充多一点,右边填充少一点,这样不对称的填充。
- 当你有一个奇数维过滤器,比如3×3或者5×5的,它就有一个中心点。有时在计算机视觉里,如果有一个中心像素点会更方便,便于指出过滤器的位置。
卷积步长
Stride表示filter在原图片中水平方向和垂直方向每次的步进长度。之前我们默认stride=1。若stride=2,则表示filter每次步进长度为2,即隔一点移动一次。

我们用s表示stride长度,p表示padding长度,如果原始图片尺寸为n x n,filter尺寸为f x f,则卷积后的图片尺寸为:

值得一提的是,互相关(cross-correlations)与卷积(convolutions)之间是有区别的。实际上,真正的卷积运算会先将filter绕其中心旋转180度,然后再将旋转后的filter在原始图片上进行滑动计算。filter旋转如下所示:

比较而言,互相关的计算过程则不会对filter进行旋转,而是直接在原始图片上进行滑动计算。
其实,目前为止我们介绍的CNN卷积实际上计算的是互相关,而不是数学意义上的卷积。但是,为了简化计算,我们一般把CNN中的这种“互相关”就称作卷积运算。之所以可以这么等效,是因为过滤器算子一般是水平或垂直对称的,180度旋转影响不大;而且最终过滤器算子需要通过CNN网络梯度下降算法计算得到,旋转部分可以看作是包含在CNN模型算法中。总的来说,忽略旋转运算可以大大提高CNN网络运算速度,而且不影响模型性能。
立体卷积
对于3通道的RGB图片,其对应的过滤器算子同样也是3通道的。例如一个图片是6 x 6 x 3,分别表示图片的高度(height)、宽度(weight)和通道(#channel)。
3通道图片的卷积运算与单通道图片的卷积运算基本一致。过程是将每个单通道(R,G,B)与对应的filter进行卷积运算求和,然后再将3通道的和相加,得到输出图片的一个像素值。
为了简化这个3×3×3过滤器的图像,我们不把它画成3个矩阵的堆叠,而画成一个三维的立方体。

不同通道的滤波算子可以不相同。例如R通道filter实现垂直边缘检测,G和B通道不进行边缘检测,全部置零,举个例子,这个过滤器是3×3×3的,如果你想检测图像红色通道的边缘,那么你可以将第一个过滤器设为\(\begin{bmatrix}1 & 0 & - 1 \\\\ 1 & 0 & - 1 \\\\ 1 & 0 & - 1 \end{bmatrix}\),和之前一样,而绿色通道全为0,\(\begin{bmatrix} 0& 0 & 0 \\\\ 0 &0 & 0 \\\\ 0 & 0 & 0 \end{bmatrix}\),蓝色也全为0。如果你把这三个堆叠在一起形成一个3×3×3的过滤器,那么这就是一个检测垂直边界的过滤器,但只对红色通道有用。或者将R,G,B三通道filter全部设置为水平边缘检测。.
如果我们不仅仅想要检测垂直边缘怎么办?如果我们同时检测垂直边缘和水平边缘,还有45°倾斜的边缘,还有70°倾斜的边缘怎么做?换句话说,如果你想同时用多个过滤器怎么办?
我们让这个6×6×3的图像和这个3×3×3的过滤器卷积,得到4×4的输出。第一个可能是一个垂直边界检测器或者是学习检测其他的特征。第二个过滤器可以用橘色来表示,它可以是一个水平边缘检测器。

所以和第一个过滤器卷积,可以得到第一个4×4的输出,然后卷积第二个过滤器,得到一个不同的4×4的输出。我们做完卷积,然后把这两个4×4的输出堆叠在一起,这样就得到了一个4×4×2的输出立方体,这里的2的来源于我们用了两个不同的过滤器。
卷积过程中,输入层有多少个通道,滤波器就要有多少个通道,但是滤波器的数量是任意的,滤波器的数量决定了卷积后 featuremap 的通道数。
总结一下,如果有一个\(n \times n \times n_{c}\)(通道数)的输入图像,在这个例子中就是6×6×3,这里的\(n_{c}\)就是通道数目,然后卷积上一个\(f×f×n_{c}\),这个例子中是3×3×3,按照惯例,这个(前一个\(n_{c}\))和这个(后一个\(n_{c}\))必须数值相同。然后你就得到了\((n-f+1)×(n-f+1)×n_{c^{'}}\),这里\(n_{c^{'}}\)其实就是下一层的通道数,它就是你用的过滤器的个数,在这个例子中,那就是4×4×2。这个例子中,使用的步幅为1,并且没有padding。如果用了不同的步幅或者padding,那么这个\(n-f+1\)数值会变化,正如前面所讲的那样。
这里一直将最后一个维度用通道数(\(n_{c}\))来表示,但某些时候在文献里大家也把它叫做3维立方体的深度。
单层卷积神经网络
卷积神经网络的单层结构如下所示:

相比前面学的单一的卷积过程,CNN的单层结构多了激活函数ReLU和偏移量b。整个过程与标准的神经网络单层结构非常类似:
卷积运算对应着上式中的乘积运算,过滤器组数值对应着权重\(W^{[l]}\) ,所选的激活函数为ReLU。
假设你有10个过滤器,而不是2个,神经网络的一层是3×3×3,那么,这一层有多少个参数呢?我们来计算一下,每一层都是一个3×3×3的矩阵,因此每个过滤器有27个参数,也就是27个数。然后加上一个偏差,用参数\(b\)表示,现在参数增加到28个。
请注意一点,不论输入图片有多大,1000×1000也好,5000×5000也好,参数始终都是280个。用这10个过滤器来提取特征,如垂直边缘,水平边缘和其它特征。即使这些图片很大,参数却很少,这就是卷积神经网络的一个特征,叫作“避免过拟合”。
最后我们总结一下用于描述卷积神经网络中的一层(以\(l\)层为例),也就是卷积层的各种标记。
- \(f^{[l]}\)表示过滤器大小。
- \(p^{[l]}\)来表示padding的长度或宽度,padding数量也可指定为一个valid卷积,即无padding。或是same卷积,即选定padding,如此一来,输出和输入图片的高度和宽度就相同了。
- \(s^{[l]}\)表示步幅。
- \(n_{c}^{[l]}\)表示过滤器数量。
- 输入值维度:\(n_{H}^{\left\lbrack l - 1 \right\rbrack} \times n_{W}^{\left\lbrack l - 1 \right\rbrack} \times n_{c}^{\left\lbrack l - 1\right\rbrack}\),因为它是上一层的激活值,所以是\({[l-1]}\),用H和W来指代图片的高度宽度。
- 输出值维度:$$n_{H}^{[l]} \times n_{W}^{[l]} \times n_{c}^{[l]}$$
- 输出图像高度:$$n_{H}^{[l]} = \lfloor\frac{n_{H}^{\left\lbrack l - 1 \right\rbrack} +2p^{[l]} - f{[l]}}{s{[l]}} +1\rfloor$$
- 输出图像宽度:$$n_{W}^{[l]} = \lfloor\frac{n_{W}^{\left\lbrack l - 1 \right\rbrack} +2p^{[l]} - f{[l]}}{s{[l]}} +1\rfloor$$
- 过滤器维度:$$f^{[l]} \times f^{[l]} \times n_{c}^{\left\lbrack l - 1 \right\rbrack}$$
- 激活值维度:应用偏差和非线性函数之后,这一层的输出等于它的激活值\(a^{[l]}\)。即\(n_{H}^{[l]} \times n_{W}^{[l]} \times n_{c}^{[l]}\)
- 参数(权重)\(W\)的维度:过滤器的维度已知,为\(f^{[l]} \times f^{[l]} \times n_{c}^{[l - 1]}\),这只是一个过滤器的维度,有多少个过滤器,这(\(n_{c}^{[l]}\))是过滤器的数量,权重也就是所有过滤器的集合再乘以过滤器的总数量,即\(f^{[l]} \times f^{[l]} \times n_{c}^{[l - 1]} \times n_{c}^{[l]}\)
- 偏差b:每个过滤器都有一个偏差参数,它是一个实数。偏差表示为一个1×1×1×\(n_{c}^{[l]}\)的四维向量或四维张量。偏差包含了这些变量,它是该维度上的一个向量。
池化层
Pooling layers是CNN中用来减小尺寸,提高运算速度的,同样能减小noise影响,让各特征更具有健壮性。
Pooling layers的做法比convolution layers简单许多,没有卷积运算,仅仅是在滤波器算子滑动区域内取最大值,即max pooling,这是最常用的做法。注意,超参数p很少在pooling layers中使用。

Max pooling的好处是只保留区域内的最大值(特征),忽略其它值,降低noise影响,提高模型健壮性。而且,max pooling需要的超参数仅为滤波器尺寸f和滤波器步进长度s,没有其他参数需要模型训练得到,计算量很小。
如果是多个通道,那么就每个通道单独进行max pooling操作。
除了max pooling之外,还有一种做法:average pooling。顾名思义,average pooling就是在滤波器算子滑动区域计算平均值。

实际应用中,max pooling比average pooling更为常用。
卷积神经网络示例
下面介绍一个简单的数字识别的CNN例子:

图中,CONV层后面紧接一个POOL层,CONV1和POOL1构成第一层,CONV2和POOL2构成第二层,这是因为POOL层没有权重,所以一般在计算神经网络层数时会把相邻的两个卷积层和池化层合并到一起计算为神经网络的一层。
现在将POOL2平整化为一个大小为400的一维向量。我们可以把平整化结果想象成这样的一个神经元集合,然后利用这400个单元构建下一层。下一层含有120个单元,这就是我们第一个全连接层,标记为FC3。这400个单元与120个单元紧密相连,这就是全连接层。它很像之前看过的单神经网络层,它的权重矩阵维度为120×400。,因为这400个单元与这120个单元的每一项连接,所以就是所谓的“全连接”。还有一个偏差参数b。最后输出120个维度,因为有120个输出,那么这一层的参数数量就应该是400*120+120=48120个。
最后的输出层(softmax)由10个神经元构成。
整个网络各层的尺寸和参数如下表格所示:

为什么选择卷积结构?
和只用全连接层相比,卷积层的两个主要优势在于参数共享和稀疏连接,举例说明一下。
假设一个网络有全连接层,这就类似于之前的最普通的神经网络结构

这样就有3072*4704个参数,14,450,688个参数
但是如果用卷积操作来进行特征的提取,那么根据前面讲的那个计算公式,可以得到每个过滤器的尺寸是5x5,一共有25个参数,再加上一个偏置参数,那么一个过滤器就是26个参数,一共6个过滤器,共156个参数。
参数数目少的原因有两个:
-
参数共享:一个特征检测器(例如垂直边缘检测)对图片某块区域有用,同时也可能作用在图片其它区域。观察发现,特征检测如垂直边缘检测如果适用于图片的某个区域,那么它也可能适用于图片的其他区域。也就是说,如果你用一个3×3的过滤器检测垂直边缘,那么图片的左上角区域,以及旁边的各个区域(左边矩阵中蓝色方框标记的部分)都可以使用这个3×3的过滤器。

-
稀疏连接:因为滤波器算子尺寸限制,每一层的每个输出只与输入部分区域内有关。这个0是通过3×3的卷积计算得到的,它只依赖于这个3×3的输入的单元格,右边这个输出单元(元素0)仅与36个输入特征中9个相连接。而且其它像素值都不会对输出产生任影响,再举一个例子,这个输出(右边矩阵中红色标记的元素 30)仅仅依赖于这9个特征(左边矩阵红色方框标记的区域),只有这9个输入特征与输出相连接,其它像素对输出没有任何影响。

神经网络可以通过这两种机制减少参数,以便我们用更小的训练集来训练它,从而预防过度拟合。
卷积神经网络还有一个平移不变性。
在欧几里得几何中,平移是一种几何变换,表示把一幅图像或一个空间中的每一个点在相同方向移动相同距离。比如对图像分类任务来说,图像中的目标不管被移动到图片的哪个位置,得到的结果(标签)应该是相同的,这就是卷积神经网络中的平移不变性。
平移不变性意味着系统产生完全相同的响应(输出),不管它的输入是如何平移的 。
简单地说,卷积+最大池化约等于平移不变性。
卷积:简单地说,图像经过平移,相应的特征图上的表达也是平移的。
下图只是一个为了说明这个问题的例子。输入图像的左下角有一个人脸,经过卷积,人脸的特征(眼睛,鼻子)也位于特征图的左下角。

假如人脸特征在图像的左上角,那么卷积后对应的特征也在特征图的左上角。

在神经网络中,卷积被定义为不同位置的特征检测器,也就意味着,无论目标出现在图像中的哪个位置,它都会检测到同样的这些特征,输出同样的响应。比如人脸被移动到了图像左下角,卷积核直到移动到左下角的位置才会检测到它的特征。
池化:比如最大池化,它返回感受野中的最大值,如果最大值被移动了,但是仍然在这个感受野中,那么池化层也仍然会输出相同的最大值。这就有点平移不变的意思了。
所以这两种操作共同提供了一些平移不变性,即使图像被平移,卷积保证仍然能检测到它的特征,池化则尽可能地保持一致的表达。
感受野:指的是神经网络中神经元“看到的”输入区域,在卷积神经网络中,feature map上某个元素的计算受输入图像上某个区域的影响,这个区域即该元素的感受野。

卷积神经网络中,越深层的神经元看到的输入区域越大,如上图所示,kernel size 均为3×3,stride均为1,绿色标记的是Layer2 每个神经元看到的区域,黄色标记的是Layer3 看到的区域,具体地,Layer2每个神经元可看到Layer1 上 3×3 大小的区域,Layer3 每个神经元看到Layer2 上 3×3 大小的区域,该区域可以又看到Layer1 上 5×5 大小的区域。
所以,感受野是个相对概念,某层feature map上的元素看到前面不同层上的区域范围是不同的,通常在不特殊指定的情况下,感受野指的是看到输入图像上的区域。

浙公网安备 33010602011771号