卷积神经网络
week1 卷积神经网络
1.1 计算机视觉
但对于计算机视觉应用来说,你肯定不想它只处理小图片,你希望它同时也要能处理大图。为此,你需要进行卷积计算,它是卷积神经网络中非常重要的一块。
1.2 边缘检测示例
这是一个简单的6×6图像,左边的一半是10,右边一般是0。如果你把它当成一个图片,左边那部分看起来是白色的,像素值10是比较亮的像素值,右边像素值比较暗,我使用灰色来表示0,尽管它也可以被画成黑的。图片里,有一个特别明显的垂直边缘在图像中间,这条垂直线是从黑到白的过渡线,或者从白色到深色。
这个3×3的过滤器可视化为,在左边有明亮的像素,然后有一个过渡,0在中间,然后右边是深色的。
如果把最右边的矩阵当成图像,它在中间有段亮一点的区域,对应检查到这个6×6图像中间的垂直边缘。这里的维数似乎有点不正确,检测到的边缘太粗了。因为在这个例子中,图片太小了。如果你用一个1000×1000的图像,而不是6×6的图片,你会发现其会很好地检测出图像中的垂直边缘。在这个例子中,在输出图像中间的亮处,表示在图像中间有一个特别明显的垂直边缘。从垂直边缘检测中可以得到的启发是,因为我们使用3×3的矩阵(过滤器),所以垂直边缘是一个3×3的区域,左边是明亮的像素,中间的并不需要考虑,右边是深色像素。在这个6×6图像的中间部分,明亮的像素在左边,深色的像素在右边,就被视为一个垂直边缘,卷积运算提供了一个方便的方法来发现图像中的垂直边缘。
1.3 更多边缘检测内容
在本视频中,你将学习如何区分正边和负边,这实际就是由亮到暗与由暗到亮的区别,也就是边缘的过渡。
白色是 R:255 R:255 R:255
现在这幅图有什么变化呢?它的颜色被翻转了,变成了左边比较暗,而右边比较亮。现在亮度为10的点跑到了右边,为0的点则跑到了左边。如果你用它与相同的过滤器进行卷积,最后得到的图中间会是-30,而不是30。如果你将矩阵转换为图片,就会是该矩阵下面图片的样子。现在中间的过渡部分被翻转了,之前的30翻转成了-30,表明是由暗向亮过渡,而不是由亮向暗过渡。
一个垂直边缘过滤器是一个3×3的区域,它的左边相对较亮,而右边相对较暗。相似的,右边这个水平边缘过滤器也是一个3×3的区域,它的上边相对较亮,而下方相对较暗。
再举个例子,这里的30(右边矩阵中绿色方框标记元素)代表了左边这块3×3的区域(左边矩阵绿色方框标记部分),这块区域确实是上边比较亮,而下边比较暗的,所以它在这里发现了一条正边缘。而这里的-30(右边矩阵中紫色方框标记元素)又代表了左边另一块区域(左边矩阵紫色方框标记部分),这块区域确实是底部比较亮,而上边则比较暗,所以在这里它是一条负边。
通过使用不同的过滤器,你可以找出垂直的或是水平的边缘。但事实上,对于这个3×3的过滤器来说,我们使用了其中的一种数字组合。
过构成这些计算的基础依然是卷积运算,使得反向传播算法能够让神经网络学习任何它所需要的3×3的过滤器,并在整幅图片上去应用它。这里,任何它所检测到的特征,不管是垂直的边缘,水平的边缘,还有其他奇怪角度的边缘,甚至是其它的连名字都没有的过滤器。
所以这种将这9个数字当成参数的思想,已经成为计算机视觉中最为有效的思想之一。
1.4 Padding
至于选择填充多少像素,通常有两个选择,分别叫做Valid卷积和Same卷积。
习惯上,计算机视觉中, f 通常是奇数,甚至可能都是这样。你很少看到一个偶数的过滤器在计算机视觉里使用,我认为有两个原因。
其中一个可能是,如果 f 是一个偶数,那么你只能使用一些不对称填充。只有 f 是奇数的情况下,Same卷积才会有自然的填充,我们可以以同样的数量填充四周,而不是左边填充多一点,右边填充少一点,这样不对称的填充。
第二个原因是当你有一个奇数维过滤器,比如3×3或者5×5的,它就有一个中心点。有时在计算机视觉里,如果有一个中心像素点会更方便,便于指出过滤器的位置。
1.5 卷积步长
如果你用一个 f∗f
的过滤器卷积一个 n ∗ n
的图像,你的padding为 p ,步幅为 s ,在这个例子中 s = 2 ,你会得到一个输出,因为现在你不是一次移动一个步子,而是一次移动 s个步子,输出于是变为 \(\frac{n+2p-f}{s}+1\)
在我们的这个例子里,n=7,p=0,f=3,s=2,\(\frac{7+0-3}{2}+1=3\),即3×3的输出。
如果商不是一个整数,我们向下取整。
如果你看的是一本典型的数学教科书,那么卷积的定义是做元素乘积求和,实际上你首先要做的是先顺时针旋转 90°,再水平翻转。我们在这些视频中定义卷积运算时,我们跳过了这个镜像操作。从技术上讲,我们实际上做的,我们在前面视频中使用的操作,有时被称为互相关(cross-correlation)而不是卷积(convolution)。但在深度学习文献中,按照惯例,我们将这(不进行翻转操作)叫做卷积操作。
1.6 三维卷积
图像的通道数必须和过滤器的通道数匹配,所以这两个数(紫色方框标记的两个数)必须相等。
这个的输出会是一个4×4的图像,注意是4×4×1,最后一个数不是3了。
举个例子,这个过滤器是3×3×3的,如果你想检测图像红色通道的边缘,那么你可以将第一个过滤器设为\(\begin{bmatrix} 1 & 0 &-1 \\ 1& 0& -1\\ 1& 0&-1 \end{bmatrix}\) ,和之前一样,而绿色通道全为0,蓝色也全为0,如果你把这三个堆叠在一起形成一个3×3×3的过滤器,那么这就是一个检测垂直边界的过滤器,但只对红色通道有用。
或者如果你不关心垂直边界在哪个颜色通道里,那么你可以用一个这样的过滤器,三个通道都是\(\begin{bmatrix} 1 & 0 &-1 \\ 1& 0& -1\\ 1& 0&-1 \end{bmatrix}\)。通过设置第二个过滤器参数,就有了一个边界检测器,可以检测任意颜色通道的边界。
我们总结一下维度,如果你有一个 $ nnn_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 数值会变化,正如前面的视频演示的那样。
1.7 单层卷积网络
假设你有10个过滤器,而不是2个,神经网络的一层是3×3×3,那么,这一层有多少个参数呢?我们来计算一下,每一层都是一个3×3×3的矩阵,因此每个过滤器有27个参数,也就是27个数。然后加上一个偏差,用参数 b 表示,现在参数增加到28个。上一页幻灯片里我画了2个过滤器,而现在我们有10个,加在一起是28×10,也就是280个参数。
请注意一点,不论输入图片有多大,1000×1000也好,5000×5000也好,参数始终都是280个。用这10个过滤器来提取特征,如垂直边缘,水平边缘和其它特征。即使这些图片很大,参数却很少,这就是卷积神经网络的一个特征,叫作“避免过拟合”。你已经知道到如何提取10个特征,可以应用到大图片中,而参数数量固定不变,此例中只有28个,相对较少。
这一层是卷积层,用 \(f^{[l]}\)表示过滤器大小,我们说过过滤器大小为$ f ∗ f$,上标 \({[l]}\)表示 l 层中过滤器大小为 f ∗ f
。通常情况下,上标 \({[l]}\)用来标记 l 层。 \(p^{[l]}\)用来标记padding的数量,padding数量也可指定为一个valid卷积,即无padding。或是same卷积,即选定padding,如此一来,输出和输入图片的高度和宽度就相同了。用 \(s^{[l]}\)标记步幅。
这一层的输入会是某个维度的数据,表示为 \(n*n*n_c\) , \(n_c\) 某层上的颜色通道数。
我们要稍作修改,增加上标$ [l-1] $,即 \(n^{[l-1]}*n^{[l-1]}*n_c^{[l-1]}\) ,因为它是上一层的激活值。
此例中,所用图片的高度和宽度都一样,但它们也有可能不同,所以分别用上下标 H 和 W来标记,即 \(n_H^{[l-1]}*n_W^{[l-1]}*n_c^{[l-1]}\)。那么在第 l 层,图片大小为 $ n_H{[l-1]}*n_W*n_c^{[l-1]}\(, l 层的输入就是上一层的输出,因此上标要用\)[l−1]$ 。神经网络这一层中会有输出,它本身会输出图像。其大小为 \(n_H^{[l]}*n_W^{[l]}*n_c^{[l]}\) ,这就是输出图像的大小。
\[n_H^{[l]} = \left \lfloor \frac{n_H^{[l-1]}+2p^{[l]}-f^{[l]}}{s^{[l]}}+1 \right \rfloor \]\[n_W^{[l]} = \left \lfloor \frac{n_W^{[l-1]}+2p^{[l]}-f^{[l]}}{s^{[l]}}+1 \right \rfloor \]通道数量:
过滤器中通道的数量必须与输入中通道的数量一致。因此,输出通道数量就是输入通道数量,所以过滤器维度等于$ f{[l]}*f*n_c^{[l-1]}$。
应用偏差和非线性函数之后,这一层的输出等于它的激活值 \(a^{[l]}\) ,也就是这个维度(输出维度)。 \(a^{[l]}\) 是一个三维体,即\(n_H^{[l]}*n_W^{[l]}*n_c^{[l]}\) 。当你执行批量梯度下降或小批量梯度下降时,如果有 m 个例子,就是有 m 个激活值的集合,那么输出 \(A^{[l]}=m*n_H^{[l]}*n_W^{[l]}*n_c^{[l]}\)。如果采用批量梯度下降,变量的排列顺序如下,首先是索引和训练示例,然后是其它三个变量。
该如何确定权重参数,即参数W呢?过滤器的维度已知,为$ f{[l]}*f*n_c^{[l-1]}$,这只是一个过滤器的维度,有多少个过滤器,这( \(n_c^{[l]}\) )是过滤器的数量,权重也就是所有过滤器的集合再乘以过滤器的总数量,即 \(f^{[l]}*f^{[l]}*n_c^{[l-1]}*n_c^{[l]}\) ,损失数量L就是 l 层中过滤器的个数。
最后我们看看偏差参数,每个过滤器都有一个偏差参数,它是一个实数。偏差包含了这些变量,它是该维度上的一个向量。后续课程中我们会看到,为了方便,偏差在代码中表示为一个\(1×1×1×n_c^{[l]}\)的四维向量或四维张量。
1.8 简单卷积网络示例
随着神经网络计算深度不断加深,通常开始时的图像也要更大一些,初始值为39×39,高度和宽度会在一段时间内保持一致,然后随着网络深度的加深而逐渐减小,从39到37,再到17,最后到7。而通道数量在增加,从3到10,再到20,最后到40。
一个典型的卷积神经网络通常有三层,一个是卷积层,我们常常用Conv来标注。
一个是池化层,我们称之为POOL。
最后一个是全连接层,用FC表示。
1.9 池化层
除了卷积层,卷积网络也经常使用池化层来缩减模型的大小,提高计算速度,同时提高所提取特征的鲁棒性,我们来看一下。
起到保留局部最重要的特征,去除掉多余的部分,以达到减少计算量的目的吧
最大化操作的功能就是只要在任何一个象限内提取到某个特征,它都会保留在最大化的池化输出里。所以最大化运算的实际作用就是,如果在过滤器中提取到某个特征,那么保留其最大值。如果没有提取到这个特征,可能在右上象限中不存在这个特征,那么其中的最大值也还是很小,这就是最大池化的直观理解。
其中一个有意思的特点就是,它有一组超参数,但并没有参数需要学习。实际上,梯度下降没有什么可学的,一旦确定了 f 和 s ,它就是一个固定运算,梯度下降无需改变任何值。
目前来说,最大池化比平均池化更常用
池化的超级参数包括过滤器大小 f 和步幅 s ,常用的参数值为 f = 2 , s = 2 ,应用频率非常高,其效果相当于高度和宽度缩减一半。也有使用 f = 3 , s = 2 的情况。至于其它超级参数就要看你用的是最大池化还是平均池化了。你也可以根据自己意愿增加表示padding的其他超级参数,虽然很少这么用。最大池化时,往往很少用到超参数padding,当然也有例外的情况,我们下周会讲。大部分情况下,最大池化很少用padding。目前 p 最常用的值是0,即 p = 0
最大池化的输入:\(n_H*n_W*n_c\),假设没有padding,则输出$\left \lfloor \frac{n_H-f}{s}+1 \right \rfloor * \left \lfloor \frac{n_H-f}{s}+1 \right \rfloor *n_c $
输入通道与输出通道个数相同,因为我们对每个通道都做了池化,但是需要注意,池化过程中没有需要学习的参数。
1.10 卷积神经网络示例
常规做法是,尽量不要自己设置超参数,而是查看文献中别人采用了哪些超参数,选一个在别人任务中效果很好的架构,那么它也有可能适用于你自己的应用程序,这块下周我会细讲。
随着神经网络深度的加深,高度 \(n_H\) 和 $ n_W$ 宽度通常都会减少,前面我就提到过,从32×32到28×28,到14×14,到10×10,再到5×5。所以随着层数增加,高度和宽度都会减小,而通道数量会增加,从3到6到16不断增加,然后得到一个全连接层。
在神经网络中,另一种常见模式就是一个或多个卷积后面跟随一个池化层,然后一个或多个卷积层后面再跟一个池化层,然后是几个全连接层,最后是一个softmax。这是神经网络的另一种常见模式。
有几点要注意,第一,池化层和最大池化层没有参数;第二卷积层的参数相对较少,前面课上我们提到过,其实许多参数都存在于神经网络的全连接层。观察可发现,随着神经网络的加深,激活值尺寸会逐渐变小,如果激活值尺寸下降太快,也会影响神经网络性能。示例中,激活值尺寸在第一层为6000,然后减少到1600,慢慢减少到84,最后输出softmax结果。我们发现,许多卷积网络都具有这些属性,模式上也相似。
1.11 为什么使用卷积
和只用全连接层相比,卷积层的两个主要优势在于参数共享和稀疏连接,举例说明一下。
假设有一张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个过滤器,有三个通道,所以参数共计26*6*3 = 468
个,参数数量还是很少。
该如何确定权重参数,即参数W呢?过滤器的维度已知,为$ f{[l]}*f*n_c^{[l-1]}$,这只是一个过滤器的维度,有多少个过滤器,这( \(n_c^{[l]}\) )是过滤器的数量,权重也就是所有过滤器的集合再乘以过滤器的总数量,即 \(f^{[l]}*f^{[l]}*n_c^{[l-1]}*n_c^{[l]}\) ,损失数量L就是 l 层中过滤器的个数。
卷积网络映射这么少参数有两个原因:
一是参数共享。观察发现,特征检测如垂直边缘检测如果适用于图片的某个区域,那么它也可能适用于图片的其他区域。也就是说,如果你用一个3×3的过滤器检测垂直边缘,那么图片的左上角区域,以及旁边的各个区域(左边矩阵中蓝色方框标记的部分)都可以使用这个3×3的过滤器。每个特征检测器以及输出都可以在输入图片的不同区域中使用同样的参数,以便提取垂直边缘或其它特征。它不仅适用于边缘特征这样的低阶特征,同样适用于高阶特征,例如提取脸上的眼睛,猫或者其他特征对象。即使减少参数个数,这9个参数同样能计算出16个输出。直观感觉是,一个特征检测器,如垂直边缘检测器用于检测图片左上角区域的特征,这个特征很可能也适用于图片的右下角区域。因此在计算图片左上角和右下角区域时,你不需要添加其它特征检测器。假如有一个这样的数据集,其左上角和右下角可能有不同分布,也有可能稍有不同,但很相似,整张图片共享特征检测器,提取效果也很好。
第二个方法是使用稀疏连接,我来解释下。这个0是通过3×3的卷积计算得到的,它只依赖于这个3×3的输入的单元格,右边这个输出单元(元素0)仅与36个输入特征中9个相连接。而且其它像素值都不会对输出产生任影响,这就是稀疏连接的概念。
神经网络可以通过这两种机制减少参数,以便我们用更小的训练集来训练它,从而预防过度拟合。你们也可能听过,卷积神经网络善于捕捉平移不变。通过观察可以发现,向右移动两个像素,图片中的猫依然清晰可见,因为神经网络的卷积结构使得即使移动几个像素,这张图片依然具有非常相似的特征,应该属于同样的输出标记。实际上,我们用同一个过滤器生成各层中,图片的所有像素值,希望网络通过自动学习变得更加健壮,以便更好地取得所期望的平移不变属性。
1.12 卷积计算(整理)
week2 深度卷积网络:实例探究
2.1 为什么要进行实例探究
可以借鉴别人的神经网络框架来解决自己的问题。
2.2 经典网络
首先看看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。然后进行池化操作,在这篇论文写成的那个年代,人们更喜欢使用平均池化,而现在我们可能用最大池化更多一些。在这个例子中,我们进行平均池化,过滤器的宽度为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个特征得到最后的输出,我们还可以在这里再加一个节点用来预测 \(\hat{y}\) 的值,\(\hat{y}\)有10个可能的值,对应识别0-9这10个数字。在现在的版本中则使用softmax函数输出十种分类结果,而在当时,LeNet-5网络在输出层使用了另外一种,现在已经很少用到的分类器。
读这篇论文时:
过去,人们使用sigmod函数和tanh函数,而不是ReLu函数,这篇论文中使用的正是sigmod函数和tanh函数。这种网络结构的特别之处还在于,各网络层之间是有关联的,这在今天看来显得很有趣。
比如说,你有一\(n_H*n_W*n_c\)的网络,有\(n_c\)个通道,使用尺寸为 \(f*f*n_c\)的过滤器,每个过滤器的通道数和它上一层的通道数相同。这是由于在当时,计算机的运行速度非常慢,为了减少计算量和参数,经典的LeNet-5网络使用了非常复杂的计算方式,每个过滤器都采用和输入模块一样的通道数量。论文中提到的这些复杂细节,现在一般都不用了。
当时所进行的最后一步其实到现在也还没有真正完成,就是经典的LeNet-5网络在池化后进行了非线性函数处理,在这个例子中,池化层之后使用了sigmod函数。如果你真的去读这篇论文,这会是最难理解的部分之一,我们会在后面的课程中讲到。
AlexNet首先用一张227×227×3的图片作为输入,实际上原文中使用的图像是224×224×3,但是如果你尝试去推导一下,你会发现227×227这个尺寸更好一些。第一层我们使用96个11×11的过滤器,步幅为4,由于步幅是4,因此尺寸缩小到55×55,缩小了4倍左右。然后用一个3×3的过滤器构建最大池化层, f = 3 ,步幅为2,卷积层尺寸缩小为27×27×96。接着再执行一个5×5的卷积,padding之后,输出是27×27×276。然后再次进行最大池化,尺寸缩小到13×13。再执行一次same卷积,相同的padding,得到的结果是13×13×384,384个过滤器。再做一次same卷积,就像这样。再做一次同样的操作,最后再进行一次最大池化,尺寸缩小到6×6×256。6×6×256等于9216,将其展开为9216个单元,然后是一些全连接层。最后使用softmax函数输出识别的结果,看它究竟是1000个可能的对象中的哪一个。
实际上,这种神经网络与LeNet有很多相似之处,不过AlexNet要大得多。正如前面讲到的LeNet或LeNet-5大约有6万个参数,而AlexNet包含约6000万个参数。当用于训练图像和数据集时,AlexNet能够处理非常相似的基本构造模块,这些模块往往包含着大量的隐藏单元或数据,这一点AlexNet表现出色。AlexNet比LeNet表现更为出色的另一个原因是它使用了ReLu激活函数。
论文还提到,经典的AlexNet结构还有另一种类型的层,叫作“局部响应归一化层”(Local Response Normalization),即LRN层,这类层应用得并不多,所以我并没有专门讲。局部响应归一层的基本思路是,假如这是网络的一块,比如是13×13×256,LRN要做的就是选取一个位置,比如说这样一个位置,从这个位置穿过整个通道,能得到256个数字,并进行归一化。进行局部响应归一化的动机是,对于这张13×13的图像中的每个位置来说,我们可能并不需要太多的高激活神经元。但是后来,很多研究者发现LRN起不到太大作用,这应该是被我划掉的内容之一,因为并不重要,而且我们现在并不用LRN来训练网络。
VGG-16网络没有那么多超参数,这是一种只需要专注于构建卷积层的简单网络。首先用3×3,步幅为1的过滤器构建卷积层,padding参数为same卷积中的参数。然后用一个2×2,步幅为2的过滤器构建最大池化层。因此VGG网络的一大优点是它确实简化了神经网络结构,下面我们具体讲讲这种网络结构。
假设要识别这个图像,在最开始的两层用64个3×3的过滤器对输入图像进行卷积,输出结果是224×224×64,因为使用了same卷积,通道数量也一样。VGG-16其实是一个很深的网络,这里我并没有把所有卷积层都画出来。
假设这个小图是我们的输入图像,尺寸是224×224×3,进行第一个卷积之后得到224×224×64的特征图,接着还有一层224×224×64,得到这样2个厚度为64的卷积层,意味着我们用64个过滤器进行了两次卷积。正如我在前面提到的,这里采用的都是大小为3×3,步幅为1的过滤器,并且都是采用same卷积,所以我就不再把所有的层都画出来了,只用一串数字代表这些网络。
接下来创建一个池化层,池化层将输入图像进行压缩,从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的结构并不复杂,这点非常吸引人,而且这种网络结构很规整,都是几个卷积层后面跟着可以压缩图像大小的池化层,池化层缩小图像的高度和宽度。同时,卷积层的过滤器数量变化存在一定的规律,由64翻倍变成128,再到256和512。作者可能认为512已经足够大了,所以后面的层就不再翻倍了。无论如何,每一步都进行翻倍,或者说在每一组卷积层进行过滤器翻倍操作,正是设计此种网络结构的另一个简单原则。这种相对一致的网络结构对研究者很有吸引力,而它的主要缺点是需要训练的特征数量非常巨大。
[!TIP]
看论文顺序:AlexNet、VGG、LeNet
2.3 残差网络
非常非常深的神经网络是很难训练的,因为存在梯度消失和梯度爆炸问题。这节课我们学习跳跃连接(Skip connection),它可以从某一层网络层获取激活,然后迅速反馈给另外一层,甚至是神经网络的更深层。我们可以利用跳跃连接构建能够训练深度网络的ResNets,有时深度能够超过100层,让我们开始吧
ResNets是由残差块(Residual block)构建的,首先我解释一下什么是残差块。
这是一个两层神经网络,在 L 层进行激活,得到 $ a^{[l+1]}\(,再次进行激活,两层之后得到\) a^{[l+2]}$。计算过程是从 \(a^{[l]}\) 开始,首先进行线性激活,根据这个公式:\(z^{[l+1]}=W^{[l+1]}a^{[l]}+b^{[l+1]}\),通过 \(a^{[l]}\) 算出 \(z^{[l+1]}\) ,即 $ a^{[l]} $乘以权重矩阵,再加上偏差因子。然后通过ReLU非线性激活函数得到 \(a^{[l+1]}\), \(a^{[l+1]}=g(z^{[l+1]})\)计算得出。接着再次进行线性激活,依据等式 $z{[l+2]}=Wa{[l+1]}+b $,最后根据这个等式再次进行ReLu非线性激活,即 \(a^{[l+2]}=g(z^{[l+2]})\) ,这里的 g 是指ReLU非线性函数,得到的结果就是$ a{[l+2]}$。换句话说,信息流从$a$到 \(a^{[l+2]}\)需要经过以上所有步骤,即这组网络层的主路径。
在残差网络中有一点变化,我们将 \(a^{[l]}\) 直接向后,拷贝到神经网络的深层,在ReLU非线性激活函数前加上 \(a^{[l]}\),这是一条捷径。 \(a^{[l]}\) 的信息直接到达神经网络的深层,不再沿着主路径传递,这就意味着最后这个等式$ a{[l+2]}=g(z)\(去掉了,取而代之的是另一个**ReLU**非线性函数,仍然对\) z^{[l+2]}\(进行 g 函数处理,**但这次要加上\) a^{[l]}\(,即:\) a{[l+2]}=g(z+a^{[l]}) \(,也就是加上的这个\)a^{[l]} $产生了一个残差块。**
在上面这个图中,我们也可以画一条捷径,直达第二层。实际上这条捷径是在进行ReLU非线性激活函数之前加上的,而这里的每一个节点都执行了线性函数和ReLU激活函数。所以 $a^{[l]} $插入的时机是在线性激活之后,ReLU激活之前。除了捷径,你还会听到另一个术语“跳跃连接”,就是指 \(a^{[l]}\) 跳过一层或者好几层,从而将信息传递到神经网络的更深层。
ResNet的发明者是何凯明(Kaiming He)、张翔宇(Xiangyu Zhang)、任少卿(Shaoqing Ren)*和*孙剑(Jiangxi Sun),他们发现使用残差块能够训练更深的神经网络。所以构建一个ResNet网络就是通过将很多这样的残差块堆积在一起,形成一个很深神经网络,我们来看看这个网络。
这并不是一个残差网络,而是一个普通网络(Plain network),这个术语来自ResNet论文。
把它变成ResNet的方法是加上所有跳跃连接,正如前一张幻灯片中看到的,每两层增加一个捷径,构成一个残差块。如图所示,5个残差块连接在一起构成一个残差网络。
如果我们使用标准优化算法训练一个普通网络,比如说梯度下降法,或者其它热门的优化算法。如果没有残差,没有这些捷径或者跳跃连接,凭经验你会发现随着网络深度的加深,训练错误会先减少,然后增多。而理论上,随着网络深度的加深,应该训练得越来越好才对。也就是说,理论上网络深度越深越好。但实际上,如果没有残差网络,对于一个普通网络来说,深度越深意味着用优化算法越难训练。实际上,随着网络深度的加深,训练错误会越来越多。
但有了ResNets就不一样了,即使网络再深,训练的表现却不错,比如说训练误差减少,就算是训练深达100层的网络也不例外。有人甚至在1000多层的神经网络中做过实验,尽管目前我还没有看到太多实际应用。但是对 x 的激活,或者这些中间的激活能够到达网络的更深层。这种方式确实有助于解决梯度消失和梯度爆炸问题,让我们在训练更深网络的同时,又能保证良好的性能。也许从另外一个角度来看,随着网络越来深,网络连接会变得臃肿,但是ResNet确实在训练深度网络方面非常有效。
2.4 残差网络为什么有用
为什么ResNets能有如此好的表现,我们来看个例子,它解释了其中的原因,至少可以说明,如何构建更深层次的ResNets网络的同时还不降低它们在训练集上的效率。希望你已经通过第三门课了解到,通常来讲,网络在训练集上表现好,才能在Hold-Out交叉验证集或dev集和测试集上有好的表现,所以至少在训练集上训练好ResNets是第一步。
先来看个例子,上节课我们了解到,一个网络深度越深,它在训练集上训练的效率就会有所减弱,这也是有时候我们不希望加深网络的原因。而事实并非如此,至少在训练ResNets网络时,并非完全如此,举个例子。
假设有一个大型神经网络,其输入为 X ,输出激活值 \(a^{[l]}\) 。假如你想增加这个神经网络的深度,那么用Big NN表示,输出为 \(a^{[l]}\) 。再给这个网络额外添加两层,依次添加两层,最后输出为 \(a^{[l+2]}\),可以把这两层看作一个ResNets块,即具有捷径连接的残差块。为了方便说明,假设我们在整个网络中使用ReLU激活函数,所以激活值都大于等于0,包括输入 X 的非零异常值。因为ReLU激活函数输出的数字要么是0,要么是正数。
我们看一下 \(a^{[l+2]}\)的值,也就是上节课讲过的表达式,即 $a{[l+2]}=g(z+a^{[l]}) $,添加项 \(a^{[l]}\)是刚添加的跳跃连接的输入。展开这个表达式 \(a^{[l+2]}=g(W^{[l+2]}a^{[l+1]}+b^{[l+2]}+a^{[l]})\),其中$z{[l+2]}=Wa{[l+1]}+b \(。注意一点,如果使用**L2**正则化或权重衰减,它会压缩\)W^{[l+2]}\(的值。如果对 b应用权重衰减也可达到同样的效果,尽管实际应用中,你有时会对 b 应用权重衰减,有时不会。这里的 W 是关键项,如果\) W^{[l+2]}=0 $,为方便起见,假设 \(b^{[l+2]}=0\),这几项就没有了,因为它们\(W^{[l+2]}a^{[l+1]}+b^{[l+2]}\)的值为0。最后$ a{[l+2]}=g(a)=a{[l]}$,因为我们假定使用**ReLU**激活函数,并且所有激活值都是非负的,$g(a) $是应用于非负数的ReLU函数,所以 $ a{[l+2]}=a$。
结果表明,残差块学习这个恒等式函数并不难,跳跃连接使我们很容易得出$ a{[l+2]}=a $。这意味着,**即使给神经网络增加了这两层,它的效率也并不逊色于更简单的神经网络,因为学习恒等函数对它来说很简单。尽管它多了两层,也只把 \(a^{[l]}\)的值赋值给 \(** a^{[l+2]}\) 。所以给大型神经网络增加两层,不论是把残差块添加到神经网络的中间还是末端位置,都不会影响网络的表现。
当然,我们的目标不仅仅是保持网络的效率,还要提升它的效率。想象一下,如果这些隐藏层单元学到一些有用信息,那么它可能比学习恒等函数表现得更好。而这些不含有残差块或跳跃连接的深度普通网络情况就不一样了,当网络不断加深时,就算是选用学习恒等函数的参数都很困难,所以很多层最后的表现不但没有更好,反而更糟。
我认为残差网络起作用的主要原因就是这些残差块学习恒等函数非常容易,你能确定网络性能不会受到影响,很多时候甚至可以提高效率,或者说至少不会降低网络的效率,因此创建类似残差网络可以提升网络性能。
除此之外,关于残差网络,另一个值得探讨的细节是,假设$z^{[l+2]} \(与\) a{[l]}$具有相同维度,所以**ResNets**使用了许多**same**卷积,所以这个$a$的维度等于这个输出层的维度。之所以能实现跳跃连接是因为same卷积保留了维度,所以很容易得出这个捷径连接,并输出这两个相同维度的向量。
如果输入和输出有不同维度,比如输入的维度是128, $a^{[l+2]} \(的维度是256,再增加一个矩阵,这里标记为\)W_s\(,\)W_s$是一个256×128维度的矩阵,所以 \(W_sa^{[l]}\)的维度是256,这个新增项是256维度的向量。你不需要对\(W_s\)做任何操作,它是网络通过学习得到的矩阵或参数,它是一个固定矩阵,padding值为0,用0填充\(a^{[l]}\) ,其维度为256,所以者几个表达式都可以。
最后,我们来看看ResNets的图片识别。这些图片是我从何凯明等人论文中截取的,这是一个普通网络,我们给它输入一张图片,它有多个卷积层,最后输出了一个Softmax。
如何把它转化为ResNets呢?只需要添加跳跃连接。这里我们只讨论几个细节,这个网络有很多层3×3卷积,而且它们大多都是same卷积,这就是添加等维特征向量的原因。所以这些都是卷积层,而不是全连接层,因为它们是same卷积,维度得以保留,这也解释了添加项 \(z^{[l+2]}+a^{[l]}\)(维度相同所以能够相加)。
ResNets类似于其它很多网络,也会有很多卷积层,其中偶尔会有池化层或类池化层的层。不论这些层是什么类型,正如我们在上一张幻灯片看到的,你都需要调整矩阵 \(W_s\) 的维度。普通网络和ResNets网络常用的结构是:卷积层-卷积层-卷积层-池化层-卷积层-卷积层-卷积层-池化层……依此重复。直到最后,有一个通过softmax进行预测的全连接层。
2.5 网络中的网络及1x1卷积
过滤器为1×1,这里是数字2,输入一张6×6×1的图片,然后对它做卷积,起过滤器大小为1×1×1,结果相当于把这个图片乘以数字2,所以前三个单元格分别是2、4、6等等。用1×1的过滤器进行卷积,似乎用处不大,只是对输入矩阵乘以某个数字。但这仅仅是对于6×6×1的一个通道图片来说,1×1卷积效果不佳。
如果是一张6×6×32的图片,那么使用1×1过滤器进行卷积效果更好。具体来说,1×1卷积所实现的功能是遍历这36个单元格,计算左图中32个数字和过滤器中32个数字的元素积之和,然后应用ReLU非线性函数。
我们以其中一个单元为例,它是这个输入层上的某个切片,用这36个数字乘以这个输入层上1×1切片,得到一个实数,像这样把它画在输出中。
这个1×1×32过滤器中的32个数字可以这样理解,一个神经元的输入是32个数字(输入图片中左下角位置32个通道中的数字),即相同高度和宽度上某一切片上的32个数字,这32个数字具有不同通道,乘以32个权重(将过滤器中的32个数理解为权重),然后应用ReLU非线性函数,在这里输出相应的结果。
一般来说,如果过滤器不止一个,而是多个,就好像有多个输入单元,其输入内容为一个切片上所有数字,输出结果是6×6✖️过滤器数量。
所以1×1卷积可以从根本上理解为对这32个不同的位置都应用一个全连接层,全连接层的作用是输入32个数字(过滤器数量标记为 $n_c^{[l+1]} $,在这36个单元上重复此过程),输出结果是6×6×#filters(过滤器数量),以便在输入层上实施一个非平凡(non-trivial)计算。
这种方法通常称为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维,因为过滤器中通道数量必须与输入层中通道的数量保持一致。但是你使用了32个过滤器,输出层为28×28×32,这就是压缩通道数( \(n_c\) )的方法,对于池化层我只是压缩了这些层的高度和宽度。
在之后我们看到在某些网络中1×1卷积是如何压缩通道数量并减少计算的。当然如果你想保持通道数192不变,这也是可行的,1×1卷积只是添加了非线性函数,当然也可以让网络学习更复杂的函数,比如,我们再添加一层,其输入为28×28×192,输出为28×28×192。
2.6 谷歌 Inception
构建卷积层时,你要决定过滤器的大小究竟是1×1(原来是1×3,猜测为口误),3×3还是5×5,或者要不要添加池化层。而Inception网络的作用就是代替你来决定,虽然网络架构因此变得更加复杂,但网络表现却非常好,我们来了解一下其中的原理。
例如,这是你28×28×192维度的输入层,Inception网络或Inception层的作用就是代替人工来确定卷积层中的过滤器类型,或者确定是否需要创建卷积层或池化层,我们演示一下。
如果使用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网络的核心内容,提出者包括Christian Szegedy、刘伟、贾阳青、Pierre Sermanet、Scott Reed、Dragomir Anguelov、Dumitru Erhan、Vincent Vanhoucke和Andrew Rabinovich。基本思想是Inception网络不需要人为决定使用哪个过滤器或者是否需要池化,而是由网络自行确定这些参数,你可以给网络添加这些参数的所有可能值,然后把这些输出连接起来,让网络自己学习它需要什么样的参数,采用哪些过滤器组合。
不难发现,我所描述的Inception层有一个问题,就是计算成本,下一张幻灯片,我们就来计算这个5×5过滤器在该模块中的计算成本。
要得到
28*28*32
个输出,每个输出都是经过5*5*192
次运算得来的,故总运算次数为两者相乘
我们把重点集中在前一张幻灯片中的5×5的过滤器,这是一个28×28×192的输入块,执行一个5×5卷积,它有32个过滤器,输出为28×28×32。前一张幻灯片中,我用一个紫色的细长块表示,这里我用一个看起来更普通的蓝色块表示。我们来计算这个28×28×32输出的计算成本,它有32个过滤器,因为输出有32个通道,每个过滤器大小为5×5×192,输出大小为28×28×32,所以你要计算28×28×32个数字。对于输出中的每个数字来说,你都需要执行5×5×192次乘法运算,所以乘法运算的总次数为每个输出值所需要执行的乘法运算次数(5×5×192)乘以输出值个数(28×28×32),把这些数相乘结果等于1.2亿(120422400)。即使在现在,用计算机执行1.2亿次乘法运算,成本也是相当高的。下一张幻灯片会介绍1×1卷积的应用,也就是我们上节课所学的。为了降低计算成本,我们用计算成本除以因子10,结果它从1.2亿减小到原来的十分之一。请记住120这个数字,一会还要和下一页看到的数字做对比。
这里还有另外一种架构,其输入为28×28×192,输出为28×28×32。其结果是这样的,对于输入层,使用1×1卷积把输入值从192个通道减少到16个通道。然后对这个较小层运行5×5卷积,得到最终输出。请注意,输入和输出的维度依然相同,输入是28×28×192,输出是28×28×32,和上一页的相同。但我们要做的就是把左边这个大的输入层压缩成这个较小的的中间层,它只有16个通道,而不是192个。
有时候这被称为瓶颈层,瓶颈通常是某个对象最小的部分,假如你有这样一个玻璃瓶,这是瓶塞位置,瓶颈就是这个瓶子最小的部分。
接下来我们看看这个计算成本,应用1×1卷积,过滤器个数为16,每个过滤器大小为1×1×192,这两个维度相匹配(输入通道数与过滤器通道数),28×28×16这个层的计算成本是,输出28×28×192中每个元素都做192次乘法,用1×1×192来表示,相乘结果约等于240万。
那第二个卷积层呢?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模块会将之前层的激活或者输出作为它的输入,作为前提,这是一个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来池化,使得输出的高和宽依然是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网络的图片,你会发现图中有许多重复的模块,可能整张图看上去很复杂,但如果你只截取其中一个环节(编号1),就会发现这是在前一页ppt中所见的Inception模块。
我们深入看看里边的一些细节,这是另一个Inception模块(编号2),这也是一个Inception模块(编号3)。这里有一些额外的最大池化层(编号6)来修改高和宽的维度。这是另外一个Inception模块(编号4),这是另外一个最大池化层(编号7),它改变了高和宽。而这里又是另一个Inception模块(编号5)。
所以Inception网络只是很多这些你学过的模块在不同的位置重复组成的网络,所以如果你理解了之前所学的Inception模块,你就也能理解Inception网络。
事实上,如果你读过论文的原文,你就会发现,这里其实还有一些分支,我现在把它们加上去。所以这些分支有什么用呢?在网络的最后几层,通常称为全连接层,在它之后是一个softmax层(编号1)来做出预测,这些分支(编号2)所做的就是通过隐藏层(编号3)来做出预测,所以这其实是一个softmax输出(编号2),这(编号1)也是。这是另一条分支(编号4),它也包含了一个隐藏层,通过一些全连接层,然后有一个softmax来预测,输出结果的标签。
你应该把它看做Inception网络的一个细节,它确保了即便是隐藏单元和中间层(编号5)也参与了特征计算,它们也能预测图片的分类。它在Inception网络中,起到一种调整的效果,并且能防止网络发生过拟合。
还有这个特别的Inception网络是由Google公司的作者所研发的,它被叫做GoogleLeNet,这个名字是为了向LeNet网络致敬。在之前的视频中你应该了解了LeNet网络。我觉得这样非常好,因为深度学习研究人员是如此重视协作,深度学习工作者对彼此的工作成果有一种强烈的敬意。
2.8 使用开源的实现方案
如果你在开发一个计算机视觉应用,一个常见的工作流程是,先选择一个你喜欢的架构,或许是你在这门课中学习到的,或者是你从朋友那听说的,或者是从文献中看到的,接着寻找一个开源实现,从GitHub下载下来,以此基础开始构建。这样做的优点在于,这些网络通常都需要很长的时间来训练,而或许有人已经使用多个GPU,通过庞大的数据集预先训练了这些网络,这样一来你就可以使用这些网络进行迁移学习
2.9 迁移学习
如果你要做一个计算机视觉的应用,相比于从头训练权重,或者说从随机初始化权重开始,如果你下载别人已经训练好网络结构的权重,你通常能够进展的相当快,用这个作为预训练,然后转换到你感兴趣的任务上。计算机视觉的研究社区非常喜欢把许多数据集上传到网上,如果你听说过,比如ImageNet,或者MS COCO,或者Pascal类型的数据集,这些都是不同数据集的名字,它们都是由大家上传到网络的,并且有大量的计算机视觉研究者已经用这些数据集训练过他们的算法了。有时候这些训练过程需要花费好几周,并且需要很多的GPU,其它人已经做过了,并且经历了非常痛苦的寻最优过程,这就意味着你可以下载花费了别人好几周甚至几个月而做出来的开源的权重参数,把它当作一个很好的初始化用在你自己的神经网络上。用迁移学习把公共的数据集的知识迁移到你自己的问题上,让我们看一下怎么做。
举个例子,假如说你要建立一个猫咪检测器,用来检测你自己的宠物猫。比如网络上的Tigger,是一个常见的猫的名字,Misty也是比较常见的猫名字。假如你的两只猫叫Tigger和Misty,还有一种情况是,两者都不是。所以你现在有一个三分类问题,图片里是Tigger还是Misty,或者都不是,我们忽略两只猫同时出现在一张图片里的情况。现在你可能没有Tigger或者Misty的大量的图片,所以你的训练集会很小,你该怎么办呢?
我建议你从网上下载一些神经网络开源的实现,不仅把代码下载下来,也把权重下载下来。有许多训练好的网络,你都可以下载。举个例子,ImageNet数据集,它有1000个不同的类别,因此这个网络会有一个Softmax单元,它可以输出1000个可能类别之一。
你可以去掉这个Softmax层,创建你自己的Softmax单元,用来输出Tigger、Misty和neither三个类别。就网络而言,我建议你把所有的层看作是冻结的,你冻结网络中所有层的参数,你只需要训练和你的Softmax层有关的参数。这个Softmax层有三种可能的输出,Tigger、Misty或者都不是。
通过使用其他人预训练的权重,你很可能得到很好的性能,即使只有一个小的数据集。幸运的是,大多数深度学习框架都支持这种操作,事实上,取决于用的框架,它也许会有trainableParameter=0这样的参数,对于这些前面的层,你可能会设置这个参数。为了不训练这些权重,有时也会有freeze=1这样的参数。不同的深度学习编程框架有不同的方式,允许你指定是否训练特定层的权重。在这个例子中,你只需要训练softmax层的权重,把前面这些层的权重都冻结。
另一个技巧,也许对一些情况有用,由于前面的层都冻结了,相当于一个固定的函数,不需要改变。因为你不需要改变它,也不训练它,取输入图像 X ,然后把它映射到这层(softmax的前一层)的激活函数。所以这个能加速训练的技巧就是,如果我们先计算这一层(紫色箭头标记),计算特征或者激活值,然后把它们存到硬盘里。你所做的就是用这个固定的函数,在这个神经网络的前半部分(softmax层之前的所有层视为一个固定映射),取任意输入图像 X ,然后计算它的某个特征向量,这样你训练的就是一个很浅的softmax模型,用这个特征向量来做预测。对你的计算有用的一步就是对你的训练集中所有样本的这一层的激活值进行预计算,然后存储到硬盘里,然后在此之上训练softmax分类器。所以,存储到硬盘或者说预计算方法的优点就是,你不需要每次遍历训练集再重新计算这个激活值了。
因此如果你的任务只有一个很小的数据集,你可以这样做。要有一个更大的训练集怎么办呢?根据经验,如果你有一个更大的标定的数据集,也许你有大量的Tigger和Misty的照片,还有两者都不是的,这种情况,你应该冻结更少的层,比如只把这些层冻结,然后训练后面的层。如果你的输出层的类别不同,那么你需要构建自己的输出单元,Tigger、Misty或者两者都不是三个类别。有很多方式可以实现,你可以取后面几层的权重,用作初始化,然后从这里开始梯度下降。
或者你可以直接去掉这几层,换成你自己的隐藏单元和你自己的softmax输出层,这些方法值得一试。但是有一个规律,如果你有越来越多的数据,你需要冻结的层数越少,你能够训练的层数就越多。这个理念就是,如果你有一个更大的数据集,也许有足够多的数据,那么不要单单训练一个softmax单元,而是考虑训练中等大小的网络,包含你最终要用的网络的后面几层。
最后,如果你有大量数据,你应该做的就是用开源的网络和它的权重,把这、所有的权重当作初始化,然后训练整个网络。再次注意,如果这是一个1000节点的softmax,而你只有三个输出,你需要你自己的softmax输出层来输出你要的标签。
如果你有越多的标定的数据,或者越多的Tigger、Misty或者两者都不是的图片,你可以训练越多的层。极端情况下,你可以用下载的权重只作为初始化,用它们来代替随机初始化,接着你可以用梯度下降训练,更新网络所有层的所有权重。
2.10 数据增强
2.11 计算机视觉现状
你可以认为大部分机器学习问题是介于少量数据和大量数据范围之间的。举个例子,我认为今天我们有相当数量的语音识别数据,至少相对于这个问题的复杂性而言。虽然现在图像识别或图像分类方面有相当大的数据集,因为图像识别是一个复杂的问题,通过分析像素并识别出它是什么,感觉即使在线数据集非常大,如超过一百万张图片,我们仍然希望我们能有更多的数据。还有一些问题,比如物体检测,我们拥有的数据更少。提醒一下,图像识别其实是如何看图片的问题,并且告诉你这张图是不是猫,而对象检测则是看一幅图,你画一个框,告诉你图片里的物体,比如汽车等等。因为获取边框的成本比标记对象的成本更高,所以我们进行对象检测的数据往往比图像识别数据要少,对象检测是我们下周要讨论的内容。
当你没有足够的数据时,手工工程对一个项目来说贡献就很大。当你有很多数据的时候我就不会花时间去做手工工程,我会花时间去建立学习系统。
基准测试中表现出色的小技巧:
- 集成,这就意味着在你想好了你想要的神经网络之后,可以独立训练几个神经网络,并平均它们的输出。
- Multi-crop at test time,我的意思是你已经看到了如何进行数据扩充,Multi-crop是一种将数据扩充应用到你的测试图像中的一种形式。
week3 目标检测
3.1 目标定位
我们不仅要用算法判断图片中是不是一辆汽车,还要在图片中标记出它的位置,用边框或红色方框把汽车圈起来,这就是定位分类问题。其中“定位”的意思是判断汽车在图片中的具体位置。
图片分类输入一张图片到多层卷积神经网络,它会输出一个特征向量,并反馈给softmax单元来预测图片类型。
如果你正在构建汽车自动驾驶系统,那么对象可能包括以下几类:行人、汽车、摩托车和背景,这意味着图片中不含有前三种对象,也就是说图片中没有行人、汽车和摩托车,输出结果会是背景对象,这四个分类就是softmax函数可能输出的结果。
定位图片中汽车的位置,我们可以让神经网络多输出几个单元,输出一个边界框。**具体说就是让神经网络再多输出4个数字,标记为 \(b_x,b_y,b_h\)和 $ b_w\(,这四个数字是被检测对象的边界框的参数化表示。**需要指定红色方框的中心点,这个点表示为\)( b_x,b_y)\(,边界框的高度为\)b_h$,宽度为 \(b_w\)
目标标签y的定义:
\[? \]\(p_c\):是否含有对象,如果对象属于前三类(行人、汽车、摩托车),则\(p_c=1\),如果是背景,则图片中没有要检测的对象,则 \(p_c=0\) 。
\(c_1、c_2\) 和$ c_3$,表示该对象属于1-3类中的哪一类,是行人,汽车还是摩托车。
神经网络的损失函数,采用平方误差策略:
\[L(\hat{y},y) = (\hat{y}_1-y_1)^2+(\hat{y}_2-y_2)^2+...+(\hat{y}_8-y_8)^2 \]如果图片中存在定位对象,那么\(y_1 = 1\),\(p_c=1\),损失值就是不同元素的平方和
如果\(y_1=0\),\(p_c=0\),损失值是\((\hat{y}_1-y_1)^2\),对于这种情况不需要考虑其他元素,只需要关注神经网络输出\(p_c\)的准确度
3.2 目标点检测
神经网络可以通过输出图片上特征点的 (x,y)坐标来实现对目标特征的识别
3.3 目标检测
重复操作,选择一个更大的窗口,截取更大的区域,并输入给卷积神经网络处理,你可以根据卷积网络对输入大小调整这个区域,然后输入给卷积网络,输出0或1。
如果你选用的步幅很大,显然会减少输入卷积网络的窗口个数,但是粗糙间隔尺寸可能会影响性能。
反之,如果采用小粒度或小步幅,传递给卷积网络的小窗口会特别多,这意味着超高的计算成本。
3.4 滑动窗口的卷积实现
把神经网络的全连接层转化成卷积层。最后几个单元集变成了1×1×400和1×1×4的维度。
假设输入给卷积网络的图片大小是14×14×3,测试集图片是16×16×3,我们在这个16×16×3的小图像上滑动窗口,卷积网络运行了4次,于是输出了了4个标签。
所以该卷积操作的原理是我们不需要把输入图像分割成四个子集,分别执行前向传播,而是把它们作为一张图片输入给卷积网络进行计算,其中的公共区域可以共享很多计算,就像这里我们看到的这个4个14×14的方块一样。
3.5 Bounding Box 预测
其中一个能得到更精准边界框的算法是YOLO算法,YOLO(You only look once)*意思是你只看一次,这是由*Joseph Redmon,Santosh Divvala,Ross Girshick和Ali Farhadi提出的算法。
这张图里有9个格子,所以对于每个格子都有这么一个向量。
\[y = \begin{bmatrix} p_c\\ b_x\\ b_y\\ b_h\\ b_w\\ c_1\\ c_2\\ c_3 \end{bmatrix} \]YOLO算法做的就是,取两个对象的中点,然后将这个对象分配给包含对象中点的格子。
所以对于这里9个格子中任何一个,你都会得到一个8维输出向量,因为这里是3×3的网格,所以有9个格子,总的输出尺寸是3×3×8,所以目标输出是3×3×8。因为这里有3×3格子,然后对于每个格子,你都有一个8维向量 y ,所以目标输出尺寸是3×3×8。
所以这个算法的优点在于神经网络可以输出精确的边界框,输入图像 x ,然后跑正向传播,直到你得到这个输出 y 。然后对于这里3×3位置对应的9个输出
这是单次卷积实现,但你使用了一个卷积网络,有很多共享计算步骤,在处理这3×3计算中很多计算步骤是共享的,或者你的19×19的网格,所以这个算法效率很高。
我们约定左上这个点是( 0 ,0 ),然后右下这个点是( 1 , 1 )
[!TIP]
YOLO论文比较难读
3.6 交并比
3.7 非极大值抑制
非极大值抑制这个方法可以确保你的算法对每个对象只检测一次
因为你要在361个格子上都运行一次图像检测和定位算法,那么可能很多格子都会举手说我的 $p_c $,我这个格子里有车的概率很高
现在我们就说,这个\(p_c\) 检测概率,首先看概率最大的那个,这个例子(右边车辆)中是0.9,然后就说这是最可靠的检测,所以我们就用高亮标记,就说我这里找到了一辆车。这么做之后,非极大值抑制就会逐一审视剩下的矩形,所有和这个最大的边框有很高交并比,高度重叠的其他边界框,那么这些输出就会被抑制。
你直接抛弃变暗的矩形,那就剩下高亮显示的那些,这就是最后得到的两个预测结果。
一直这么做,把没处理的都处理完,直到每个边界框都判断过了,它们有的作为输出结果,剩下的会被抛弃
3.8 Anchor Boxes
到目前为止,对象检测中存在的一个问题是每个格子只能检测出一个对象,如果你想让一个格子检测出多个对象,你可以这么做,就是使用anchor box这个概念
现在每个对象都和之前一样分配到同一个格子中,分配到对象中点所在的格子中,以及分配到和对象形状交并比最高的anchor box中。所以这里有两个anchor box,然后你观察哪一个anchor box和实际边界框的交并比更高,不管选的是哪一个,这个对象不只分配到一个格子,而是分配到一对,即(grid cell,anchor box)
如果你有两个anchor box,但在同一个格子中有三个对象,这种情况算法处理不好,你希望这种情况不会发生,但如果真的发生了,这个算法并没有很好的处理办法,对于这种情况,我们就引入一些打破僵局的默认手段。
3.9 YOLO算法
所以你这样遍历9个格子,遍历3×3网格的所有位置,你会得到这样一个向量,得到一个16维向量,所以最终输出尺寸就是3×3×16。
3.10 候选区域
所以Ross Girshick,Jeff Donahue,Trevor Darrell,Jitendra Malik,在本幻灯片底部引用到的论文中提出一种叫做R-CNN的算法,意思是带区域的卷积网络,或者说带区域的CNN。这个算法尝试选出一些区域,在这些区域上运行卷积网络分类器是有意义的,所以这里不再针对每个滑动窗运行检测算法,而是只选择一些窗口,在少数窗口上运行卷积网络分类器。
选出候选区域的方法是运行图像分割算法,分割的结果是下边的图像,为了找出可能存在对象的区域。
- R-CNN算法还是很慢的,得到的边界框比较精确,比单纯使用图像分割算法给出的色块边界要好,所以它可以得到相当精确的边界框。
- 快速的R-CNN算法:它基本上是R-CNN算法,不过用卷积实现了滑动窗法,快一些,但是还是比YOLO要慢
week 4 特殊应用:人脸识别和神经风格转换
4.1 什么是人脸识别
人脸验证问题,如果你有一张输入图片,以及某人的ID或者是名字,这个系统要做的是,验证输入图片是否是这个人。有时候也被称作1对1问题,只需要弄明白这个人是否和他声称的身份相符。
人脸识别问题比人脸验证问题难很多,1对多问题
如果你把这个验证系统应用在100个人身上,人脸识别上,你犯错的机会就是100倍了。如果每个人犯错的概率是1%,如果你有一个上百人的数据库,如果你想得到一个可接受的识别误差,你要构造一个验证系统,其准确率为99.9%或者更高,然后才可以在100人的数据库上运行,而保证有很大几率不出错。事实上,如果我们有一个100人的数据库,正确率可能需要远大于99%,才能得到很好的效果。
4.2 One-Shot 学习
在大多数人脸识别应用中,你需要通过单单一张图片或者单单一个人脸样例就能去识别这个人。
所以在一次学习问题中,只能通过一个样本进行学习,以能够认出同一个人。大多数人脸识别系统都需要解决这个问题,因为在你的数据库中每个雇员或者组员可能都只有一张照片。
有一种办法是,将人的照片放进卷积神经网络中,使用softmax单元来输出4种标签,分别对应这4个人,或者4个都不是,所以softmax里我们会有5种输出。但实际上这样效果并不好,因为如此小的训练集不足以去训练一个稳健的神经网络。
学习Similarity函数。详细地说,你想要神经网络学习这样一个用 d 表示的函数, d(img1,img2)=degree of difference between images
,它以两张图片作为输入,然后输出这两张图片的差异值。****如果你放进同一个人的两张照片,你希望它能输出一个很小的值,如果放进两个长相差别很大的人的照片,它就输出一个很大的值。所以在识别过程中,如果这两张图片的差异值小于某个阈值 τ ,它是一个超参数,那么这时就能预测这两张图片是同一个人,**如果差异值大于 τ ,就能预测这是不同的两个人,这就是解决人脸验证问题的一个可行办法。
4.3 Siamese
函数 d 的作用就是输入两张人脸,然后告诉你它们的相似度。实现这个功能的一个方式就是用Siamese网络
你经常看到这样的卷积网络,输入图片$ x^{(1)}$,然后通过一些列卷积,池化和全连接层,最终得到128维的特征向量
建立一个人脸识别系统的方法就是,如果你要比较两个图片的话,例如这里的第一张(编号1)和第二张图片(编号2),你要做的就是把第二张图片喂给有同样参数的同样的神经网络,然后得到一个不同的128维的向量(编号3),这个向量代表或者编码第二个图片,我要把第二张图片的编码叫做 \(f(x^{(2)})\) 。
最后如果你相信这些编码很好地代表了这两个图片,你要做的就是定义 d ,将$ x^{(1)}$和 $ x{(2)}$的距离定义为这两幅图片的编码之差的范数,$d(x,x{(2)})=||f(x)-f(x{(2)})||2_2$
怎么训练这个Siamese神经网络呢?不要忘了这两个网络有相同的参数,所以你实际要做的就是训练一个网络,它计算得到的编码可以用于函数 d ,它可以告诉你两张图片是否是同一个人。更准确地说,神经网络的参数定义了一个编码函数 $ f(x^{(i)}) $,如果给定输入图像 \(x^{(i)}\),这个网络会输出 \(x^{(i)}\)的128维的编码。你要做的就是学习参数,使得如果两个图片\(x^{(i)}\)和 \(x^{(j)}\)是同一个人,那么你得到的两个编码的距离就小。
如果你改变这个网络所有层的参数,你会得到不同的编码结果,你要做的就是用反向传播来改变这些所有的参数,以确保满足这些条件。
4.4 Triplet 损失
要想通过学习神经网络的参数来得到优质的人脸图片编码,方法之一就是定义三元组损失函数然后应用梯度下降。
用三元组损失的术语来说,你要做的通常是看一个 Anchor 图片,你想让Anchor图片和Positive图片(Positive意味着是同一个人)的距离很接近。然而,当Anchor图片与Negative图片(Negative意味着是非同一个人)对比时,你会想让他们的距离离得更远一点。
\[||f(A)-f(P)||^2 \le ||f(A)-f(N)||^2 \]如果 f 总是输出0,即0-0≤0,这就是0减去0还等于0,**如果所有图像的 f 都是一个零向量,那么总能满足这个方程。 ** 所以为了确保网络对于所有的编码不会总是输出0,也为了确保它不会把所有的编码都设成互相相等的。另一种方法能让网络得到这种没用的输出,就是如果每个图片的编码和其他图片一样,这种情况,你还是得到0-0。
\[||f(A)-f(P)||^2 - ||f(A)-f(N)||^2 \le 0 \]这个不能是刚好小于等于0,应该是比0还要小,所以这个应该小于一个 \(-\alpha\) 值
\[||f(A)-f(P)||^2 - ||f(A)-f(N)||^2 \le -\alpha \]你可以把这项调大或者这个调小,超参数 α 拉大或者缩小了了Anchor和Positive 图片对和Anchor与Negative 图片对之间的差距。
损失函数:
\[L(A,P,N) = max(||f(A)-f(P)||^2 - ||f(A)-f(N)||^2+\alpha,0) \]相减如果为负值,等于0。
为了定义三元组的数据集你需要成对的 A 和 P ,即同一个人的成对的图片,为了训练你的系统你确实需要一个数据集,里面有同一个人的多个照片。对于你的人脸识别系统,可能你只有想要识别的某个人的一张照片。但对于训练集,你需要确保有同一个人的多个图片,至少是你训练集里的一部分人,这样就有成对的Anchor和Positive图片了。
你如何选择这些三元组来形成训练集。一个问题是如果你从训练集中,随机地选择 A 、 P 和 N ,遵守 A 和 P 是同一个人,而 A 和 N 是不同的人这一原则。
- 随机选择,\(d(A,P) + \alpha \le d(A,N)\) A和N选择不同的人很容易满足
- 尽可能选择难训练的三元组A、P和N,所有的三元组都满足\(d(A,P) + \alpha \le d(A,N)\),A、P和N的选择 \(d(A,P) \approx d(A,N)\),还可以增加你的学习算法的计算效率,如果随机的选择这些三元组,其中有太多会很简单,梯度算法不会有什么效果,因为网络总是很轻松就能得到正确的结果,只有选择难的三元组梯度下降法才能发挥作用,使得这两边离得尽可能远。
这个观点来自于Florian Schroff, Dmitry Kalenichenko, James Philbin (2015). FaceNet: A Unified Embedding forFace Recognition and Clustering
4.5 人脸验证与二分类
选取一对神经网络,选取Siamese网络,使其同时计算这些嵌入,比如说128维的嵌入(编号1),或者更高维,然后将其输入到逻辑回归单元,然后进行预测,如果是相同的人,那么输出是1,若是不同的人,输出是0。这就把人脸识别问题转换为一个二分类问题,训练这种系统时可以替换Triplet loss的方法。
\[\hat{y} = \sigma(\sum_{k=1}^{128}w_i|f(x^{(i)})_k - f(x^{(j)})_k|+b) \]把他们放入逻辑回归中,最后的逻辑回归可以增加参数 $w_i $和 b ,就像普通的逻辑回归一样。你将在这128个单元上训练合适的权重,用来预测两张图片是否是一个人,这是一个很合理的方法来学习预测0或者1,即是否是同一个人。
还有其他不同的形式来计算绿色标记的这部分公式\(|f(x^{(i)})_k - f(x^{(j)})_k|\),比如说,公式可以是\(\frac{(f(x^{(i)})_k - f(x^{(j)})_k)^2}{f(x^{(i)})_k+f(x^{(j)})_k}\),这个公式也被叫做$ \chi^2$公式,也被称为 $ \chi$平方相似度。
论文:Yaniv Taigman, Ming Yang, Marc’Aurelio Ranzato, Lior Wolf (2014). DeepFace:Closing the gap to human-level performance in face verification
训练的Siamese网络,意味着上面这个神经网络拥有的参数和下面神经网络的相同(编号3和4所示的网络),两组参数是绑定的,这样的系统效果很好。
之前提到一个计算技巧可以帮你显著提高部署效果,如果这是一张新图片(编号1),当员工走进门时,希望门可以自动为他们打开,这个(编号2)是在数据库中的图片,不需要每次都计算这些特征(编号6),不需要每次都计算这个嵌入,你可以提前计算好,那么当一个新员工走近时,你可以使用上方的卷积网络来计算这些编码(编号5),然后使用它,和预先计算好的编码进行比较,然后输出预测值 \(\hat{y}\)。
提前计算好这个人存的一张照片的编码,当一个人走近的时候用相同的卷积网络和预先计算好的编码比较
这个预先计算的思想,可以节省大量的计算,这个预训练的工作可以用在Siamese网路结构中,将人脸识别当作一个二分类问题,也可以用在学习和使用Triplet loss函数上,我在之前的视频中描述过。
4.6 什么是神经风格迁移
C 来表示内容图像, S 表示风格图像, G 表示生成的图像。
4.7 CNN 特征可视化
假如你训练了一个卷积神经网络,是一个Alexnet,轻量级网络,你希望将看到不同层之间隐藏单元的计算结果。
你可以这样做,从第一层的隐藏单元开始,假设你遍历了训练集,然后找到那些使得单元激活最大化的一些图片,或者是图片块。换句话说,将你的训练集经过神经网络,然后弄明白哪一张图片最大限度地激活特定的单元。注意在第一层的隐藏单元,只能看到小部分卷积神经,如果要画出来哪些激活了激活单元,只有一小块图片块是有意义的,因为这就是特定单元所能看到的全部。你选择一个隐藏单元,发现有9个图片最大化了单元激活,你可能找到这样的9个图片块(编号1),似乎是图片浅层区域显示了隐藏单元所看到的,找到了像这样的边缘或者线(编号2),这就是那9个最大化地激活了隐藏单元激活项的图片块。
然后你可以选一个另一个第一层的隐藏单元,重复刚才的步骤,这是另一个隐藏单元,似乎第二个由这9个图片块(编号1)组成。看来这个隐藏单元在输入区域,寻找这样的线条(编号2),我们也称之为接受域。
对其他隐藏单元也进行处理,会发现其他隐藏单元趋向于激活类似于这样的图片。这个似乎对垂直明亮边缘左边有绿色的图片块(编号1)感兴趣,这一个隐藏单元倾向于橘色,这是一个有趣的图片块(编号2),红色和绿色混合成褐色或者棕橙色,但是神经元仍可以激活它。
以此类推,这是9个不同的代表性神经元,每一个不同的图片块都最大化地激活了。你可以这样理解,第一层的隐藏单元通常会找一些简单的特征,比如说边缘或者颜色阴影。
我在这个视频中使用的所有例子来自于Matthew Zener和Rob Fergus的这篇论文,题目是(Zeiler M D, Fergus R.Visualizing and Understanding Convolutional Networks[J]. 2013, 8689:818-833.)《可视化理解卷积神经网络》,我会使用一种更简单的方法来可视化神经网络隐藏单元的计算内容。如果你读过他们的论文,他们提出了一些更复杂的方式来可视化卷积神经网络的计算。
你已经在第一层的9个隐藏单元重复了这个过程好几遍,如果在深层的隐藏单元中进行这样的计算呢?卷积神经网络的深层部分学到了什么?在深层部分,一个隐藏单元会看到一张图片更大的部分,在极端的情况下,可以假设每一个像素都会影响到神经网络更深层的输出,靠后的隐藏单元可以看到更大的图片块,我还会画出和这页中的大小相同的图片块。
但如果我们重复这一过程,这(Layer 1所示图片)是之前第一层得到的,这个(Layer 2所示图片)是可视化的第2层中最大程度激活的9个隐藏单元。
重复这些过程可以检测到更加复杂的事物。
4.8 代价函数
给你一个内容图像 C ,给定一个风格图片 S ,而你的目标是生成一个新图片 G 。为了实现神经风格迁移,你要做的是定义一个关于 G 的代价函数 J 用来评判某个生成图像的好坏,我们将使用梯度下降法去最小化 J ( G ) ,以便于生成这个图像。
风格迁移代价函数:
\[J(G) = \alpha J_{content}(C,G) + \beta J_{style}(S,G) \]两者之间的权重用两个超参数来确定。两个代价的权重似乎是多余的,我觉得一个超参数似乎就够了,但提出神经风格迁移的原始作者使用了两个不同的超参数,我准备保持一致。
[!TIP]
基于Leon Gatys, Alexandra Ecker和Matthias Bethge的这篇论文
Leon A. Gatys, Alexander S. Ecker, Matthias Bethge, (2015). A Neural Algorithm of Artistic Style (https://arxiv.org/abs/1508.06576)
为了生成一个新图像,你接下来要做的是随机初始化生成图像 G
然后使用在之前的幻灯片上定义的代价函数 J ( G ) ,你现在可以做的是使用梯度下降的方法将其最小化,更新\(G:=G-\frac{\partial}{\partial G}\) 。在这个步骤中,你实际上更新的是图像 G 的像素值。
4.9 内容代价函数
风格迁移网络的代价函数有一个内容代价部分,还有一个风格代价部分
\[J(G) = \alpha J_{content}(C,G) + \beta J_{style}(S,G) \]假如说,你用隐含层 l来计算内容代价,如果 l 是个很小的数,比如用隐含层1,这个代价函数就会使你的生成图片像素上非常接近你的内容图片。在编程练习中的具体例子里通常 l 会选择在网络的中间层,既不太浅也不很深
现在你需要衡量假如有一个内容图片和一个生成图片他们在内容上的相似度,我们令这个\(a^{[l](C)}\) 和 \(a^{[l](G)}\),代表这两个图片 C 和 G 的 l 层的激活函数值。如果这两个激活值相似,那么就意味着两个图片的内容相似。
内容代价函数:
\[J_{content}(C,G) = \frac{1}{2}||a^{[l](C) - a^{[l](G)}}||^2 \]4.10 风格代价函数
现在我们要做的就是将图片的风格定义为 l 层中各个通道之间激活项的相关系数。
为了能捕捉图片的风格,前两个通道(编号1、2)分别是图中的红色和黄色部分,那我们该如何计算这两个通道间激活项的相关系数呢?
这个红色的通道(编号1)对应的是这个神经元,它能找出图片中的特定位置是否含有这些垂直的纹理(编号3),而第二个通道也就是黄色的通道(编号2),对应这个神经元(编号4),它可以粗略地找出橙色的区域。什么时候两个通道拥有高度相关性呢?如果它们有高度相关性,那么这幅图片中出现垂直纹理的地方(编号2),那么这块地方(编号4)很大概率是橙色的。如果说它们是不相关的,又是什么意思呢?显然,这意味着图片中有垂直纹理的地方很大概率不是橙色的。而相关系数描述的就是当图片某处出现这种垂直纹理时,该处又同时是橙色的可能性。
如果我们在通道之间使用相关系数来描述通道的风格,你能做的就是测量你的生成图像中第一个通道(编号1)是否与第二个通道(编号2)相关,通过测量,你能得知在生成的图像中垂直纹理和橙色同时出现或者不同时出现的频率,这样你将能够测量生成的图像的风格与输入的风格图像的相似程度。
对于这两个图像,也就是风格图像与生成图像,你需要计算一个风格矩阵,说得更具体一点就是用 l 层来测量风格。
\(a_{i,j,k}^{[l]}\):隐藏层\(l\)中 (高度,宽度,通道数)位置的激活项
\(G^{[l]S}\):关于l层和风格图像的矩阵
风格矩阵:
\[G_{kk^{'}}^{[l](S)} = \sum_{i=1}^{n_H^{[l]}} \sum_{j=1}^{n_W^{[l]}}a_{i,j,k}^{[l](S)}a_{i,j,k^{'}}^{[l](S)} \]生成图像矩阵:
\[G_{kk^{'}}^{[l](G)} = \sum_{i=1}^{n_H^{[l]}} \sum_{j=1}^{n_W^{[l]}}a_{i,j,k}^{[l](G)}a_{i,j,k^{'}}^{[l](G)} \]要注意,如果两个通道中的激活项数值都很大,那么 \(G^{[l]}_{kk'}\) 也会变得很大,对应地,如果他们不相关那么\(G^{[l]}_{kk'}\)就会很小。
最后,如果我们将 S 和 G 代入到风格代价函数中去计算,这将得到这两个矩阵之间的误差,因为它们是矩阵,所以在这里加一个 F (Frobenius范数,编号1所示),这实际上是计算两个矩阵对应元素相减的平方的和。作者在这里使用了一个归一化常数,也就是 $ \frac{1}{2n_H{[l]}n_Wn_c^{[l]}}$,再在外面加一个平方,但是一般情况下你不用写这么多,一般我们只要将它乘以一个超参数 β 就行。