深度学习入门笔记(二十一):经典神经网络(残差网络ResNets)

欢迎关注WX公众号:【程序员管小亮】

专栏——深度学习入门笔记

声明

1)该文章整理自网上的大牛和机器学习专家无私奉献的资料,具体引用的资料请看参考文献。
2)本文仅供学术交流,非商用。所以每一部分具体的参考资料并没有详细对应。如果某部分不小心侵犯了大家的利益,还望海涵,并联系博主删除。
3)博主才疏学浅,文中如有不当之处,请各位指出,共同进步,谢谢。
4)此属于第一版本,若有错误,还需继续修正与增删。还望大家多多指点。大家都共享一点点,一起为祖国科研的推进添砖加瓦。

深度学习入门笔记(二十一):经典神经网络(残差神经网络ResNets)

1、为什么要进行实例探究?

最快最直观地熟悉这些网络结构(比如卷积层、池化层以及全连接层这些组件)的方法就是看看一些卷积神经网络的实例分析,就像很多人通过看别人的代码来学习编程一样,通过研究别人构建有效组件的案例是个不错的办法,实际上在计算机视觉任务中表现良好的神经网络框架往往也适用于其它任务,比如,有人已经训练或者计算出擅长识别猫、狗、人的神经网络或者神经网络框架,那么对于计算机视觉识别任务的自动驾驶汽车,是完全可以借鉴的。

按照进度,你应该可以读一些计算机视觉方面的研究论文了,比如这几个经典的网络:
在这里插入图片描述

  • LeNet-5 网络,初代手写数字识别网络,我记得应该是1980年代的;
  • 经常被引用的 AlexNet,开启新一轮深度学习热潮;
  • 还有 VGG 网络,发现了深度对网络效果提升的巨大影响;
  • 然后是 ResNet,又称残差网络;
  • 还会讲谷歌 Inception 神经网络的实例分析。

了解了这些神经网络,相信你会对如何构建有效的卷积神经网络更有感觉!!还有期中使用的各种网络组件,即使计算机视觉并不是你的主要方向,你也会从 ResNetInception 网络这样的实例中找到一些不错的想法。

2、残差网络(ResNets)

论文地址:https://arxiv.org/pdf/1512.03385.pdf

深度学习入门笔记(二十):经典神经网络(LeNet-5、AlexNet和VGGNet) 中讲过的深度是对训练效果的提升是非常很大的,在 VGG 中,卷积网络达到了19层,在后面要讲的 GoogLeNet 中,网络史无前例的达到了22层!

那么,网络的精度会随着网络的层数增多而增多吗?

答案当然是否,非常非常非常深 的神经网络是很难训练的,因为存在梯度消失和梯度爆炸问题(深度学习100问之深入理解Vanishing/Exploding Gradient(梯度消失/爆炸)),但是跳跃连接(Skip connection)的出现解决了这一问题,因为它可以从某一层网络层获取激活,然后迅速反馈给另外一层,甚至是神经网络的更深层,利用 跳跃连接 构建能够训练深度网络的ResNets,有时深度能够超过100层!!!

ResNets 是由残差块(Residual block)构建的,首先解释一下什么是残差块?
在这里插入图片描述
来看一个例子。
在这里插入图片描述
如图是一个两层神经网络,在 LL 层进行激活,得到 a[l+1]a^{leftlbrack l + 1 ight brack},再次进行激活,两层之后得到 a[l+2]a^{leftlbrack l + 2 ight brack}。计算过程是从 a[l]a^{[l]} 开始的,首先进行线性激活,根据这个公式:

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

通过 a[l]a^{[l]} 算出 z[l+1]z^{leftlbrack l + 1 ight brack},然后通过 ReLU 非线性激活函数得到

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

这里的 gg 是指 ReLU 非线性函数,接着再次进行线性激活,依据等式

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

最后根据这个等式再次进行 ReLu 非线性激活,即

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

这里的 gg 是指 ReLU 非线性函数,信息流从 a[l]a^{leftlbrack l ight brack}a[l+2]a^{leftlbrack l + 2 ight brack} 需要经过以上所有步骤,即 这组网络层的主路径
在这里插入图片描述
在残差网络中,和正常网络有一点变化,将 a[l]a^{[l]} 直接向后,拷贝到神经网络的深层,在 ReLU 非线性激活函数前加上 a[l]a^{[l]},不再沿着主路径传递,这是一条 捷径

最后这个等式(a[l+2]=g(z[l+2])a^{leftlbrack l + 2 ight brack} = g(z^{leftlbrack l + 2 ight brack}))去掉了,取而代之的是另一个 ReLU 非线性函数,仍然对 z[l+2]z^{leftlbrack l + 2 ight brack} 进行 gg 函数处理,但这次要加上 a[l]a^{[l]},即:

 a[l+2]=g(z[l+2]+a[l]) a^{leftlbrack l + 2 ight brack} = gleft(z^{leftlbrack l + 2 ight brack} + a^{[l]} ight)

要注意实际上这条捷径是在进行 ReLU 非线性激活函数之前加上的,而这里的每一个节点都执行了线性函数和 ReLU 激活函数。
在这里插入图片描述
除了 捷径,还会听到另一个术语 跳跃连接,就是指 a[l]a^{[l]} 跳过一层或者好几层,从而将信息传递到神经网络的更深层,比如在上面这个图中也可以画一条捷径直达第二层。

ResNet 的发明者是 何凯明Kaiming He)、张翔宇Xiangyu Zhang)、任少卿Shaoqing Ren)和 孙剑Jiangxi Sun),他们这四个大佬发现使用残差块能够训练更更更深的神经网络,所以通过将很多这样的残差块堆积在一起,形成一个很深神经网络,就是 ResNet 网络,来看看这个网络:
在这里插入图片描述
这并不是一个残差网络,而是一个普通网络(Plain network),这个术语来自 ResNet 论文。把它变成 ResNet 的方法是加上所有 跳跃连接,可以每两层增加一个 捷径 构成一个残差块。
在这里插入图片描述
如果使用标准优化算法训练一个普通网络,比如说梯度下降法,或者其它热门的优化算法(如上图)。

  • 当没有残差(没有这些捷径或者跳跃连接)时,凭经验会发现随着网络深度的加深,训练错误会先减少,然后增多,而理论上,随着网络深度的加深,应该训练得越来越好才对,也就是说,理论上网络深度越深越好。但实际上,如果没有残差网络,对于一个普通网络来说,深度越深意味着用优化算法越难训练,训练错误越多。
  • 但有了 ResNets 就不一样了,即使网络再深,训练的表现却不错,比如说深达100层的网络,有人甚至在1000多层的神经网络中做过实验。对 xx 的激活,或者这些中间的激活能够到达网络的更深层。这种方式确实有助于解决梯度消失和梯度爆炸问题,让我们在训练更深网络的同时,又能保证良好的性能。也许从另外一个角度来看,随着网络越来深,网络连接会变得臃肿,但是 ResNet 确实在训练深度网络方面非常有效!

3、残差网络为什么有用?

为什么 ResNets 能有如此好的表现?

来看个例子,也许没法完全解释其中的原因,至少可以说明,如何构建更深层次的 ResNets 网络的同时还不降低训练集上的效率。前面讲过了一个网络深度越深,它在训练集上训练的效率就会有所减弱,这也是有时不希望加深网络的原因,而事实并非如此,至少在训练 ResNets 网络时,并非完全如此。

假设有一个大型神经网络,其输入为 XX,如果想增加网络的深度,用 Big NN 表示,输出为 a[l]a^{leftlbrack l ight brack},再给这个网络额外添加两层,输出为 a[l+2]a^{leftlbrack l + 2 ight brack},可以把这两层看作一个 ResNets 块,即具有 捷径 的残差块。为了方便说明,假设在整个网络中使用 ReLU 激活函数,所以激活值都大于等于0,包括输入 XX 的非零异常值,因为 ReLU 激活函数输出的数字要么是0,要么是正数。
在这里插入图片描述
a[l+2]a^{leftlbrack l + 2 ight brack} 就是上面讲过的,即 a[l+2]=g(z[l+2]+a[l])a^{leftlbrack l + 2 ight brack} = g(z^{leftlbrack l + 2 ight brack} + a^{leftlbrack l ight brack}),添加项 a[l]a^{leftlbrack l ight brack} 是刚添加的 跳跃连接 的输入,展开这个表达式 a[l+2]=g(W[l+2]a[l+1]+b[l+2]+a[l])a^{leftlbrack l + 2 ight brack} = g(W^{leftlbrack l + 2 ight brack}a^{leftlbrack l + 1 ight brack} + b^{leftlbrack l + 2 ight brack} + a^{leftlbrack l ight brack}),其中 z[l+2]=W[l+2]a[l+1]+b[l+2]z^{leftlbrack l + 2 ight brack} = W^{leftlbrack l + 2 ight brack}a^{leftlbrack l + 1 ight brack} + b^{leftlbrack l + 2 ight brack}

注意一点,如果使用 L2 正则化或权重衰减(深度学习入门笔记(十):正则化),它会压缩 W[l+2]W^{leftlbrack l + 2 ight brack} 的值。如果对 bb 应用权重衰减也可达到同样的效果,尽管实际应用中有时会对 bb 应用权重衰减,有时不会。

这里的 WW 是关键项,如果 W[l+2]=0W^{leftlbrack l + 2 ight brack} = 0b[l+2]=0b^{leftlbrack l + 2 ight brack} = 0,这几项就没有了,因为它们(W[l+2]a[l+1]+b[l+2]W^{leftlbrack l + 2 ight brack}a^{leftlbrack l + 1 ight brack} + b^{leftlbrack l + 2 ight brack})的值为0,最后 a[l+2]= g(a[l])=a[l]a^{leftlbrack l + 2 ight brack} = gleft( a^{[l]} ight) = a^{leftlbrack l ight brack},因为假定都使用 ReLU 激活函数,所以所有激活值都是非负的,所以 a[l+2]=a[l]a^{[l+2]} =a^{[l]}
在这里插入图片描述
结果表明,残差块学习这个恒等式函数并不难,跳跃连接 使我们很容易得出 a[l+2]=a[l]a^{leftlbrack l + 2 ight brack} = a^{leftlbrack l ight brack},这意味着,即使给神经网络增加了这两层,它的效率也并不逊色于更简单的神经网络,因为学习恒等函数对它来说很简单!!!所以给大型神经网络增加两层,不论是把残差块添加到神经网络的中间还是末端位置,都不会影响网络的表现。

当然,我们的目标不仅仅是保持网络的效率,还要提升它的效率。想象一下,如果这些隐藏层单元学到一些有用信息,那么它可能比学习恒等函数表现得更好,而这些不含有残差块或 跳跃连接 的深度普通网络情况就不一样了,当网络不断加深时,就算是选用学习恒等函数的参数都很困难,所以很多层最后的表现不但没有更好,反而更糟,而残差网络最差的结果也就是和原来一样!!!网络性能不会受到影响,很多时候甚至可以提高效率,或者说至少不会降低网络的效率!!!

除此之外,关于残差网络的另一个值得探讨的细节是,假设 z[l+2]z^{leftlbrack l + 2 ight brack}a[l]a^{[l]} 具有相同维度,所以 ResNets 使用了许多 same 卷积,所以才能实现 跳跃连接。如果输入和输出有不同维度,比如输入的维度是128,a[l+2]a^{leftlbrack l + 2 ight brack} 的维度是256,再增加一个矩阵,这里标记为 WsW_{s},是一个256×128维度的矩阵,所以 Wsa[l]W_{s}a^{leftlbrack l ight brack} 的维度是256,这个新增项是256维度的向量。

最后,来看看 ResNets 的图片识别,这些图片是我从论文中截取的,这是一个普通网络,输入一张图片,它有多个卷积层,最后输出了一个 Softmax
在这里插入图片描述
如何把它转化为 ResNets 呢?

只需要添加跳跃连接!这个网络有很多层3×3卷积,而且它们大多都是 same 卷积,这就是添加等维特征向量的原因,这也解释了添加项 z[l+2]+a[l]z^{leftlbrack l + 2 ight brack} + a^{leftlbrack l ight brack}(维度相同所以能够相加)。
在这里插入图片描述
ResNets 类似于其它很多网络,也会有很多卷积层,其中偶尔会有池化层或类池化层的层,不论这些层是什么类型,都需要调整矩阵 WsW_{s} 的维度。普通网络和 ResNets 网络常用的结构是:

卷积层-卷积层-卷积层-池化层-卷积层-卷积层-卷积层-池化层……依此重复,直到最后,有一个通过 softmax 进行预测的全连接层。
在这里插入图片描述

4、网络中的网络以及 1×1 卷积

在架构内容设计方面,其中一个比较有帮助的想法是使用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个权重(将过滤器中的32个数理解为权重),然后应用 ReLU 非线性函数,在这里输出相应的结果。

一般来说,如果过滤器不止一个,而是多个,就好像有多个输入单元,其输入内容为一个切片上所有数字,输出结果是6×6过滤器数量。
在这里插入图片描述
所以1×1卷积可以从根本上理解为对这32个不同的位置都应用一个全连接层,全连接层的作用是输入32个数字(过滤器数量标记为 nC[l+1]n_{C}^{leftlbrack l + 1 ight brack},在这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,这就是压缩通道数(ncn_{c})的方法,。
在这里插入图片描述
当然如果想保持通道数192不变,这也是可行的,1×1卷积只是添加了非线性函数,比如,再添加一层28×28×192,那么输出为28×28×192。
在这里插入图片描述
1×1卷积层就是这样实现了一些重要功能的(doing something pretty non-trivial),它给神经网络添加了一个非线性函数,从而减少或保持输入层中的通道数量不变,当然如果愿意,也可以增加通道数量。

推荐阅读

参考文章

  • 吴恩达——《神经网络和深度学习》视频课程
原文地址:https://www.cnblogs.com/hzcya1995/p/13302627.html