一 实例探索
卷积神经网络的基本构建,比如卷积层、池化层以及全连接层这些组件。事实上,过去几年计算机视觉研究中的大量研究都集中在如何把这些基本构件组合起来,形成有效的卷积神经网络。最直观的方式之一就是去看一些案例,就像很多人通过看别人的代码来学习编程一样,通过研究别人构建有效组件的案例是个不错的办法。实际上在计算机视觉任务中表现良好的神经网络框架往往也适用于其它任务,也许你的任务也不例外。也就是说,如果有人已经训练或者计算出擅长识别猫、狗、人的神经网络或者神经网络框架,而你的计算机视觉识别任务是构建一个自动驾驶汽车,你完全可以借鉴别人的神经网络框架来解决自己的问题。
首先我们来看几个经典的网络。
LeNet-5 网络,应该是 1980 年代的,经常被引用的 AlexNet,还有 VGG 网络。这些都是非常有效的神经网络范例,当中的一些思路为现代计算机视觉技术的发展奠定了基础。论文中的这些想法可能对你大有裨益,对你的工作也可能有所帮助 。
然后是 ResNet,又称残差网络。神经网络正在不断加深,对此你可能有所了解。 ResNet 神经网络训练了一个深达 152 层的神经网络,并且在如何有效训练方面,总结出了一些有趣的想法和窍门。
二 经典网络
1. LeNet-5网络
首先看看 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 个特征得到最后的输出。
我们还可以在这里再加一个节点用来预测y_hat的值,y_hat有是个可能的值,对应识别 0-9 这 10 个数字。在现在的版本中则使用 softmax函数输出十种分类结果,而在当时, LeNet-5 网络在输出层使用了另外一种 tanh 函数,现在已经很少 用到的分类器。相比现代版本,这里得到的神经网络会小一些,只有约 6 万个参数。而现在,我们经常看到含有一千万到一亿个参数的神经网络,比这大 1000 倍的神经网络也不在少数。 不管怎样,如果我们从左往右看,随着网络越来越深,图像的高度和宽度在缩小,从最初的 32×32 缩小到 28×28,再到 14×14、 10×10,最后只有 5×5。与此同时,随着网络层次的加深,通道数量一直在增加,从 1 增加到 6 个,再到 16 个。
这个神经网络中还有一种模式至今仍然经常用到,就是一个或多个卷积层后面跟着一个池化层,然后又是若干个卷积层再接一个池化层,然后是全连接层,最后是输出,这种排列方式很常用 。
读到这篇经典论文时,你会发现,过去,人们使用 sigmod 函数和 tanh 函数,而不是ReLu 函数,这篇论文中使用的正是 sigmod 函数和 tanh 函数。这种网络结构的特别之处还在于,各网络层之间是有关联的,这在今天看来显得很有趣。
2.AlexNet网络
是以论文的第一作者 Alex Krizhevsky 的名字命名的,另外两位合著者是 ilya Sutskever 和 Geoffery Hinton。
- AlexNet 首先用一张 227×227×3 的图片作为输入,实际上原文中使用的图像是 224×224×3,但是如果你尝试去推导一下,你会发现 227×227 这个尺寸更好一些。
- C1 第一层我们使用 96 个11×11 的过滤器,步幅为 4,由于步幅是 4,因此尺寸缩小到 55×55,缩小了 4 倍左右。然后用一个 3×3 的过滤器构建最大池化层,f=3,s=2,尺寸缩小为 27×27×96。
- C2 接着再执行一个 5×5 的same卷积, p=3,s=1,输出是 27×27×256。然后再次进行最大池化,尺寸缩小到 13×13。
- C3 再执行一次 same 卷积,p=1,s=1,得到的结果是 13×13×384, 384 个过滤器。
- C4 再做一次 same 卷积,p=1,s=1,得到的结果是 13×13×384, 384 个过滤器。
- C5 再做一次 same 卷积,p=1,s=1,得到的结果是 13×13×256。最后再进行一次最大池化,尺寸缩小到 6×6×256。
- 6×6×256 等于 9216,将其展开为 9216 个单元,然后是一些全连接层。最后使用 softmax 函数输出识别的结果。
实际上,这种神经网络与 LeNet 有很多相似之处,不过 AlexNet 要大得多。正如前面讲到的 LeNet 或 LeNet-5 大约有 6 万个参数,而 AlexNet 包含约 6000 万个参数。当用于训练图像和数据集时, AlexNet 能够处理非常相似的基本构造模块,这些模块往往包含着大量的隐藏单元或数据,这一点 AlexNet 表现出色。 AlexNet 比 LeNet 表现更为出色的另一个原因是 使用了 ReLu 激活函数。
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。
- 然后又是若干个卷积层,使用 128个过滤器,以及一些 same 卷积,我们看看输出什么结果, 112×112×128。
- 然后进行池化,可以推导出池化后的结果是这样56×56×128。
- 接着再用 256 个相同的过滤器进行三次卷积操作,输出56x56x256。
- 然后再池化,输出28x28x256。
- 然后再卷积三次,再池化。
- 如此进行几轮操作后,将最后得到的 7×7×512 的特征图进行全连接操作,得到 4096 个单元,然后进行 softmax 激活输出。
顺便说一下, VGG-16 的这个数字 16,就是指在这个网络中包含 16 个卷积层和全连接层。确实是个很大的网络,总共包含约 1.38 亿个参数,即便以现在的标准来看都算是非常大的网络。但 VGG-16 的结构并不复杂,这点非常吸引人,而且这种网络结构很规整,都是几个卷积层后面跟着可以压缩图像大小的池化层,池化层缩小图像的高度和宽度。同时,卷积层的过滤器数量变化存在一定的规律,由 64 翻倍变成 128,再到 256 和 512。作者可能认为 512 已经足够大了,所以后面的层就不再翻倍了。无论如何,每一步都进行翻倍,或者说在每一组卷积层进行过滤器翻倍操作,正是设计此种网络结构的另一个简单原则。这种相对一致的网络结构对研究者很有吸引力,而它的主要缺点是需要训练的特征数量非常巨大。
有些文章还介绍了 VGG-19 网络,它甚至比 VGG-16 还要大,如果你想了解更多细节,请参考幻灯片下方的注文,阅读由 Karen Simonyan 和 Andrew Zisserman 撰写的论文。由于VGG-16 的表现几乎和 VGG-19 不分高下,所以很多人还是会使用 VGG-16。我最喜欢它的一点是,文中揭示了,随着网络的加深,图像的高度和宽度都在以一定的规律不断缩小,每次池化后刚好缩小一半,而通道数量在不断增加,而且刚好也是在每组卷积操作后增加一倍。也就是说,图像缩小的比例和通道数增加的比例是有规律的。
从这个角度来看,这篇论文很吸引人。以上就是三种经典的网络结构,如果你对这些论文感兴趣,我建议从介绍 AlexNet 的论文开始,然后就是 VGG 的论文,最后是 LeNet 的论文。虽然有些晦涩难懂,但对于了解这些网络结构很有帮助。
三 残差网络
非常非常深的神经网络是很难训练的, 因为存在梯度消失和梯度爆炸问题。这节我们讲解跳跃连接(Skip connection),它可以从某一层网络层获取激活,然后迅速反馈给另外一层,甚至是神经网络的更深层。我们可以利用跳跃连接构建能够训练深度网络的 ResNets,有时深度能够超过 100 层 。
ResNets 是由残差块(Residual block)构建的,首先我解释一下什么是残差块。
这是一个两层神经网络,在层进行激活,得到
,再次进行激活,两层之后得到
,计算过程是从
开始,首先进行线性激活,根据这个公式:
,然后通过ReLU 非线性激活函数得到
,
计算得出。接 着 再 次 进 行 线 性 激 活 , 依 据 等 式
,最后根据这个等式再次进行 ReLu 非线性激活,
。
在残差网络中有一点变化,我们将直接向后,拷贝到神经网络的深层,在 ReLU 非线性激活函数前加上
,这是一条捷径。
的信息直接到达神经网络的深层,不再沿着主路径传递,这就意味着最后这个等式
去掉了,取而代之的是另一个 ReLU 非线性函数,仍然对
进行
函数处理,但这次要加上
,即:
。也就是加上的这个
产生了一个残差块。
在上面这个图中,我们画一条捷径,直达第二层。实际上这条捷径是在进行 ReLU非线性激活函数之前加上的,而这里的每一个节点都执行了线性函数和 ReLU 激活函数。所以插入的时机是在线性激活之后, ReLU 激活之前。 除了捷径, 你还会听到另一个术语“跳跃连接”, 就是指
跳过一层或者好几层,从而将信息传递到神经网络的更深层。
ResNets 的发明者是何凯明(Kaiming He) 、 张翔宇(Xiangyu Zhang) 、 任少卿(ShaoqingRen) 和孙剑(Jiangxi Sun) ,他们发现使用残差块能够训练更深的神经网络。所以构建一个 ResNet 网络就是通过将很多这样的残差块堆积在一起,形成一个很深神经网络,我们来看看这个网络。
这并不是一个残差网络,而是一个普通网络(Plain network),这个术语来自 ResNets 论文 。把它变成 ResNets 的方法是加上所有跳跃连接,正如之前看到的,每两层增加一个捷径,构成一个残差块。如图所示, 5 个残差块连接在一起构成一个残差网络。
如果我们使用标准优化算法训练一个普通网络,比如说梯度下降法,或者其它热门的优化算法。如果没有残差,没有这些捷径或者跳跃连接,凭经验你会发现随着网络深度的加深,训练错误会先减少,然后增多。而理论上,随着网络深度的加深,应该训练得越来越好才对。也就是说,理论上网络深度越深越好。但实际上,如果没有残差网络,对于一个普通网络来说,深度越深意味着用优化算法越难训练。实际上,随着网络深度的加深,训练错误会越来越多 。
但有了 ResNets 就不一样了,即使网络再深,训练的表现却不错,比如说训练误差减少,就算是训练深达 100 层的网络也不例外。有人甚至在 1000 多层的神经网络中做过实验,尽管目前我还没有看到太多实际应用。但是对的激活,或者这些中间的激活能够到达网络的更深层。这种方式确实有助于解决梯度消失和梯度爆炸问题,让我们在训练更深网络的同时,又能保证良好的性能。也许从另外一个角度来看,随着网络越来深,网络连接会变得臃肿,但是 ResNet 确实在训练深度网络方面非常有效。
残差网络为什么有用?
为什么 ResNet 能有如此好的表现,我们来看个例子,它解释了其中的原因,至少可以说明,如何构建更深层次的 ResNets 网络的同时还不降低它们在训练集上的效率。 通常来讲,网络在训练集上表现好,才能在 Hold-Out 交叉验证集或 dev 集和测试集上有好的表现,所以至少在训练集上训练好 ResNets 是第一步。
先来看个例子, 一个网络深度越深,它在训练集上训练的效率就会有所减弱,这也是有时候我们不希望加深网络的原因。而事实并非如此,至少在训练 ResNets网络时,并非完全如此,举个例子。
假设有一个大型神经网络,其输入为, 输出激活值
, 假如你想增加这个神经网络的深度,那么用 Big NN 表示,输出为
, 再给这个网络额外添加两层,依次添加两层,最后输出为
, 可以把这两层看作一个 ResNets 块,即具有捷径连接的残差块。为了方便说明,假设我们在整个网络中使用 ReLU 激活函数, 所以激活值都大于等于 0,包括输入
的非零异常值。因为 ReLU 激活函数输出的数字要么是 0,要么是正数 。
我们看一下的值,
,添加项
,是刚添加的跳跃连接的输入。 展开这个表达式
,其中
。注意一点,如果使用 L2正则化或权重衰减,它会压缩
的值,如果对
应用权重衰减也可达到同样的效果,尽管实际应用中,你有时会对
应用权重衰减也可达到同样的效果,有时不会。这里的
是关键项,如果
,为了方便起见,假设
,这几项就没有了,因为
,最后
,因为我们假定使用 ReLU 激活函数,并且所有激活值都是非负的。
结果表明,残差块学习这个恒等式函数并不难,跳跃连接使我们很容易得出,这意味着,即使给神经网络增加了这两层,它的效率也并不逊色于更简单的神经网络,因为学习恒等函数对它来说很简单。尽管它多了两层,也只把
的值赋值给了
。所以给大型神经网络增加两层,不论是把残差块添加到神经网络的中间还是末端位置,都不会影响网络的表现。
当然,我们的目标不仅仅是保持网络的效率,还要提升它的效率。想象一下,如果这些隐藏层单元学到一些有用信息,那么它可能比学习恒等函数表现得更好。而这些不含有残差块或跳跃连接的深度普通网络情况就不一样了,当网络不断加深时,就算是选用学习恒等函数的参数都很困难,所以很多层最后的表现不但没有更好,反而更糟。我认为残差网络起作用的主要原因就是这些残差块学习恒等函数非常容易,你能确定网络性能不会受到影响,很多时候甚至可以提高效率,或者说至少不会降低网络的效率, 因此创建类似残差网络可以提升网络性能。
除此之外,关于残差网络,另一个值得探讨的细节是,假设与
具有相同维度,所以 ResNets 使用了许多 same 卷积,所以这个
的维度等于这个输出层的维度。之所以能实现跳跃连接是因为 same 卷积保留了维度,所以很容易得出这个捷径连接,并输出这两个相同维度的向量。 如果输入和输出有不同维度,比如输入的维度是 128,
的维度是256,再增加一个矩阵,这里标记为
,
是一个256x128维度的矩阵,所以
的维度是256。这个新增项是 256 维度的向量。你不需要对
做任何操作,它是网络通过学习得到的矩阵或参数,它是一个固定矩阵, padding 值为 0,用 0 填充
,其维度为 256,所以者几个表达式都可以。
最后,我们来看看 ResNets 的图片识别。这些图片是我从何凯明等人论文中截取的,这是一个普通网络,我们给它输入一张图片,它有多个卷积层,最后输出了一个 Softmax。
如何把它转化为 ResNets 呢?只需要添加跳跃连接。这里我们只讨论几个细节,这个网络有很多层 3×3 卷积,而且它们大多都是 same 卷积,因为它们是 same 卷积,维度得以保留,这也解释了添加项(维度相同所以能相加)。
ResNets 类似于其它很多网络,也会有很多卷积层,其中偶尔会有池化层或类池化层的层。不论这些层是什么类型,你都需要调整矩阵的维度。普通网络和 ResNets 网络常用的结构是:卷积层-卷积层-卷积层-池化层-卷积层-卷积层-卷积层-池化层……依此重复。 直到最后, 有一个通过 softmax 进行预测的全连接层。
参考文章:
[1]ImageNet Classification with Deep Convolutional Neural Networks 中文版 中英文对照(AlexNet)
[2]Very Deep Convolutional Networks for Large-Scale Image Recognition 中文版 中英文对照(VGG)
[3]Deep Residual Learning for Image Recognition 中文版 中英文对照(ResNet)
[4]Going Deeper With Convolutions 中文版 中英文对照(GoogLeNet)
[5]Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift 中文版 中英文对照(BN-GoogLeNet)
[6]Rethinking the Inception Architecture for Computer Vision 中文版 中英文对照(Inception-v3)
[7]Squeeze-and-Excitation Networks 中文版 中英文对照(SENet)
-------------------------------------------------------------------------------------