—-摘自黄海广博士笔记
1.为什么看实例
和很多人通过看别人的代码来学习编程一样,通过研究别人构建有效组建的案例也不错。
实际上在计算机视觉任务中表现良好的神经网络框架往往也适用于其它任务,也许你的任务也不例外。也就是说,如果有人已经训练或者计算出擅长识别猫、狗、人的神经网络或者神经网络框架,而你的识别任务是构建一个自动驾驶汽车,你完全可以借鉴别人的神经网络框架来解决自己的问题。
学完这里,你应该可以读一些计算机视觉方面的研究论文了。
下面看几个经典的网络:
LeNet-5网络,AlexNet,还有VGG网络,这些都是非常有效的神经网络范例,思路对你大有裨益。
ResNet,又称为残差网络。神经网络正在不断加深,对此你可能有所了解。ResNet神经网络训练了一个深达152层神经网络,并且在如何有效训练方面,总结出了一些有趣的想法和窍门。
2.经典网络
2.1 LeNet-5
假设有一张32×32×1(灰度图片)的图片,LeNet-5(针对灰度图片训练)可以识别图中的手写数字,比如像这样手写数字7。
实际上,和这篇博客最后的案例很相似。
使用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年撰写的,但是人们总是使用valid卷积,这就是为什么每进行一次卷积,图像的高度和宽度都会缩小,所以这个图像从14×14缩小到了10×10,然后又是池化层,高度和宽度再缩小一半,输出一个5×5×16的图像。将所有数字相乘,乘积是400。
下一层是全连接层,在全连接层中,有400个节点,每个节点有120个神经元,这里已经有了一个全连接层。但有时还会从这400个节点中抽取一部分节点构建另一个全连接层,就像这样,有两个全连接层。
最后一步就是利用这84个特征得到最后的输出,我们还可以在这里再加一个节点用来预测
y^
y
^
的值,
y^
y
^
有10个可能的值,对应识别0~9这10个数字。在现在的版本中则使用softmax函数输出10种分类结果,而在当时,LeNet网络在输出层使用了另外一种现在已经很少用到的分类器。
相比现代版本,这里得到的神经网络会小一些,只有约6万个参数。而现在,我们经常看到一千万到一亿个参数的神网。
如果我们从左往右看,随着网络越来越深,图像的高度和宽度再缩小,从最初的32×32到28×28,再到14×14,10×10,最后只有5×5。与此同时,随着网络层次的加深,通道数量一直在增加,从1到6,再到16。
这个神网中还有一种模式至今仍常用,就是一个或多个卷积层后面跟着一个池化层,然后又是若干个卷积层再接一个池化层,然后是全连接层,最后是输出,这种排列方式很常用。
2.2 AlexNet
AlexNet首先用一张227×227×3的图片作为输入,第一层使用96个11×11的过滤器,步幅为4,由于步幅是4,因此尺寸缩小到55×55,缩小了4倍左右。然后用一个3×3的过滤器构建最大的池化层,f=3,步幅s为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激活函数。
多说一下:
1)在写这篇论文的时候,GPU的处理速度还比较慢,所以AlexNet采用了非常复杂的方法在两个GPU上进行训练。大致原理是,这些层分别拆分到两个不同的GPU上,同时还专门有一个方法用于两个GPU进行交流。
论文还提到,经典的AlexNet结构还有另一种类型的层,叫做“局部响应归一化层”,即LRN层。改层思路是,假如这是网络的一块,比如是13×13×256,LRN要做的就是选取一个位置,比如说这样一个位置,从这个位置穿过整个通道,能得到256个数字,并进行归一化。进行局部响应归一化的动机是,对于这张13×13的图像中每个位置来说,我们可能并不需要太多的高激活神经元。但是后来,很多研究者发现 LRN 起不到太大作用,这应该是被我划掉的内容之一,因为并不重要,而且我们现在并不用 LRN 来训练网络。
以上只是对读论文有帮助。
2.3 VGG-16网络
值得注意的是,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 已经足够大了,所以后面的层就不再翻倍了。无论如何,每一步都进行翻倍,或者说在每一组卷积层进行过滤器翻倍操作,正是设计此种网络结构的另一个简单原则。这种相对一致的网络结构对研究者很有吸引力,而它的主要缺点是需要训练的特征数量非常巨大。
论文相关:
有些文章还介绍了VGG-19网络,它甚至比VGG-16还要大。请参考上图的论文。由于VGG-16 的表现几乎和 VGG-19 不分高下,所以很多人还是会使用 VGG-16。
吴恩达老师最喜欢它的一点是,文中揭示了,随着网络的加深,图像的高度和宽度都在以一定的规律不断缩小,每次池化后刚好缩小一半,而通道数量在不断增加,而且刚好也是在每组卷积操作后增加一倍。也就是说,图像缩小的比例和通道数增加的比例是有规律的。从这个角度来看,这篇论文很吸引人。
以上就是三种经典的网络结构,如果你对这些论文感兴趣,我建议从介绍 AlexNet 的论文开始,然后就是 VGG 的论文,最后是 LeNet 的论文。虽然有些晦涩难懂,但对于了解这些网络结构很有帮助。
3.残差网络(ResidualNetwoks)##—没明白,没懂跳过几层网络和直接删掉有啥区别
非常非常深的神经网络是很难训练的,因为存在梯度消失和梯度爆炸问题。这次学习跳跃连接(Skip connection),它可以从某一层网络层获取激活,然后迅速反馈给另外一层,甚至是神经网络的更深层。我们可以利用跳跃连接构建能够训练深度网络的 ResNets,有时深度能够超过 100 层,我们开始吧。
ResNets 是由残差块(Residual block)构建的,首先解释一下什么是残差块。
这是一个两层神经网络,在L层进行激活,得到
a[l+1]
a
[
l
+
1
]
,再次进行激活,两层之后得到
a[l+2]
a
[
l
+
2
]
,计算过程是从
a[l]
a
[
l
]
开始,首先进行线性激活,根据这个公式:
z[l+1]
z
[
l
+
1
]
=
W[l+1]
W
[
l
+
1
]
a[l]
a
[
l
]
+
b[l+1]
b
[
l
+
1
]
,通过
a[l]
a
[
l
]
算出
z[l+1]
z
[
l
+
1
]
,即
a[l]
a
[
l
]
乘以权重矩阵,再加上偏差因子。然后通过ReLu非线性激活函数得到
a[l+1]
a
[
l
+
1
]
,
a[l+1]
a
[
l
+
1
]
= g(
z[l+1]
z
[
l
+
1
]
)计算得出。接着再次进行线性激活,即
a[l+2]
a
[
l
+
2
]
= g(
z[l+2]
z
[
l
+
2
]
),这里的g是指ReLu非线性函数,得到的结果就是
a[l+2]
a
[
l
+
2
]
。换句话说,信息流从
a[l]
a
[
l
]
到
a[l+2]
a
[
l
+
2
]
需要经过以上所有步骤,即这组网络层的主路径。
在残差网络中有一点变化,我们将
a[l]
a
[
l
]
直接向后,拷贝到神经网络的深层,在ReLU非线性激活函数前加上
a[l]
a
[
l
]
,这是一条捷径。
a[l]
a
[
l
]
的信息直接到达神经网络的深层,不再沿着主路径传递,这就意味着最后这个等式(
a[l+2]
a
[
l
+
2
]
= g(
z[l+2]
z
[
l
+
2
]
))去掉了,取而代之的是另一个ReLU非线性函数,仍然对
z[l+2]
z
[
l
+
2
]
进行g函数处理,但这次要加上
a[l]
a
[
l
]
,即:
a[l+2]
a
[
l
+
2
]
= g(
z[l+2]
z
[
l
+
2
]
+
a[l]
a
[
l
]
),也就是加上的这个
a[l]
a
[
l
]
产生了一个残差块。
在上面图中,我们可一画出一条捷径,直达第二层。实际上这条捷径实在进行ReLu非线性激活函数之前加上的,而这里的每一个节点都执行了线性函数和ReLU激活函数。所以
a[l]
a
[
l
]
插入的时机是在线性函数激活之后,ReLu激活之前。除了捷径,你还会听到另一个术语“跳跃连接”,就是
a[l]
a
[
l
]
跳过一层或者好几层,从而将信息传递到神经网络的更深层。
ResNet的发明者何凯明,张翔宇,任少卿和孙剑,他们发现使用残差块能够训练更深的神经网络。所以构建一个ResNet网络就是通过将很多这样的残差块堆积在一起,形成一个很深的神经网络,我们来看一下这个网络。
这并不是残差网络,是普通网络。
把它变成ResNet的方法是加上所有的跳跃连接,每两层加一个捷径,构成一个残差块。如图所示,5个残差块连接在一起构成一个残差网络。(被跳过的这一层有什么用)
如果我们使用标准优化算法训练一个普通网络,比如说梯度下降法,或者其他热门的优化算法。如果没有残差,没有这些捷径或者跳跃连接,凭经验你会发现随着网络深度的加深,训练错误会先减少,然后增多。而理论上,随着网络深度的加深,应该训练得越来越好才对。也就是说,理论上深度越深越好,但实际上,如果没有残差网络,对于一个普通网络来说,深度意味着用优化算法越难训练。实际上,随着网络深度的加深,训练错误会越来越多。
但有了ResNet就不同了,即使网络再深,训练的表现却不错,比如说训练误差减少,就算是训练深达100层的网络也不例外。有人甚至在1000多层的神经网络中做过实验,尽管目前我还没有看到有太多实际应用。但是对x的激活,或者这些中间的激活能够达到网络的更深层。这种方式确实有助于解决梯度消失和梯度爆炸问题(Why)让我们在训练更深网络的同时,又能保证良好的性能。也许从另一个角度来看,随着网络越来越深,网络连接会变的臃肿,但ResNet确实在训练深度网络方面非常有效。
4.残差网络为什么有用
一般认为,一个网络深度越深,它在训练集上训练的效率就会有所减弱吗,这也是有时候我们不希望加深网络的原因。但事实并非如此。
假设有一个大型神网,其输入为X,输出激活值
a[l]
a
[
l
]
,假如你想增加这个神网的深度,那么用BigNN表示,输出为
a[l]
a
[
l
]
。再给这个网络额外添加两层,依次添加两层,最后输出为
a[l+2]
a
[
l
+
2
]
,可以把这两层看作一个ResNets块,即具有捷径连接的残差块。为了方便说明,假设我们在整个网络中使用ReLU激活函数,所有激活值都大于等于0,包括输入x的非零异常值。因为ReLU激活函数输出的数字要么是0,要么是正数。
看一下
a[l+2]
a
[
l
+
2
]
的值,也就是上节课讲过的表达式,即
a[l+2]
a
[
l
+
2
]
=g(
z[l+2]
z
[
l
+
2
]
+
a[l]
a
[
l
]
),添加项
a[l]
a
[
l
]
是刚添加的跳跃连接的输入。展开这个表达式
a[l+2]
a
[
l
+
2
]
=g(
W[l+2]
W
[
l
+
2
]
a[l+1]
a
[
l
+
1
]
+
b[l+2]
b
[
l
+
2
]
+
a[l]
a
[
l
]
),其中
z[l+2]
z
[
l
+
2
]
=
W[l+2]
W
[
l
+
2
]
a[l+1]
a
[
l
+
1
]
+
b[l+2]
b
[
l
+
2
]
。注意一点,如果使用L2正则化或者权重衰减,它会压缩
W[l+2]
W
[
l
+
2
]
的值。如果对b应用权重衰减也可达到同样的效果,尽管实际应用中,你有时会对b应用权重衰减,有时不会。这里的W是关键项,如果
W[l+2]
W
[
l
+
2
]
=0,为方便起见,假设
b[l+2]
b
[
l
+
2
]
=0,这几项就没有了,因为
z[l+2]
z
[
l
+
2
]
的值是0。最后
a[l+2]
a
[
l
+
2
]
=g(
a[l]
a
[
l
]
)=
a[l]
a
[
l
]
,因为我们假定使用ReLU激活函数,并且多有激活值都是非负的,g(
a[l]
a
[
l
]
)是应用于非负函数的ReLU函数,所以
a[l+2]
a
[
l
+
2
]
=
a[l]
a
[
l
]
。
结果表明,残差块学习这个恒等式函数并不难,跳跃连接使我们很容易得出
a[l+2]
a
[
l
+
2
]
=
a[l]
a
[
l
]
,这意味着,即使给神经网络增加了这两层,它的效率也并不逊色于更简单的神经网络,因为学习恒等函数很简单,尽管多了两层,也只是把
a[l]
a
[
l
]
的值赋给
a[l+2]
a
[
l
+
2
]
,所以给大型神经网络增加两层,不论是把残差块添加到神经网络的中间还是末端位置,都不会影响网络的表现。
目标不仅仅是保持网络的效率,还要提升它的效率。想象一下,如果这些隐藏单元学到一些有用信息,那么它可能比学习恒等函数表现更好。而这些不含有残差块或跳跃连接的深度普通网络情况就不同了,当网络不断加深时,就算是选用学习恒等函数的参数都很困难,所以很多层最后的表现不但没有更好,反而更糟。
我认为残差网络起作用的主要原因就是这些残差块学习恒等函数非常容易,你能确定网络性能不会受影响,很多时候甚至可以提高效率,或者说至少不会降低网络的效率,因此创建类似残差网络可以提升网络性能。
关于残差网络,另一个值得探讨的细节是,假设
z[l+2]
z
[
l
+
2
]
具有相同的维度,所以ResNets使用了许多same卷积,所以这个
a[l]
a
[
l
]
的维度等于这个输出层的维度。之所以能实现跳跃连接是因为same卷积保留了维度,所以很容易得出这个捷径连接,并输出这两个相同维度的向量。
最后,看一下ResNets的图片识别。这些图片是何凯明论文中截取的,这是一个普通的网络,我们给它输入一张图片,有多个卷积层,最后输出了一个SoftMax。
如何转化为ResNet呢?只需要添加跳跃连接。这里我们只讨论几个细节,这个网络有很多层3×3卷积,大多数是same卷积,所以这些都是卷积层, 而不是全连接层,因为它们是same卷积,维度得以保留,这也解释了添加项
z[l+2]
z
[
l
+
2
]
+
a[l]
a
[
l
]
,维度相同所以能够相加。
ResNets类似于其它很多网络,也会有很多卷层,其中偶尔会有池化层或类池化层的层,不论这些层是什么类型,正如我们在上一张幻灯片看到的,你都需要调整矩阵
W[s]
W
[
s
]
的维度。普通网络和 ResNets 网络常用的结构是:卷积层-卷积层-卷积层-池化层-卷积层-卷积层-卷积层-池化层……依此重复。 直到最后, 有一个通过 softmax 进行预测的全连接层。
以上就是ResNets的内容。使用1×1的过滤器,即1×1卷积。
5.网络中的网络以及1×1卷积
首先解释一下1×1网络:不就是乘以数字吗?并非如此
过滤器为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个数理解为权重),然后应用ReLU非线性函数,输出相应结果。