Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(六)(1)

简介: Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(六)

第十四章:使用卷积神经网络进行深度计算机视觉

尽管 IBM 的 Deep Blue 超级计算机在 1996 年击败了国际象棋世界冠军加里·卡斯帕罗夫,但直到最近计算机才能可靠地执行看似微不足道的任务,比如在图片中检测小狗或识别口语。为什么这些任务对我们人类来说如此轻松?答案在于感知主要发生在我们的意识领域之外,在我们大脑中专门的视觉、听觉和其他感觉模块中。当感觉信息达到我们的意识时,它已经被赋予高级特征;例如,当你看到一张可爱小狗的图片时,你无法选择看到小狗,注意到它的可爱。你也无法解释如何识别一个可爱的小狗;对你来说这是显而易见的。因此,我们不能信任我们的主观经验:感知并不是微不足道的,要理解它,我们必须看看我们的感觉模块是如何工作的。

卷积神经网络(CNNs)起源于对大脑视觉皮层的研究,自上世纪 80 年代以来就被用于计算机图像识别。在过去的 10 年里,由于计算能力的增加、可用训练数据的增加,以及第十一章中介绍的用于训练深度网络的技巧,CNNs 已经成功在一些复杂的视觉任务上实现了超人类表现。它们驱动着图像搜索服务、自动驾驶汽车、自动视频分类系统等。此外,CNNs 并不局限于视觉感知:它们在许多其他任务上也取得了成功,比如语音识别和自然语言处理。然而,我们现在将专注于视觉应用。

在本章中,我们将探讨 CNNs 的起源,它们的构建模块是什么样的,以及如何使用 Keras 实现它们。然后我们将讨论一些最佳的 CNN 架构,以及其他视觉任务,包括目标检测(对图像中的多个对象进行分类并在其周围放置边界框)和语义分割(根据对象所属的类别对每个像素进行分类)。

视觉皮层的结构

David H. Hubel 和 Torsten Wiesel 在1958 年对猫进行了一系列实验⁠¹,1959 年(以及几年后对猴子进行的实验⁠^(3)),为视觉皮层的结构提供了关键见解(这两位作者因其工作于 1981 年获得了诺贝尔生理学或医学奖)。特别是,他们表明视觉皮层中许多神经元具有小的局部感受野,这意味着它们只对位于视觉场有限区域内的视觉刺激做出反应(见图 14-1,其中五个神经元的局部感受野由虚线圈表示)。不同神经元的感受野可能重叠,它们共同覆盖整个视觉场。

图 14-1. 视觉皮层中的生物神经元对视觉场中称为感受野的小区域中的特定模式做出反应;随着视觉信号通过连续的大脑模块,神经元对更大感受野中的更复杂模式做出反应

此外,作者们表明,一些神经元只对水平线的图像做出反应,而另一些只对具有不同方向的线做出反应(两个神经元可能具有相同的感受野,但对不同的线方向做出反应)。他们还注意到一些神经元具有更大的感受野,它们对更复杂的模式做出反应,这些模式是低级模式的组合。这些观察结果导致了这样一个想法,即高级神经元基于相邻低级神经元的输出(在图 14-1 中,注意到每个神经元只连接到前一层附近的神经元)。这种强大的架构能够在视觉领域的任何区域检测各种复杂的模式。

这些对视觉皮层的研究启发了 1980 年引入的neocognitron,逐渐演变成我们现在称之为卷积神经网络的东西。一个重要的里程碑是 Yann LeCun 等人在 1998 年发表的一篇论文,介绍了著名的LeNet-5架构,这种架构被银行广泛用于识别支票上的手写数字。这种架构具有一些你已经了解的构建块,如全连接层和 Sigmoid 激活函数,但它还引入了两个新的构建块:卷积层池化层。现在让我们来看看它们。

注意

为什么不简单地使用具有全连接层的深度神经网络来进行图像识别任务呢?不幸的是,尽管这对于小图像(例如 MNIST)效果很好,但对于较大的图像来说,由于需要的参数数量巨大,它会崩溃。例如,一个 100×100 像素的图像有 10,000 个像素,如果第一层只有 1,000 个神经元(这已经严重限制了传递到下一层的信息量),这意味着总共有 1 千万个连接。而这只是第一层。CNN 通过部分连接的层和权重共享来解决这个问题。

卷积层

CNN 最重要的构建块是卷积层:第一个卷积层中的神经元不与输入图像中的每个像素相连接(就像在前几章讨论的层中那样),而只与其感受野中的像素相连接(参见图 14-2)。反过来,第二个卷积层中的每个神经元只与第一层中一个小矩形内的神经元相连接。这种架构允许网络在第一个隐藏层集中于小的低级特征,然后在下一个隐藏层中将它们组合成更大的高级特征,依此类推。这种分层结构在现实世界的图像中很常见,这也是 CNN 在图像识别方面表现出色的原因之一。

图 14-2。具有矩形局部感受野的 CNN 层
注意

到目前为止,我们看到的所有多层神经网络都由一长串神经元组成,我们必须在将输入图像馈送到神经网络之前将其展平为 1D。在 CNN 中,每一层都以 2D 表示,这使得更容易将神经元与其对应的输入匹配。

给定层中位于第i行,第j列的神经元连接到前一层中位于第i到第i + f[h] – 1 行,第j到第j + f[w] – 1 列的神经元的输出,其中f[h]和f[w]是感受野的高度和宽度(参见图 14-3)。为了使一层具有与前一层相同的高度和宽度,通常在输入周围添加零,如图中所示。这称为零填充

还可以通过间隔感受野来将大输入层连接到一个较小的层,如图 14-4 所示。这显着降低了模型的计算复杂性。从一个感受野到下一个感受野的水平或垂直步长称为步幅。在图中,一个 5×7 的输入层(加上零填充)连接到一个 3×4 的层,使用 3×3 的感受野和步幅为 2(在这个例子中,步幅在两个方向上是相同的,但不一定要这样)。上层中位于第i行,第j列的神经元连接到前一层中位于第i×s[h]到第i×s[h]+f[h]–1 行,第j×s[w]到第j×s[w]+f[w]–1 列的神经元的输出,其中s[h]和s[w]是垂直和水平步幅。

图 14-3。层与零填充之间的连接

图 14-4。使用步幅为 2 降低维度

滤波器

一个神经元的权重可以表示为一个与感受野大小相同的小图像。例如,图 14-5 显示了两组可能的权重,称为滤波器(或卷积核,或只是内核)。第一个滤波器表示为一个黑色正方形,中间有一条垂直白线(它是一个 7×7 的矩阵,除了中间列全是 1,其他都是 0);使用这些权重的神经元将忽略其感受野中的所有内容,除了中间的垂直线(因为所有输入将被乘以 0,除了中间的垂直线)。第二个滤波器是一个黑色正方形,中间有一条水平白线。使用这些权重的神经元将忽略其感受野中的所有内容,除了中间的水平线。

图 14-5。应用两个不同的滤波器以获得两个特征图

现在,如果一个层中的所有神经元使用相同的垂直线滤波器(和相同的偏置项),并且您将输入图像输入到网络中,如图 14-5 所示(底部图像),该层将输出左上角的图像。请注意,垂直白线得到增强,而其余部分变得模糊。类似地,如果所有神经元使用相同的水平线滤波器,则会得到右上角的图像;请注意,水平白线得到增强,而其余部分被模糊化。因此,一个充满使用相同滤波器的神经元的层会输出一个特征图,突出显示激活滤波器最多的图像区域。但不用担心,您不必手动定义滤波器:相反,在训练期间,卷积层将自动学习其任务中最有用的滤波器,上面的层将学会将它们组合成更复杂的模式。

堆叠多个特征图

到目前为止,为了简单起见,我已经将每个卷积层的输出表示为一个 2D 层,但实际上,卷积层有多个滤波器(您决定有多少个),并且每个滤波器输出一个特征图,因此在 3D 中更准确地表示(请参见图 14-6)。每个特征图中的每个像素都有一个神经元,并且给定特征图中的所有神经元共享相同的参数(即相同的内核和偏置项)。不同特征图中的神经元使用不同的参数。神经元的感受野与之前描述的相同,但它跨越了前一层的所有特征图。简而言之,卷积层同时将多个可训练滤波器应用于其输入,使其能够在其输入的任何位置检测多个特征。

图 14-6。两个具有多个滤波器(内核)的卷积层,处理具有三个颜色通道的彩色图像;每个卷积层输出一个特征图每个滤波器
注意

所有特征图中的所有神经元共享相同的参数,这显著减少了模型中的参数数量。一旦 CNN 学会在一个位置识别模式,它就可以在任何其他位置识别它。相比之下,一旦全连接的神经网络学会在一个位置识别模式,它只能在那个特定位置识别它。

输入图像也由多个子层组成:每个颜色通道一个。如第九章中所述,通常有三个:红色、绿色和蓝色(RGB)。灰度图像只有一个通道,但有些图像可能有更多通道,例如捕捉额外光频率(如红外线)的卫星图像。

具体来说,在给定卷积层l中特征图k中第i行、第j列的神经元与前一层l – 1 中位于第i × s[h]至i × s[h] + f[h] – 1 行和第j × s[w]至j × s[w] + f[w] – 1 列的神经元的输出相连,跨所有特征图(在第l – 1 层)。请注意,在同一层中,位于相同行i和列j但在不同特征图中的所有神经元与前一层中相同位置的神经元的输出相连。

方程 14-1 总结了前面的解释,用一个大数学方程表示:它展示了如何计算卷积层中给定神经元的输出。由于所有不同的索引,它看起来有点丑陋,但它的作用只是计算所有输入的加权和,再加上偏置项。

方程 14-1。计算卷积层中神经元的输出

z i,j,k = b k + ∑ u=0 f h -1 ∑ v=0 f w -1 ∑ k‘=0 f n ’ -1 x i ‘ ,j ’ ,k ‘ × w u,v,k ’ ,k with i ‘ = i × s h + u j ’ = j × s w + v

在这个方程中:

  • z[i,] [j,] [k] 是位于卷积层(第l层)特征图k中第i行、第j列的神经元的输出。
  • 如前所述,s[h] 和 s[w] 是垂直和水平步幅,f[h] 和 f[w] 是感受野的高度和宽度,f[n′] 是前一层(第l – 1 层)中特征图的数量。
  • x[i′,] [j′,] [k′] 是位于第l – 1 层,第i′行、第j′列、特征图k′(或通道k′,如果前一层是输入层)的神经元的输出。
  • b[k] 是特征图k(在第l层)的偏置项。您可以将其视为微调特征图k的整体亮度的旋钮。
  • w[u,] [v,] [k′,] [k]是层l中特征图k中的任何神经元与其输入之间的连接权重,该输入位于行u、列v(相对于神经元的感受野),以及特征图k′。

让我们看看如何使用 Keras 创建和使用卷积层。

使用 Keras 实现卷积层

首先,让我们加载和预处理一些样本图像,使用 Scikit-Learnload_sample_image()函数和 Keras 的CenterCropRescaling层(这些都是在第十三章中介绍的):

from sklearn.datasets import load_sample_images
import tensorflow as tf
images = load_sample_images()["images"]
images = tf.keras.layers.CenterCrop(height=70, width=120)(images)
images = tf.keras.layers.Rescaling(scale=1 / 255)(images)

让我们看一下images张量的形状:

>>> images.shape
TensorShape([2, 70, 120, 3])

哎呀,这是一个 4D 张量;我们以前从未见过这个!所有这些维度是什么意思?嗯,有两个样本图像,这解释了第一个维度。然后每个图像是 70×120,因为这是我们在创建CenterCrop层时指定的大小(原始图像是 427×640)。这解释了第二和第三维度。最后,每个像素在每个颜色通道上保存一个值,有三个颜色通道——红色、绿色和蓝色,这解释了最后一个维度。

现在让我们创建一个 2D 卷积层,并将这些图像输入其中,看看输出是什么。为此,Keras 提供了一个Convolution2D层,别名为Conv2D。在幕后,这个层依赖于 TensorFlowtf.nn.conv2d()操作。让我们创建一个具有 32 个滤波器的卷积层,每个滤波器大小为 7×7(使用kernel_size=7,相当于使用kernel_size=(7 , 7)),并将这个层应用于我们的两个图像的小批量:

conv_layer = tf.keras.layers.Conv2D(filters=32, kernel_size=7)
fmaps = conv_layer(images)
注意

当我们谈论 2D 卷积层时,“2D”指的是空间维度(高度和宽度),但正如你所看到的,该层接受 4D 输入:正如我们所看到的,另外两个维度是批量大小(第一个维度)和通道数(最后一个维度)。

现在让我们看一下输出的形状:

>>> fmaps.shape
TensorShape([2, 64, 114, 32])

输出形状与输入形状类似,有两个主要区别。首先,有 32 个通道而不是 3 个。这是因为我们设置了filters=32,所以我们得到 32 个输出特征图:在每个位置的红色、绿色和蓝色的强度代替,我们现在有每个位置的每个特征的强度。其次,高度和宽度都减小了 6 个像素。这是因为Conv2D层默认不使用任何零填充,这意味着我们在输出特征图的两侧丢失了一些像素,取决于滤波器的大小。在这种情况下,由于卷积核大小为 7,我们水平和垂直各丢失 6 个像素(即每侧 3 个像素)。

警告

默认选项令人惊讶地被命名为padding="valid",实际上意味着根本没有零填充!这个名称来自于这样一个事实,即在这种情况下,每个神经元的感受野严格位于输入内部的有效位置(不会超出边界)。这不是 Keras 的命名怪癖:每个人都使用这种奇怪的命名法。

如果我们设置padding="same",那么输入将在所有侧面填充足够的零,以确保输出特征图最终与输入具有相同大小(因此这个选项的名称):

>>> conv_layer = tf.keras.layers.Conv2D(filters=32, kernel_size=7,
...                                     padding="same")
...
>>> fmaps = conv_layer(images)
>>> fmaps.shape
TensorShape([2, 70, 120, 32])

这两种填充选项在图 14-7 中有所说明。为简单起见,这里只显示了水平维度,但当然相同的逻辑也适用于垂直维度。

如果步幅大于 1(在任何方向上),那么输出大小将不等于输入大小,即使padding="same"。例如,如果设置strides=2(或等效地strides=(2, 2)),那么输出特征图将是 35×60:垂直和水平方向都减半。图 14-8 展示了当strides=2时会发生什么,两种填充选项都有。

图 14-7。当strides=1时的两种填充选项

图 14-8。当步长大于 1 时,即使使用"same"填充(和"valid"填充可能会忽略一些输入),输出也会小得多

如果您感兴趣,这是输出大小是如何计算的:

  • padding="valid"时,如果输入的宽度为i[h],那么输出宽度等于(i[h] - f[h] + s[h]) / s[h],向下取整。请记住f[h]是卷积核的宽度,s[h]是水平步长。除法中的余数对应于输入图像右侧被忽略的列。同样的逻辑也可以用来计算输出高度,以及图像底部被忽略的行。
  • padding="same"时,输出宽度等于i[h] / s[h],向上取整。为了实现这一点,在输入图像的左右两侧填充适当数量的零列(如果可能的话,数量相等,或者在右侧多一个)。假设输出宽度为o[w],那么填充的零列数为(o[w] - 1) × s[h] + f[h] - i[h]。同样的逻辑也可以用来计算输出高度和填充行数。

现在让我们来看一下层的权重(在方程 14-1 中被标记为w[u,] [v,] [k’,] [k]和b[k])。就像Dense层一样,Conv2D层保存所有层的权重,包括卷积核和偏置。卷积核是随机初始化的,而偏置初始化为零。这些权重可以通过weights属性作为 TF 变量访问,也可以通过get_weights()方法作为 NumPy 数组访问:

>>> kernels, biases = conv_layer.get_weights()
>>> kernels.shape
(7, 7, 3, 32)
>>> biases.shape
(32,)

kernels数组是 4D 的,其形状为[kernel_height, kernel_width, input_channels, output_channels]。biases数组是 1D 的,形状为[output_channels]。输出通道的数量等于输出特征图的数量,也等于滤波器的数量。

最重要的是,需要注意输入图像的高度和宽度不会出现在卷积核的形状中:这是因为输出特征图中的所有神经元共享相同的权重,正如之前解释的那样。这意味着您可以将任何大小的图像馈送到这一层,只要它们至少与卷积核一样大,并且具有正确数量的通道(在这种情况下为三个)。

最后,通常情况下,您会希望在创建Conv2D层时指定一个激活函数(如 ReLU),并指定相应的内核初始化器(如 He 初始化)。这与Dense层的原因相同:卷积层执行线性操作,因此如果您堆叠多个卷积层而没有任何激活函数,它们都等同于单个卷积层,它们将无法学习到真正复杂的内容。

正如您所看到的,卷积层有很多超参数:filterskernel_sizepaddingstridesactivationkernel_initializer等。通常情况下,您可以使用交叉验证来找到正确的超参数值,但这是非常耗时的。我们将在本章后面讨论常见的 CNN 架构,以便让您了解在实践中哪些超参数值效果最好。

内存需求

CNN 的另一个挑战是卷积层需要大量的 RAM。这在训练过程中尤为明显,因为反向传播的反向传递需要在前向传递期间计算的所有中间值。

例如,考虑一个具有 200 个 5×5 滤波器的卷积层,步幅为 1,使用"same"填充。如果输入是一个 150×100 的 RGB 图像(三个通道),那么参数数量为(5×5×3+1)×200=15,200(+1 对应于偏置项),与全连接层相比相当小。然而,这 200 个特征图中的每一个包含 150×100 个神经元,每个神经元都需要计算其 5×5×3=75 个输入的加权和:总共有 2.25 亿次浮点乘法。虽然不像全连接层那么糟糕,但仍然相当计算密集。此外,如果使用 32 位浮点数表示特征图,那么卷积层的输出将占用 200×150×100×32=9600 万位(12 MB)的 RAM。而这只是一个实例的情况——如果一个训练批次包含 100 个实例,那么这一层将使用 1.2 GB 的 RAM!

在推断(即对新实例进行预测时),一个层占用的 RAM 可以在计算下一层后立即释放,因此你只需要两个连续层所需的 RAM。但在训练期间,前向传播期间计算的所有内容都需要保留以进行反向传播,因此所需的 RAM 量至少是所有层所需 RAM 的总量。

提示

如果由于内存不足错误而导致训练崩溃,你可以尝试减小小批量大小。或者,你可以尝试使用步幅减少维度,去掉一些层,使用 16 位浮点数代替 32 位浮点数,或者将 CNN 分布在多个设备上(你将在第十九章中看到如何做)。

现在让我们来看看 CNN 的第二个常见构建块:池化层

池化层

一旦你理解了卷积层的工作原理,池化层就很容易理解了。它们的目标是对输入图像进行子采样(即缩小),以减少计算负载、内存使用和参数数量(从而限制过拟合的风险)。

就像在卷积层中一样,池化层中的每个神经元连接到前一层中有限数量的神经元的输出,这些神经元位于一个小的矩形感受野内。你必须像以前一样定义它的大小、步幅和填充类型。然而,池化神经元没有权重;它所做的只是使用聚合函数(如最大值或平均值)聚合输入。图 14-9 展示了最大池化层,这是最常见的池化层类型。在这个例子中,我们使用了一个 2×2 的池化核,步幅为 2,没有填充。在图 14-9 中的左下角感受野中,输入值为 1、5、3、2,因此只有最大值 5 传播到下一层。由于步幅为 2,输出图像的高度和宽度都是输入图像的一半(向下取整,因为我们没有使用填充)。

图 14-9。最大池化层(2×2 池化核,步幅 2,无填充)
注意

池化层通常独立地处理每个输入通道,因此输出深度(即通道数)与输入深度相同。

除了减少计算、内存使用和参数数量之外,最大池化层还引入了一定程度的不变性,如图 14-10 所示。在这里,我们假设亮像素的值低于暗像素的值,并考虑三个图像(A、B、C)通过一个 2×2 内核和步幅 2 的最大池化层。图像 B 和 C 与图像 A 相同,但向右移动了一个和两个像素。正如您所看到的,图像 A 和 B 的最大池化层的输出是相同的。这就是平移不变性的含义。对于图像 C,输出是不同的:向右移动一个像素(但仍然有 50%的不变性)。通过在 CNN 中的几层之间插入一个最大池化层,可以在更大的尺度上获得一定程度的平移不变性。此外,最大池化还提供了一定程度的旋转不变性和轻微的尺度不变性。这种不变性(即使有限)在预测不应该依赖这些细节的情况下可能是有用的,比如在分类任务中。

然而,最大池化也有一些缺点。显然,它非常破坏性:即使使用一个微小的 2×2 内核和步幅为 2,输出在两个方向上都会变小两倍(因此其面积会变小四倍),简单地丢弃了输入值的 75%。在某些应用中,不变性并不理想。以语义分割为例(根据像素所属的对象对图像中的每个像素进行分类的任务,我们将在本章后面探讨):显然,如果输入图像向右平移一个像素,输出也应该向右平移一个像素。在这种情况下的目标是等变性,而不是不变性:对输入的微小变化应导致输出的相应微小变化。

图 14-10。对小平移的不变性

使用 Keras 实现池化层

以下代码创建了一个MaxPooling2D层,别名为MaxPool2D,使用一个 2×2 内核。步幅默认为内核大小,因此此层使用步幅为 2(水平和垂直)。默认情况下,它使用"valid"填充(即根本不填充):

max_pool = tf.keras.layers.MaxPool2D(pool_size=2)

要创建一个平均池化层,只需使用AveragePooling2D,别名为AvgPool2D,而不是MaxPool2D。正如您所期望的那样,它的工作方式与最大池化层完全相同,只是计算均值而不是最大值。平均池化层曾经非常流行,但现在人们大多使用最大池化层,因为它们通常表现更好。这可能看起来令人惊讶,因为计算均值通常比计算最大值丢失的信息更少。但另一方面,最大池化仅保留最强的特征,摆脱了所有无意义的特征,因此下一层得到了一个更干净的信号来处理。此外,最大池化比平均池化提供更强的平移不变性,并且需要稍少的计算。

请注意,最大池化和平均池化可以沿深度维度而不是空间维度执行,尽管这不太常见。这可以让 CNN 学习对各种特征具有不变性。例如,它可以学习多个滤波器,每个滤波器检测相同模式的不同旋转(例如手写数字;参见图 14-11),深度最大池化层将确保输出不管旋转如何都是相同的。CNN 也可以学习对任何东西具有不变性:厚度、亮度、倾斜、颜色等等。

图 14-11。深度最大池化可以帮助 CNN 学习旋转不变性(在这种情况下)

Keras 不包括深度最大池化层,但实现一个自定义层并不太困难:

class DepthPool(tf.keras.layers.Layer):
    def __init__(self, pool_size=2, **kwargs):
        super().__init__(**kwargs)
        self.pool_size = pool_size
    def call(self, inputs):
        shape = tf.shape(inputs)  # shape[-1] is the number of channels
        groups = shape[-1] // self.pool_size  # number of channel groups
        new_shape = tf.concat([shape[:-1], [groups, self.pool_size]], axis=0)
        return tf.reduce_max(tf.reshape(inputs, new_shape), axis=-1)

这一层将其输入重塑为所需大小的通道组(pool_size),然后使用tf.reduce_max()来计算每个组的最大值。这种实现假定步幅等于池大小,这通常是你想要的。或者,您可以使用 TensorFlow 的tf.nn.max_pool()操作,并在Lambda层中包装以在 Keras 模型中使用它,但遗憾的是,此操作不实现 GPU 的深度池化,只实现 CPU 的深度池化。

在现代架构中经常看到的最后一种类型的池化层是全局平均池化层。它的工作方式非常不同:它只是计算每个整个特征图的平均值(就像使用与输入具有相同空间维度的池化核的平均池化层)。这意味着它只输出每个特征图和每个实例的一个数字。尽管这当然是极其破坏性的(大部分特征图中的信息都丢失了),但它可以在输出层之前非常有用,稍后您将在本章中看到。要创建这样的层,只需使用GlobalAveragePooling2D类,别名GlobalAvgPool2D

global_avg_pool = tf.keras.layers.GlobalAvgPool2D()

这等同于以下Lambda层,它计算空间维度(高度和宽度)上的平均值:

global_avg_pool = tf.keras.layers.Lambda(
    lambda X: tf.reduce_mean(X, axis=[1, 2]))

例如,如果我们将这一层应用于输入图像,我们将得到每个图像的红色、绿色和蓝色的平均强度:

>>> global_avg_pool(images)
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[0.64338624, 0.5971759 , 0.5824972 ],
 [0.76306933, 0.26011038, 0.10849128]], dtype=float32)>

现在您知道如何创建卷积神经网络的所有构建模块了。让我们看看如何组装它们。

CNN 架构

典型的 CNN 架构堆叠了几个卷积层(每个通常后面跟着一个 ReLU 层),然后是一个池化层,然后又是几个卷积层(+ReLU),然后是另一个池化层,依此类推。随着图像通过网络的传递,图像变得越来越小,但也通常变得越来越深(即具有更多的特征图),这要归功于卷积层(参见图 14-12)。在堆栈的顶部,添加了一个常规的前馈神经网络,由几个全连接层(+ReLUs)组成,最后一层输出预测(例如,一个 softmax 层,输出估计的类别概率)。

图 14-12. 典型的 CNN 架构
提示

一个常见的错误是使用太大的卷积核。例如,不要使用一个 5×5 的卷积层,而是堆叠两个 3×3 的卷积层:这将使用更少的参数,需要更少的计算,并且通常表现更好。一个例外是第一个卷积层:它通常可以有一个大的卷积核(例如 5×5),通常具有 2 或更大的步幅。这将减少图像的空间维度,而不会丢失太多信息,而且由于输入图像通常只有三个通道,因此成本不会太高。

这是如何实现一个基本的 CNN 来处理时尚 MNIST 数据集的(在第十章介绍):

from functools import partial
DefaultConv2D = partial(tf.keras.layers.Conv2D, kernel_size=3, padding="same",
                        activation="relu", kernel_initializer="he_normal")
model = tf.keras.Sequential([
    DefaultConv2D(filters=64, kernel_size=7, input_shape=[28, 28, 1]),
    tf.keras.layers.MaxPool2D(),
    DefaultConv2D(filters=128),
    DefaultConv2D(filters=128),
    tf.keras.layers.MaxPool2D(),
    DefaultConv2D(filters=256),
    DefaultConv2D(filters=256),
    tf.keras.layers.MaxPool2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(units=128, activation="relu",
                          kernel_initializer="he_normal"),
    tf.keras.layers.Dropout(0.5),
    tf.keras.layers.Dense(units=64, activation="relu",
                          kernel_initializer="he_normal"),
    tf.keras.layers.Dropout(0.5),
    tf.keras.layers.Dense(units=10, activation="softmax")
])

让我们来看一下这段代码:

  • 我们使用functools.partial()函数(在第十一章介绍)来定义DefaultConv2D,它的作用就像Conv2D,但具有不同的默认参数:一个小的 3 的内核大小,"same"填充,ReLU 激活函数,以及相应的 He 初始化器。
  • 接下来,我们创建Sequential模型。它的第一层是一个具有 64 个相当大的滤波器(7×7)的DefaultConv2D。它使用默认的步幅 1,因为输入图像不是很大。它还设置input_shape=[28, 28, 1],因为图像是 28×28 像素,具有单个颜色通道(即灰度)。当您加载时尚 MNIST 数据集时,请确保每个图像具有这种形状:您可能需要使用np.reshape()np.expanddims()来添加通道维度。或者,您可以在模型中使用Reshape层作为第一层。
  • 然后我们添加一个使用默认池大小为 2 的最大池化层,因此它将每个空间维度除以 2。
  • 然后我们重复相同的结构两次:两个卷积层后面跟着一个最大池化层。对于更大的图像,我们可以多次重复这个结构。重复次数是一个可以调整的超参数。
  • 请注意,随着我们向 CNN 向输出层上升,滤波器的数量会翻倍(最初为 64,然后为 128,然后为 256):这是有道理的,因为低级特征的数量通常相当低(例如,小圆圈,水平线),但有许多不同的方法可以将它们组合成更高级别的特征。在每个池化层后将滤波器数量翻倍是一种常见做法:由于池化层将每个空间维度除以 2,我们可以在下一层中加倍特征图的数量,而不用担心参数数量、内存使用或计算负载的激增。
  • 接下来是全连接网络,由两个隐藏的密集层和一个密集输出层组成。由于这是一个有 10 个类别的分类任务,输出层有 10 个单元,并且使用 softmax 激活函数。请注意,我们必须在第一个密集层之前扁平化输入,因为它期望每个实例的特征是一个 1D 数组。我们还添加了两个 dropout 层,每个的 dropout 率为 50%,以减少过拟合。

如果您使用"sparse_categorical_crossentropy"损失编译此模型,并将模型拟合到 Fashion MNIST 训练集,它应该在测试集上达到超过 92%的准确率。这并不是最先进的,但是相当不错,显然比我们在第十章中使用密集网络取得的成绩要好得多。

多年来,这种基本架构的变体已经被开发出来,导致了该领域的惊人进步。这种进步的一个很好的衡量标准是在 ILSVRC(ImageNet 挑战)等比赛中的错误率。在这个比赛中,图像分类的前五错误率,即系统的前五个预测中没有包括正确答案的测试图像数量,从超过 26%下降到不到 2.3%仅仅用了六年。这些图像相当大(例如,高度为 256 像素),有 1000 个类别,其中一些非常微妙(尝试区分 120 种狗品种)。查看获胜作品的演变是了解 CNN 如何工作以及深度学习研究如何进展的好方法。

我们将首先看一下经典的 LeNet-5 架构(1998 年),然后看一下几位 ILSVRC 挑战的获胜者:AlexNet(2012),GoogLeNet(2014),ResNet(2015)和 SENet(2017)。在此过程中,我们还将看一些其他架构,包括 Xception,ResNeXt,DenseNet,MobileNet,CSPNet 和 EfficientNet。

LeNet-5

LeNet-5 架构可能是最广为人知的 CNN 架构。正如前面提到的,它是由 Yann LeCun 在 1998 年创建的,并且被广泛用于手写数字识别(MNIST)。它由表 14-1 中显示的层组成。

表 14-1. LeNet-5 架构

类型 特征图 尺寸 核大小 步幅 激活函数
Out 全连接 10 RBF
F6 全连接 84 tanh
C5 卷积 120 1 × 1 5 × 5 1 tanh
S4 平均池化 16 5 × 5 2 × 2 2 tanh
C3 卷积 16 10 × 10 5 × 5 1 tanh
S2 平均池化 6 14 × 14 2 × 2 2 tanh
C1 卷积 6 28 × 28 5 × 5 1 tanh
In 输入 1 32 × 32

正如您所看到的,这看起来与我们的时尚 MNIST 模型非常相似:一堆卷积层和池化层,然后是一个密集网络。也许与更现代的分类 CNN 相比,主要的区别在于激活函数:今天,我们会使用 ReLU 而不是 tanh,使用 softmax 而不是 RBF。还有一些其他不太重要的差异,但如果您感兴趣,可以在本章的笔记本中找到https://homl.info/colab3。Yann LeCun 的网站还展示了 LeNet-5 对数字进行分类的精彩演示。

AlexNet

AlexNet CNN 架构⁠¹¹在 2012 年 ILSVRC 挑战赛中大幅领先:它实现了 17%的前五错误率,而第二名竞争对手仅实现了 26%!AlexaNet 由 Alex Krizhevsky(因此得名)、Ilya Sutskever 和 Geoffrey Hinton 开发。它类似于 LeNet-5,只是更大更深,它是第一个直接将卷积层堆叠在一起的模型,而不是将池化层堆叠在每个卷积层之上。表 14-2 展示了这种架构。

表 14-2. AlexNet 架构

类型 特征图 大小 核大小 步幅 填充 激活函数
Out 全连接 1,000 Softmax
F10 全连接 4,096 ReLU
F9 全连接 4,096 ReLU
S8 最大池化 256 6 × 6 3 × 3 2 valid
C7 卷积 256 13 × 13 3 × 3 1 same ReLU
C6 卷积 384 13 × 13 3 × 3 1 same ReLU
C5 卷积 384 13 × 13 3 × 3 1 same ReLU
S4 最大池化 256 13 × 13 3 × 3 2 valid
C3 卷积 256 27 × 27 5 × 5 1 same ReLU
S2 最大池化 96 27 × 27 3 × 3 2 valid
C1 卷积 96 55 × 55 11 × 11 4 valid ReLU
In 输入 3(RGB) 227 × 227

为了减少过拟合,作者使用了两种正则化技术。首先,他们在训练期间对 F9 和 F10 层的输出应用了 50%的 dropout 率的 dropout(在第十一章中介绍)。其次,他们通过随机移动训练图像的各种偏移量、水平翻转它们和改变光照条件来执行数据增强。

AlexNet 还在 C1 和 C3 层的 ReLU 步骤之后立即使用了一个竞争性归一化步骤,称为局部响应归一化(LRN):最强烈激活的神经元抑制了位于相邻特征图中相同位置的其他神经元。这种竞争性激活已经在生物神经元中观察到。这鼓励不同的特征图专门化,将它们分开并迫使它们探索更广泛的特征,最终提高泛化能力。方程 14-2 展示了如何应用 LRN。

方程 14-2. 局部响应归一化(LRN)

b i = a i k+α∑ j=j low j high a j 2 -β with j high = min i + r 2 , f n - 1 j low = max 0 , i - r 2

在这个方程中:

  • b[i] 是位于特征图i中的神经元的归一化输出,在某一行u和列v(请注意,在这个方程中,我们只考虑位于这一行和列的神经元,因此uv没有显示)。
  • a[i] 是 ReLU 步骤后,但规范化之前的神经元的激活。
  • kαβr是超参数。k称为偏置r称为深度半径
  • f[n] 是特征图的数量。

例如,如果r = 2,并且一个神经元具有强烈的激活,则它将抑制位于其上下特征图中的神经元的激活。

在 AlexNet 中,超参数设置为:r = 5,α = 0.0001,β = 0.75,k = 2。您可以使用tf.nn.local_response_normalization()函数来实现这一步骤(如果要在 Keras 模型中使用它,可以将其包装在Lambda层中)。

由 Matthew Zeiler 和 Rob Fergus 开发的 AlexNet 的一个变体称为ZF Net⁠¹²,并赢得了 2013 年 ILSVRC 挑战赛。它本质上是 AlexNet,只是调整了一些超参数(特征图数量、卷积核大小、步幅等)。

GoogLeNet

GoogLeNet 架构由 Google Research 的 Christian Szegedy 等人开发,⁠¹³,并通过将前五错误率降低到 7%以下赢得了 ILSVRC 2014 挑战。这一出色的性能在很大程度上来自于该网络比以前的 CNN 更深(如您将在图 14-15 中看到的)。这得益于称为inception 模块的子网络,⁠¹⁴,它允许 GoogLeNet 比以前的架构更有效地使用参数:实际上,GoogLeNet 的参数比 AlexNet 少 10 倍(大约 600 万个而不是 6000 万个)。

图 14-14 显示了 Inception 模块的架构。符号“3×3 + 1(S)”表示该层使用 3×3 内核,步幅 1 和"same"填充。输入信号首先并行输入到四个不同的层中。所有卷积层使用 ReLU 激活函数。请注意,顶部卷积层使用不同的内核大小(1×1、3×3 和 5×5),使它们能够捕获不同尺度的模式。还要注意,每个单独的层都使用步幅 1 和"same"填充(即使是最大池化层),因此它们的输出与它们的输入具有相同的高度和宽度。这使得可以在最终的深度连接层(即将来自所有四个顶部卷积层的特征图堆叠在一起)中沿深度维度连接所有输出。可以使用 Keras 的Concatenate层来实现,使用默认的axis=-1

图 14-14。Inception 模块

您可能会想知道为什么 Inception 模块具有具有 1×1 内核的卷积层。毕竟,这些层不能捕获任何特征,因为它们一次只查看一个像素,对吧?实际上,这些层有三个目的:

  • 尽管它们不能捕获空间模式,但它们可以捕获沿深度维度(即跨通道)的模式。
  • 它们被配置为输出比它们的输入更少的特征图,因此它们充当瓶颈层,意味着它们降低了维度。这降低了计算成本和参数数量,加快了训练速度并提高了泛化能力。
  • 每对卷积层([1×1、3×3]和[1×1、5×5])就像一个强大的卷积层,能够捕获更复杂的模式。卷积层等效于在图像上扫过一个密集层(在每个位置,它只查看一个小的感受野),而这些卷积层对等于在图像上扫过两层神经网络。

简而言之,您可以将整个 Inception 模块视为一个超级卷积层,能够输出捕获各种尺度复杂模式的特征图。

现在让我们来看看 GoogLeNet CNN 的架构(参见图 14-15)。每个卷积层和每个池化层输出的特征图数量在内核大小之前显示。该架构非常深,以至于必须用三列来表示,但实际上 GoogLeNet 是一个高高的堆叠,包括九个 Inception 模块(带有旋转顶部的方框)。Inception 模块中的六个数字表示模块中每个卷积层输出的特征图数量(与图 14-14 中的顺序相同)。请注意,所有卷积层都使用 ReLU 激活函数。

让我们来看看这个网络:

  • 前两层将图像的高度和宽度分别除以 4(因此其面积除以 16),以减少计算负载。第一层使用大的内核大小,7×7,以便保留大部分信息。
  • 然后,本地响应归一化层确保前面的层学习到各种各样的特征(如前面讨论的)。
  • 接下来是两个卷积层,其中第一个充当瓶颈层。正如前面提到的,您可以将这一对看作一个更聪明的单个卷积层。
  • 再次,本地响应归一化层确保前面的层捕获各种各样的模式。
  • 接下来,一个最大池化层将图像的高度和宽度减少了一半,以加快计算速度。
  • 然后是 CNN 的骨干:一个高高的堆叠,包括九个 Inception 模块,交替使用一对最大池化层来降低维度并加快网络速度。
  • 接下来,全局平均池化层输出每个特征图的平均值:这会丢弃任何剩余的空间信息,这没关系,因为在那一点上剩下的空间信息并不多。事实上,GoogLeNet 的输入图像通常期望为 224×224 像素,因此经过 5 个最大池化层后,每个将高度和宽度除以 2,特征图缩小到 7×7。此外,这是一个分类任务,而不是定位任务,因此物体在哪里并不重要。由于这一层带来的降维,不需要在 CNN 的顶部有几个全连接层(就像在 AlexNet 中那样),这大大减少了网络中的参数数量,并限制了过拟合的风险。
  • 最后几层很容易理解:用于正则化的 dropout,然后是一个具有 1,000 个单元的全连接层(因为有 1,000 个类别),以及一个 softmax 激活函数来输出估计的类别概率。

图 14-15。GoogLeNet 架构

原始的 GoogLeNet 架构包括两个辅助分类器,插在第三和第六个 inception 模块的顶部。它们都由一个平均池化层、一个卷积层、两个全连接层和一个 softmax 激活层组成。在训练过程中,它们的损失(缩小了 70%)被添加到整体损失中。目标是解决梯度消失问题并对网络进行正则化,但后来证明它们的效果相对较小。

后来,Google 的研究人员提出了 GoogLeNet 架构的几个变体,包括 Inception-v3 和 Inception-v4,使用略有不同的 inception 模块以实现更好的性能。

VGGNet

在 ILSVRC 2014 挑战赛中的亚军是VGGNet,Karen Simonyan 和 Andrew Zisserman,来自牛津大学视觉几何组(VGG)研究实验室,开发了一个非常简单和经典的架构;它有 2 或 3 个卷积层和一个池化层,然后再有 2 或 3 个卷积层和一个池化层,依此类推(达到 16 或 19 个卷积层,取决于 VGG 的变体),再加上一个最终的具有 2 个隐藏层和输出层的密集网络。它使用小的 3×3 滤波器,但数量很多。

ResNet

Kaiming He 等人在 ILSVRC 2015 挑战赛中使用Residual Network (ResNet)赢得了冠军,其前五错误率令人惊叹地低于 3.6%。获胜的变体使用了一个由 152 层组成的极深 CNN(其他变体有 34、50 和 101 层)。它证实了一个普遍趋势:计算机视觉模型变得越来越深,参数越来越少。能够训练如此深的网络的关键是使用跳跃连接(也称为快捷连接):输入到一个层的信号也被添加到堆栈中更高的层的输出中。让我们看看为什么这很有用。

在训练神经网络时,目标是使其模拟目标函数h(x)。如果将输入x添加到网络的输出中(即添加一个跳跃连接),那么网络将被迫模拟f(x) = h(x) - x而不是h(x)。这被称为残差学习

图 14-16。残差学习

当初始化一个常规的神经网络时,它的权重接近于零,因此网络只会输出接近于零的值。如果添加一个跳跃连接,结果网络将只输出其输入的副本;换句话说,它最初模拟的是恒等函数。如果目标函数与恒等函数相当接近(这通常是情况),这将大大加快训练速度。

此外,如果添加许多跳跃连接,即使有几个层尚未开始学习,网络也可以开始取得进展(参见图 14-17)。由于跳跃连接,信号可以轻松地在整个网络中传播。深度残差网络可以看作是一堆残差单元(RUs),其中每个残差单元是一个带有跳跃连接的小型神经网络。

现在让我们看一下 ResNet 的架构(参见图 14-18)。它非常简单。它的开头和结尾与 GoogLeNet 完全相同(除了没有丢弃层),中间只是一个非常深的残差单元堆栈。每个残差单元由两个卷积层组成(没有池化层!),使用 3×3 的卷积核和保持空间维度(步幅 1,"same"填充)的批量归一化(BN)和 ReLU 激活。

图 14-17。常规深度神经网络(左)和深度残差网络(右)

图 14-18。ResNet 架构

请注意,每隔几个残差单元,特征图的数量会加倍,同时它们的高度和宽度会减半(使用步幅为 2 的卷积层)。当这种情况发生时,输入不能直接添加到残差单元的输出中,因为它们的形状不同(例如,这个问题影响了由虚线箭头表示的跳跃连接在图 14-18 中的情况)。为了解决这个问题,输入通过一个步幅为 2 的 1×1 卷积层,并具有正确数量的输出特征图(参见图 14-19)。

图 14-19。更改特征图大小和深度时的跳跃连接

存在不同变体的架构,具有不同数量的层。ResNet-34 是一个具有 34 层的 ResNet(仅计算卷积层和全连接层),包含 3 个输出 64 个特征图的 RU,4 个输出 128 个特征图的 RU,6 个输出 256 个特征图的 RU,以及 3 个输出 512 个特征图的 RU。我们将在本章后面实现这个架构。

注意

Google 的Inception-v4⁠¹⁸架构融合了 GoogLeNet 和 ResNet 的思想,并在 ImageNet 分类中实现了接近 3%的前五错误率。

比 ResNet-152 更深的 ResNet,例如 ResNet-152,使用略有不同的残差单元。它们不是使用两个具有 256 个特征图的 3×3 卷积层,而是使用三个卷积层:首先是一个只有 64 个特征图的 1×1 卷积层(少了 4 倍),它充当瓶颈层(如前所述),然后是一个具有 64 个特征图的 3×3 层,最后是另一个具有 256 个特征图的 1×1 卷积层(4 倍 64),恢复原始深度。ResNet-152 包含 3 个输出 256 个映射的这样的 RU,然后是 8 个输出 512 个映射的 RU,一个令人惊叹的 36 个输出 1024 个映射的 RU,最后是 3 个输出 2048 个映射的 RU。

Xception

值得注意的是 GoogLeNet 架构的另一个变种:Xception(代表Extreme Inception)由 Keras 的作者 François Chollet 于 2016 年提出,并在一个庞大的视觉任务(3.5 亿张图片和 1.7 万个类别)上明显优于 Inception-v3。就像 Inception-v4 一样,它融合了 GoogLeNet 和 ResNet 的思想,但是用一个特殊类型的层称为深度可分离卷积层(或简称可分离卷积层)替换了 inception 模块。这些层在一些 CNN 架构中之前已经被使用过,但在 Xception 架构中并不像现在这样核心。常规卷积层使用滤波器,试图同时捕捉空间模式(例如,椭圆)和跨通道模式(例如,嘴+鼻子+眼睛=脸),而可分离卷积层则做出了空间模式和跨通道模式可以分别建模的强烈假设(见图 14-20)。因此,它由两部分组成:第一部分对每个输入特征图应用一个单一的空间滤波器,然后第二部分专门寻找跨通道模式——这只是一个具有 1×1 滤波器的常规卷积层。

由于可分离卷积层每个输入通道只有一个空间滤波器,所以应避免在通道较少的层之后使用它们,比如输入层(尽管图 14-20 中是这样的,但那只是为了说明目的)。因此,Xception 架构以 2 个常规卷积层开始,然后剩下的架构只使用可分离卷积(总共 34 个),再加上一些最大池化层和通常的最终层(一个全局平均池化层和一个密集输出层)。

你可能会想为什么 Xception 被认为是 GoogLeNet 的一个变种,因为它根本不包含任何 inception 模块。嗯,正如之前讨论的那样,一个 inception 模块包含有 1×1 滤波器的卷积层:这些滤波器专门寻找跨通道模式。然而,位于它们之上的卷积层是常规卷积层,既寻找空间模式又寻找跨通道模式。因此,你可以将一个 inception 模块看作是一个常规卷积层(同时考虑空间模式和跨通道模式)和一个可分离卷积层(分别考虑它们)之间的中间层。实际上,可分离卷积层通常表现更好。

图 14-20。深度可分离卷积层
提示

可分离卷积层使用更少的参数、更少的内存和更少的计算量比常规卷积层,通常表现更好。考虑默认使用它们,除了在通道较少的层之后(比如输入通道)。在 Keras 中,只需使用SeparableConv2D代替Conv2D:这是一个即插即用的替代。Keras 还提供了一个DepthwiseConv2D层,实现深度可分离卷积层的第一部分(即,对每个输入特征图应用一个空间滤波器)。

SENet

在 ILSVRC 2017 挑战中获胜的架构是Squeeze-and-Excitation Network (SENet)。这个架构扩展了现有的架构,如 inception 网络和 ResNets,并提升了它们的性能。这使得 SENet 以惊人的 2.25%的前五错误率赢得了比赛!扩展版本的 inception 网络和 ResNets 分别称为SE-InceptionSE-ResNet。提升来自于 SENet 在原始架构的每个 inception 模块或残差单元中添加了一个小型神经网络,称为SE 块,如图 14-21 所示。

图 14-21. SE-Inception 模块(左)和 SE-ResNet 单元(右)

一个 SE 块分析其所附加的单元的输出,专注于深度维度(不寻找任何空间模式),并学习哪些特征通常是最活跃的。然后,它使用这些信息来重新校准特征映射,如图 14-22 所示。例如,一个 SE 块可能学习到嘴巴、鼻子和眼睛通常一起出现在图片中:如果你看到嘴巴和鼻子,你应该期望也看到眼睛。因此,如果该块在嘴巴和鼻子特征映射中看到强烈的激活,但在眼睛特征映射中只有轻微的激活,它将增强眼睛特征映射(更准确地说,它将减少不相关的特征映射)。如果眼睛有些混淆,这种特征映射的重新校准将有助于解决模糊性。

图 14-22. 一个 SE 块执行特征映射重新校准

一个 SE 块由三层组成:一个全局平均池化层,一个使用 ReLU 激活函数的隐藏密集层,以及一个使用 sigmoid 激活函数的密集输出层(见图 14-23)。

图 14-23. SE 块架构

与之前一样,全局平均池化层计算每个特征映射的平均激活:例如,如果其输入包含 256 个特征映射,它将输出 256 个数字,表示每个滤波器的整体响应水平。接下来的层是“挤压”发生的地方:这一层的神经元数量明显少于 256 个——通常比特征映射的数量少 16 倍(例如,16 个神经元)——因此 256 个数字被压缩成一个小向量(例如,16 维)。这是特征响应分布的低维向量表示(即嵌入)。这个瓶颈步骤迫使 SE 块学习特征组合的一般表示(当我们讨论自编码器时,我们将再次看到这个原则在第十七章中)。最后,输出层接受嵌入并输出一个包含每个特征映射的重新校准向量(例如,256 个),每个数字在 0 到 1 之间。然后特征映射乘以这个重新校准向量,因此不相关的特征(具有低重新校准分数)被缩小,而相关的特征(具有接近 1 的重新校准分数)被保留。

其他值得注意的架构

还有许多其他 CNN 架构可以探索。以下是一些最值得注意的简要概述:

ResNeXt⁠²²

ResNeXt 改进了 ResNet 中的残差单元。而最佳 ResNet 模型中的残差单元只包含 3 个卷积层,ResNeXt 的残差单元由许多并行堆栈组成(例如,32 个堆栈),每个堆栈有 3 个卷积层。然而,每个堆栈中的前两层只使用少量滤波器(例如,只有四个),因此总参数数量与 ResNet 中的相同。然后,所有堆栈的输出相加,并将结果传递给下一个残差单元(以及跳跃连接)。

DenseNet⁠²³

DenseNet 由几个密集块组成,每个块由几个密集连接的卷积层组成。这种架构在使用相对较少的参数的同时实现了出色的准确性。什么是“密集连接”?每一层的输出被馈送为同一块内每一层之后的每一层的输入。例如,块中的第 4 层以该块中第 1、2 和 3 层的输出的深度级联作为输入。密集块之间由几个过渡层分隔。

MobileNet⁠²⁴

MobileNets 是精简的模型,旨在轻量且快速,因此在移动和 Web 应用程序中很受欢迎。它们基于深度可分离卷积层,类似于 Xception。作者提出了几个变体,以牺牲一点准确性换取更快速和更小的模型。

CSPNet⁠²⁵

交叉阶段部分网络(CSPNet)类似于 DenseNet,但是每个密集块的部分输入直接连接到该块的输出,而不经过该块。

EfficientNet⁠²⁶

EfficientNet 可以说是这个列表中最重要的模型。作者提出了一种有效地扩展任何 CNN 的方法,通过以原则性的方式同时增加深度(层数)、宽度(每层的滤波器数量)和分辨率(输入图像的大小)。这被称为复合缩放。他们使用神经架构搜索来找到一个适合 ImageNet 的缩小版本(具有更小和更少的图像)的良好架构,然后使用复合缩放来创建这种架构的越来越大的版本。当 EfficientNet 模型推出时,它们在所有计算预算中都远远超过了所有现有的模型,并且它们仍然是当今最好的模型之一。

理解 EfficientNet 的复合缩放方法有助于更深入地理解 CNN,特别是如果您需要扩展 CNN 架构。它基于计算预算的对数度量,标记为ϕ:如果您的计算预算翻倍,则ϕ增加 1。换句话说,用于训练的浮点运算数量与 2^(ϕ)成比例。您的 CNN 架构的深度、宽度和分辨率应分别按α(*ϕ*)、*β*(ϕ)和γ^(ϕ)缩放。因子αβγ必须大于 1,且α + β² + γ²应接近 2。这些因子的最佳值取决于 CNN 的架构。为了找到 EfficientNet 架构的最佳值,作者从一个小的基线模型(EfficientNetB0)开始,固定ϕ = 1,然后简单地运行了一个网格搜索:他们发现α = 1.2,β = 1.1,γ = 1.1。然后,他们使用这些因子创建了几个更大的架构,命名为 EfficientNetB1 到 EfficientNetB7,对应不断增加的ϕ值。

选择正确的 CNN 架构

有这么多 CNN 架构,您如何选择最适合您项目的架构?这取决于您最关心的是什么:准确性?模型大小(例如,用于部署到移动设备)?在 CPU 上的推理速度?在 GPU 上的推理速度?表 14-3 列出了目前在 Keras 中可用的最佳预训练模型(您将在本章后面看到如何使用它们),按模型大小排序。您可以在https://keras.io/api/applications找到完整列表。对于每个模型,表格显示要使用的 Keras 类名(在tf.keras.applications包中)、模型的大小(MB)、在 ImageNet 数据集上的 Top-1 和 Top-5 验证准确率、参数数量(百万)以及在 CPU 和 GPU 上使用 32 张图像的推理时间(毫秒),使用性能较强的硬件。⁠²⁷ 对于每列,最佳值已突出显示。正如您所看到的,通常较大的模型更准确,但并非总是如此;例如,EfficientNetB2 在大小和准确性上均优于 InceptionV3。我之所以将 InceptionV3 保留在列表中,是因为在 CPU 上它几乎比 EfficientNetB2 快一倍。同样,InceptionResNetV2 在 CPU 上速度很快,而 ResNet50V2 和 ResNet101V2 在 GPU 上速度极快。

表 14-3。Keras 中可用的预训练模型

类名 大小(MB) Top-1 准确率 Top-5 准确率 参数 CPU(ms) GPU(ms)
MobileNetV2 14 71.3% 90.1% 3.5M 25.9 3.8
MobileNet 16 70.4% 89.5% 4.3M 22.6 3.4
NASNetMobile 23 74.4% 91.9% 5.3M 27.0 6.7
EfficientNetB0 29 77.1% 93.3% 5.3M 46.0 4.9
EfficientNetB1 31 79.1% 94.4% 7.9M 60.2 5.6
EfficientNetB2 36 80.1% 94.9% 9.2M 80.8 6.5
EfficientNetB3 48 81.6% 95.7% 12.3M 140.0 8.8
EfficientNetB4 75 82.9% 96.4% 19.5M 308.3 15.1
InceptionV3 92 77.9% 93.7% 23.9M 42.2 6.9
ResNet50V2 98 76.0% 93.0% 25.6M 45.6 4.4
EfficientNetB5 118 83.6% 96.7% 30.6M 579.2 25.3
EfficientNetB6 166 84.0% 96.8% 43.3M 958.1 40.4
ResNet101V2 171 77.2% 93.8% 44.7M 72.7 5.4
InceptionResNetV2 215 80.3% 95.3% 55.9M 130.2 10.0
EfficientNetB7 256 84.3% 97.0% 66.7M 1578.9 61.6

希望您喜欢这次对主要 CNN 架构的深入探讨!现在让我们看看如何使用 Keras 实现其中一个。


Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(六)(2)https://developer.aliyun.com/article/1482441


相关文章
|
算法框架/工具 机器学习/深度学习 算法
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)(2)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)
32 0
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)(2)
|
17天前
|
机器学习/深度学习 算法框架/工具 算法
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(二)(3)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(二)
95 0
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(二)(3)
|
算法框架/工具 机器学习/深度学习 Python
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(一)(3)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(一)
25 0
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(一)(3)
|
17天前
|
机器学习/深度学习 算法框架/工具 API
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(四)(2)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(四)
60 0
|
机器学习/深度学习 算法 算法框架/工具
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(七)(3)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(七)
15 0
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(七)(3)
|
17天前
|
数据挖掘 算法 机器学习/深度学习
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)(4)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)
33 0
|
16天前
|
算法框架/工具 Python 算法
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)(3)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)
57 0
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)(3)
|
17天前
|
算法 机器学习/深度学习 算法框架/工具
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(八)(2)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(八)
43 0
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(八)(2)
|
17天前
|
机器学习/深度学习 算法框架/工具 自然语言处理
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(七)(1)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(七)
31 0
|
17天前
|
机器学习/深度学习 算法框架/工具 算法
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)(1)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)
46 0
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(三)(1)