第一周 卷积神经网络(Foundations of Convolutional Neural Networks)
计算机视觉(Computer vision)
计算机视觉是一个飞速发展的一个领域,这多亏了深度学习。
- 深度学习与计算机视觉可以帮助汽车,查明周围的行人和汽车,并帮助汽车避开它们。
- 还使得人脸识别技术变得更加效率和精准,你们即将能够体验到或早已体验过仅仅通过刷脸就能解锁手机或者门锁。
- 当你解锁了手机,我猜手机上一定有很多分享图片的应用。在上面,你能看到美食,酒店或美丽风景的图片。
- 有些公司在这些应用上使用了深度学习技术来向你展示最为生动美丽以及与你最为相关的图片。
- 机器学习甚至还催生了新的艺术类型。
深度学习之所以让我兴奋有下面两个原因,我想你们也是这么想的。
第一,计算机视觉的高速发展标志着新型应用产生的可能,这是几年前,人们所不敢想象的。通过学习使用这些工具,你也许能够创造出新的产品和应用。
其次,即使到头来你未能在计算机视觉上有所建树,但我发现,人们对于计算机视觉的研究是如此富有想象力和创造力,由此衍生出新的神经网络结构与算法,这实际上启发人们去创造出计算机视觉与其他领域的交叉成果。举个例子,之前我在做语音识别的时候,我经常从计算机视觉领域中寻找灵感, 并将其应用于我的文献当中。所以即使你在计算机视觉方面没有做出成果,我也希望你也可以将所学的知识应用到其他算法和结构。就介绍到这儿,让我们开始学习吧。
这是我们本节课将要学习的一些问题,你应该早就听说过图片分类,或者说图片识别。比如给出这张 64×64 的图片,让计算机去分辨出这是一只猫。
还有一个例子,在计算机视觉中有个问题叫做目标检测,比如在一个无人驾驶项目中,你不一定非得识别出图片中的物体是车辆,但你需要计算出其他车辆的位置,以确保自己能够避开它们。所以在目标检测项目中,首先需要计算出图中有哪些物体,比如汽车,还有图片中的其他东西,再将它们模拟成一个个盒子,或用一些其他的技术识别出它们在图片中的位置。注意在这个例子中,在一张图片中同时有多个车辆,每辆车相对与你来说都有一个确切的距离。
还有一个更有趣的例子,就是神经网络实现的图片风格迁移,比如说你有一张图片,但你想将这张图片转换为另外一种风格。所以图片风格迁移,就是你有一张满意的图片和一张风格图片,实际上右边这幅画是毕加索的画作,而你可以利用神经网络将它们融合到一起,描绘出一张新的图片。它的整体轮廓来自于左边,却是右边的风格,最后生成下面这张图片。这种神奇的算法创造出了新的艺术风格,所以在这门课程中,你也能通过学习做到这样的事情。
但在应用计算机视觉时要面临一个挑战,就是数据的输入可能会非常大。举个例子,在过去的课程中,你们一般操作的都是 64×64 的小图片,实际上,它的数据量是 64×64×3,因为每张图片都有 3 个颜色通道。如果计算一下的话,可得知数据量为 12288,所以我们的特征向量 \(x\) 维度为 12288。这其实还好,因为 64×64 真的是很小的一张图片。
如果你要操作更大的图片,比如一张 1000×1000 的图片,它足有 1 兆那么大,但是特征向量的维度达到了 1000×1000×3,因为有 3 个 RGB 通道,所以数字将会是 300 万。如果你在尺寸很小的屏幕上观察,可能察觉不出上面的图片只有 64×64 那么大,而下面一张是 1000×1000 的大图。
如果你要输入 300 万的数据量,这就意味着,特征向量 \(x\) 的维度高达 300 万。所以在第一隐藏层中,你也许会有 1000 个隐藏单元,而所有的权值组成了矩阵 \(W^{[1]}\)。如果你使用了标准的全连接网络,就像我们在第一门和第二门的课程里说的,这个矩阵的大小将会是 1000×300 万。因为现在 \(x\) 的维度为 \(3m\),\(3m\) 通常用来表示 300 万。这意味着矩阵 \(W^{[1]}\) 会有 30 亿个参数,这是个非常巨大的数字。在参数如此大量的情况下,难以获得足够的数据来防止神经网络发生过拟合和竞争需求,要处理包含 30 亿参数的神经网络,巨大的内存需求让人不太能接受。
但对于计算机视觉应用来说,你肯定不想它只处理小图片,你希望它同时也要能处理大图。为此,你需要进行卷积计算,它是卷积神经网络中非常重要的一块。下节课中,我会为你介绍如何进行这种运算,我将用边缘检测的例子来向你说明卷积的含义。
边缘检测示例(Edge detection example)
卷积运算是卷积神经网络最基本的组成部分,使用边缘检测作为入门样例。接下来,你会看到卷积是如何进行运算的。
在之前的课程中,我说过神经网络的前几层是如何检测边缘的,然后,后面的层有可能检测到物体的部分区域,更靠后的一些层可能检测到完整的物体,这个例子中就是人脸。在这一节中,你会看到如何在一张图片中进行边缘检测。
让我们举个例子,给了这样一张图片,让电脑去搞清楚这张照片里有什么物体,你可能做的第一件事是检测图片中的垂直边缘。比如说,在这张图片中的栏杆就对应垂直线,与此同时,这些行人的轮廓线某种程度上也是垂线,这些线是垂直边缘检测器的输出。同样,你可能也想检测水平边缘,比如说这些栏杆就是很明显的水平线,它们也能被检测到,结果在这。所以如何在图像中检测这些边缘?
看一个例子,这是一个 6×6 的灰度图像。因为是灰度图像,所以它是 6×6×1 的矩阵,而不是 6×6×3 的,因为没有 RGB 三通道。为了检测图像中的垂直边缘,你可以构造一个 3×3 矩阵。在共用习惯中,在卷积神经网络的术语中,它被称为过滤器。我要构造一个 3×3 的过滤器,像这样 \(\begin{bmatrix} 1 & 0 & -1 \\ 1 & 0 & -1 \\ 1 & 0 & -1 \end{bmatrix}\)。在论文它有时候会被称为核,而不是过滤器,但此处,我将使用过滤器这个术语。对这个 6×6 的图像进行卷积运算,卷积运算用 * 来表示,用 3×3 的过滤器对其进行卷积。
关于使用符号 * 表示卷积运算,有一些问题,在数学中 * 就是卷积的标准标志,但是在 Python 中,这个标识常常被用来表示乘法或者元素乘法。所以这个 * 有多层含义,它是一个重载符号,所以这里当使用 * 表示卷积的时候我会特别说明。
这个卷积运算的输出将会是一个 4×4 的矩阵,你可以将它看成一个 4×4 的图像。下面来说明是如何计算得到这个 4×4 矩阵的。为了计算第一个元素,在 4×4 左上角的那个元素,使用 3×3 的过滤器,将其覆盖在输入图像,如下图所示。然后进行元素乘法(element-wise products)运算,所以 \(\begin{bmatrix} 3 \times 1 & 0 \times 0 & 1 \times -1 \\ 1 \times 1 & 5 \times 0 & 8 \times -1 \\ 2 \times 1 & 7 \times 0 & 2 \times - 1 \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\)
接下来也是一样,继续右移一步,把 9 个数的点积加起来得到 0,继续移得到 8。
接下来为了得到下一行的元素,现在把蓝色块下移,重复进行元素乘法,然后加起来。通过这样做得到 -10。再将其右移得到 -2,接着是 2,3。以此类推,这样计算完矩阵中的其他元素。为了解释的得更清楚一点,最后的那个 -16 是通过底部右下角的 3×3 区域得到的。
因此 6×6 矩阵和 3×3 矩阵进行卷积运算得到 4×4 矩阵。这些图片和过滤器是不同维度的矩阵,可以把左边的矩阵理解为一张图片,中间的矩阵理解为过滤器,右边的图片我们可以理解为另一张图片。这个就是垂直边缘检测器。
如果你要使用编程语言实现这个运算,不同的编程语言有不同的函数,而不是用 * 来表示卷积。所以在编程练习中,你会使用一个叫 conv_forward 的函数。如果在 tensorflow 下,这个函数叫 tf.conv2d。在其他深度学习框架中,在后面的课程中,你将会看到 Keras 这个框架,在这个框架下用 Conv2D 实现卷积运算。所有的编程框架都有一些函数来实现卷积运算。
为什么这个可以做垂直边缘检测呢?让我们来看另外一个例子。
为了讲清楚,我会用一个简单的例子。如下图左边是一个简单的 6×6 图像。其中,左边一半是 10,右边一半是 0。如果你把它当成一个图片,左边那部分看起来是白色的,像素值 10 是比较亮的像素值,右边像素值比较暗,我使用灰色来表示 0,尽管它也可以被画成黑的。图片里,有一个特别明显的垂直边缘在图像中间,这条垂直线是从黑到白的过渡线,或者从白色到深色。
所以,当你用一个 3×3 过滤器进行卷积运算的时候,这个 3×3 的过滤器可视化为在左边有明亮的像素,然后有一个过渡,0 在中间,然后右边是深色的。卷积运算后,你得到的是右边的矩阵。如果你愿意,可以通过数学运算去验证。举例来说,最左上角的元素 0,就是由这个 3×3 块(绿色方框标记)经过元素乘积运算再求和得到的 :
\(10×1+10×1+10×1+10×0+10×0+10×0+10×(-1)+10×(-1)+10×(-1)=0\)
相反 30 是由这个(红色方框标记)得到的 :
\(10×1+10×1+10×1+10×0+10×0+10×0+0×(-1)+0×(-1)+ 0×(-1)=30\)
如果把最右边的矩阵当成图像,那么它会是一个在中间有段亮一点的区域,对应检查到这个 6×6 图像中间的垂直边缘。这里的维数似乎有点不正确,检测到的边缘太粗了。因为在这个例子中,图片太小了。如果你用一个 1000×1000 的图像,而不是 6×6 的图片,你会发现其会很好地检测出图像中的垂直边缘。在这个例子中,在输出图像中间的亮处,表示在图像中间有一个特别明显的垂直边缘。从垂直边缘检测中可以得到的启发是,因为我们使用 3×3 的矩阵(过滤器),所以垂直边缘是一个 3×3 的区域,左边是明亮的像素,中间的并不需要考虑,右边是深色像素。在这个 6×6 图像的中间部分,明亮的像素在左边,深色的像素在右边,就被视为一个垂直边缘,卷积运算提供了一个方便的方法来发现图像中的垂直边缘。
所以你已经了解卷积是怎么工作的,在下一节中,你将会看到如何使用卷积运算作为卷积神经网络的基本模块的。
更多边缘检测内容(More edge detection)
你已经见识到用卷积运算实现垂直边缘检测,接下来,你将学习如何区分正边和负边,实际就是 由亮到暗 与 由暗到亮 的区别,也就是边缘的过渡。你还可以了解到其他类型的边缘检测以及如何去实现这些算法,而不要总想着去自己编写一个边缘检测程序,让我们开始吧。
Vertical edge detection examples
还是上一节中的例子,下面这张 6×6 的图片,左边较亮,而右边较暗,将它与垂直边缘检测滤波器进行卷积,检测结果就显示在了右边图的中间部分。
现在将它的颜色翻转一下,变成左边比较暗,而右边比较亮。现在亮度为 10 的点跑到了右边,为 0 的点则跑到了左边。如果你使用相同的过滤器进行卷积,最后得到的图中间会是 -30,而不是 30。如果你将矩阵转换为图片,就会发现中间的过渡部分被翻转了,之前的 30 翻转成了 -30,表明是由暗向亮过渡,而不是由亮向暗过渡。
如果你不在乎这两者的区别,你可以取出矩阵的绝对值。但这个特定的过滤器确实可以为我们区分这两种明暗变化的区别。
Vertical and Horizontal edge detection examples
如下图,我们已经见过了左边的 3×3 过滤器,它可以检测出垂直的边缘。所以,对于右边这个过滤器,它能让你检测出水平的边缘。
一个更复杂些的例子关于水平检测,如果你用下面左侧的图与水平边缘过滤器卷积,就会对应得到右边的矩阵。
30 代表由亮到暗,发现了正边缘。-30 代表由暗到亮,发现了一条负边。
再次强调,现在所使用的都是相对很小的图片,仅有 6×6。如果这个一个非常大的 1000×1000 的大图,就不会出现这些亮度为 10 的过渡带了,因为图片尺寸很大,这些中间值就会变得非常小。
总而言之,通过使用不同的过滤器,你可以找出垂直的或是水平的边缘。但事实上,对于这个 3×3 的过滤器来说,我们只使用了其中的一种数字组合。
但在计算机视觉的历史文献中,曾公平地争论过怎样的数字组合才是最好的,所以你还可以使用这种:\(\begin{bmatrix}1 & 0 & - 1 \\ 2 & 0 & - 2 \\ 1 & 0 & - 1 \end{bmatrix}\),叫做 Sobel 的过滤器,它的优点在于增加了中间一行元素的权重,这使得结果的鲁棒性会更高一些。
同样,计算机视觉的研究者们也会经常使用其他的数字组合,比如这种:\(\begin{bmatrix} 3 & 0 & - 3 \\ 10 & 0 & - 10 \\ 3 & 0 & - 3 \end{bmatrix}\),这叫做 Scharr 过滤器,它有着和之前完全不同的特性,实际上也是一种垂直边缘检测,如果你将其翻转 90 度,你就能得到对应水平边缘检测。
随着深度学习的发展,我们学习的其中一件事就是需要检测出更加复杂的图像的边缘,所以你不一定要去使用那些研究者们所选择的这 9 个数字,但我们也是从中获益匪浅,得到很多启发。所以,你可以把这矩阵中的 9 个数字当成 9 个参数,并且在之后你可以使用反向传播算法来学习它们,其目标就是让算法自己去理解这 9 个参数。
这种全参数的过滤器对于数据的捕捉能力甚至可以胜过任何之前的那些手写的过滤器。它可以检测出 45° 或 70° 或 73°,甚至是任何角度的边缘。所以将矩阵的所有数字都设置为参数,通过数据反馈,让神经网络自动去学习它们,我们会发现神经网络可以学习一些低级的特征,尽管比起那些研究者们,我们要更费劲一些。
所以这种将这 9 个数字当成参数的思想,已经成为计算机视觉中最为有效的思想之一。下一周的课程,我们将详细去探讨如何使用反向传播去让神经网络学习这 9 个数字。但在此之前,我们需要先讨论一些其它细节,比如一些基础的卷积运算的变量。在下面两小节中,我将与你们讨论如何去使用 padding,以及卷积各种不同的发展,这两节内容将会是卷积神经网络中卷积模块的重要组成部分。
Padding
为了构建深度神经网络,你需要学会使用的一个基本的卷积操作就是 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 的区域与之重叠。所以那些在角落或者边缘区域的像素点在输出中采用较少,意味着你丢掉了图像边缘位置的许多信息。
为了解决这些问题,你可以在卷积操作之前填充这幅图像。在这个案例中,你可以沿着图像边缘再填充一层像素。如果你这样操作了,那么 6×6 的图像就被你填充成了一个 8×8 的图像。如果你用 3×3 的图像对这个 8×8 的图像卷积,你得到的输出就不是 4×4 的,而是 6×6 的图像,你就得到了一个尺寸和原始图像一样的 6×6 的图像。习惯上,你可以用 0 去填充,如果 \(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\) 是一个奇数的时候,只要选择相应的填充尺寸,你就能确保得到和输入相同尺寸的输出。所以前面的例子中,当过滤器是 3×3 时,要使得输出尺寸等于输入尺寸,所需要的填充是 (3-1) / 2,也就是 1 个像素。另一个例子,当你的过滤器是 5×5 时,即:\(f=5\),然后代入那个式子,你就会发现需要 2 层填充才能使得输出和输入一样大,这是过滤器 5×5 的情况。
习惯上,计算机视觉中,\(f\) 通常是奇数,甚至可能都是这样。你很少看到一个偶数的过滤器在计算机视觉里使用,我认为有两个原因。
其中一个可能是,如果 \(f\) 是一个偶数,那么你只能使用一些不对称填充。只有 \(f\) 是奇数的情况下,Same 卷积才会有自然的填充,我们可以以同样的数量填充四周,而不是左边填充多一点,右边填充少一点,这样不对称的填充。
第二个原因是当你有一个奇数维过滤器,比如 3×3 或者 5×5 的,它就有一个中心点。有时在计算机视觉里,如果有一个中心像素点会更方便,便于指出过滤器的位置。
也许这些都不是为什么 \(f\) 通常是奇数的充分原因,但如果你看了卷积的文献,你经常会看到 3×3 的过滤器,你也可能会看到一些 5×5,7×7 的过滤器。后面我们也会谈到 1×1 的过滤器,以及什么时候它是有意义的。但是习惯上,我推荐你只使用奇数的过滤器。我想如果你使用偶数 \(f\) 也可能会得到不错的表现,如果遵循计算机视觉的惯例,我通常使用奇数值的 \(f\)。
你已经看到如何使用 padding 卷积,为了指定卷积操作中的 padding,你可以指定 \(p\) 的值。也可以使用 Valid 卷积,也就是 \(p=0\)。也可使用 Same 卷积填充像素,使你的输出和输入大小相同。以上就是 padding,在下一节中我们将讨论如何在卷积中设置步长。
卷积步长(Strided convolutions)
卷积中的步长是另一个构建卷积神经网络的基本操作,我们来举例说明。
如果用 3×3 的过滤器卷积这个 7×7 的图像,和之前不同的是,我们把步长设置成了 2。还和之前一样取左上方的 3×3 区域的元素的乘积,再加起来,最后结果为 91。
之前我们移动蓝框的步长是 1,现在移动的步长是 2,我们让过滤器跳过 2 个步长。然后你还是将每个元素相乘并求和,你将会得到的结果是 100。
继续将蓝色框移动两个步长,你将会得到 83 的结果。
当你移动到下一行的时候,你也是使用步长 2 而不是步长 1,所以我们将蓝色框移动到如下图所示的位置,注意我们跳过了一个位置,最后得到 69 的结果。
继续移动两个步长,会得到 91,127,最后一行分别是 44,72,74。
所以在这个例子中,我们用 3×3 的矩阵卷积一个 7×7 的矩阵,得到一个 3×3 的输出。输入和输出的维度是由下面的公式决定的。如果你用一个 \(f×f\) 的过滤器卷积一个 \(n×n\) 的图像,你的 padding 为 \(p\),步幅为 \(s\),在这个例子中 \(s=2\),你会得到一个输出,因为现在你不是一次移动一个步子,而是一次移动 \(s\) 个步子,输出于是变为\(\left[\frac{n+2p - f}{s} + 1 \right] \times \left[ \frac{n+2p - f}{s} + 1 \right]\)
在我们的这个例子里,\(n=7,p=0,f=3,s=2,\frac{7 + 0 - 3}{2} + 1 =3\),即 3×3 的输出。
现在只剩下最后的一个细节了,如果商不是一个整数怎么办? 在这种情况下,我们向下取整。\(\sqcup\) 是向下取整的符号,这也叫做地板除 (floor),这意味着向下取整到最近的整数。原则实现的方式是,只有所有蓝框(一个过滤器维度的像素点)必须完全处于图像中或被填充后的图像内部时,才对它进行运算。如果有任意一个蓝框移动到了外面,那就不对其进行相乘操作,这是惯例。因此正确计算输出维度的方法是向下取整,以免 \(\frac{n + 2p - f}{s}\) 不是整数。
总结一下维度情况,如果你有一个 \(n×n\) 的矩阵或者 \(n×n\) 的图像,与一个 \(f×f\) 的矩阵卷积,或者说 \(f×f\) 的过滤器。Padding 是 \(p\),步长为 \(s\),那么输出的尺寸就是这样:
\(\left[\frac{n+2p - f}{s} + 1 \right] \times \left[ \frac{n+2p - f}{s} + 1 \right]\)
你可以尽量选择使结果是整数的数,虽然一些时候,你不必这样做,只要向下取整也是可以的。同样,你也可以自己选择一些 \(n\),\(f\),\(p\) 和 \(s\) 的值来验证这个输出尺寸的公式是对的。
在讲下一部分之前,这里有一个关于互相关和卷积的技术性建议,这不会影响到你构建卷积神经网络的方式,但取决于你读的是数学教材还是信号处理教材,在不同的教材里符号可能不一致。如果你看的是一本典型的数学教科书,那么卷积的定义是做元素乘积求和,实际上还有一个步骤是你首先要做的,也就是在把这个 6×6 的矩阵和 3×3 的过滤器卷积之前,首先你将 3×3 的过滤器沿副对角线翻转,所以 \(\begin{bmatrix} 3 & 4 & 5 \\ 1 & 0 & 2 \\ - 1 & 9 & 7 \end{bmatrix}\) 变为 \(\begin{bmatrix} 7 & 2 & 5 \\ 9 & 0 & 4 \\ - 1 & 1 & 3 \end{bmatrix}\)。然后使用这个新的过滤器进行卷积运算。
我们在之前课程中定义卷积运算时,跳过了这个翻转操作。从技术上讲,我们实际上做的,有时被称为互相关(cross-correlation)而不是卷积(convolution)。但在深度学习文献中,按照惯例,我们也将它(不进行翻转操作的)叫做卷积操作。
总结来说,按照机器学习的惯例,我们通常不进行翻转操作。从技术上说,这个操作可能叫做互相关更好。但在大部分的深度学习文献中都把它叫做卷积运算,因此我们在本课程中使用这个约定。如果你读了很多机器学习文献的话,你会发现许多人都把它叫做卷积运算,不需要用到这些翻转。
事实证明在信号处理或某些数学分支中,在卷积的定义中包含了 翻转
这个操作,从而使得卷积运算符拥有了下面这个性质,即:
\((A * B) * C = A * (B * C)\),
这在数学中被称为结合律。这对于一些信号处理应用来说很好,但对于深度神经网络来说它真的不重要,因此省略了这个翻转操作,就简化了代码,而且神经网络也能正常工作。
根据惯例,我们大多数人都叫它 卷积,尽管数学家们更喜欢称之为互相关,但这不会影响到你在编程练习中要实现的任何东西,也不会影响你阅读和理解深度学习文献。
现在你已经看到了如何进行 卷积,以及如何使用 填充,如何在卷积中选择 步长。但到目前为止,我们所使用的是关于矩阵的卷积,例如 6×6 的矩阵。在下一节中,你将看到如何对立体进行卷积,这将会使你的卷积变得更加强大,让我们继续下一节的内容。
三维卷积(Convolutions over volumes)
你已经知道如何对二维图像做卷积了,现在看看如何执行卷积不仅仅在二维图像上,而是三维立体上。
我们从一个例子开始,假如说你不仅想检测灰度图像的特征,也想检测 RGB 彩色图像的特征。彩色图像如果是 6×6×3,这里的 3 指的是三个颜色通道,你可以把它想象成三个 6×6 图像的堆叠。为了检测图像的边缘或者其他的特征,不是把它跟原来的 3×3 的过滤器做卷积,而是跟一个三维的过滤器,它的维度是 3×3×3,如此,这个过滤器也有三层,对应红、绿、蓝三个通道。
如上图,第一个 6 代表图像高度,第二个 6 代表宽度,3 代表通道的数目。同样你的过滤器也有一个高,宽和通道数,并且 图像的通道数必须和过滤器的通道数匹配,所以图中紫色方框标记的两个数必须相等。注意,最后的输出是一个 4×4×1 的图像,而不是 4×4×3 了。
我们来研究下这背后的细节,换一张好看的图片。如下图是一个 6×6×3 的图像,还有一个 3×3×3 的过滤器。为了简化这个 3×3×3 过滤器的图像,我们不把它画成 3 个矩阵的堆叠,而是画成一个三维的立方体。
为了计算这个卷积操作的输出,你要做的就是把这个 3×3×3 的过滤器先放到最左上角的位置,这个 3×3×3 的过滤器有 27 个数,27 个参数就是 3 的立方。依次取这 27 个数,然后乘以相应的红绿蓝通道中的数字。先取红色通道的前 9 个数字,然后是绿色通道,然后再是蓝色通道,乘以左边黄色立方体覆盖的对应的 27 个数,然后把这些数都加起来,就得到了输出的第一个数字。
如果要计算下一个输出,你把这个立方体滑动一个单位,再与这 27 个数相乘,再把它们都加起来,就得到了下一个输出,以此类推。
那么,这个能干什么呢?举个例子,这个过滤器是 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}\)。如果你把这三个堆叠在一起就形成了一个 3×3×3 的过滤器,那么这就是一个检测垂直边界的过滤器,但只对红色通道有用。
或者如果你不关心垂直边界在哪个颜色通道里,那么你可以用一个这样的过滤器:
\(\begin{bmatrix} 1 & 0 & - 1 \\ 1 & 0 & - 1 \\ 1 & 0 & - 1 \end{bmatrix}\),\(\begin{bmatrix} 1 & 0 & - 1 \\ 1 & 0 & - 1 \\ 1 & 0 & - 1 \end{bmatrix}\),\(\begin{bmatrix}1 & 0 & - 1 \\ 1 & 0 & - 1 \\ 1 & 0 & - 1 \end{bmatrix}\),
所有三个通道都是这样。你就有了一个 3×3×3 的边界检测器,用来检测任意颜色通道里的边界。通过设置不同的过滤器参数,你就可以得到不同的特征检测器,所有的都是 3×3×3 的过滤器。
按照计算机视觉的惯例,当你的输入有特定的高宽和通道数时,你的过滤器也可以有不同的高,不同的宽,但通道数必须相等。理论上,如果我们的过滤器只关注红、绿、蓝通道中的其中一个也是可行的。
再注意一下这个立方体卷积时的维度情况 :一个 6×6×3 的输入图像上卷积一个 3×3×3 的过滤器,得到一个 4×4×1 的二维输出。
现在你已经了解了如何对立方体卷积,还有最后一个概念,对建立卷积神经网络至关重要。就是,如果我们不仅仅想要检测垂直边缘,同时还想检测水平边缘,还有 45° 倾斜的边缘,以及 70° 倾斜的边缘呢?该怎么做?换句话说,如果你想 同时使用多个过滤器 该怎么办?
如下图,其中有两个 3×3×3 的过滤器。我们让这个 6×6×3 的图像和第一个 3×3×3 的过滤器(黄色)卷积,得到一个 4×4×1 的输出。它可能是一个垂直边界检测器或者是其他特征的检测器。然后,让这个 6×6×3 的图像再和第二个过滤器(橘色)进行卷积运算,它可以是一个水平边缘检测器,从而得到另一个不同的 4×4×1 的输出。接着,我们取第一个过滤器输出把它放到前面,然后取第二个过滤器输出放到后面。堆叠在一起之后,这样你就都得到了一个 4×4×2 的输出立方体。这里的 2 来源于我们用了两个不同的过滤器。
总结一下维度,如果你有一个 \(n \times n \times n_{c}\)(通道数)的输入图像,在这个例子中就是 6×6×3,这里的 \(n_{c}\) 就是通道数目,然后卷积上一个 \(f×f×n_{c}\) 的过滤器,这个例子中是 3×3×3,按照惯例,前一个 \(n_{c}\) 和后一个 \(n_{c}\) 必须数值相同。然后你就得到了 \(\left[\frac{n+2p - f}{s} + 1 \right] \times \left[ \frac{n+2p - f}{s} + 1 \right] \times n_c^{'}\) 的输出,这里 \(n_c^{'}\) 是指你用的过滤器的个数,在我们的例子中就是 4×4×2。我写下这个假设时,用的 \(s = 1\),并且 \(p = 0\)。
这个对立方体卷积的概念真的很有用,你现在可以用它的一小部分直接在三个通道的 RGB 图像上进行操作。更重要的是,你可以检测两个特征,比如垂直和水平边缘或者 10 个或者 128 个或者几百个不同的特征,并且输出的通道数会等于你要检测的特征数。
对于这里的符号,我一直用通道数(\(n_{c}\))来表示最后一个维度,在文献里大家也把它叫做 3 维立方体的深度。这两个术语,即通道或者深度,经常被用在文献中。但我觉得深度容易让人混淆,因为你通常也会说神经网络的深度。所以,在这们课程里我会用通道这个术语来表示过滤器的第三个维度的大小。
现在,你已经知道怎么对立方体做卷积了,你已经准备好了实现卷积神经网络的其中一层了,在下一节里让我们看看具体是怎么做的。
单层卷积网络(One layer of a convolutional network)
那么该如何构建卷积神经网络的卷积层呢?下面让我们来看一下。
上节课中,我们使用了两个过滤器卷积处理一个三维图像,并输出两个不同的 4×4×1 矩阵。
现在,我们需要在每一个 4×4×1 矩阵后面增加偏差(不同的过滤器对应不同的偏差),它是一个实数,通过 Python 的广播机制给这 16 个元素都加上同一偏差。然后应用一个非线性激活函数 ReLU,输出结果是两个 4×4×1 矩阵。
接着,重复我们之前的步骤,把这两个矩阵堆叠起来,最终得到一个 4×4×2 的矩阵。
总结来说,我们从 6×6×3 的输入推导出一个 4×4×2 矩阵,它是卷积神经网络的一层。
注意前向传播中一个操作就是 \(z^{[1]} = W^{[1]}a^{[0]} + b^{[1]}\),执行非线性函数得到 \(a^{[1]}\),即 \(a^{[1]} = g(z^{[1]})\)。这里的输入是 \(a^{\left\lbrack 0\right\rbrack}\),也就是 \(x\),这些过滤器用变量 \(W^{[1]}\) 表示。在卷积过程中,我们对这 27 个数进行操作,其实是 27×2,因为我们用了两个过滤器,我们取这些数与 \(a^{[1]}\) 做矩阵乘法,这就是卷积操作,它的作用类似于 \(W^{[1]}a^{[0]}\),然后加上偏差 \(b^{[1]}\),可以看到,它实际上执行了一个线性函数,并最后输出两个 4×4×1 的矩阵。最后应用非线性函数,得到一个 4×4×2 的矩阵,成为神经网络的下一层的输入,也就是 \(a^{[1]}\)。
如上图,这就是 \(a^{[0]}\) 到 \(a^{[1]}\) 的演变过程,示例中我们有两个过滤器,因此我们才最终得到一个 4×4×2 的输出。但如果我们用了 10 个过滤器,那么最后会得到一个 4×4×10 维度的输出图像,也就是 \(a^{[1]}\)。
为了加深理解,我们来做一个练习。假设你有 10 个 3×3×3 的过滤器,那么,这一层有多少个参数呢? 我们来计算一下,每一个过滤器都是一个 3×3×3 的矩阵,也就是 27 个参数,加上一个偏差项 \(b\),现在参数增加到 28 个。这是一个过滤器的参数,现在我们有 10 个,那就是 28×10,也就是 280 个参数。
请注意一点,不论输入图片有多大,1000×1000 也好,5000×5000 也好,参数始终都是 280 个。用这 10 个过滤器来提取特征,如垂直边缘,水平边缘和其它特征。即使这些图片很大,参数却很少,这就是卷积神经网络的一个特点,叫作 避免过拟合。
至此,你已经知道了如何从大图片中提取 10 个特征,并同时保持参数的数量固定不变。
最后我们总结一下用于描述卷积神经网络中的一层(以\(l\)层为例),也就是卷积层的各种标记。
Summary of notation - If layer \(l\) is a convolution layer :
\(f^{[l]}\) = filter size
\(p^{[l]}\) = padding, 可指定为 valid 卷积,即无 padding。或 same 卷积,即选定 padding 使输出和输入图片的高度和宽度相同
\(s^{[l]}\) = stride
\(n_c^{[l]}\) = number of filters in layer \(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\)
Each filter is : \(f^{[l]} \times f^{[l]} \times n_c^{[l-1]}\)
Activations : \(a^{[l]} \to n_H^{[l]} \times n_W^{[l]} \times n_c^{[l]}\)
Weights : \(f^{[l]} \times f^{[l]} \times n_c^{[l-1]} \times n_c^{[l]}\)
bias : \(b^{[l]} - (1, 1, 1, n_c^{[l]})\)
Input : \(A^{[l-1]} \to m \times n_H^{[l-1]} \times n_W^{[l-1]} \times n_c^{[l-1]}\)
Outout : \(A^{[l]} \to m \times n_H^{[l]} \times n_W^{[l]} \times n_c^{[l]}\)
上面是我们最常用的卷积相关的符号,但卷积有很多种标记方法,大家在线搜索或查看开源代码时,会发现,卷积中关于高度,宽度以及通道的顺序并没有完全统一的标准,所以在查看 GitHub 上的源代码或阅读一些开源实现的时候,你会发现有些作者会采用把通道放在首位的编码标准,有时所有变量都采用这种标准。实际上在某些架构中,当卷积图片时,会有一个变量或参数来标识它们的先后顺序。只要保持一致,这两种卷积标准都可用。很遗憾,这只是一部分标记法,因为深度学习文献并未对标记达成一致,但课上我会采用这种卷积标识法,按高度,宽度和通道数量的顺序依次计算。
我知道,忽然间接触到这么多新的标记方法,你可能会说,这么多怎么记呢?别担心,不用全都记住,你可以通过本周的练习来熟悉它们。而这节课我想讲的重点是,卷积神经网络的某一卷积层的工作原理,以及如何计算某一卷积层的激活函数,并映射到下一层的激活值。了解了卷积神经网络中某一卷积层的工作原理,我们就可以把它们堆叠起来形成一个深度卷积神经网络,我们下节课再讲。
简单卷积网络示例(A simple convolution network example)
上节课,我们讲了如何为卷积网络构建一个卷积层。今天我们看一个深度卷积神经网络的具体示例,顺便练习一下我们上节课所学的标记法。
假设你有一张图片,你想做图片分类或图片识别,把这张图片输入定义为 \(x\),然后辨别图片中有没有猫,用 0 或 1 表示,这是一个分类问题,我们来构建适用于这项任务的卷积神经网络。针对这个示例,我用了一张比较小的图片,大小是 39×39×3,这样设定可以使其中一些数字效果更好。所以 \(n_{H}^{[0]} = n_{W}^{[0]}\),即高度和宽度都等于 39,\(n_{c}^{[0]} = 3\),即 0 层的通道数为 3。
假设第一层我们用一个 3×3 的过滤器来提取特征,那么 \(f^{[1]} = 3\),因为过滤器是 3×3 的矩阵。\(s^{[1]} = 1\),\(p^{[1]} =0\),所以高度和宽度使用 valid 卷积。如果有 10 个过滤器,神经网络下一层的激活值为 37×37×10,写 10 是因为我们用了10个过滤器,37 是公式 \(\frac{n + 2p - f}{s} + 1\) 的计算结果,也就是 \(\frac{39 + 0 - 3}{1} + 1 = 37\),所以输出是 37×37,它是一个 valid 卷积,这是输出结果的大小。第一层标记为 \(n_{H}^{[1]} = n_{W}^{[1]} = 37\),\(n_{c}^{[1]} = 10\),\(n_{c}^{[1]}\) 等于第一层中过滤器的个数,这 37×37×10 是第一层激活值的维度。
假设还有另外一个卷积层,这次我们采用的过滤器是 5×5 的矩阵。在标记法中,神经网络下一层的 \(f=5\),即 \(f^{\left\lbrack 2 \right\rbrack} = 5\) 步幅为 2,即 \(s^{\left\lbrack 2 \right\rbrack} = 2\)。padding 为 0,即 \(p^{\left\lbrack 2 \right\rbrack} = 0\),且有 20 个过滤器。所以其输出结果会是一张新图像,这次的输出结果为 17×17×20,因为步幅是 2,维度缩小得很快,大小从 37×37 减小到 17×17,减小了一半还多,过滤器是 20 个,所以通道数也是 20,17×17×20 即激活值 \(a^{\left\lbrack 2 \right\rbrack}\) 的维度。因此 \(n_{H}^{\left\lbrack 2 \right\rbrack} = n_{W}^{\left\lbrack 2 \right\rbrack} = 17\),\(n_{c}^{\left\lbrack 2 \right\rbrack} = 20\)。
我们来构建最后一个卷积层,假设过滤器还是 5×5,步幅为 2,即 \(f^{\left\lbrack 2 \right\rbrack} = 5\),\(s^{\left\lbrack 3 \right\rbrack} = 2\),计算过程我跳过了,最后输出为 7×7×40,假设使用了 40 个过滤器。padding 为 0,40 个过滤器,最后结果为 7×7×40。
到此,这张 39×39×3 的输入图像就处理完毕了,为图片提取了 7×7×40 个特征,计算出来就是 1960 个特征。然后对该卷积进行处理,可以将其平滑或展开成 1960 个单元。平滑处理后可以输出一个向量,然后将其填充到 logistic 回归单元还是 softmax 回归单元,完全取决于我们是想识图片上有没有猫,还是想识别 \(K\) 种不同对象中的一种,用 \(\hat y\) 表示最终神经网络的预测输出。明确一点,最后这一步是处理所有数字,即全部的 1960 个数字,把它们展开成一个很长的向量。为了预测最终的输出结果,我们把这个长向量填充到 softmax 回归函数中。
这是卷积神经网络的一个典型范例,设计卷积神经网络时,确定这些超参数比较费工夫。要决定过滤器的大小、步幅、padding 以及使用多少个过滤器。这周和下周,我会针对选择参数的问题提供一些建议和指导。
而这节课你要掌握的一点是,随着神经网络计算深度不断加深,通常开始时的图像也要更大一些,初始值为 39×39,高度和宽度会在一段时间内保持一致,然后随着网络深度的加深而逐渐减小,从 39 到 37,再到 17,最后到 7。而通道数量在增加,从 3 到 10,再到 20,最后到 40。在许多其它卷积神经网络中,你也可以看到这种趋势。关于如何确定这些参数,后面课上我会更详细讲解,这是我们讲的第一个卷积神经网络示例。
一个典型的卷积神经网络通常有三层,一个是卷积层,我们常常用 CONV 来标注。还有两种常见类型的层,我们留在后两节课讲。一个是池化层,我们称之为 POOL。最后一个是全连接层,用 FC 表示。虽然仅用卷积层也有可能构建出很好的神经网络,但大部分神经网络架构师依然会添加 池化层 和 全连接层。幸运的是,池化层和全连接层比卷积层更容易设计。后两节课我们会快速讲解这两个概念以便你更好的了解神经网络中最常用的这几种层,你就可以利用它们构建更强大的网络了。
Types of layer in a convolutional network :
- Convolution (CONV)
- Pooling (POOL)
- Fully connected (FC)
再次恭喜你已经掌握了第一个卷积神经网络,本周后几节课,我们会学习如何训练这些卷积神经网络。不过在这之前,我还要简单介绍一下 池化层 和 全连接层。然后再训练这些网络,到时我会用到大家熟悉的反向传播训练方法。那么下节课,我们就先来了解如何构建神经网络的池化层。
池化层(Pooling layers)
除了卷积层,卷积网络也经常使用池化层来缩减模型的大小,提高计算速度,同时提高所提取特征的鲁棒性,我们来看一下。
先举一个池化层的例子,然后我们再讨论池化层的必要性。假如输入是一个 4×4 矩阵,用到的池化类型是最大池化(max pooling)。执行最大池化的输出是一个 2×2 矩阵。执行过程非常简单,把 4×4 的输入拆分成不同的区域,我把这个区域用不同颜色来标记。对于 2×2 的输出,输出的每个元素都是其对应颜色区域中的最大元素值。
左上区域的最大值是 9,右上区域的最大元素值是 2,左下区域的最大值是 6,右下区域的最大值是 3。为了计算出右侧这 4 个元素值,我们需要对输入矩阵的 2×2 区域做最大值运算。这就像是应用了一个规模为 2 的过滤器,因为我们选用的是 2×2 区域,步幅是 2,这些就是最大池化的超参数。
因为我们使用的过滤器为 2×2,最后输出是 9。然后向右移动 2 个步幅,计算出最大值 2。然后是第二行,向下移动 2 步得到最大值 6。最后向右移动 2 个步幅,得到最大值 3。这是一个 2×2 矩阵,即 \(f=2\),步幅是 2,即 \(s=2\)。
直观理解下最大池化功能,你可以把这个 4×4 输入看作是某些特征的集合,也就是神经网络中某一层的非激活值集合。数字大意味着可能探测到了某些特定的特征,左上象限具有的特征可能是一个垂直边缘或是一只眼睛。显然左上象限中存在这个特征,它可能是一只猫眼探测器。然而,右上象限并不存在这个特征。最大化操作的功能就是只要在任何一个象限内提取到某个特征,它都会保留在最大化的池化输出里。所以最大化运算的实际作用就是,如果在过滤器中提取到某个特征,那么会保留其最大值。如果没有提取到这个特征,比如在右上象限中不存在这个特征,那么其中的最大值也还是很小,这就是最大池化的直观理解。必须承认,人们使用最大池化的主要原因是此方法在很多实验中效果都很好。尽管刚刚描述的直观理解经常被引用,不知大家是否能完全的理解最大池化效率很高的真正原因。
最大池化有一组超参数,但并没有参数需要学习。实际上,梯度下降没有什么可学的,一旦确定了 \(f\) 和 \(s\),它就是一个固定运算,梯度下降无需改变任何值。
我们来看一个有若干个超级参数的示例,输入是一个 5×5 的矩阵。我们采用最大池化法,它的过滤器参数为 3×3,即 \(f=3\),步幅为 1,\(s=1\),输出矩阵是 3×3。之前讲的计算卷积层输出大小的公式同样适用于最大池化,即 \(\frac{n + 2p - f}{s} + 1\),这个公式也可以计算最大池化的输出大小。
此例是计算 3×3 输出的每个元素,我们看左上角这些元素,注意这是一个 3×3 区域,因为有 3 个过滤器,取最大值 9。然后移动一个元素,因为步幅是 1,蓝色区域的最大值是 9。继续向右移动,蓝色区域的最大值是 5。然后移到下一行,因为步幅是 1,我们只向下移动一个格,所以该区域的最大值是 9。这个区域也是 9。这两个区域的最大值都是 5。最后这三个区域的最大值分别为 8,6 和 9。超参数 \(f=3\),\(s=1\),最终输出如图所示。
以上就是一个二维输入的最大池化的演示,如果输入是三维的,那么输出也是三维的。例如,输入是 5×5×2,那么输出是 3×3×2。计算最大池化的方法就是分别对每个通道执行刚刚的计算过程。如上图所示,第一个通道依然保持不变。对于第二个通道,我刚才画在下面的,在这个层做同样的计算,得到第二个通道的输出。一般来说,如果输入是 5×5×\(n_{c}\),输出就是 3×3×\(n_{c}\),\(n_{c}\) 个通道中每个通道都单独执行最大池化计算,以上就是最大池化算法。
另外还有一种类型的池化,平均池化,它不太常用。我简单介绍一下,这种运算顾名思义,选取的不是每个过滤器的最大值,而是平均值。示例中,紫色区域的平均值是 3.75,后面依次是 1.25、4 和 2。这个平均池化的超级参数 \(f=2\),\(s=2\),我们也可以选择其它超级参数。
目前来说,最大池化比平均池化更常用。但也有例外,就是深度很深的神经网络,你可以用平均池化来分解规模为 7×7×1000 的网络的表示层,在整个空间内求平均值,得到 1×1×1000,一会我们看个例子。但在神经网络中,最大池化要比平均池化用得更多。
总结一下,池化的超级参数包括过滤器大小 \(f\) 和步幅 \(s\),常用的参数值为 \(f=2\),\(s=2\),应用频率非常高,其效果相当于高度和宽度缩减一半。也有使用 \(f=3\),\(s=2\) 的情况。至于其它超级参数就要看你用的是最大池化还是平均池化了。你也可以根据自己意愿增加表示 padding 的其他超级参数,虽然很少这么用。最大池化时,往往很少用到超参数 padding,当然也有例外的情况,我们下周会讲。大部分情况下,最大池化很少用 padding。目前 \(p\) 最常用的值是 0,即 \(p=0\)。最大池化的输入就是 \(n_H \times n_W \times n_{c}\),假设没有 padding,则输出 \(\lfloor\frac{n_H - f}{s} +1\rfloor \times \lfloor\frac{n_W - f}{s} + 1\rfloor \times n_{c}\)。输入通道与输出通道个数相同,因为我们对每个通道都做了池化。需要注意的一点是,池化过程中没有需要学习的参数。执行反向传播时,反向传播没有参数适用于最大池化。只有这些设置过的超参数,可能是手动设置的,也可能是通过交叉验证设置的。
Summary of pooling
Hyperparameters :
f : filter size
s : stride
Max or average pooling
除了这些,池化的内容就全部讲完了。最大池化只是计算神经网络某一层的静态属性,没有什么需要学习的,它只是一个静态属性。
关于池化我们就讲到这儿,现在我们已经知道如何构建卷积层和池化层了。下节课,我们会分析一个更复杂的可以引进全连接层的卷积网络示例。
卷积神经网络示例(Convolutional neural network example)
构建全卷积神经网络的构造模块我们已经掌握得差不多了,下面来看个例子。
假设,有一张大小为 32×32×3 的输入图片,这是一张 RGB 模式的图片,你想做手写体数字识别。32×32×3 的 RGB 图片中含有某个数字,比如 7,你想识别它是从 0-9 这 10 个数字中的哪一个,我们构建一个神经网络来实现这个功能。
我用的这个网络模型和经典网络 LeNet-5 非常相似,灵感也来源于此。LeNet-5 是多年前 Yann LeCun 创建的,我所采用的模型并不是 LeNet-5,但是受它启发,许多参数选择都与 LeNet-5 相似。输入是 32×32×3 的矩阵,假设第一层使用过滤器大小为 5×5,步幅是 1,padding 是 0,过滤器个数为 6,那么输出为 28×28×6。将这层标记为 CONV1,它用了 6 个过滤器,增加了偏差,应用了非线性函数,可能是 ReLU 非线性函数,最后输出 CONV1 的结果。
然后构建一个池化层,这里我选择用最大池化,参数 \(f=2\),\(s=2\),因为 padding 为 0,我就不写出来了。现在开始构建池化层,最大池化使用的过滤器为 2×2,步幅为 2,表示层的高度和宽度会减少一半。因此,28×28 变成了 14×14,通道数量保持不变,所以最终输出为 14×14×6,将该输出标记为 POOL1。
人们发现在卷积神经网络文献中,卷积有两种分类,这与所谓层的划分存在一致性。一类卷积是一个卷积层和一个池化层一起作为一层,这就是神经网络的 Layer1。另一类卷积是把卷积层作为一层,而池化层单独作为一层。人们在计算神经网络有多少层时,通常只统计具有权重和参数的层。因为池化层没有权重和参数,只有一些超参数。这里,我们把 CONV1 和 POOL1 共同作为一个卷积,并标记为 Layer1。虽然你在阅读网络文章或研究报告时,你可能会看到卷积层和池化层各为一层的情况,这只是两种不同的标记术语。一般我在统计网络层数时,只计算具有权重的层,也就是把 CONV1 和 POOL1 作为 Layer1。这里我们用 CONV1 和 POOL1 来标记,两者都是神经网络 Layer1 的一部分,POOL1 也被划分在 Layer1 中,因为它没有权重,得到的输出是 14×14×6。
我们再为它构建一个卷积层,过滤器大小为 5×5,步幅为 1,这次我们用 10 个过滤器,最后输出一个 10×10×10 的矩阵,标记为 CONV2。
然后做最大池化,超参数 \(f=2\),\(s=2\)。你大概可以猜出结果,\(f=2\),\(s=2\),高度和宽度会减半,最后输出为 5×5×10,标记为 POOL2,这就是神经网络的第二个卷积层,即 Layer2。
如果对 Layer1 应用另一个卷积层,过滤器为 5×5,即 \(f=5\),步幅是 1,padding 为 0,所以这里省略了,过滤器 16 个,所以 CONV2 输出为 10×10×16。
继续执行做大池化计算,参数 \(f=2\),\(s=2\),对 10×10×16 输入执行最大池化计算,输入的高度和宽度会减半,结果为 5×5×16,通道数和之前一样,标记为 POOL2。这就是神经网络的第二个卷积层,即 Layer2。
5×5×16 矩阵包含 400 个元素,现在将 POOL2 平整化为一个大小为 400 的一维向量。我们可以把平整化结果想象成这样的一个神经元集合,然后利用这 400 个单元构建下一层。下一层含有 120 个单元,这就是我们第一个全连接层,标记为 FC3。这 400 个单元与 120 个单元紧密相连,这就是全连接层。它很像我们在第一和第二门课中讲过的单神经网络层,这是一个标准的神经网络。它的权重矩阵为 \(W^{\left\lbrack 3 \right\rbrack}\),维度为 120×400。这就是所谓的 全连接,因为这 400 个单元与这 120 个单元的每一项连接,还有一个偏差参数。最后输出 120 个维度,因为有 120 个输出。
然后我们对这个 120 个单元再添加一个全连接层,这层更小,假设它含有 84 个单元,标记为 FC4。
最后,用这 84 个单元填充一个 softmax 单元。如果我们想通过手写数字识别来识别手写 0-9 这 10 个数字,这个 softmax 就会有 10 个输出。
此例中的卷积神经网络很典型,看上去它有很多超参数,关于如何选定这些参数,后面我提供更多建议。常规做法是,尽量不要自己设置超参数,而是查看文献中别人采用了哪些超参数,选一个在别人任务中效果很好的架构,那么它也有可能适用于你自己的应用程序,这块下周我会细讲。
现在,我想指出的是,随着神经网络深度的加深,高度 \(n_{H}\) 和宽度 \(n_{W}\) 通常都会减少,前面我就提到过,从 32×32 到 28×28,到 14×14,到 10×10,再到 5×5。所以随着层数增加,高度和宽度都会减小,而通道数量会增加,从 3 到 6 到 16 不断增加,然后得到一个全连接层。
在神经网络中,另一种常见模式就是一个或多个卷积后面跟随一个池化层,然后一个或多个卷积层后面再跟一个池化层,然后是几个全连接层,最后是一个 softmax。这是神经网络的另一种常见模式。
接下来我们讲讲神经网络的激活值形状,激活值大小和参数数量。输入为 32×32×3,这些数做乘法,结果为 3072,所以激活值 \(a^{[0]}\) 有 3072 维,激活值矩阵为 32×32×3,输入层没有参数。计算其他层的时候,试着自己计算出激活值,这些都是网络中不同层的激活值形状和激活值大小。
有几点要注意,第一,池化层和最大池化层没有参数;第二卷积层的参数相对较少,前面课上我们提到过,其实许多参数都存在于神经网络的全连接层。观察可发现,随着神经网络的加深,激活值尺寸会逐渐变小,如果激活值尺寸下降太快,也会影响神经网络性能。示例中,激活值尺寸在第一层为 6000,然后减少到 1600,慢慢减少到84,最后输出 softmax 结果。我们发现,许多卷积网络都具有这些属性,模式上也相似。
神经网络的基本构造模块我们已经讲完了,一个卷积神经网络包括卷积层、池化层和全连接层。许多计算机视觉研究正在探索如何把这些基本模块整合起来,构建高效的神经网络,整合这些基本模块确实需要深入的理解。根据我的经验,找到整合基本构造模块最好方法就是大量阅读别人的案例。下周我会演示一些整合基本模块,成功构建高效神经网络的具体案例。我希望下周的课程可以帮助你找到构建有效神经网络的感觉,或许你也可以将别人开发的框架应用于自己的应用程序,这是下周的内容。下节课,也是本周最后一节课,我想花点时间讨论下,为什么大家愿意使用卷积,使用卷积的好处和优势是什么,以及如何整合多个卷积,如何检验神经网络,如何在训练集上训练神经网络来识别图片或执行其他任务,我们下节课继续讲。
为什么使用卷积?(Why convolutions?)
这是本周最后一节课,我们来分析一下卷积在神经网络中如此受用的原因,然后对如何整合这些卷积,如何通过一个标注过的训练集训练卷积神经网络做个简单概括。和只用全连接层相比,卷积层的两个主要优势在于参数共享和稀疏连接,举例说明一下。
假设有一张 32×32×3 维度的图片,这是上节课的示例,假设用了 6 个大小为 5×5 的过滤器,输出维度为 28×28×6。32×32×3 = 3072,28×28×6 = 4704。我们构建一个神经网络,其中一层含有 3072 个单元,下一层含有 4074 个单元,两层中的每个神经元彼此相连,然后计算权重矩阵,它等于 4074×3072 ≈ 1400 万,所以要训练的参数很多。虽然以现在的技术,我们可以用 1400 多万个参数来训练网络,因为这张 32×32×3 的图片非常小,训练这么多参数没有问题。如果这是一张 1000×1000 的图片,权重矩阵会变得非常大。我们看看这个卷积层的参数数量,每个过滤器都是 5×5,一个过滤器有 25 个参数,再加上偏差参数,那么每个过滤器就有 26 个参数,一共有 6 个过滤器,所以参数共计 156 个,参数数量还是很少。
卷积网络映射这么少参数有两个原因:
第一个原因是 参数共享。观察发现,特征检测如垂直边缘检测如果适用于图片的某个区域,那么它也可能适用于图片的其他区域。也就是说,如果你用一个 3×3 的过滤器检测垂直边缘,那么图片的左上角区域,以及旁边的各个区域都可以使用这个 3×3 的过滤器。每个特征检测器以及输出都可以在输入图片的不同区域中使用同样的参数,以便提取垂直边缘或其它特征。它不仅适用于边缘特征这样的低阶特征,同样适用于高阶特征,例如提取脸上的眼睛,猫或者其他特征对象。即使减少参数个数,这 9 个参数同样能计算出 16 个输出。直观感觉是,一个特征检测器,如垂直边缘检测器用于检测图片左上角区域的特征,这个特征很可能也适用于图片的右下角区域。因此在计算图片左上角和右下角区域时,你不需要添加其它特征检测器。假如有一个这样的数据集,其左上角和右下角可能有不同分布,也有可能稍有不同,但很相似,整张图片共享特征检测器,提取效果也很好。
第二个原因是 稀疏连接,我来解释下。这个 0 (上图绿色圈)是通过 3×3 的卷积计算得到的,它只依赖于左边矩阵绿色方框标记的 3×3 区域。总结下就是,右边的输出单元(元素 0 )仅与 36 个输入特征中 9 个相连接。而且其它像素值都不会对输出产生任何影响,这就是稀疏连接的概念。再举一个例子,这个输出(右边矩阵中红色标记的元素 30 )仅仅依赖于这 9 个特征(左边矩阵红色方框标记的区域),看上去只有这 9 个输入特征与输出相连接,其它像素对输出没有任何影响。
神经网络可以通过这两种机制减少参数,以便我们用更小的训练集来训练它,从而预防过度拟合。你们也可能听过,卷积神经网络善于捕捉平移不变。通过观察可以发现,向右移动两个像素,图片中的猫依然清晰可见,因为神经网络的卷积结构使得即使移动几个像素,这张图片依然具有非常相似的特征,应该属于同样的输出标记。实际上,我们用同一个过滤器生成各层中,图片的所有像素值,希望网络通过自动学习变得更加健壮,以便更好地取得所期望的平移不变属性。
这就是卷积或卷积网络在计算机视觉任务中表现良好的原因。
最后,我们把这些层整合起来,看看如何训练这些网络。比如我们要构建一个猫咪检测器,我们有标记好的训练集,\(x\) 表示一张图片,\(\hat{y}\) 是二进制标记或某个重要标记。我们选定了一个卷积神经网络,输入图片,增加卷积层和池化层,然后添加全连接层,最后输出一个 softmax,即 \(\hat{y}\)。卷积层和全连接层有不同的参数 \(w\) 和偏差 \(b\),我们可以用任何参数集合来定义代价函数。一个类似于我们之前讲过的那种代价函数,并随机初始化其参数 \(w\) 和 \(b\),代价函数 \(J\) 等于神经网络对整个训练集的预测的损失总和再除以 \(m\)(即 \(\text{Cost}\ J = \frac{1}{m}\sum\limits_{i = 1}^{m}{L(\hat{y}^{(i)},y^{(i)})}\))。所以训练神经网络,你要做的就是使用梯度下降法,或其它算法,例如 Momentum 梯度下降法,含 RMSProp 或其它因子的梯度下降来优化神经网络中所有参数,以减少代价函数 \(J\) 的值。通过上述操作你可以构建一个高效的猫咪检测器或其它检测器。
恭喜你完成了这一周的课程,你已经学习了卷积神经网络的所有基本构造模块,以及如何在高效图片识别系统中整合这些模块。透过本周编程练习,你可以更加具体了解这些概念,试着整合这些构造模块,并用它们解决自己的问题。
下周,我们将继续深入学习卷积神经网络。我曾提到卷积神经网络中有很多超参数,下周,我打算具体展示一些最有效的卷积神经网络示例,你也可以尝试去判断哪些网络架构类型效率更高。人们通常的做法是将别人发现和发表在研究报告上的架构应用于自己的应用程序。下周看过更多具体的示例后,相信你会做的更好。此外,下星期我们也会深入分析卷积神经网络如此高效的原因,同时讲解一些新的计算机视觉应用程序,例如,对象检测和神经风格迁移以及如何利用这些算法创造新的艺术品形式。