跟着吴恩达学深度学习(四)

前言

第一门课的笔记见:跟着吴恩达学深度学习(一)

第二门课的笔记见:跟着吴恩达学深度学习(二)

第三门课的笔记见:跟着吴恩达学深度学习(三)

本文对应了吴恩达深度学习系列课程中的第四门课程《卷积神经网络》,本门课程主要教我们如何搭建卷积神经网络并将其应用到图像数据上。

1 卷积神经网络

1.1  计算机视觉

 受益于深度学习,计算机视觉(computer vision)领域飞速发展。

图片分类(image classification)为例,有时候也叫图片识别(image recognition)。给出这张64×64的图片,让计算机去分辨出这是一只猫。在计算机视觉中有个问题叫做目标检测(object detection),比如在一个无人驾驶项目中,我们不一定非得识别出图片中的物体是车辆,但我们需要计算出其他车辆的位置,以确保自己能够避开它们。还有一个更有趣的例子,就是神经网络实现的图片风格迁移(neural style transfer),比如说有一张图片(如下图),但我们想将这张图片转换为另外一种风格。

但在应用计算机视觉时要面临一个挑战,就是数据的输入可能会非常大。

在过去的图片分类中,一般操作的都是64×64的小图片,实际上,它的数据量是64×64×3,因为每张图片都有3个颜色通道(RGB)。如果计算一下的话,可得知数据量为12288,所以我们的特征向量维度为12288。这其实还好,因为64×64真的是很小的一张图片。如果你要操作更大的图片,比如一张1000×1000的图片,它足有1兆那么大,但是特征向量的维度达到了1000×1000×3,因为有3个RGB通道,所以数字将会是300万。如果要输入300万的数据量,这就意味着,特征向量的维度高达300万。所以在第一隐藏层中,也许会有1000个隐藏单元,如果使用了标准的全连接网络,权重矩阵W[1]的大小将会是1000×300万,有30亿个参数。

在参数如此大量的情况下,难以获得足够的数据来防止神经网络发生过拟合和竞争需求。为此,你需要进行卷积计算,它是卷积神经网络中非常重要的一块。

1.2  边缘检测示例

卷积运算(convolutional operation)是卷积神经网络最基本的组成部分,使用边缘检测(edge detection)作为入门样例。本小节将会看到卷积是如何进行运算的。

在之前的人脸例子中,我们了解到神经网络的前几层是如何检测边缘的,然后,后面的层有可能检测到物体的部分区域,更靠后的一些层可能检测到完整的物体。举个例子,给一张图片,让电脑去搞清楚这张照片里有什么物体,我们可能做的第一件事是检测图片中的垂直边缘(vertical edges)水平边缘(horizontal edges)。所以如何在图像中检测这些边缘?

上图是一个6×6的灰度图像。因为是灰度图像,所以它是6×6×1(不是6×6×3,这是RGB)的矩阵,为了检测图像中的垂直边缘,需要构造一个3×3矩阵。在共用习惯中,在卷积神经网络的术语中,它被称为过滤器(filter),也被称为核(kernel)。对这个6×6的图像进行卷积运算,卷积运算用星号“*”(asterisk)来表示,用3×3的过滤器对其进行卷积。在过滤器覆盖的区域执行:每个元素对应相乘再求和,这个卷积运算的输出将会是一个4×4的矩阵,这个就是垂直边缘检测器

✏️补充4×4的矩阵是怎么得到的? 

第一行从左边数第一个是由6×6图像中绿色边框的部分每一行每一列对应乘过滤器相加得到。3\times 1+0\times 1+1\times -1+1\times 1+5\times 1+8\times -1+2\times 1+7\times 1+2\times -1=-4

然后-4是由绿色边框向右平移一格使用同样的计算方法得到的。然后一次类推平移到最右端后,向下平移然后回到最多端,3×3矩阵中心点的移动区域为上图橙色虚线框。

在编程练习中,python卷积用conv_forward()表示;tensorflow中,卷积用tf.nn.conv2d()表示;keras中,卷积用Conv2D()表示。

为什么这样可以做垂直边缘检测呢?看另外一个例子。

上图是一个简单的6×6图像,左边的一半是10,右边一般是0。如果把它当成一个图片,左边那部分看起来是白色的,像素值10是比较亮的像素值,右边像素值比较暗,使用灰色来表示0,尽管它也可以被画成黑的。图片里,有一个特别明显的垂直边缘在图像中间,这条垂直线是从黑到白的过渡线,或者从白色到深色。这里的维数看起来不大对,检测出来的边缘看起来很厚,那是因为在这个例子中使用很小的图像。如果用一个1000×1000的图像,而不是6×6的图片,可以很好地检测出图像中的垂直边缘。在这个例子中,在输出图像中间的亮处,表示在图像中间有一个特别明显的垂直边缘。

从垂直边缘检测中可以得到的启发是,因为使用3×3的矩阵(过滤器),所以垂直边缘是一个3×3的区域,左边是明亮的像素,中间的并不需要考虑,右边是深色像素。在这个6×6图像的中间部分,明亮的像素在左边,深色的像素在右边,就被视为一个垂直边缘,卷积运算提供了一个方便的方法来发现图像中的垂直边缘。

1.3  更多边缘检测内容

1.2小节了解到卷积操作是如何进行垂直边缘检测的,本小节将学习如何区分正边和负边,这实际就是由亮到暗(light to dark)与由暗到亮(dark to light)的区别,也就是边缘的过渡(edge transitions)

还是上一小节的例子。这张6×6的图片,左边较亮,而右边较暗,将它与垂直边缘检测滤波器进行卷积,检测结果就显示在了右边这幅图的中间部分。现在将图片进行翻转,变成了左边比较暗,右边比较亮。从结果可以看到这个过滤器确实可以区分这两种明暗变化的区别。

如上图,左边的垂直边缘过滤器(vertical edge detection)是一个3×3的区域,左边相对较亮,而右边相对较暗。右边这个水平边缘过滤器(horizontal edge detection)也是一个3×3的区域,上边相对较亮,而下方相对较暗。

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

再次强调,现在使用的都是相对很小的图片,但假如这个一个非常大的1000×1000的类似这样棋盘风格的大图,就不会出现这些亮度为10的过渡带了,因为图片尺寸很大,这些中间值就会变得非常小。

总之,不同的过滤器可以找到水平和垂直边界。事实上,这些3×3的垂直边缘检测器,只是一种的可能的选择。在计算机视觉领域,使用哪一种数字组合最好,仍然存在相当大的争议。

也可以使用如上图的过滤器,常被称为Sobel的过滤器,它的优点在于增加了中间一行元素的权重,这使得结果的鲁棒性会更高一些

或者还有Scharr过滤器,它有着和之前完全不同的特性,实际上也是一种垂直边缘检测,如果将其翻转90度,就能得到对应水平边缘检测。

随着深度学习的发展,我们学习的其中一件事就是当我们真正想去检测出复杂图像的边缘,不一定要去使用那些研究者们所选择的这九个数字,但可以从中获益匪浅。把这矩阵中的9个数字当成9个参数,并且在之后你可以学习使用反向传播算法,其目标就是去理解这9个参数。这已经成为计算机视觉中最为有效的思想之一。

1.4  Padding

 为了构建深度神经网络,我们 需要学会使用的一个基本的卷积操作就是填充(padding),下面来看看它是如何工作的。

如果使用用一个3×3的过滤器卷积一个6×6的图像,最终会得到一个4×4的输出,也就是一个4×4矩阵。这背后的数学解释是,如果我们有一个n×n的图像,用f×f的过滤器做卷积,那么输出的维度就是(n - f + 1) × (n - f + 1)。在这个例子里是6-3+1=4。

这样的话会有两个缺点。

  • 输出缩小:每次做卷积操作,图像就会缩小
  • 图像边缘的大部分信息丢失:那些在角落或者边缘区域的像素点在输出中采用较少。

为了解决这两个问题,可以在卷积操作之前填充这幅图像。在这个案例中,可以沿着图像边缘再填充一层像素。如果这样操作了,那么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)。这样一来,丢失信息这一缺点就被削弱了。

如果想的话,也可以填充两个像素点。至于选择填充多少像素,通常有两个选择,分别叫做Valid卷积Same卷积

  • Valid卷积意味着不填充。n×n去卷积f×f的过滤器会得到(n - f + 1) × (n - f + 1)。
  • Same卷积,意味着填充后,输出大小和输入大小是一样的。利用n+2p-f+1=n求解,使得输出和输入大小相等,那么p = (f - 1)/2。

习惯上,计算机视觉中,f通常是奇数,甚至可能都是这样。很少看到一个偶数的过滤器在计算机视觉里使用,有两个原因。

其中一个可能是,如果f是一个偶数,那么你只能使用一些不对称填充(asymmetric padding)。只有是奇数的情况下,Same卷积才会有自然的填充,我们可以以同样的数量填充四周,而不是左边填充多一点,右边填充少一点,这样不对称的填充。

第二个原因是当我们有一个奇数维过滤器(odd dimension filter),比如3×3或者5×5的,它就有一个中心点。有时在计算机视觉里,如果有一个中心像素点会更方便,便于指出过滤器的位置。

1.5  卷积步长

卷积中的步长(strided convolutions)是另一个构建卷积神经网络的基本操作,看下面一个例子。

如上图,如果想用3×3的过滤器卷积7×7的图像,和之前不同的是,我们把步幅设置成了2。注意一下左上角,这个点移动到其后两格的点,跳过了一个位置。然后还是将每个元素相乘并求和,将会得到的结果是100。依次类推,得到最终结果。如果使用一个f x f的过滤器卷积一个n x n的图像,padding为p,步幅为s,这样输出的大小变为:

(\frac{n+2p-f}{s}+1)\times(\frac{n+2p-f}{s}+1)

在这个例子中,n = 7,p = 0, f = 3,s = 2,(7 + 0 - 3)/2 + 1 = 3,即3×3的输出。

注意一个细节,如果商不是一个整数,则向下取整。这个原则实现的方式是,只在蓝框完全包括在图像或填充完的图像内部时,才对它进行运算。如果有任意一个蓝框移动到了外面,那就不要进行相乘操作,这是一个惯例。3×3的过滤器必须完全处于图像中或者填充之后的图像区域内才输出相应结果

1.6  三维卷积

本小节看看如何执行卷积不仅仅在二维(2D)图像上,而是三维立体上。

如果我们不仅想检测灰度图像的特征,还想检测RGB彩色图像(红、绿、蓝)的特征。彩色图像如果是6×6×3,这里的3指的是三个颜色通道(可以想象成三个6×6图像的堆叠)。为了检测图像的边缘或者其他的特征,不是把它跟原来的3×3的过滤器做卷积,而是跟一个三维的过滤器,它的维度是3×3×3,这样这个过滤器也有三层,对应红绿、蓝三个通道。

注意6×6×3中,第一个6代表图像高度(height),第二个6代表宽度(width),这个3代表通道的数目(channels)。同样过滤器(filter)也有高,宽和通道数,并且图像的通道数必须和过滤器的通道数匹配

如上图有6×6×3的图像和3×3×3的过滤器。为了简化这个3×3×3过滤器的图像,画成一个三维的立方体。为了计算这个卷积操作的输出,要做的就是把这个3×3×3的过滤器先放到最左上角的位置,这个3×3×3的过滤器有27个数,27个参数就是3的立方。依次取这27个数,然后乘以相应的红绿蓝通道中的数字。先取红色通道的前9个数字,然后是绿色通道,然后再是蓝色通道,乘以左边黄色立方体覆盖的对应的27个数,然后把这些数都加起来,就得到了输出的第一个数字。计算下一个输出,则把立方体滑动一个单位,再分别与27个数相乘求和,以此类推。

如果想检测图像红色通道的边缘,那么可以设置绿色和蓝色通道的元素全部为0。或者也可以将三个通道的过滤器全部设置为水平边缘检测。

按照计算机视觉的惯例,当输入有特定的高宽和通道数时,过滤器可以有不同的高,不同的宽,但是必须一样的通道数。理论上,我们的过滤器只关注红色通道,或者只关注绿色或者蓝色通道也是可行的。同时注意一下,一个6×6×6的输入图像卷积上一个3×3×3的过滤器,得到一个4×4的二维输出

🙋如果我们不仅仅想要检测垂直边缘怎么办?如果我们同时检测垂直边缘和水平边缘,还有45°倾斜的边缘,还有70°倾斜的边缘怎么做?

如上图,黄色表示可能是一个垂直边界检测器或者是学习检测其他的特征。橘色表示可以是一个水平边缘检测器。这样的输出是一个4×4×2的立方体,这里的2的来源于用了两个不同的过滤器。

总结一下维度,如果有一个n \times n \times n_c的输入图像,这里是6×6×3,这里n_c是通道数目,也称深度(depth),然后卷积一个f \times f \times n_c,这里是3×3×3,然后得到结果的维度为(假设步幅是1,并且没有padding):

(n-f+1)\times (n-f+1)\times n_c

更重要的是,我们可以检测两个特征,比如垂直和水平边缘或者10个或者128个或者几百个不同的特征,并且输出的通道数会等于要检测的特征数。

1.7  单层卷积网络

本小节将学习如何构建卷积神经网络的卷积层(convolutional layer),看一个例子。

1.6小节,我们知道如何通过两个过滤器卷积处理一个三维图像,并输出两个不同的4×4矩阵。假设使用第一个过滤器进行卷积,得到第一个4×4矩阵,形成一个卷积神经网络层,然后增加偏差,它是一个实数,通过Python的广播机制给这16个元素都加上同一偏差。然后应用非线性函数,它是一个非线性激活函数ReLU,输出结果是一个4×4矩阵。

对于第二个4×4矩阵,我们加上不同的偏差,它也是一个实数,16个数字都加上同一个实数,然后应用非线性函数,也就是一个非线性激活函数ReLU,最终得到另一个4×4矩阵。重复我们之前的步骤,把这两个矩阵堆叠起来,最终得到一个4×4×2的矩阵。我们通过计算,从6×6×3的输入推导出一个4×4×2矩阵,它是卷积神经网络的一层,把它映射到标准神经网络中四个卷积层中的某一层或者一个非卷积神经网络中。

这个示例中有两个过滤器(两个特征),因此最终得到一个4×4×2的输出。如果我们用了10个过滤器,而不是2个,最后会得到一个4×4×10维度的输出图像,因为我们选取了其中10个特征映射。

🙋假设有10个过滤器,神经网络的一层是3×3×3,那么,这一层有多少个参数呢?

下面来计算一下,每一层都是一个3×3×3的矩阵,因此每个过滤器有27个参数,然后加上一个偏差,用参数b表示,现在参数为28个。上一个例子有2个过滤器,而现在有10个,加在一起是28×10,也就是280个参数。注意一点,无论输入的图片多大,参数始终是280个。用这10个过滤器来提取特征,如垂直边缘,水平边缘和其它特征。即使这些图片很大,参数却很少,这就是卷积神经网络的一个特征,叫作“避免过拟合”

最后总结一下,用于描述卷积神经网络的一层,例如l层的各种标记。

其中,上标[l]用来标记l层,f^{[l]}表示过滤器大小,p^{[l]}表示padding的数量,s^{[l]}表示步幅。输入为上一层的激活值,所用图片的高度和宽度可能不同,分别用下标H和W标记,即:

n_H^{[l-1]}\times n_W^{[l-1]}\times n_c^{[l-1]}

输出图像大小

n_H^{[l]}\times n_W^{[l]}\times n_c^{[l]}

联系之前的内容,l层输出图像的高度和宽度分别为:

n_H=\left \lfloor \frac{n_H^{[l-1]}+2p^{[l]-f^{[l]}}}{s^{[l]}}+1 \right \rfloor,n_W=\left \lfloor \frac{n_H^{[l-1]}+2p^{[l]-f^{[l]}}}{s^{[l]}}+1 \right \rfloor

🙋那么通道数量又是什么?这些数字从哪儿来的?

我们知道输出图像也有深度,它等于该层中过滤器的数量。如果有2个过滤器,输出图像就是4×4×2,它是二维的,如果有10个过滤器,输出图像就是4×4×10。输出图像中的通道数量就是神经网络中这一层所使用的过滤器的数量。因此,输出通道数量就是输入通道数量,所以过滤器维度为:

f^{[l]}\times f^{[l]}\times n_c^{[l-1]}

加上偏差和应用非线性函数之后,这一层的输出等于它的激活值a^[l],它的输出维度为:

n_H^{[l]}\times n_W^{[l]}\times n_c^{[l]}

当执行批量梯度下降或小批量梯度下降算法时,如果有m个例子,就是有m个激活值的集合,那么输出表示为:

A^{[l]}=m\times n_H^{[l]}\times n_W^{[l]}\times n_c^{[l]}

如果采用批量梯度下降,变量的排列顺序如下:首先是索引和训练示例,然后是其它三个变量。

🙋该如何确定权重参数(weights parameters),即参数W呢?

权重也就是所有过滤器的集合再乘以过滤器的总数量,即

f^{[l]}\times f^{[l]}\times n_c^{[l-1]}\times n_c^{[l]}

最后我们看看偏差参数,每个过滤器都有一个偏差参数,它是一个实数。偏差包含了这些变量,它是该维度上的一个向量。后续为了方便,偏差表示为一个的四维矩阵或四维张量,即

1\times 1\times 1\times n_c^{[l]}

注意一下,关于高度,宽度和通道的顺序并没有完全统一的标准卷积,有些会采用把通道放在首位的编码标准。实际上在某些架构中,当会有一个变量或参数来标识计算通道数量和通道损失数量的先后顺序。只要保持一致,这两种卷积标准都可用。吴恩达的课程中,按高度,宽度和通道损失数量的顺序依次计算。

1.8  简单卷积网络示例

 本小节将研究一个深度卷积神经网络实例,来看例子。

如上图,假设有一张图片,想做图片分类(image classification)图片识别(image recognition),输入定义为x,输出为0或1,来判断有没有猫。这里用了一张比较小的图片,大小是39×39×3,高度和宽度都等于39,第0层的通道数为3。符号为:

n_H^{[0]}=n_W^{[0]}=39,n_c^{[0]}=3

(1)假设第一层用一个3×3的过滤器来提取特征,步长为1,采用valid卷积,有10个过滤器,则输出为:

n_H^{[1]}=n_W^{[1]}=39-3+1=37,n_c^{[1]}=10

(37×37×10)是第一层激活值的维度。

(2)然后下一个卷积层,这次我们采用的过滤器是5×5的矩阵,步幅为2,p=0,有20个过滤器,则输出为:(\frac{n+2p-f}{s}+1)\times(\frac{n+2p-f}{s}+1)

n_H^{[2]}=n_W^{[2]}=\left \lfloor \frac{37+2\times0-5}{2}+1 \right \rfloor=17,n_c^{[2]}=20

(17×17×20)是第一层激活值的维度。

(3)最后一个卷积层,假设过滤器还是5×5,步幅为2,假设使用了40个过滤器。padding为0,40个过滤器。

n_H^{[3]}=n_W^{[3]}=\left \lfloor \frac{17+2\times0-5}{2}+1 \right \rfloor=7,n_c^{[3]}=40

最后结果为7×7×40。

至此,这张39×39×3的输入图像就处理完毕了,为图片提取了7×7×40个特征,计算出来就是1960个特征。然后对该卷积进行处理,可以将其平滑或展开成1960个单元。平滑处理后可以输出一个向量,其填充内容是logistic回归单元还是softmax回归单元,完全取决于我们是想识图片上有没有猫(二元)还是想识别K种不同对象中的一种(多元,用\hat{y}表示最终神经网络的预测输出。明确一点,最后这一步是处理所有数字,即全部的1960个数字,把它们展开成一个很长的向量。为了预测最终的输出结果,我们把这个长向量填充到softmax回归函数中。

这是卷积神经网络的一个典型范例,设计卷积神经网络时,确定这些超参数比较费工夫。要决定过滤器的大小、步幅、padding以及使用多少个过滤器。

注意一点,随着网络加深,高度和宽度会在一段时间内保持一致,然后随着网络深度的加深而逐渐减小,从39到37,再到17,最后到7。而通道数量在增加,从3到10,再到20,最后到40。

如上图,一个卷积网络通常有三层,一个是卷积层(convolution layer),我们常常用Conv来标注。一个是池化层(pooling layer),我们称之为POOL。最后一个是全连接层(fully connected layer),用FC表示。虽然仅用卷积层也有可能构建出很好的神经网络,但大部分神经网络架构师依然会添加池化层和全连接层。幸运的是,池化层和全连接层比卷积层更容易设计。

1.9  池化层

 除了卷积层,卷积网络也经常使用池化层来缩减模型的大小,提高计算速度),同时提高所提取特征的鲁棒性,下面来看一下。

如上图是池化层的例子,假设输入是4×4矩阵,池化类型是最大池化(max pooling)。执行最大池化的树池是一个2×2矩阵。执行过程非常简单,把4×4的输入拆分成不同的区域(不同区域用不同颜色来标记)。对于2×2的输出,输出的每个元素都是其对应颜色区域中的最大元素值。例如,左上区域的最大值是9,右上区域的最大元素值是2,左下区域的最大值是6,右下区域的最大值是3。这就像是应用了一个规模为2的过滤器,因为我们选用的是2×2区域,步幅是2,这些就是最大池化的超参数,即f = 2,s = 2。

这是对最大池化功能的直观理解,可以把这个4×4区域看作是某些特征的集合,也就是神经网络中某一层的非激活值集合。数字大意味着可能探测到了某些特定的特征,左上象限具有的特征可能是一个垂直边缘,一只眼睛,或是大家害怕遇到的CAP特征。显然左上象限中存在这个特征,这个特征可能是一只猫眼探测器。然而,右上象限并不存在这个特征。最大化操作的功能就是只要在任何一个象限内提取到某个特征,它都会保留在最大化的池化输出里。所以最大化运算的实际作用就是,如果在过滤器中提取到某个特征,那么保留其最大值;如果没有提取到这个特征,可能在右上象限中不存在这个特征,那么其中的最大值也还是很小

必须承认,人们使用最大池化的主要原因是此方法在很多实验中效果都很好

其中一个有意思的特点就是,它有一组超参数,但并没有参数需要学习。在实际操作中,一旦确定f和s,就是一个固定运算,梯度下降无需改变任何值。先看一下最终输出结果:

这是一个新的例子,输入是5×5的矩阵,采用最大池化法,过滤器参数为3×3,即f = 3,步幅为1,s = 1,输出矩阵是3×3。之前讲的计算卷积层输出大小的公式同样适用于最大池化,可以用来计算最大池化的输出大小,即

\frac{n+2p-f}{s}+1

上面学习的是最大池化(max pooling),还有一种池化类型是平均池化(average pooling),但是并不常用,运算时选取的不是每个过滤器的最大值,而是平均值。

上图的例子,紫色区域的平均值是3.75,后面依次是1.25、4和2。这个平均池化的超级参数f  = 2,s = 2,我们也可以选择其它超级参数。目前来说,最大池化比平均池化更常用。但也有例外,就是深度很深的神经网络,可以用平均池化来分解规模为7×7×1000的网络的表示层,在整个空间内求平均值,得到1×1×1000。

池化的超级参数包括过滤器大小和步幅,常用的参数值为f = 2,s = 2,应用频率非常高,其效果相当于高度和宽度缩减一半。也有使用f =3,s=2的情况。至于其它超级参数就要看用的是最大池化还是平均池化了。最大池化时,往往很少用到超参数padding,当然也有例外的情况。假设没有padding,最大池化的输入和输出分别为:

n_H\times n_W\times n_c \rightarrow \left \lfloor \frac{n_H-f}{s}+1 \right \rfloor\times \rightarrow \left \lfloor \frac{n_W-f}{s}+1 \right \rfloor\times n_c

需要注意的一点是,池化过程中没有需要学习的参数。执行反向传播时,反向传播没有参数适用于最大池化。只有这些设置过的超参数(f,s),可能是手动设置的,也可能是通过交叉验证设置的。

1.10  卷积神经网络示例

如上图,假设有一张大小为32×32×3的输入图片,这是一张RGB模式的图片,想做手写体数字识别。32×32×3的RGB图片中含有某个数字,比如7,你想识别它是从0-9这10个数字中的哪一个,我们构建一个神经网络来实现这个功能。

输入是32×32×3的矩阵。

①假设第一层使用过滤器大小为5×5,步幅是1,padding是0,过滤器个数为6,那么输出为28×28×6。将这层标记为CONV1,它用了6个过滤器,增加了偏差,应用了非线性函数,可能是ReLU非线性函数,最后输出CONV1的结果。

②构建一个池化层,这里选择用最大池化(max pooling),参数f = 2,s = 2,padding为0,最大池化使用的过滤器为2×2,步幅为2,表示层的高度和宽度会减少一半。因此,28×28变成了14×14,通道数量保持不变,所以最终输出为14×14×6,将该输出标记为POOL1。

卷积神经网络文献中,卷积有两种分类,这与所谓层的划分存在一致性。(1)一类卷积是一个卷积层和一个池化层一起作为一层,这就是神经网络的Layer1。(2)另一类卷积是把卷积层作为一层,而池化层单独作为一层。人们在计算神经网络有多少层时,通常只统计具有权重和参数的层。因为池化层没有权重和参数,只有一些超参数。这里,我们把CONV1和POOL1共同作为一个卷积,并标记为Layer1。

③再构建一个卷积层,过滤器大小为5×5,步幅为1,这次我们用10个过滤器,最后输出一个10×10×10的矩阵,标记为CONV2。

④然后最大池化,参数f =2,s=2,高度和宽度会减半,最后输出为5×5×10,标记为POOL2,这就是神经网络的第二个卷积层,即Layer2。

下面介绍另一种卷积层方式,前两步①②同上

③如果对Layer1应用另一个卷积层,过滤器为5×5,即f=5,步幅是1,padding为0,所以这里省略了,过滤器16个,所以CONV2输出为10×10×16。我们看看CONV2,这是CONV2层。

④继续执行做大池化计算,参数f =2,s=2,你能猜到结果么?对10×10×16输入执行最大池化计算,参数f =2,s=2,高度和宽度减半,结果为5×5×16,通道数和之前一样,标记为POOL2。这是一个卷积,即Layer2,因为它只有一个权重集和一个卷积层CONV2。

⑤5×5×16矩阵包含400个元素,现在将POOL2平整化为一个大小为400的一维向量。然后利用这400个单元构建下一层。下一层含有120个单元,这就是我们第一个全连接层(full connection),标记为FC3。这400个单元与120个单元紧密相连,这就是全连接层。它的权重矩阵为W^{[3]},维度为120×400。这就是所谓的“全连接”,因为这400个单元与这120个单元的每一项连接,还有一个偏差参数。最后输出120个维度,因为有120个输出。

⑥然后我们对这个120个单元再添加一个全连接层,这层更小,假设它含有84个单元,标记为FC4。

⑦最后,用这84个单元填充一个softmax单元。如果我们想通过手写数字识别来识别手写0-9这10个数字,这个softmax就会有10个输出。

此例中的卷积神经网络很典型,看上去它有很多超参数,关于选定这些参数。常规做法是,尽量不要自己设置超参数,而是查看文献中别人采用了哪些超参数,选一个在别人任务中效果很好的架构,那么它也有可能适用于我们的应用程序。

需要注意的是,随着层数增加,高度和宽度都会减小,从32×32到28×28,到14×14,到10×10,再到5×5;而通道数量会增加,从3到6到16不断增加,然后得到一个全连接层。

在神经网络中,另一种常见模式就是一个或多个卷积后面跟随一个池化层,然后一个或多个卷积层后面再跟一个池化层,然后是几个全连接层,最后是一个softmax。

接下来看一下神经网络的激活值形状(activation shape)激活值大小(the activation size)参数数量(the number of parameters)

activation shapethe activation sizethe number of parameters
Input(32,32,3)30720
CONV1(f=5,s=1)(28,28,8)6272208
POOL1(14,14,8)15680
CONV1(f=5,s=1)(10,10,16)1600416
POOL2(5,5,16)4000
FC3(120,1)12048001
FC4(84,1)8410081
Softmax(10,1)10841

最后一列结果:输入层没有参数;CONV1是(5*5+1)*8=208;池化层参数为0;CONV2是26*16=416;池化层参数为0;FC3是400*120+1=48001;FC4是120*84+1=10081;Softmax是84*10+1=841。 

⚠️注意:

  • 池化层和最大池化层没有参数;
  • 第二卷积层的参数相对较少,其实许多参数都存在于神经网络的全连接层。随着神经网络的加深,激活值尺寸会逐渐变小,如果激活值尺寸下降太快,也会影响神经网络性能。示例中,激活值尺寸在第一层为6000,然后减少到1600,慢慢减少到84,最后输出softmax结果。

1.11  为什么使用卷积?

本小节我们来分析一下卷积在神经网络中如此受用的原因。和只用全连接层相比,卷积层的两个主要优势在于参数共享(parameter sharing)稀疏连接(sparsity of connections),举例说明一下。

输入是一张32×32×3维度的图片,假设用了6个大小为5×5的过滤器,输出维度为28×28×6。我们构建一个神经网络,其中一层含有32×32×3=3072个单元,下一层含有28×28×6=4704个单元,如果采用全连接的方式,两层中的每个神经元彼此相连,然后计算权重矩阵,它等于4074×3072≈1400万,所以要训练的参数很多。虽然以现在的技术,我们可以用1400多万个参数来训练网络,因为这张32×32×3的图片非常小,训练这么多参数没有问题。如果这是一张1000×1000的图片,权重矩阵会变得非常大。我们看看这个卷积层的参数数量,每个过滤器都是5×5,一个过滤器有25个参数,再加上偏差参数,那么每个过滤器就有26个参数,一共有6个过滤器,所以参数共计156个,参数数量还是很少。

卷积网络映射这么少的参数有两个原因。

一是参数共享(parameter sharing)。观察发现,特征检测如垂直边缘检测如果适用于图片的某个区域,那么它也可能适用于图片的其他区域。因此,如果用一个3×3的过滤器检测垂直边缘,那么图片的左上角区域,以及旁边的各个区域(左边矩阵中蓝色方框标记的部分)都可以使用这个3×3的过滤器。每个特征检测器以及输出都可以在输入图片的不同区域中使用同样的参数,以便提取垂直边缘或其它特征。它不仅适用于边缘特征这样的低阶特征(low level features),同样适用于高阶特征(high level features),例如提取脸上的眼睛,猫或者其他特征对象。即使减少参数个数,这9个参数同样能计算出16个输出。直观感觉是,一个特征检测器,如垂直边缘检测器用于检测图片左上角区域的特征,这个特征很可能也适用于图片的右下角区域。因此在计算图片左上角和右下角区域时,就不需要添加其它特征检测器。假如有一个这样的数据集,其左上角和右下角可能有不同分布,也有可能稍有不同,但很相似,整张图片共享特征检测器,提取效果也很好。

二是使用稀疏连接(sparsity of connections)。这个0(等式右端输出结果矩阵的第一个元素,绿色圆圈标记)是通过3×3的卷积计算得到的,它只依赖于这个3×3的输入的单元格,右边这个输出单元(元素0)仅与36个输入特征中9个相连接。而且其它像素值都不会对输出产生任影响,这就是稀疏连接的概念。


2 深度卷积网络:实例探究

2.1  为什么要进行实例探究

上一章节,我们学习了一些基本结构模块,例如卷积层 、池化层以及全连接层。过去几年计算机视觉研究中的大量研究都集中在如何把这些基本结构模块组合起来,形成有效的卷积神经网络。最直观的方式之一就是去看一些实例,就像很多人通过看别人的代码来学习编程一样,通过研究别人构建有效组件的案例。实际上在计算机视觉任务中表现良好的神经网络框架往往也适用于其它任务,如果有人已经训练或者计算出擅长识别猫、狗、人的神经网络或者神经网络框架,而我们的计算机视觉识别任务是构建一个自动驾驶汽车,我们完全可以借鉴别人的神经网络框架来解决自己的问题。

2.2  经典网络

本小节,我们来学习几个经典的神经网络结构(classic neural network architectures),分别是LeNet-5、AlexNet和VGGNet

(一)LeNet-5

如上图,假设有一张32×32×1的图片(输入),LeNet-5可以识别图中的手写数字,比如像这样手写数字7。LeNet-5是针对灰度图片训练的,所以图片的大小只有32×32×1。实际上LeNet-5的结构和我们上一章节讲的最后一个范例非常相似,使用6个5×5的过滤器,步幅为1。由于使用了6个过滤器,步幅为1,padding为0,输出结果为28×28×6,图像尺寸从32×32缩小到28×28。然后进行池化(pooling)操作,在这篇论文发布的那个年代,人们更喜欢使用平均池化,而现在我们可能用最大池化更多一些。在这个例子中,我们使用平均池化,过滤器的宽度为2,步幅为2,图像的尺寸,高度和宽度都缩小了2倍,输出结果是一个14×14×6的图像。

接下来是卷积层,用一组16个5×5的过滤器,新的输出结果有16个通道。LeNet-5的论文是在1998年撰写的,当时人们并不使用padding,或者总是使用valid卷积,这就是为什么每进行一次卷积,图像的高度和宽度都会缩小,所以这个图像从14到14缩小到了10×10。然后又是池化层,高度和宽度再缩小一半,输出一个5×5×16的图像。将所有数字相乘,乘积是400。

下一层是全连接层,在全连接层中,有400个节点,每个节点有120个神经元,这里已经有了一个全连接层。但有时还会从这400个节点中抽取一部分节点构建另一个全连接层,就像这样,有2个全连接层。

最后一步就是利用这84个特征得到最后的输出,我们还可以在这里再加一个节点用来预测y帽的值,y帽有10个可能的值(对应识别0-9这10个数字)。在现在的版本中则使用softmax函数输出十种分类结果,而在当时,LeNet-5网络在输出层使用了另外一种,现在已经很少用到的分类器。

相比现代版本,这里得到的神经网络会小一些,只有约6万个参数。而现在经常看到含有一千万(10 million)到一亿(100 million)个参数的神经网络,比这大1000倍的神经网络也不在少数。

不管怎样,如果我们从左往右看,随着网络越来越深,图像的高度和宽度在缩小,从最初的32×32缩小到28×28,再到14×14、10×10,最后只有5×5。与此同时,随着网络层次的加深,通道数量一直在增加,从1增加到6个,再到16个。

这个神经网络中还有一种模式(other pattern)至今仍然经常用到,就是一个或多个卷积层后面跟着一个池化层,然后又是若干个卷积层再接一个池化层,然后是全连接层,最后是输出,这种排列方式很常用。

(二)AlexNet

AlexNet首先用一张227×227×3的图片作为输入(实际上原文中使用的图像是224×224×3),但是如果你尝试去推导一下,会发现227×227这个尺寸更好一些。

(1)第一层使用96个11×11的过滤器,步幅为4,因此尺寸缩小到55×55,缩小了4倍左右。

(2)然后用一个3×3的过滤器构建最大池化层,f = 3,步幅为2,卷积层尺寸缩小为27×27×96。(3)接着再执行一个5×5的卷积,padding之后,输出是27×27×276。

(4)然后再次进行最大池化,尺寸缩小到13×13。

(5)再执行一次same卷积,相同的padding,得到的结果是13×13×384,384个过滤器。

(6)再做一次same卷积。再做一次同样的操作,最后再进行一次最大池化,尺寸缩小到6×6×256。6×6×256等于9216,将其展开为9216个单元,然后是一些全连接层。

(7)最后使用softmax函数输出识别的结果,看它究竟是1000个可能的对象中的哪一个。

实际上,AlexNet神经网络与LeNet有很多相似之处,不过AlexNet要大得多。正如前面讲到的LeNet或LeNet-5大约有6万个参数,而AlexNet包含约6000万个参数。当用于训练图像和数据集时,AlexNet能够处理非常相似的基本构造模块,这些模块往往包含着大量的隐藏单元或数据,这一点AlexNet表现出色。AlexNet比LeNet表现更为出色的另一个原因是它使用了ReLu激活函数

(三)VGG(也叫作VGG-16网络)

VGG-16网络没有那么多超参数,这是一种只需要专注于构建卷积层的简单网络

首先用3×3,步幅为1的过滤器构建卷积层,padding参数为same卷积中的参数。然后用一个2×2,步幅为2的过滤器构建最大池化层。因此VGG网络的一大优点是它确实简化了神经网络结构

如上图,假设要识别这个图像224×224×3,在最开始的两层用64个3×3的过滤器对输入图像进行卷积,输出结果是224×224×64,因为使用了same卷积,通道数量也一样。(注意上图没有画出所有的卷积层)进行第一个卷积之后得到224×224×64的特征图,接着还有一层224×224×64,得到这样2个厚度为64的卷积层,意味着我们用64个过滤器进行了两次卷积。接下来创建一个池化层,池化层将输入图像进行压缩,从224×224×64缩小到多少呢?没错,减少到112×112×64。然后又是若干个卷积层,使用129个过滤器,以及一些same卷积,我们看看输出什么结果,112×112×128。然后进行池化,可以推导出池化后的结果是这样(56×56×128)。接着再用256个相同的过滤器进行三次卷积操作,然后再池化,然后再卷积三次,再池化。如此进行几轮操作后,将最后得到的7×7×512的特征图进行全连接操作,得到4096个单元,然后进行softmax激活,输出从1000个对象

VGG-16的16,就是指这个网络中包含16个卷积层和全连接层。确实是个很大的网络,总共包含约1.38亿个参数,即便以现在的标准来看都算是非常大的网络。但VGG-16的结构并不复杂,这点非常吸引人,而且这种网络结构很规整(quite uniform),都是几个卷积层后面跟着可以压缩图像大小的池化层,池化层缩小图像的高度和宽度。同时,卷积层的过滤器数量变化存在一定的规律,由64翻倍变成128,再到256和512。作者可能认为512已经足够大了,后面的层就不再翻倍了。无论如何,每一步都进行翻倍,或者说在每一组卷积层进行过滤器翻倍操作,正是设计此种网络结构的另一个简单原则。这种相对一致的网络结构对研究者很有吸引力,而它的主要缺点是需要训练的特征数量非常巨大

2.3  残差网络

非常非常深的神经网络是很难训练的,因为存在梯度消失和梯度爆炸问题。本小节我们学习的跳跃连接(Skip connection),它可以从某一层网络层获取激活,然后迅速反馈给另外一层,甚至是神经网络的更深层。利用跳跃连接构建能够训练深度网络的ResNets,有时深度能够超过100层。ResNets是由残差块(Residual block)构建的,首先看一下什么是残差块。

上图是一个两层神经网络。

z^{[l+1]}=W^{[l+1]}a^{[l]}+b^{[l+1]}

a^{[l+1]}=g(z^{[l+1]})

z^{[l+2]}=W^{[2+1]}a^{[l+1]}+b^{[l+2]}

a^{[l+2]}=g(z^{[l+2]})

在残差网络中有一点变化:

如上图的紫色部分,我们直接将a^{[l]}向后,到神经网络的深层,在ReLU非线性激活函数前加上a^{[l]},将激活值a^{[l]}的信息直接传达到神经网络的深层,不再沿着主路进行,因此a^{[l+2]}的计算公式为:

a^{[l+2]}=g(z^{[l+2]}+a^{[l]})

加上a^{[l]}后产生了一个残差块(residual block)。插入的时机是在线性激活之后,ReLU激活之前。而在本场景下,“跳跃连接”(skip connection),就是指a^{[l]}跳过一层或者好几层,从而将信息传递到神经网络的更深层

ResNet的发明者发现使用残差块能够训练更深的神经网络。所以构建一个ResNet网络就是通过将很多这样的残差块堆积在一起,形成一个很深神经网络。首先回忆一个普通网络(Plain network),这个术语来自ResNet论文。如下图:

如果要把普通网络变成ResNet,就需要加上所有跳跃连接,每两层增加一个捷径,构成一个残差块。如下图所示,5个残差块连接在一起构成一个残差网络

假设使用标准优化算法(梯度下降法等)训练一个普通网络,如果没有残差,没有这些捷径或者跳跃连接,凭经验会发现随着网络深度的加深,训练错误会先减少,然后增多。而理论上,随着网络深度的加深,应该训练得越来越好才对,网络深度越深模型效果越好。但实际上,如果没有残差网络,对于一个普通网络来说,深度越深意味着用优化算法越难训练,随着网络深度的加深,训练错误会越来越多

但有了ResNets就不一样了,即使网络再深,训练的表现却不错,比如说训练误差减少,就算是训练深达100层的网络也不例外。对x的激活,或者这些中间的激活能够到达网络的更深层。这种方式有助于解决梯度消失和梯度爆炸问题,在训练更深网络的同时,又能保证良好的性能

2.4  残差网络为什么有用?

如上图,有一神经网络其输入为X,并输出a^{[l]}。如果要调整该神经网络,使其再深一些,将要再额外增加几层,最后输出a^{[l+2]}。我们把添加的两层做成ResNet块。为了证明我们的观点,我们假设在整个网络中使用ReLU激活函数,所以激活值都大于等于0。下面展开a^{[l+2]} 的表达式,注意如果使用L2正则化或权重衰减,会压缩W^{[l+2]}的值,这里W是关键项(key term),如果假设W^{[l+2]}=0b^{[l+2]}=0,等式如下:

a^{[l+2]}=g(z^{[l+2]}+a^{[l]})=g(W^{[l+2]}a^{[l+1]}+b^{[l+2]}+a^{[l]})=g(a^{[l]})=a^{[l]}

结果表明,残差块学习这个恒等式函数并不难,跳跃连接使我们很容易得出a^{[l+2]}=a^{[l]}。这意味着,即使给神经网络增加了 两层,它的效率也并不逊色于更简单的神经网络,因为学习恒等函数(identity function)对它来说很简单。所以给大型的神经网络增加两层,不论是把残差块添加到神经网络的中间还是末端位置,都不会影响网络的表现。想象一下,如果这些隐藏层单元学到一些有用信息,那么它可能比学习恒等函数表现得更好。

残差网络起作用的主要原因就是这些残差块学习恒等函数非常容易,能确定网络性能不会受到影响,很多时候甚至可以提高效率,或者说至少不会降低网络的效率,因此创建类似残差网络可以提升网络性能。

另一个值得探讨的细节是,假设z^{[l+2]}a^{[l]}具有相同纬度,所以ResNets使用了许多same卷积(same convolutions)。如果输入和输出有不同纬度,例如输入是128,输出是256,需要进行padding,使用0填充。

最后看看ResNets的图片识别。

上图是一个普通网络,给它输入一张图片,它有多个卷积层,最后输出了一个Softmax。

上图是添加跳跃连接(skip connection)由普通网络转化为ResNets的网络。这个网络有很多层3×3卷积,而且它们大多都是same卷积,这就是添加等维特征向量(equal dimension feature vectors)的原因。所以这些都是卷积层,而不是全连接层,因为它们是same卷积。当然也会有池化层或类池化层。

普通网络和ResNets网络常用的结构是:卷积层-卷积层-卷积层-池化层-卷积层-卷积层-卷积层-池化层……依此重复。直到最后,有一个通过softmax进行预测的全连接层。

2.5  网络中的网络以及 1×1 卷积

在设计卷积网络架构时,其中一个比较有帮助的想法是使用1×1卷积。

如上图,输入一张6×6×1的图片,然后对它做卷积,过滤器大小为1×1×1,这里是数字2,结果相当于把这个图片乘以数字2,所以前三个单元格分别是2、4、6等等。用1×1的过滤器进行卷积,似乎用处不大,只是对输入矩阵乘以某个数字。但这仅仅是对于6×6×1的一个通道图片来说,1×1卷积效果不佳。

如上图,如果输入是一张6×6×32的图片,那么使用1×1过滤器进行卷积效果更好。具体来说,1×1卷积所实现的功能是遍历这36个单元格,计算左图中32个数字和过滤器中32个数字的元素积之和,然后应用ReLU非线性函数。

所以1×1卷积可以从根本上理解为对这32个不同的位置都应用一个全连接层,全连接层的作用是输入32个数字(在这36个单元上重复此过程),输出结果是6×6×#filters(过滤器数量),以便在输入层上实施一个非平凡计算(non-trivial computation)

这种方法通常称为1×1卷积,有时也被称为Network in Network,在林敏、陈强和颜水成的论文中有详细描述。虽然论文中关于架构的详细内容并没有得到广泛应用,但是1×1卷积或Network in Network这种理念却很有影响力,很多神经网络架构都受到它的影响,包括下一小节要介绍的Inception网络。

下面介绍1×1卷积的一个应用:

如上图,这是一个28×28×192的输入层,该如何把它压缩为28×28×32维度的层呢?可以用32个大小为1×1的过滤器,严格来讲每个过滤器大小都是1×1×192维,因为过滤器中通道数量必须与输入层中通道的数量保持一致。在某些网络中1×1卷积是如何压缩通道数量并减少计算的。

当然如果想保证通道数量仍为192个,这时1×1卷积层的效果就是给神经网络添加了一个非线性函数,从而减少或保持输入层中的通道数量不变,当然如果愿意,也可以增加通道数量。在之后的学习中会发现这对构建Inception网络很有帮助。

2.6  谷歌 Inception 网络简介

构建卷积层时,需要决定过滤器的大小,而Inception网络的作用就是代替我们来决定过滤器的大小,虽然网络架构因此变得更加复杂,但网络表现却非常好。本小节将一起学习Inception网络的原理。

如上图,是28×28×192维度的输入层,Inception网络Inception层的作用就是代替人工来确定卷积层中的过滤器类型或者确定是否需要创建卷积层(conv)或池化层(pooling)

如果使用1×1卷积,输出结果会是28×28×?,假设输出为28×28×64(绿色),并且这里只有一个层。

如果使用3×3的过滤器,那么输出是28×28×128(蓝色)。然后我们把第二个值堆积到第一个值上,为了匹配维度,我们应用same卷积,输出维度依然是28×28,和输入维度相同,即高度和宽度相同。

如果希望提升网络的表现,用5×5过滤器或许会更好,不妨试一下,输出变成28×28×32(紫色),我们再次使用same卷积,保持维度不变。

如果不想要卷积层,那就可以使用池化层,这里用最大池化操作,得到一些不同的输出结果,我们把它也堆积起来,这里的池化输出是28×28×32(橘色)。为了匹配所有维度,对最大池化使用padding,它是一种特殊的池化形式,因为如果输入的高度和宽度为28×28,则输出的相应维度也是28×28。然后再进行池化,padding不变,步幅为1。

有了这样的Inception模块,就可以输入某个量,这里的最终输出为32+32+128+64=256(因为它累加了所有数字)。Inception模块的输入为28×28×192,输出为28×28×256。这就是Inception网络的核心内容。基本思想是Inception网络不需要人为决定使用哪个过滤器或者是否需要池化,而是由网络自行确定这些参数,可以给网络添加这些参数的所有可能值,然后把这些输出连接起来,让网络自己学习它需要什么样的参数,采用哪些过滤器组合

 下面,计算5×5过滤器在该模块中的计算成本(computational cost)

如上图,这是一个28×28×192的输入块。执行一个5×5卷积,它有32个过滤器,每个过滤器大小为5×5×192,输出为28×28×32。对于输出中的每个数字来说,你都需要执行5×5×192次乘法运算,所以乘法运算的总次数为每个输出值所需要执行的乘法运算次数(5×5×192)乘以输出值个数(28×28×32),把这些数相乘结果等于1.2亿(120422400)。即使在现在,用计算机执行1.2亿次乘法运算,成本也是相当高的。为了降低计算成本,用计算成本除以因子10,结果它从1.2亿减小到原来的十分之一。请记住120这个数字。

上图是另一种结构:1*1卷积降低参数和计算量。对于输入层,使用1×1卷积把输入值从192个通道减少到16个通道。然后对这个较小层运行5×5卷积,得到最终输出。请注意,输入和输出的维度依然相同,输入是28×28×192,输出是28×28×32,和上一页的相同。但我们要做的就是把左边这个大的输入层压缩成这个较小的的中间层,它只有16个通道,而不是192个。有时候这被称为瓶颈层(bottleneck layer),瓶颈通常是某个对象最小的部分。因此我们先缩小网络表示,然后再扩大它。应用1×1卷积,过滤器个数为16,每个过滤器大小为1×1×192,这两个维度(输入通道数与过滤器通道数)相匹配,28×28×16这个层的计算成本是,输出28×28×192中每个元素都做192次乘法,用1×1×192来表示,相乘结果约等于240万。第二层卷积层的输出是28×28×32,对每个输出值应用一个5×5×16维度的过滤器,计算结果为1000万。

因此所需要乘法运算的总次数是这两层的计算成本之和,即1204万,与第一种结构中的值做比较,计算成本从1.2亿下降到了原来的十分之一,即1204万。所需要的加法运算=与乘法运算=的次数近似相等,所以这里只统计了乘法运算的次数。

总之,当我们在构建神经网络层的时候,不想决定池化层是使用1×1,3×3还是5×5的过滤器,那么Inception模块就是最好的选择。可以应用各种类型的过滤器,只需要把输出连接起来。对于计算成本问题,通过使用1×1卷积来构建瓶颈层,从而大大降低计算成本。

🙋仅仅大幅缩小表示层规模会不会影响神经网络的性能?

事实证明,只要合理构建瓶颈层,既可以显著缩小表示层规模,又不会降低网络性能。

2.7  Inception 网络

 在本小节中,我们将学习如何将这些模块组合起来,构建我们自己的Inception网络。Inception网络又叫做GoogLeNet。

Inception模块会将之前层的激活或者输出作为它的输入。如上图,这是一个28×28×192的输入。

①先通过一个1×1的层,再通过一个5×5的层,1×1的层可能有16个通道,而5×5的层输出为28×28×32,共32个通道。

②为了在这个3×3的卷积层中节省运算量,你也可以做相同的操作,这样的话3×3的层将会输出28×28×128。

③直接通过一个1×1的卷积层,这时就不必在后面再跟一个1×1的层了,这样的话过程就只有一步,假设这个层的输出是28×28×64。

④最后是池化层,这里进行最大池化操作,采用same类型的padding进行池化。

为了能在最后将这些输出都连接起来,我们会使用same类型的padding来池化,使得输出的高和宽依然是28×28。但注意,如果进行了最大池化,即便用了same padding,3×3的过滤器,stride为1,其输出将会是28×28×192,其通道数或者说深度与这里的输入相同。所以看起来它会有很多通道,我们实际要做的就是再加上一个1×1的卷积层,去进行我们在1×1卷积层的视频里所介绍的操作,将通道的数量缩小,缩小到28×28×32。也就是使用32个维度为1×1×192的过滤器,所以输出的维度其通道数缩小为32。这样就避免了最后输出时,池化层占据所有的通道。这些操作之后,将这些方块全都连接起来。在这过程中,把得到的各个层的通道都加起来,最后得到一个28×28×256的输出。这就是一个Inception模块,而Inception网络所做的就是将这些模块都组合到一起。

上图是一张取自Szegety et al的论文中关于Inception网络的图片,图中有许多重复的模块,可能整张图看上去很复杂,但如果只截取其中一个环节,就会发现这是Inception模块。所以Inception网络只是很多这些学过的模块在不同的位置重复组成的网络,所以如果理解了之前所学的Inception模块,也就能理解Inception网络。

事实上,论文中还有一些分支(上图绿色笔记),在网络的最后几层,通常称为全连接层(fully connected layer),在它之后是一个softmax层来做出预测,这些分支所做的就是通过隐藏层来做出预测,所以这其实是一个softmax输出。这是另一条分支,它也包含了一个隐藏层,通过一些全连接层,然后有一个softmax来预测,输出结果的标签。这个可以看做Inception网络的一个细节,它确保了即便是隐藏单元和中间层也参与了特征计算,它们也能预测图片的分类。在Inception网络中,起到一种调整的效果,并且能防止网络发生过拟合

2.8  使用开源的实现方案

如果在开发一个计算机视觉应用,一个常见的工作流程。

(1)先选择一个你喜欢的架构,或许是在这门课中学习到的,或者是从朋友那听说的,或者是从文献中看到的.

(2)接着寻找一个开源实现,从GitHub下载下来,以此基础开始构建。这样做的优点在于,这些网络通常都需要很长的时间来训练,而或许有人已经使用多个GPU,通过庞大的数据集预先训练了这些网络,这样一来就可以使用这些网络进行迁移学习。

2.9  迁移学习

 如果要做一个计算机视觉的应用,相比于从头训练权重,下载别人已经训练好的网络结构权重通常会更为高效。用这个作为预训练,然后转换到感兴趣的任务上。计算机视觉的研究社区有许多数据集,比如ImageNet、MS COCO、Pascal类型的数据集,它们都是有大量的计算机视觉研究者已经用这些数据集训练过他们的算法了。有时候这些训练过程需要花费好几周,并且需要很多的GPU,其它人已经做过了,并且经历了非常痛苦的找最优过程,那么我们就没必要再去训练一遍,可以下载别人做出来的开源的权重参数,把它当作一个很好的初始化用在我们的神经网络上。用迁移学习(transfer learning)把公共的数据集的知识迁移到我们的问题上,本小节来看一下怎么做。

假如我们要建立一个猫咪检测器,用来检测宠物猫。假如两只猫分别叫Tigger和Misty,还有一种情况是,两者都不是。所以是一个三分类问题,我们忽略两只猫同时出现在一张图片里的情况。现在我们可能没有Tigger或者Misty的大量的图片,所以训练集会很小。

🙋那么该怎么办呢?

我们可以先从网上下载一些神经网络开源的实现,把代码和权重都下载下来。例如,ImageNet数据集,它有1000个不同的类别,因此这个网络会有一个Softmax单元,它可以输出1000个可能类别之一。你可以去掉这个Softmax层,创建自己的Softmax单元,用来输出Tigger、Misty和neither三个类别。就网络而言,可以把所有的层看作是冻结的,冻结网络中所有层的参数,只需要训练和Softmax层有关的参数。幸运的是,大多数深度学习框架都支持这种操作。它也许会有trainableParameter=0这样的参数,有时也会有freeze=1这样的参数。不同的深度学习编程框架有不同的方式,允许我们指定是否训练特定层的权重。

还有一个技巧,也许对一些很小的数据集的情况有用,由于前面的层都冻结了,相当于一个固定的函数,不需要改变。输入图像,直接把它映射到softmax前一层的激活函数。所以这个能加速训练的技巧就是,如果我们先计算softmax前一层,计算特征或者激活值,然后把它们存到硬盘里。我们所做的就是用这个固定的函数,在这个神经网络的前半部分,即softmax层之前的所有层视为一个固定映射,取任意输入图像,然后计算它的某个特征向量(feature vector),这样训练的就是一个很浅的softmax模型,用这个特征向量来做预测。对计算有用的一步就是对训练集中所有样本的这一层的激活值进行预计算,然后存储到硬盘里,然后在此之上训练softmax分类器。所以,存储到硬盘或者说预计算方法的优点就是,就不需要每次遍历训练集再重新计算这个激活值了。

🙋要是有一个更大一些的数据集呢?

根据经验,如果你有一个更大的标定的数据集,也许有大量的Tigger和Misty以及两者都不是的照片,应该冻结更少的层,比如只把上图红色方框包裹的层冻结,然后训练后面的层。如果输出层的类别不同,那么需要构建自己的输出单元,Tigger、Misty或者两者都不是三个类别。有很多方式可以实现,可以取后面几层的权重,用作初始化,然后从这里开始梯度下降。或者也可以直接去掉后面这几层,换成我们自己的隐藏单元和softmax输出层。注意有一个规律,如果有越来越多的数据,需要冻结的层数越少,能够训练的层数就越多

如果数据量非常大,就可以用开源的网络和它的权重,把所有的权重当作初始化,然后训练整个网络。如果这是一个1000节点的softmax,而我们只有三个输出,需要我们的softmax输出层来输出想要的标签。如果有越多的标定的数据,可以训练越多的层。极端情况下,可以用下载的权重只作为初始化,用它们来代替随机初始化,接着可以用梯度下降训练,更新网络所有层的所有权重。

事实上,对于很多计算机视觉的应用,如果下载其他人的开源的权重来初始化,会做的更好。在所有不同学科中,以及所有深度学习不同的应用中,计算机视觉是一个经常用到迁移学习的领域,除非有非常非常大的数据集,可以从头开始训练所有的东西。总之,迁移学习是非常值得考虑的,除非有一个极其大的数据集和非常大的计算量预算来从头训练你的网络

2.10  数据增强

大部分的计算机视觉任务需要很多的数据,所以数据增强(data augmentation)是经常使用的一种技巧来提高计算机视觉系统的表现。计算机视觉是一个相当复杂的工作,输入图像的像素值,然后弄清楚图片中有什么,似乎需要学习一个复杂方程来做这件事。在实践中,更多的数据对大多数计算机视觉任务都有所帮助。但是,当下计算机视觉的主要问题是没有办法得到充足的数据。这就意味着当我们训练计算机视觉模型的时候,数据增强会有所帮助。无论是使用迁移学习,使用别人的预训练模型开始,或者从源代码开始训练模型。本小节来看一下计算机视觉中常见的数据增强的方法。

(一)垂直镜像对称(mirroring on the vertical axis)

垂直镜像对称(mirroring on the vertical axis)方法是最简单的数据增强方法。假如,训练集中有这张图片,然后将其翻转得到右边的图像。对大多数计算机视觉任务,左边的图片是猫,然后镜像对称仍然是猫,如果镜像操作保留了图像中想识别的物体的前提下,这是个很实用的数据增强技巧。

(二)随机裁剪(random cropping)

给定一个数据集,然后开始随机裁剪。随机裁剪并不是一个完美的数据增强的方法,如果你随机裁剪的上图红色方框一部分,这部分看起来不像猫。但在实践中,这个方法还是很实用的,随机裁剪构成了很大一部分的真实图片。

 镜像对称和随机裁剪是经常被使用的。当然,理论上,也可以使用旋转(rotation)剪切(shearing)局部弯曲(local warping)等等。当然使用这些方法并没有坏处,尽管在实践中,因为太复杂了所以使用的很少。

(三)色彩转换(color shifting)

如上图,有一张猫的图片,分别给R、G和B三个通道上加上不同的失真值。在这个例子中,红色方框,给红色、蓝色通道加值(+20),绿色通道减值(-20)。红色和蓝色会产生紫色,使整张图片看起来偏紫。绿色方框,少用了一点红色(-20),更多的绿色和蓝色色调(+20),这就使得图片偏黄一点。蓝色方框,使用了更多的蓝色(+50),仅仅多了点红色(+5)。

在实践中,R、G和B的值是根据某种概率分布来决定的。这么做的理由是,可能阳光会有一点偏黄,或者是灯光照明有一点偏黄,这些可以轻易的改变图像的颜色,但是对猫的识别,或者是内容的识别,以及标签,还是保持不变的。颜色失真或者是颜色变换方法,这样会使得学习算法对照片的颜色更改更具鲁棒性

对R、G和B有不同的采样方式,其中一种影响颜色失真的算法是PCA,即主成分分析(Principles Components Analysis)。大概含义是,如果图片呈现紫色,即主要含有红色和蓝色,绿色很少,然后PCA颜色增强算法就会对红色和蓝色增减很多,绿色变化相对少一点,所以使总体的颜色保持一致。

如果数据存在硬盘上,然后使用符号,如上图,用圆桶来表示硬盘。如果有很多数据,可能会使用CPU线程,然后它不停的从硬盘中读取数据,所以有一个从硬盘过来的图片数据流。用CPU线程来实现这些失真变形,可以是随机裁剪、颜色变化,或者是镜像。但是对每张图片得到对应的某一种变形失真形式,对其进行镜像变换,以及使用颜色失真,这张图最后会颜色变化,从而得到不同颜色的猫。与此同时,CPU线程持续加载数据,然后实现任意失真变形,从而构成批数据或者最小批数据,这些数据持续的传输给其他线程或者其他的进程,然后开始训练,可以在CPU或者GPU上实现训一个大型网络的训练。常用的实现数据增强的方法是使用一个线程或者是多线程

这就是数据增强,与训练深度神经网络的其他部分类似,在数据增强过程中也有一些超参数,比如说颜色变化了多少,以及随机裁剪的时候使用的参数。与计算机视觉其他部分类似,一个好的开始可能是使用别人的开源实现,了解他们如何实现数据增强。当然如果想获得更多的不变特性,而其他人的开源实现并没有实现这个,也可以去调整这些参数。

2.11  计算机视觉现状

深度学习已经成功地应用于计算机视觉(computer version)自然语言处理(natural language processing)语音识别(speech recognition)在线广告(online advertising)、物流(logistics)还有其他许多问题。在计算机视觉的现状下,深度学习应用于计算机视觉应用有一些独特之处。

大部分机器学习问题是介于少量数据和大量数据范围之间的。举个例子,今天我们有相当数量的语音识别数据,至少相对于这个问题的复杂性而言。虽然现在图像识别或图像分类方面有相当大的数据集,因为图像识别是一个复杂的问题,通过分析像素并识别出它是什么,感觉即使在线数据集非常大,如超过一百万张图片,仍然希望我们能有更多的数据。比如目标检测(object detection),我们拥有的数据更少。提醒一下,图像识别其实是如何看图片的问题,并且告诉我们这张图是不是猫,而目标检测则是看一幅图,你画一个框,告诉你图片里的物体,比如汽车等等。因为获取边框的成本比标记对象的成本更高,所以我们进行对象检测的数据往往比图像识别数据要少,目标检测是我们下一章节要讨论的内容。

如果能从机器学习中发散思维,会发现当我们有很多数据时,人们倾向于使用更简单的算法和更少的手工工程,因为我们不需要为这个问题精心设计特征。当我们有大量的数据时,只要有一个大型的神经网络,甚至一个更简单的架构,可以是一个神经网络,就可以去学习它想学习的东西。相反当我们没有那么多的数据时,人们从事更多的是手工工程

当看机器学习应用时,我们认为通常我们的学习算法有两种知识来源,一个来源是被标记的数据,就像(x,y)应用在监督学习。第二个知识来源是手工工程,有很多方法去建立一个手工工程系统,它可以是源于精心设计的特征,手工精心设计的网络体系结构或者是系统的其他组件

计算机视觉是在试图学习一个非常复杂的函数,我们经常感觉我们没有足够的数据,即使获得了更多数据,我们还是经常觉得还是没有足够的数据来满足需求。这就是为什么计算机视觉,从过去甚至到现在都更多地依赖于手工工程。当没有足够的数据时,手工工程对一个项目来说贡献就很大。当有很多数据的时候应该花时间去建立学习系统。计算机视觉领域还只是使用了非常小的数据集,因此从历史上来看计算机视觉还是依赖于大量的手工工程。甚至在过去的几年里,计算机视觉任务的数据量急剧增加,这导致了手工工程量大幅减少,但是在计算机视觉上仍然有很多的网络架构使用手工工程,这就是为什么你会在计算机视觉中看到非常复杂的超参数选择,比你在其他领域中要复杂的多。幸运的是,当你有少量的数据时,有一件事对你很有帮助,那就是迁移学习(transfer learning)

下面是一些有助于在基准测试中表现出色的小技巧,如果把一个系统投入生产(production),那就是为客户服务(serve customers)。

其中一个是集成(ensembling),这就意味着在你想好了你想要的神经网络之后,可以独立训练几个神经网络,并平均它们的输出。另一个技巧就是Multi-crop at test time,假设你已经看到了如何进行数据增强,Multi-crop是一种将数据增强应用到你的测试图像中的一种形式。集成的一个大问题是需要保持所有这些不同的神经网络,这就占用了更多的计算机内存。对于multi-crop,只保留一个网络,所以它不会占用太多的内存,但它仍然会让你的运行时间变慢


3 目标检测

3.1  目标定位

 从这一小节开始,就进入到目标检测的学习,这是计算机视觉发展极为迅速的一个领域。在构建目标检测之前,我们先来了解一下目标定位(object localization),首先我们看看它的定义。

在之前的学习之中,我们已经了解了图像分类,图片分类(image classification)就是算法遍历图片,判断其中的对象是不是汽车。本章节我们要学习构建神经网络的另一个问题,即定位分类问题。这意味着,我们不仅要用算法判断图片中是不是一辆汽车,还要在图片中标记出它的位置,用边框或红色方框把汽车圈起来,这就是定位分类问题(classification with localization problem)。其中“定位”的意思是判断汽车在图片中的具体位置。比如,在自动驾驶程序中,程序不但要检测其它车辆,还要检测其它对象,如行人、摩托车等,之后的内容会详细介绍。

本章我们要研究的分类定位问题,通常只有一个较大的对象位于图片中间位置,我们要对它进行识别和定位。而在目标检测问题中,图片可以含有多个对象(multiple objects),甚至单张图片中会有多个不同分类的对象。因此,图片分类(image classification)的思路可以帮助学习分类定位(classification with localization),而目标定位的思路又有助于学习目标检测(detection),我们先从分类和定位开始讲起。

如上图,这是图片分类问题,输入一张图片到多层卷积神经网络。这就是卷积神经网络,它会输出一个特征向量,并反馈给softmax单元来预测图片类型。在构建汽车自动驾驶系统时,识别的对象可能包括以下几类:行人、汽车、摩托车和背景,这意味着图片中没有行人、汽车和摩托车,输出结果会是背景对象,这四个分类就是softmax函数可能输出的结果。

这就是标准的分类过程,如果还想定位图片中汽车的位置,可以让神经网络多输出几个单元,输出一个边界框。具体就是让神经网络再多输出4个数字,标记为b_x,b_y,b_hb_w,这四个数字是被检测对象的边界框的参数化表示。

约定一下本章节将使用的符号表示,图片左上角的坐标为(0,0),右下角标记为(1,1)。要确定边界框的具体位置,需要指定红色方框的中心点,这个点表示为(b_x,b_y),边界框的高度为b_h,宽度为b_w。因此训练集不仅包含神经网络要预测的对象分类标签,还要包含表示边界框的这四个数字,接着采用监督学习算法,输出一个分类标签,还有四个参数,从而给出检测对象的边框位置。在这个例子中,b_x的理想值是0.5,因为它表示汽车位于图片水平方向的中间位置;b_y大约是0.7,表示汽车位于距离图片底部十分之三的位置;b_h约为0.3,因为红色方框的高度是图片高度的0.3倍;b_w约为0.4,红色方框的宽度是图片宽度的0.4倍。

上例中,有四个分类,神经网络输出的是这四个数字和一个分类标签,或分类标签出现的概率。目标标签y的定义如下:

y=\begin{bmatrix} p_c\\ b_x\\ b_y\\ b_h\\ b_w\\ c_1\\ c_2\\ c_3 \end{bmatrix}

这是一个向量,第一个p_c表示是否含有对象,在这里如果对象属于前三类(行人、汽车、摩托车),则p_c=1如果是背景,则图片中没有要检测的对象,则p_c=0。如果检测到对象,输出被检测对象的边界框参数b_x,b_y,b_hb_w,同时输出c_1c_2c_3,表示该对象属于1-3类中的哪一类(行人、汽车或者摩托车)。

只这么讲,可能理解的不够深刻,来看两个例子

上图中给出了两个例子的标签y的表示:只有一辆车(紫色框)和背景图像(绿色框,当p_c=0时,y的其它参数变得毫无意义,这里都写成问号,表示“毫无意义”的参数)。针对给定的被标记的训练样本,不论图片中是否含有定位对象,构建输入图片x和分类标签y的具体过程都是如此。这些数据最终定义了训练集。

最后看一下神经网络的损失函数(loss function),参数为网络输出y帽和类别y,采用平方误差策略(squared error),则损失值等于每个元素相应差值的平方和,公式如上图橙色部分。

y_1=1时,平方误差策略为这8个元素预测值和实际输出结果之间差值的平方。当y_1=0时,y中的后7个元素都不用考虑,只需要考虑神经网络评估p_c的准确度。

3.2  特征点检测

神经网络也可以通过输出图片上特征点的(x,y)坐标来实现对目标特征的识别,下面看几个例子。

假设正在构建一个人脸识别应用,希望算法可以给出眼角的具体位置。眼角坐标为(x,y),让神经网络的最后一层多输出两个数字l_xl_y,为眼角的坐标值。如果想知道两只眼睛的四个眼角的具体位置,那么依次用四个特征点来表示从左到右的四个眼角,例如第一个特征点(l_{1x},l_{1y}),第二个特征点(l_{2x},l_{2y})等。

同样也可以根据嘴部的关键点输出值来确定嘴的形状,也可以提取鼻子周围得到关键特征点。假设脸部有64个特征点,有些点甚至可以定义脸部轮廓或下颌轮廓。选定特征点个数,并生成包含这些特征点的标签训练集,然后利用神经网络输出脸部关键特征点的位置。

具体做法是,准备一个卷积网络和一些特征集,将人脸图片输入卷积网络,输出1或0表示是否有人脸,1表示有人脸,0表示没有人脸,然后输出(l_{1x},l_{1y})......直到(l_{64x},l_{64y})。这里一共有129(1+2*64=129)个输出单元,由此实现对图片的人脸检测和定位。

再看一个例子,人体姿态检测,也可以定义一些关键特征点,如胸部的中点,左肩,左肘,腰等,从胸部中心点(l_{1x},l_{1y})一直向下,一直到(l_{32x},l_{32y})。然后通过神经网络标注人物姿态的关键特征点,再输出这些标注过的特征点,就相当于输出了人物的姿态动作。一旦了解如何用二维坐标系定义人物姿态,操作起来就相当简单了。要明确一点,特征点的特性在所有图片中必须保持一致,就好比,特征点1始终是右眼的外眼角,特征点2是右眼的内眼角,特征点3是左眼内眼角,特征点4是左眼外眼角等等。同样可以利用特征点实现其他有趣的效果,比如判断人物的动作姿态,识别图片中的人物表情等等。

3.3  目标检测

在本小节,我们将学习如何通过卷积网络进行目标检测,采用的是基于滑动窗口的目标检测算法(the sliding windows detection algorithm)

假如在构建一个汽车检测算法(car detection algorithm)

(1)首先创建一个标签训练集,也就是x和y表示适当剪切的汽车图片样本,前三张图片是正样本,因为它们是汽车图片。

(2)然后可以训练卷积网络,输入为这些适当裁剪过的图片,输出为y(0或1表示图片中有汽车或没有汽车)。

(3)训练完这个卷积网络,就可以用它来实现滑动窗口目标检测(sliding windows detection),具体步骤如下。

上图是一张测试图片,操作步骤如下三步:

①首先选定一个特定大小的窗口,将这个红色小方块输入卷积神经网络,卷积网络开始进行预测,即判断红色方框内有没有汽车。

②接下来会继续处理第二个图像,即红色方框稍向右滑动之后的区域,并输入给卷积网络,因此输入给卷积网络的只有红色方框内的区域,再次运行卷积网络。

③然后处理第三个图像,依次重复操作,直到这个窗口滑过图像的每一个角落。

为了滑动得更快,这里选用的步幅比较大,思路是以固定步幅移动窗口,遍历图像的每个区域,把这些剪切后的小图像输入卷积网络,对每个位置按0或1进行分类,这就是所谓的图像滑动窗口操作。

重复上述操作,不过这次我们选择一个更大的窗口,截取更大的区域,如下图:

然后第三次重复操作,这次选用更大的窗口。如下图:

这种算法叫作滑动窗口目标检测(sliding windows detection),因为以某个步幅滑动这些方框窗口遍历整张图片,对这些方形区域进行分类,判断里面有没有汽车

滑动窗口目标检测算法也有很明显的缺点,就是计算成本(computational cost),因为你在图片中剪切出太多小方块,卷积网络要一个个地处理。如果你选用的步幅很大,显然会减少输入卷积网络的窗口个数,但是粗糙间隔尺寸可能会影响性能。反之,如果采用小粒度或小步幅,传递给卷积网络的小窗口会特别多,这意味着超高的计算成本。

3.4  卷积的滑动窗口实现

3.5  Bounding Box预测

3.6  交并比

3.7  非极大值抑制

3.8  Anchor Boxes

3.9  YOLO 算法

3.10  RPN网络


4  特殊应用:人脸识别和神经风格转换

4.1  什么是人脸识别?

4.2  One-Shot 学习

4.3  Siamese 网络

4.4  Triplet 损失

4.5  面部验证与二分类

4.6  什么是神经风格转换?

4.7  什么是深度卷积网络?

4.8  代价函数

4.9  内容代价函数

4.10  风格代价函数

4.11 一维到三维推广

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值