第一课: 为什么要进行案例研究?
上周我们学习了一些基本结构模块, 例如卷积层, 池化层和全连接层. 事实证明, 过去几年的计算机视觉研究已经着手于如何将这些基础结构模块组合起来以形成高效的神经网络, 让你自己受到启发的最好方法就是去研究案例.
一个更好的方法学会如何构建卷积网络的机制就是去看别人的高效的卷积网络的例子. 事实证明, 一个神经网络结构如果在一个计算机视觉问题中表现良好, 通常也会在其他问题中表现良好.
一些计算机领域的研究论文, 我希望你也会觉得它们有用.
总览几个经典的神经网络(有些观点已经成为了现代计算机视觉的基石):
开锤!
第二课: 经典神经网络
在这节课, 你会学到一些经典的神经网络结构, 如 LeNet 5,AlexNet 和 VGGNet, 让我们看一下:
LeNet 5 在灰度图上进行训练, 所以图层通道数目为 1.
识别数字 7,32 * 32 * 1 的原始图像, 5 * 5 过滤器, 步长为 1, 得到 28 * 28 * 6 的输出.
然后进行池化, 在发表这篇文章时, 人们更常用均值池化, 虽然现在一般选择最大池化, f = 2,s = 2 的滤波器进行均值池化, 进行降维, 得到 14 * 14 * 6 的体积结果.
接着进行另一层卷积. 16 个滤波器尺寸为 5 * 5, 得到 10 * 10 * 16 的结果.
这篇文章完成于 1998 年, 那时人们不怎么用填充, 或总用有效卷积进行计算, 这就是为什么每次使用卷积时, 结果的尺寸都会缩小.
接着进行进一步池化, 得到 5 * 5 * 16 的结果.
然后接下来一层是进行全连接层, 其本身 120 个神经元每个都连接这 400 个节点, 因此叫做全连接层.
接着是另一个全连接层, 得到 84 维特征.
最后一步是将这 84 维特征生成一个最终结果来预测 y 帽, y 帽包括 10 个预测值.
作为该网络结构的现代版, 我们用 softmax 层来生成 10 个分类结果, 尽管 LeNet 5 原先是用别的分类器做输出层, 而那个分类器现在已经不用了.
这是一个小型的神经网络, 大概有 60000 个参数, 而如今你经常可以见到包含千万到亿量级参数的神经网络, 这些字面上比 LeNet 5 大千倍的神经网络.
但是当你深入研究网络, 值得注意的一个模式是从左到右, 高度和宽度往往会下降, 通道数是增加的, 另一个神经网络至今仍然使用的模式是你会用一层或多层卷积, 随后是池化层, 然后再是一层或多层卷积层, 然后是池化层, 随后是几层全连接层, 然后输出, 这种层次排列很常见.
最后对于想读这篇文献的人, 添加一些脚注:
人们用 sigmoid 函数和 tanh 非线性函数而不用 ReLu 非线性函数, 且还有一些有趣的方式, 即以现代标准来看这个网络联级的方式很有趣, 例如你会发现如果有一个 nh * nw * nc 的网络结构, 然后用 f * f * nc 维的滤波器, 其中每个滤波器都处理了每一维通道, 但那时计算机比较慢, 所以为了节省计算量和参数数量, 原始 LeNet 5 有种比较疯狂的计算方法, 其中不同的滤波器会处理输入块的不同通道, 而现在先进的应用则不会用这种复杂的方法. 另外一个变化就是 LeNet 5 在池化后有非典型处理, 应该是在池化后使用 sigmoid 非线性函数, 可以重点看这篇文章的二三两章.
第二个经典卷积神经网络算法是 AlexNet:
227 * 227 * 3 原始图像, 用 96 个 11 * 11, 步长为 4 的过滤器, 得到 55 * 55 * 96 的输出, 之后用 3 * 3 步长为 2 的滤波器进行最大池化, 体积降到 27 * 27 * 96, 然后用 5 * 5 进行卷积, 相同填充, 得到 27 * 27 * 256, 再次最大池化, 得到 13 * 13 * 256, 然后相同的卷积, 相同填充, 得到 13 * 13 * 384 然, 然后再次 3 * 3 相同卷积, 最后最大池化, 得到 6 * 6 * 256 的输出.
6 * 6 * 256 = 9216. 因此我们把它展开为 9216 个节点, 然后有几层全连接层, 然后用 softmax 输出结果, 即目标可能是 1000 类中的哪一类.
这个网络结构实际上很像 LeNet, 但更大. LeNet 5 有 60000 个参数, AlexNet 大概有 6 千万个参数, 而且 AlexNet 采用非常相似的构造板块, 拥有更多的隐藏神经元, 在更多的数据上训练, AlexNet 在 ImageNet 数据库上进行训练, 大数据库使它有更好的性能. 使 AlexNet 比 LeNet 更好的一个因素是:
ReLu 函数的使用.
脚注: 当时 GPU 还比较慢, 因此该文章有在两块 GPU 上训练的复杂方法, 基本思想是网络中很多层被分割到两块不同的 GPU 上, 两块 GPU 可以互相通讯, 而且原始的 AlexNet 结构还有另一种层, 叫做局部响应归一层(LRN), 这种层实际上用的很少. 但局部响应归一层的基本思想是: 当你看一个块体, 你看一个位置高和宽, 然后看跨越通道的位置, 跨越了 256 个通道然后归一化, 这个局部响应操作是针对处理该 13 * 13 图像的每一个位置但是不想要太多高激活率的神经元.
鉴于 AlexNet 有很多复杂的结构和超参数, 在哪放置这些超参数, 是 AlexNet 算法必须考虑的问题.
第三个 VGG/VGG-16 算法:
VGG-16 非常值得注意的一点是:
与大量超参数不同, VGG-16 结构更简单, 更能关注卷积层, 即 3 * 3,s=1,same 填充的卷积滤波器, 所有最大池化都采用 2 * 2,s=2.
VGG-16 有一个优点:
真正简化了神经网络结构.
原始图像 224 * 224 * 3, 卷积池化卷积池化... 最后得到 7 * 7 * 512 的结果输入全连接层, 通过两层 4096 个神经元的全连接层, 然后是 softmax 函数输出 1000 类结果.
VGG-16 中的 16 表示该网络有 16 层带权重的层, 这是个相当大的网络, 该网络总共有 1 亿 3 千八百万个参数, 即使以现在的标准衡量也是很大了. 但 VGG-16 结构的简洁性非常吸引人, 看得出这个结构相当统一, 有几组卷积, 然后是池化层, 以降低高和宽, 另一方面, 如果你看卷积层的滤波器数量, 你有 64 个滤波器, 然后两倍到 128 到 256 到 512, 这种在每步粗略的双倍增加, 或在每组卷积层双倍增加的方式, 是设计这个网络时用的另一个简单原则.
所以我认为这种相对统一性对研究者来说很有趣, 而该结构的主要缺点是: 以训练参数的数量来看是个非常大的网络.
VGG-19 是比 VGG-16 更大的版本, 但是 VGG-19 和 VGG-16 性能差不多, 很多人用 VGG-16. 我最喜欢的是这种高和宽随着网络深入而减少的模式, 尺寸每次会因为池化层按因子 2 减少, 与此同时, 通道数量增加. 每次用一组卷积层时, 通道数会按照因子 2 增加.
这种按照比率减少或增加是非常有条理性的.
以上就是三种经典神经网络结构.
更先进更强大的神经网络?
第三课: ResNets(残差网络)
太深的神经网络训练起来很难, 因为有梯度消失和爆炸这类问题.
本节讲述跳跃连接, 它能让你从一层中得到激活, 并突然把它传递给下一层, 甚至更深的神经网络层, 利用它, 你就可以训练网络层很深很深的残差网络(ResNets), 有时能超过 100 层.
残差网络是使用了残差结构的网络. 这里有两层神经网络, a[l]代表第 l 层中的激活函数, 然后到了第 a[l+1], 两层之后是 a[l+2], 在这个计算步骤中, 你先有一个 a[l], 之后你做的第一件事就是将这个线性运算应用到它上面, 即用 a[l]计算 z[l+1], 通过乘以一个加权矩阵, 并加上一个偏置向量, 之后你通过非线性 ReLu 函数来得到 a[l+1], 然后在下一层, 再次应用这个线性步骤, 最后再运行 ReLu 函数, 得到 a[l+2].
换言之, 从 a[l]到 a[l+2]的信息, 它需要经过这些步骤, 我们把这个步骤称为这组层的主路径.
在残差网络中, 我们要做一点改变.
我们把 a[l], 把它往前提, 然后复制, 提到神经网络非常靠后的位置, 最后应用一些非线性处理, 比如线性整流函数 ReLu, 我将这个称为快捷路径.
所以无需遵循主路径, a[l]中的信息现在可以遵循快捷路径进入到更深层的神经网络中, 这意味着最后一个等式:
消失了, 我们有了:
使这成为了一个残差块. 实际上快捷路径是加在 ReLu 非线性函数之前的, 所以在第二个神经网络层中每个节点都应用一个线性方程和 ReLu 函数.
有时这个术语不叫快捷路径, 叫做跳跃连接, 是指 a[l]跳过一层或者跳过几乎两层把信息传递到更深的神经网络中去.
作者发现使用残差块, 让你可以训练更深层的神经网络, 而你建立一个 ResNets 的方法就是通过大量的这些残差块, 然后把它们堆叠起来, 形成一个深层网络.
看一下 plain 网络:
要把一个 plain 网络变成 ResNets, 你需要做的就是添加所有这些跳跃连接(或称为快捷路径连接), 所以每两层结束于额外的改变, 将这个 10 层的神经网络结构变为 5 个残差块堆积在一起, 这就是一个残差网络.
事实证明:
如果你使用标准的优化算法, 如梯度下降法或者高级优化算法来训练 plain 网络, 没有额外的快捷路径或者跳跃连接. 从经验上来说, 随着你层数的增加, 训练误差会在下降一段时间后, 又回升上去. 从理论上说, 当你使神经网络更深, 它在训练数据上的性能应该会更好, 但是在实践中或者现实中, 没有 ResNets, 一个 plain 网络如果很深意味着你的优化算法训练起来很困难.
所以在现实中, 如果你选择的网络太深, 则训练误差会更糟糕. 但是有了 ResNets 的情况是, 即使层数越来越深, 你仍然可以让误差继续下降, 即使我们训练超过 100 层的网络.
使用 ResNets 确实对解决梯度消失和爆炸问题非常有帮助. 这使得我们可以训练深得多的神经元网络而不会看到性能倒退的现象, 尽管在某一层可能会达到平原现象, 但是 ResNets 对训练很深的算法确实有帮助.
为什么 ResNets 有效?
第四课: 为什么 ResNets 有效?
看一个例子:
你可以通过使用 ResNets 来训练非常深的神经网络, 让它们越来越深而又没有真正的损害让它们在训练集上好好工作的能力, 在训练集上表现出色通常是你出色地控制你在训练集上的深度的前提条件, 所以使用 ResNets 是对付你训练集的第一步.
对于一个普通的神经网络, 我们在后面添加几层, 使神经网络的层数更深一点.
通过以上公式推导可以得出:
残差块比较容易学习恒等函数, 由于跳跃连接也可以得到 a[l+2] = a[l]. 这意味着将这两层加入到你的神经网络与上面没有这两层的神经网络相比, 并不会影响神经网络的能力, 因为对它来说学习恒等函数非常容易, 只需要复制 a[l]到 a[l+2], 即便它中间有额外的两层, 所以这就是为什么要添加这额外的两层, 这个残差块到大型神经网络, 中间或者尾部并不会影响神经网络的表现.
当然我们目标并不是维持现有的表现, 而是获得更好的表现, 你可以想象成我们额外添加的两层, 应该学习到有意义的东西, 可能要比学习恒等函数更好.
有极多层的神经网络里比较深层的网络在没有残差块跳跃连接的情况下, 容易出错的地方是: 当你的神经网络越深层, 它就很难选择参数来学习, 即便是恒等参数也很难, 这就是为什么有很多层数却会使你的结果更糟而不是更好.
我认为残差块网络有效的主要原因是:
这些额外层学习恒等函数非常简单, 你几乎总能保证它不会影响总体的表现, 甚至有些时候幸运的话, 可以提升网络的表现. 至少不会影响效果, 然后梯度下降在这里开始可以改善最终的结果.
残差网络另一个值得讨论的细节是:
我们假定 a[l+2]和 a[l]是同一维度的, 所以你将看到在 ResNet 中许多相同卷积的应用, 输出层的维度相同, 所以我们可以做一个快捷连接, 因为相同的卷积会保留维度, 也使你运算这个快捷连接更加方便, 因为运算的是两个相同维度的矢量的和.
如果不是相同维度怎么办?
你需要做的是增加一个额外的矩阵, 称它为 ws. 那么这个 ws 可以更改 a[l]的维度和 z[l]相同.
ws 矩阵也可以做一些其他的事: 比如它可以是一个包含已学习到参数的矩阵, 也可以是一个固定的矩阵, 其中包含很多 0 项.
最后我们来看一下 ResNet 在图像上的应用:
你输入一张图像, 然后经过一系列卷积层, 直到最终你输出一个 softmax 分类, 要把这个变为 ResNet, 你需要添加那些额外的跳跃连接.
细节: 这里有很多 3 * 3 卷积, 其中大部分都是相同的 3 * 3 卷积, 这也是为什么你需要做相同维度矢量的相加. 所以与其说是相互充分连接的层, 还不如说这些是卷积层, 但由于它们是相同的卷积层, 这里的维度相同, 因此 z[l+2]+a[l]的算数得以成立.
和你之前见过的 ResNet 相同的是:
你有一堆卷积层, 然后偶尔有池化层或者类似于池化的层, 你可以利用我们提到的方法调整维度, 你可以用矩阵 ws 人, 然后在这些网络中比较常见的是, 你有个池, 最后连接一个 softmax 作出预测.
接下来, 1 * 1 过滤, 1 * 1 卷积的神经网络, 如何使用它?
第五课: 网中网, 网络中的 1x1 卷积
在设计卷积网络的架构时, 一种很有用的方法是用 1 * 1 的卷积, 1 * 1 的卷积能做什么?
如果你有一个 6 * 6 * 1 的图像, 那么经过 1 * 1 卷积就是进行像素乘法, 但是当你的图像有两个维度的时候, 和一个 1 * 1 的过滤器做卷积很有意义:
比如 6 * 6 * 32, 这个 1 * 1 的卷积会逐一扫过这里的 36 个不同的位置, 然后进行对应元素之间的乘法, 将左边的 32 个数和过滤器中的 32 个数相乘 (做内积), 然后将一个非线性映射 ReLu(线性整流函数) 作用于它.
事实上, 你可以这样理解: 这个 * 1 * 32 过滤器中的 32 个数, 它类似于你有一个神经元, 接收一个 32 个数的输入向量, 将这一条位于相同位置, 即相同高度和宽度, 但位于 32 个不同通道的数和 32 个权重相乘, 然后将线性整流函数 (ReLu 函数) 作用于它, 再把对应的结果输出到这里.
更通常地, 有多个过滤器, 那么这类似于, 你有不止一个单元, 而有多个单元, 接收用一面中的所有数作为输入, 然后将它们生成一个 6 * 6 滤波器数的输出量...
所以对 1 * 1 卷积的一种理解就是:
它本质上是一个完全连接的神经网络, 逐一作用于这 36 个不同的位置上, 这个完全连接的神经网络所做的是:
它接收 32 个数的输入, 然后输出过滤器数个输出值, 即 nc[l+1], 然后对这 36 个位置中的每一个都进行相同的操作, 最终得到输出为 6 * 6 * 过滤器数.
这对你的输入量所进行的是一个非常不平凡的计算, 这个想法被称为 1 * 1 卷积. 但有时也被称为网中网.
举一个 1 * 1 卷积有应用价值的例子:
28 * 28 * 192, 通道数太多, 想要缩小通道数到 28 * 28 * 32, 你可以使用 32 个 1 * 1 的过滤器, 从技术上说, 每一个过滤器都会是 1 * 1 * 19 大小的, 因为过滤器的通道数需要和输入量的通道数保持一致.
由于你使用了 32 个过滤器, 所以这个过程的输出将会是 28 * 28 * 32 大小的立方体.
这是一种让你缩小 nc 的方式.
而池化层我只可以用它来缩小 nh 和 nw, 即这个立方体的高度和宽度.
之后我们会分析为什么 1 * 1 卷积会缩小通道数, 从而达到在某些网络中减少计算量的目的. 当然, 如果你想保持 192 个通道数, 这也是可行的.
采用 192 个 1 * 1 的过滤器, 这时 1 * 1 卷积的效果是增加非线性, 它通过添加一层输入 28 * 28 * 192 再输出 28 * 28 * 192 的操作, 使得你的网络可以学习到更复杂的函数形式.
综上就是 1 * 1 卷积, 可以做一些不平凡的操作, 以及在神经网络中增加非线性, 以及允许你减少或不改变增加输入的通道数. 接下来你会看到这种思想在构建 inception network 中是十分有用的.
第六课: Inception Network
为卷积网络设计某一层时, 你可能需要选择一个 1 * 3 的滤波器或者 3 * 3 或则 5 * 5 等, 或者你需要一个池化层, Inception Network 是: 为什么不全用呢?
这使得网络结构更加复杂, 但它的效果也会越好, 比如:
Inception Network 是指: 与其在卷积神经网络中选择一个你想使用的卷积核尺寸, 乃至选择你是否需要一个卷积层还是一个池化层, 让我们全部都做了吧.
你要做的是, 对于不同的过滤器, 将第二个量并列的堆叠在第一个量旁边, 并且保证维度是匹配的, 所以要使用 same 填充, 保证输出与输入的维度相同.
为了保证维度的匹配, 事实上你需要为最大池化操作使用填充, 所以这是一种不一般的池化.
对于像这样的一个 Inception Network, 你可以输入一些量并且获得一个输出, 如果你把所有过滤器得到的维度加起来, 得到 256.
即你将有一个 Inception Network, 输入 28 * 28 * 192, 输出为 28 * 28 * 256.
这就是 Inception Network 的核心.
这个网络基础的一个特点就是: 你不用去只挑选一个卷积核的大小或者是池化, 你可以都做, 然后把所有的结果结合起来, 让神经网络去学习它想要的参数以及他想要用到的卷积核大小.
但是这里会出现一个 Inception Network 的问题:
计算成本问题.
对于 5 * 5 过滤器进行分析: 看一下获得 28 * 28 * 32 的输出的计算成本是多少?
你有 32 个卷积核, 因为你有 32 个通道, 然后每一个卷积核大小应该是 5x5x192, 那么输出张量的大小将是: 28x28x32, 所以你需要计算 28x28x32, 每一个都需要做那么多乘法, 对应你做乘法的总数为: 5x5x192x28x28x32 = 1.2 亿.
但是通过 1 * 1 卷积可以将这个运算降低到其 1/10.
另一种计算方法:
你用 1 * 1 的卷积运算将通道的个数从 192 减小到 16, 然后我们用这个相对较小的量去做 5x5 卷积并最终得到结果, 两者的输入和输出维度相同, 但是这种计算方法, 我们先把这较大的输入减小为一个较小的中间值, 也就是只有 16 个通道而不是 192 个通道, 我们把中间层称为瓶颈层. 这个方法的计算成本是:
第一个卷积层的计算成本: 28x28x16x192 = 240 万
28x28x32x5x5x16 = 1000 万
1240 万次乘法
第二种方法中, 你想加的次数和相乘的次数差不多.
总结一下: 如果你在建立一个卷积网络层, 并且你不想去决定到底使用 1x1, 3x3,5x5 的卷积核或者是否使用池化, 那么就用这个 Inception Network, 它会做所有的工作, 并将结果都连接起来, 通过别计较计算成本, 可以看到 1 * 1 卷积计算可以生成一个瓶颈层, 并且显著降低计算成本.
如此剧烈的缩小特征表示的大小会不会影响神经网络的性能?
只要你合理的去实现这个瓶颈层, 你既可以缩小输入张量的维度, 又不会影响到整体的性能, 还能给你节省计算成本.
完整的 Inception Network 是什么样的?
第七课: Inception Network
你已经学到了所有构建 Inception Network 的基本模块, 让我们把这些模块连接起来从而建立自己的 Inception Network.
Inception Network 模块的输入值一般是激活值或者来自上层的输出.
这里:
当我们为了保持输出的维度, 而对池化使用填充操作的时候, 这里的输出和输入相同都是 28x28x192, 它的通道的个数以及深度和我们给的输入是一样的, 这个看上去好像有很多通道, 因此我们要做的是加上一个 1x1 的卷积层, 来增强通道的个数.
这样的话, 我们避免了在最后的输出中把所有的通道加到池化层.
最后, 所有的模块都用来做渠道连接, 得到 28x28x256 的输出
这就是一个 Inception 模块 Inception Network 所做的事, 差不多把这些模块放到一起.
图中的每一个模块就是一个 Inception 模块, 还有另一个池化层来改变高和宽的维度.
最后一个细节就是: 加入了你读的论文.
加入旁支的作用就是: 它把隐藏层作为输入, 来做预测, 在中间就得到 softmax 输出. 它的作用就是保证所计算的特征值, 即使它们在最头部的单元里, 或者在中间层里, 它们对于预测结果不算太差.
这就是 Inception Network 的正则化, 用来防止这个网络过度学习.
GoogleNet
来源: http://www.jianshu.com/p/5d46192f8ccd