【DL】第 3 章:高级卷积网络

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

文章目录

介绍 AlexNet

视觉几何组简介

带有 PyTorch 和 TensorFlow 的 VGG

了解残差网络

实现残差块

了解 Inception 网络

 Inception v1

Inception v2 and v3

Inception v4 和 Inception-ResNet

介绍 Xception

介绍 MobileNet

DenseNets 简介

神经架构搜索的工作原理

介绍胶囊网络(capsule networks)

卷积网络的局限性

 Capsules(胶囊)

动态路由

胶囊网络的结构

概括


第 2 章理解卷积网络中,我们讨论了卷积神经网络CNNs ) 的构建块。) 以及它们的一些属性。在本章中,我们将更进一步,讨论一些最流行的 CNN 架构。这些网络通常将多个原始卷积和/或池化操作组合在一个新颖的构建块中,作为复杂架构的基础。这使我们能够构建具有高表征能力的非常深(有时是宽)的网络,这些网络在 ImageNet 分类、​​图像分割、语音识别等复杂任务上表现良好。其中许多模型最初是作为 ImageNet 挑战的参与者发布的,他们通常会获胜。为了简化我们的任务,我们将讨论图像分类上下文中的所有架构。我们仍将讨论更复杂的任务,但我们将在第 4 章中讨论,目标检测和图像分割

本章将涵盖以下主题:

  • 介绍 AlexNet
  • 视觉几何组简介
  • 了解残差网络
  • 了解Inception 网络
  • 介绍 Xception
  • 介绍 MobileNet
  • DenseNets 简介
  • 神经架构搜索的工作原理
  • 介绍胶囊网络

介绍 AlexNet

我们将讨论的第一个模型是 2012 年ImageNet 大规模视觉识别挑战赛ILSVRC,或简称为ImageNet ) 的获胜者。它的昵称是 AlexNet(使用深度卷积神经网络进行 ImageNet 分类https ://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf ),以它的一位作者 Alex 命名克里热夫斯基。虽然这个模型现在很少使用,但它是当代深度学习的一个重要里程碑。

下图显示了网络架构:

AlexNet 架构。原始模型被一分为二,因此它可以安装在两个 GPU 的内存上

该模型有五个交叉相关的卷积层、三个重叠的最大池化层、三个全连接层和 ReLU 激活。输出是一个 1,000 路 softmax(每个ImageNet类一个)。第一和第二卷积层使用局部响应归一化——一种归一化,有点类似于批量归一化。全连接层的 dropout 率为 0.5。为了防止过度拟合,网络使用256 × 256 输入图像的随机 227 × 227 裁剪进行训练。该网络实现了 37.5% 和 17.0% 的 top-1 和 top-5 测试集错误率。

在下一节中,我们将讨论牛津视觉几何小组在 2014 年推出的 NN 架构,当时它在当年的 ImageNet 挑战赛中获得亚军。 

视觉几何组简介

我们将要 讨论的下一个架构是Visual Geometry Group ( VGG )(来自Oxford 的 Visual Geometry Group,Very Deep Convolutional Networks for Large-Scale Image Recognition  https : //arxiv.org/abs/1409.1556 。VGG 系列网络今天仍然很流行,并且经常被用作新架构的基准。在 VGG 之前(例如,LeNet-5: http: //yann.lecun.com/exdb/lenet/和 AlexNet ),网络的初始卷积层使用具有大感受野的过滤器,例如 11 × 11。此外,网络通常 具有交替的单个卷积层和池化层。该论文的作者观察到,具有较大滤波器大小的卷积层可以用两个或多个具有较小滤波器的卷积层的堆栈来代替(分解卷积)。例如,我们可以将一个 5 × 5 层替换为两个 3 × 3 层的堆栈,或者一个 7 × 7 层替换为三个 3 × 3 层的堆栈。

这种结构有几个优点,如下所示:

  • 最后一个堆叠层的神经元具有与具有大滤波器的单层等效的感受野大小。
  • 与具有大过滤器尺寸的单层相比,堆叠层的权重和操作数量较少。假设我们想用两个 3 × 3 层替换一个 5 × 5 层。我们还假设所有层都具有相同数量的输入和输出通道(切片)M。5 × 5 层的权重总数(不包括偏差)为5*5*M*M = 25*M 2。另一方面,单个 3 × 3 层的总权重为3*3*M*M = 9*M 2,简单来说* ( 3*3*M*M) = 18*M 2 对于两层,这使得这种安排的效率提高了 28% (18/25 = 0.72)。使用更大的过滤器,效率将进一步提高。
  • 堆叠多个层使决策函数更具辨别力。

VGG 网络由两个、三个或四个堆叠的卷积层和一个最大池化层组成的多个块。我们可以在下表中看到两个最流行的变体VGG16VGG19

VGG16 和 VGG19 网络的架构,以每个网络中的加权层数命名

随着 VGG 网络深度的增加,卷积层的宽度(滤波器数量)也会增加。我们有多对体积深度为 128/256/512 的跨通道卷积,它们连接到具有相同深度的其他层。此外,我们还有两个4,096 单元的全连接层,然后是一个 1000 单元的全连接层和一个 softmax(每个 ImageNet 类一个)。正因为如此,VGG 网络有大量的参数(权重),这使得它们的内存效率低下,并且计算成本很高。尽管如此,这是一种流行且直接的网络架构,通过添加批量标准化进一步改进了它。

在下一节中,我们将使用 VGG 作为示例,说明如何使用 TensorFlow 和 PyTorch 加载预训练的网络模型。

带有 PyTorch 和 TensorFlow 的 VGG

PyTorch 和 TensorFlow 都有预训练的 VGG 模型。让我们看看如何使用它们。

Keras 是 TensorFlow 2 的官方部分,因此,我们将使用它来加载模型:

import tensorflow as tf

# VGG16
vgg16 = tf.keras.applications.vgg16.VGG16(include_top=True,
                                          weights='imagenet',
                                          input_tensor=None,
                                          input_shape=None,
                                          pooling=None,
                                          classes=1000)

# VGG19 
vgg19 = tf.keras.applications.vgg19.VGG19(include_top=True,
                                          weights='imagenet',
                                          input_tensor=None,
                                          input_shape=None,
                                          pooling=None,
                                          classes=1000)

通过设置weights='imagenet'参数,网络将加载预训练的 ImageNet 权重(它们将自动下载)。您可以设置include_top为,这将排除迁移学习场景的全连接层。在这种情况下,您还可以通过将uple值设置为来使用任意输入大小——卷积层将自动缩放以匹配所需的输入形状。这是可能的,因为卷积滤波器沿整个特征图共享。因此,我们可以在不同大小的特征图上使用相同的过滤器。 False input_shape

我们将继续使用 PyTorch,您可以在其中选择是否要使用预训练模型(同样,自动下载):

import torchvision.models as models
model = models.vgg16(pretrained=True)

您可以使用我们描述的相同程序尝试其他预训练模型。为避免重复,我们不会在本节中包含其他架构的相同代码示例。

在下一节中,我们将讨论最流行的 CNN 架构之一,它是在 VGG 之后发布的。

了解残差网络

残差网络(ResNets , Deep Residual Learning for Image Recognition , https://arxiv.org/abs/1512.03385)于 2015 年发布,当时他们赢得了当年 ImageNet 挑战的所有五个类别。在第 1 章,神经网络的基本要素中,我们提到神经网络的层不限于顺序,而是形成一个图。这是我们将学习的第一个架构,它利用了这种灵活性。这也是第一个成功训练深度超过 100 层的网络的网络架构。

由于更好的权重初始化、新的 激活函数以及归一化层,现在可以训练深度网络。但是,该论文的作者进行了一些实验,并观察到与 20 层的网络相比,具有 56 层的网络具有更高的训练和测试错误。他们争辩说不应该是这样。理论上,我们可以采用一个浅层网络并在其上堆叠身份层(这些层的输出只是重复输入),以产生一个更深层的网络,其行为方式与浅层网络完全相同。然而,他们的实验无法与浅层网络的性能相匹配。

为了解决这个问题,他们提出了一个由残差块构成的网络。一个残差块由两个或三个顺序卷积层和一个单独的并行标识(中继器)快捷连接组成,连接第一层的输入和最后一层的输出。我们可以在下面的截图中看到三种类型的残差块:

从左到右:原始残差块;原始瓶颈残差块;预激活残差块;预激活瓶颈残差块

每个块有两条平行路径。左侧路径与我们见过的其他网络类似,由顺序卷积层 + 批量归一化组成。正确的路径包含身份快捷连接(也称为跳过连接)。两条路径通过元素总和合并。即左右张量具有相同的形状,并且将第一个张量的一个元素添加到第二个张量中相同位置的元素中。输出是与输入具有相同形状的单个张量。实际上,我们向前传播块学习的特征,但也传播原始未修改的信号。通过这种方式,我们可以更接近作者描述的原始场景。由于跳过连接,网络可以决定跳过一些卷积层,实际上减少了它自己的深度。残差块以这样一种方式使用填充,即块的输入和输出具有相同的维度。多亏了这一点,我们可以为任意深度的网络堆叠任意数量的块。

现在,让我们看看图中的块有何不同:

  • 第一个块包含两个 3 × 3 卷积层。这是原始的残差块,但如果层很宽,堆叠多个块的计算成本会很高。
  • 第二个块相当于第一个块,但它使用了所谓的瓶颈层。首先,我们使用 1 × 1 卷积对输入体积深度进行下采样(我们在第 2 章理解卷积网络”中对此进行了讨论)。然后,我们对减少的输入应用 3 × 3(瓶颈)卷积。最后,我们用另一个 1 × 1 卷积将输出扩展回所需的深度。这一层的计算成本低于第一层。
  • 第三个区块是该想法的最新版本,由同一作者于 2016 年发表(深度残差网络中的身份映射https://arxiv.org/abs/1603.05027 )。它使用预激活,批量归一化和激活函数位于卷积层之前。起初这可能看起来很奇怪,但由于这种设计,跳过连接路径可以在整个网络中不间断地运行。这与其他剩余块相反,其中至少一个激活函数位于跳过连接的路径上。堆叠的残差块的组合仍然具有正确顺序的层。
  • 第四块是第三层的瓶颈版本。它遵循与瓶颈残差​​层 v1 相同的原理。

在下表中,我们可以看到论文作者提出的网络家族:

最受欢迎的残差网络家族。残差块由圆角矩形表示

它们的一些属性如下:

  • 它们从步长为 2 的 7 × 7 卷积层开始,然后是 3 × 3 最大池化。该层还用作下采样步骤——网络的其余部分从 56 × 56 的小切片开始,而输入的切片为224×224 。
  • 网络其余部分的下采样是使用步长为 2 的修改残差块实现的。
  • 平均池化在所有残差块之后和 1,000 个单元的完全连接的 softmax 层之前对输出进行下采样。

ResNet 系列网络之所以受欢迎,不仅是因为它们的准确性,还因为它们相对简单和残差块的多功能性。正如我们所提到的,由于填充,残差块的输入和输出形状可以相同。我们可以堆叠不同配置的残差块,以解决具有广泛训练集大小和输入维度的各种问题。由于这种普遍性,我们将在下一节中实现一个 ResNet 的示例。

实现残差块

在本节中,我们将实现一个预激活 ResNet,以使用 PyTorch 1.3.1 和torchvision0.4.2 对 CIFAR-10 图像进行分类。开始吧:

1.像往常一样,我们将从导入开始。请注意,我们将使用 F PyTorch 功能模块的简写(https://pytorch.org/docs/stable/nn.html#torch-nn-functional ):

import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
from torchvision import transforms

2.接下来,让我们定义预激活常规(非瓶颈)残差块。我们将把它实现为nn.Module——所有神经网络模块的基类。让我们从类定义和__init__方法开始:

class PreActivationBlock(nn.Module):
    expansion = 1
    def __init__(self, in_slices, slices, stride=1):
        super(PreActivationBlock, self).__init__()

        self.bn_1 = nn.BatchNorm2d(in_slices)
            
                                out_channels=slices,kernel_size=3, 
                                stride=stride, padding=1,
                                bias=False)

        self.bn_2 = nn.BatchNorm2d(slices)
        self.conv_2 = nn.Conv2d(in_channels=slices, 
                                out_channels=slices,kernel_size=3, 
                                stride=1, padding=1,
                                bias=False)

        # if the input/output dimensions differ use convolution for 
        the shortcut
        if stride != 1 or in_slices != self.expansion * slices:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels=in_slices,
                          out_channels=self.expansion * slices,
                          kernel_size=1,
                          stride=stride,
                          bias=False)
            )

我们将仅在该__init__方法中定义可学习的块组件——这些组件包括卷积和批量归一化操作。另外,请注意我们实现shortcut连接的方式。如果输入维度与输出维度相同,我们可以直接使用输入张量作为快捷方式。但是,如果尺寸不同,我们必须借助 1 × 1 卷积来转换输入,该卷积具有与主路径中相同的步幅和输出通道。尺寸可能因高度/宽度 ( stride != 1) 或深度 ( in_slices != self.expansion * slices) 而不同。self.expansion是一个超参数,包含在原始 ResNet 实现中。它允许我们扩展残差块的输出深度。

3.实际的数据传播在forward方法中实现(请注意缩进,因为它是 的成员PreActivationBlock):

def forward(self, x):
    out = F.relu(self.bn_1(x))

    # reuse bn+relu in downsampling layers
    shortcut = self.shortcut(out) if hasattr(self, 'shortcut')
    else x

    out = self.conv_1(out)

    out = F.relu(self.bn_2(out))
    out = self.conv_2(out)

    out += shortcut

    return out

我们使用函数F.relu作为激活函数,因为它没有可学习的参数。然后,如果快捷连接是卷积而不是恒等式(即块的输入/输出维度不同),我们将重用F.relu(self.bn_1(x))为快捷连接添加非线性和批量归一化。否则,我们将重复输入。

4.然后,让我们实现残差块的瓶颈版本。我们将使用与非瓶颈实现相同的蓝图。我们将从类定义和__init__方法开始:

class PreActivationBottleneckBlock(nn.Module):
    expansion = 4
    def __init__(self, in_slices, slices, stride=1):
        super(PreActivationBottleneckBlock, self).__init__()

        self.bn_1 = nn.BatchNorm2d(in_slices)
        self.conv_1 = nn.Conv2d(in_channels=in_slices, 
                                out_channels=slices, kernel_size=1,
                                bias=False)

        self.bn_2 = nn.BatchNorm2d(slices)
        self.conv_2 = nn.Conv2d(in_channels=slices, 
                                out_channels=slices, kernel_size=3, 
                                stride=stride, padding=1,
                                bias=False)

        self.bn_3 = nn.BatchNorm2d(slices)
        self.conv_3 = nn.Conv2d(in_channels=slices,
                                out_channels=self.expansion * 
                                slices,
                                kernel_size=1,
                                bias=False)

        # if the input/output dimensions differ use convolution for the shortcut
        if stride != 1 or in_slices != self.expansion * slices:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels=in_slices,
                          out_channels=self.expansion * slices,
                          kernel_size=1, stride=stride,
                          bias=False)
            )

该expansion参数4在原始实现之后。self.conv_1卷积操作代表1×1下采样bottleneck连接,是self.conv_2实际卷积,self.conv_3是上采样1×1卷积。快捷机制遵循与中相同的逻辑PreActivationBlock。

5.接下来,让我们实现该PreActivationBottleneckBlock.forward方法。再一次,它遵循与中相同的逻辑PreActivationBlock:

def forward(self, x):
    out = F.relu(self.bn_1(x))

    #  reuse bn+relu in downsampling layers
    shortcut = self.shortcut(out) if hasattr(self, 'shortcut') 
    else x

    out = self.conv_1(out)

    out = F.relu(self.bn_2(out))
    out = self.conv_2(out)

    out = F.relu(self.bn_3(out))
    out = self.conv_3(out)

    out += shortcut

    return out

6.接下来,让我们自己实现残差网络。我们将从类定义(它继承nn.Module)和__init__方法开始:

class PreActivationResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        """
        :param block: type of residual block (regular or 
        bottleneck)
        :param num_blocks: a list with 4 integer values.
            Each value reflects the number of residual blocks in 
            the group
        :param num_classes: number of output classes
        """

        super(PreActivationResNet, self).__init__()

        self.in_slices = 64

        self.conv_1 = nn.Conv2d(in_channels=3, out_channels=64,
                                kernel_size=3, stride=1, padding=1,
                                bias=False)

        self.layer_1 = self._make_group(block, 64, num_blocks[0], 
        stride=1)
        self.layer_2 = self._make_group(block, 128, num_blocks[1], 
        stride=2)
        self.layer_3 = self._make_group(block, 256, num_blocks[2], 
        stride=2)
        self.layer_4 = self._make_group(block, 512, num_blocks[3], 
        stride=2)
        self.linear = nn.Linear(512 * block.expansion, num_classes)

该网络包含四组残差块,就像最初的实现一样。每组的块数由参数num_blocks指定。初始卷积使用步长为 1 的 3×3 滤波器,而不是原始实现的步长为 2 的 7×7。这是因为32×32 CIFAR-10 图像比224×224 ImageNet 图像小很多,不需要下采样。

7.然后,我们将实现PreActivationResNet._make_group创建一个残差块组的方法。组中的所有块都具有步长 1,除了第一个块,其中stride作为参数提供:

def _make_group(self, block, slices, num_blocks, stride):
    """Create one residual group"""

    strides = [stride] + [1] * (num_blocks - 1)
    layers = []
    for stride in strides:
        layers.append(block(self.in_slices, slices, stride))
        self.in_slices = slices * block.expansion

    return nn.Sequential(*layers)

8.接下来,我们将实现PreActivationResNet.forward通过网络传播数据的方法。我们可以看到全连接最后一层之前的下采样平均池化:

def forward(self, x):
    out = self.conv_1(x)
    out = self.layer_1(out)
    out = self.layer_2(out)
    out = self.layer_3(out)
    out = self.layer_4(out)
    out = F.avg_pool2d(out, 4)
    out = out.view(out.size(0), -1)
    out = self.linear(out)

    return out

9.一旦我们完成了网络,我们就可以实现几个 ResNet 配置。以下是ResNet3434 个卷积层,按[3, 4, 6, 3]非瓶颈残差块分组:

def PreActivationResNet34():
    return PreActivationResNet(block=PreActivationBlock,
                               num_blocks=[3, 4, 6, 3])

10.最后,我们可以训练网络。我们将从定义训练和测试数据集开始。我们不会详细介绍实现,因为我们已经在第 2 章理解卷积网络”中研究了类似的场景。我们将通过用四个像素填充样本来增加训练集,然后我们将随机抽取 32 × 32 个裁剪。以下是实现:

# training data transformation
transform_train = transforms.Compose([
    transforms.RandomCrop(32, padding=4),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4821, 0.4465), (0.2470, 0.2435, 
    0.2616))
])

# training data loader
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, 
                                        transform=transform_train)

train_loader = torch.utils.data.DataLoader(dataset=train_set, 
                                        batch_size=100,
                                        shuffle=True, 
                                        num_workers=2)

# test data transformation
transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4821, 0.4465), (0.2470, 0.2435, 
    0.2616))
])

# test data loader
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                        download=True, 
                                        transform=transform_test)

test_loader = torch.utils.data.DataLoader(dataset=testset, 
                                        batch_size=100,
                                        shuffle=False,
                                        num_workers=2)

11.然后,我们将实例化网络模型和训练参数——交叉熵损失和 Adam 优化器:

# load the pretrained model
model = PreActivationResNet34()

# select gpu 0, if available
# otherwise fallback to cpu
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# transfer the model to the GPU
model = model.to(device)

# loss function
loss_function = nn.CrossEntropyLoss()

# We'll optimize all parameters
optimizer = optim.Adam(model.parameters())

12.我们现在可以训练网络的EPOCHSepochs。、train_model、test_model和函数与我们在第 2 章理解卷积网络的PyTorch 实现迁移学习部分中plot_accuracy定义的相同,这里不再赘述。以下是代码:

# train
EPOCHS = 15

test_acc = list()  # collect accuracy for plotting
for epoch in range(EPOCHS):
    print('Epoch {}/{}'.format(epoch + 1, EPOCHS))

    train_model(model, loss_function, optimizer, train_loader)
    _, acc = test_model(model, loss_function, test_loader)
    test_acc.append(acc)

plot_accuracy(test_acc)

而且,在下图中,我们可以看到 15 次迭代的测试准确度(训练可能需要一段时间):

ResNet34 CIFAR 15 个 epoch 的准确率

在本节中,我们讨论了各种类型的 ResNet,然后我们使用 PyTorch 实现了一种。在下一节中,我们将讨论 Inception 网络——又一个网络家族,它将并行连接的使用提升到一个新的水平。

了解 Inception 网络

Inception 网络(Going Deeper with Convolutionshttps: //arxiv.org/abs/1409.4842)于 2014 年推出,当时他们赢得了当年的 ImageNet 挑战赛 (这里似乎有一种模式)。从那时起,作者发布了架构的多个改进(版本)。

有趣的事实:盗梦空间这个名字部分来自我们需要更深入的互联网模因,与电影盗梦空间有关。

Inception 网络背后的想法是从图像中的对象具有不同比例的基本前提开始的。远处的物体可能会占据图像的一小部分区域,但同一个物体一旦更近,可能会占据图像的大部分。这给标准 CNN 带来了困难,因为不同层中的神经元在输入图像上具有固定的感受野大小。常规网络可能是特定规模物体的良好检测器,但否则可能会错过它们。为了解决这个问题,论文的作者 提出了一种新颖的架构:由 Inception 块组成的架构。Inception 块从一个公共输入开始,然后将其拆分为不同的并行路径(或塔)。每条路径都包含具有不同大小过滤器的卷积层或池化层。通过这种方式,我们在相同的输入数据上应用不同的感受野。在 Inception 块的末尾,不同路径的输出被连接起来。在接下来的几节中,我们将讨论 Inception 网络的不同变体。

 Inception v1

下图显示了 Inception 块的第一个版本,它是 GoogLeNet 网络架构的一部分 ( https://arxiv.org/abs/1409.4842 )。GoogLeNet 包含九个这样的 Inception 块:

Inception v1 块,灵感来自 https://arxiv.org/abs/1409.4842

v1 块有四个路径:

  • 1 × 1卷积,作为输入的一种中继器
  • 1 × 1 卷积,然后是 3 × 3 卷积
  • 1 × 1 卷积,然后是 5 × 5 卷积
  • 步幅为 1 的3 × 3 最大池化

块中的层以这样一种方式使用填充,即输入和输出具有相同的形状(但深度不同)。填充也是必要的,因为每条路径都会产生不同形状的输出,具体取决于过滤器的大小。这对所有版本的 Inception 块都有效。

这个 Inception 块的另一个主要创新是使用下采样 1 × 1 卷积。它们是必需的,因为所有路径的输出被连接起来以产生块的最终输出。连接的结果是具有四倍深度的输出。如果另一个 Inception 块跟随当前块,它的输出深度将再次翻两番。为了避免这种指数增长,block 使用 1 × 1 卷积来减少每条路径的深度,这反过来又降低了 block 的输出深度。这使得创建更深层次的网络成为可能,而不会耗尽资源。

GoogLeNet 还利用了辅助分类器——也就是说,它在不同的中间层有两个额外的分类输出(具有相同的 groundtruth 标签)。在训练过程中,损失的总值是辅助损失和真实损失的加权和。

Inception v2 and v3

Inception v2 和 v3 一起发布, 并提出了对原始 Inception 块的一些改进(重新思考计算机视觉的 Inception 架构https://arxiv.org/abs/1512.00567 )。第一个是将 5 × 5 卷积分解为两个堆叠的 3 × 3 卷积。我们在Visual Geometry Group 简介部分讨论了这样做的优点。

我们可以在下图中看到新的 Inception 块:

起始区块 A,灵感来自 https://arxiv.org/abs/1512.00567

下一个改进是将× n卷积分解为两个堆叠的不对称1 × n× 1卷积。例如,我们可以将单个 3 × 3 卷积拆分为两个 1 × 3 和 3 × 1 卷积,其中 3 × 1 卷积应用于 1 × 3 卷积的输出。在第一种情况下,过滤器大小为 3*3 = 9,而在第二种情况下,我们将有 (3*1) + (1*3) = 3 + 3 = 6 的组合大小,结果为 33 % 效率,如下图所示:

 
在 1 × 3 和 3 × 1 卷积中分解 3 × 3卷积。灵感来自https://arxiv.org/abs/1512.00567

作者介绍了两个利用分解卷积的新块。这些块中的第一个(以及总共第二个)相当于 Inception 块 A:

Inception block B。当n=3时,相当于block A。灵感来自https://arxiv.org/abs/1512.00567

第二个(总共第三个)块是相似的,但不对称卷积是平行的,导致更高的输出深度(更多的连接路径)。这里的假设是网络具有的特征(不同的过滤器)越多,它的学习速度就越快(我们还在第 2 章理解卷积网络”中讨论了对更多过滤器的需求)。另一方面,更宽的层需要更多的内存和计算时间。作为折衷方案,此块仅用于网络的较深部分,在其他块之后:

Inception 块 C,灵感来自https://arxiv.org/abs/1512.00567

使用这些新块,作者提出了两个新的 Inception 网络:v2 和 v3。此版本的另一个 主要改进是使用 了由同一作者引入的批量标准化。

Inception v4 和 Inception-ResNet

在 Inception 网络的最新版本中, 作者引入了三个新的流线型 Inception 块,它们建立在先前版本的思想之上(Inception-v4、Inception-ResNet 和残差连接对学习的影响https://arxiv.org/绝对/1602.07261 )。他们引入了 7 × 7 非对称分解卷积和平均池化而不是最大池化。更重要的是,他们创建了一个称为 Inception-ResNet 的残差/Inception 混合网络,其中 Inception 块还包括残差连接。我们可以在下图中看到一个这样的块的示意图:

带有残差跳过连接的 Inception 块

在本节中,我们讨论了不同类型的 Inception 网络以及各种 Inception 模块中使用的不同原理。接下来,我们将讨论一种更新的 CNN 架构,它将 Inception 概念提升到一个新的深度(或应有的宽度)。

介绍 Xception

到目前为止,所有 Inception 模块都是从将输入分成几个并行路径开始的。每条路径都继续进行降维的1×1 跨通道卷积,然后是常规的跨通道卷积。一方面,1×1 连接映射跨通道相关性,但不映射空间相关性(因为 1×1 滤波器大小)。另一方面,随后的跨通道卷积映射两种类型的相关性。让我们回想一下,在第 2 章理解卷积网络”中,我们介绍了深度可分离卷积DSC ),它结合了以下两个操作:

  • 深度卷积:在深度卷积中,单个输入切片产生单个输出切片,因此它仅映射空间(而不是跨通道)相关性。
  • 1×1 跨通道卷积:对于 1×1 卷积,我们有相反的情况,即它们仅映射跨通道相关性。

Xception(Xception: Deep Learning with Depthwise Separable Convolutionshttps ://arxiv.org/abs/1610.02357 )的作者认为,事实上,我们可以将DSC视为 Inception 块的极端(因此得名)版本,其中每个深度输入/输出切片对代表一个并行路径。我们有与输入切片数量一样多的并行路径。下图显示了一个简化的 Inception 模块及其到 Xception 模块的转换:

左:简化的 Inception 模块。右:异常块。灵感来自 https://arxiv.org/abs/1610.02357

Xception 块和 DSC 有两个不同之处:

  • 在 Xception 中,1 × 1 卷积首先出现,而不是 DSC 中的最后。但是,这些操作无论如何都是要堆叠的,我们可以假设顺序没有意义。
  • Xception 块在每次卷积后使用 ReLU 激活,而 DSC 在跨通道卷积后不使用非线性。根据作者的实验,没有非线性深度卷积的网络收敛速度更快,也更准确。

下图描述了 Xception 网络的架构:

从左到右:进入流程;中流,重复八次;退出流程。来源:https://arxiv.org/abs/1610.02357

它由线性堆叠的 DSC 构成,其一些特性如下:

  • 该网络包含 36 个卷积层,分为 14 个模块,除了第一个和最后一个模块外,所有模块周围都有线性残差连接。这些模块分为三个连续的虚拟流——入口、中间和出口。
  • 在入口和出口流中使用 3 × 3 最大池化进行下采样;中间流没有下采样;全连接层之前的全局平均池化。
  • 所有卷积和 DSC 之后都是批量归一化。
  • 所有 DSC 的深度乘数均为 1(无深度扩展)。

本节总结了一系列基于 Inception 的模型。在下一节中,我们将关注一个特殊模型,它优先考虑占用空间小和计算效率。

介绍 MobileNet

在本节中,我们将讨论一个称为 MobileNet 的轻量级 CNN 模型(MobileNetV2:Inverted Residuals 和线性瓶颈https ://arxiv.org/abs/1801.04381 ​​)。我们将专注于这个想法的第二次修订(MobileNetV1 是在MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications中介绍的,https ://arxiv.org/abs/1704.04861 )。

MobileNet 针对的是内存和计算能力有限的设备,例如手机(这个名字有点暴露了它)。为了减少其占用空间,该网络使用 DSC、线性瓶颈和倒置残差。

我们已经熟悉 DSC,所以让我们讨论另外两个:

  • 线性瓶颈:为了理解这个概念,我们将引用论文:
“考虑一个由nL i组成的深度神经网络,每个层都有一个维度为 的激活张量 。在本节中,我们将讨论这些激活张量的基本属性,我们将把它们视为具有i 的“像素”容器尺寸。非正式地,对于一组真实图像的输入,我们说层激活集(对于任何层i)形成“感兴趣的流形”。长期以来,人们一直假设神经网络中感兴趣的流形可以嵌入在低维子空间中。换句话说,当我们查看所有单个d- 深度卷积层的通道像素,这些值中编码的信息实际上位于某个流形中,可以嵌入到低维子空间中。”

一种方法是使用1×1瓶颈卷积。但是,该论文的作者认为,如果这种卷积之后是 ReLU 等非线性,这可能会导致流形信息的丢失。如果ReLU输入大于0,那么这个单元的输出就相当于输入的线性变换。但是,如果输入更小,那么 ReLU 就会崩溃,并且该单元的信息会丢失。正因为如此,MobileNet 使用 1×1 瓶颈卷积,没有非线性激活。

  • 倒置残差:在残差网络部分,我们介绍了瓶颈残差块,其中非捷径路径中的数据流为输入 -> 1 × 1bottleneck conv -> 3 × 3conv -> 1 × 1 unsampling conv。换句话说,它遵循宽 -> 窄 -> 宽数据表示。作者认为,瓶颈实际上包含所有必要的信息,而扩展层仅充当伴随张量非线性变换的实现细节。因此,他们建议在瓶颈连接之间建立快捷连接。

基于这些属性,MobileNet 模型由以下构建块组成:

顶部:步幅为 1 的倒置残差块。底部:步幅为 2 的块

该模型使用 ReLU6 非线性: ReLU6 = min(max(input, 0),6)。最大激活值限制为 6——这样,非线性在低精度浮点计算中更加鲁棒。这是因为6 最多可以占用 3 位,剩下的留给数字的浮点部分。

除了步幅之外,块还由扩展因子t来描述,它决定了瓶颈卷积的扩展率。

下表显示了块的输入和输出维度之间的关系:

输入和输出维度关系。来源:https://arxiv.org/abs/1801.04381

在上表中,hw是输入的高度和宽度,s是步幅,kk'是输入和输出的通道数。

最后,这是完整的模型架构:

                 MobileNetV2 架构。来源:https://arxiv.org/abs/1801.04381

每行描述一组一个或多个相同的块,重复n次。同一组中的所有层都具有相同数量的输出通道c。每个序列的第一层都有一个步幅s,所有其他层都使用步幅 1。所有空间卷积都使用 3 × 3 内核。扩展因子t始终应用于输入大小,如上表中所述。

我们将讨论的下一个模型是具有新型构建块的网络模型,其中所有层都是互连的。

DenseNets 简介

DenseNet ( Densely Connected Convolutional Networkshttps: //arxiv.org/abs/1608.06993 )试图缓解梯度消失问题并改善特征传播,同时减少网络参数的数量。我们已经看到 ResNets 如何引入带有跳跃连接的残差块来解决这个问题。DenseNets 从这个想法中获得了一些灵感,并通过引入密集块将其进一步发展。密集块由顺序卷积层组成,其中任何层都与所有后续层直接连接。换句话说,网络层l将接收来自所有先前网络层的输入l

                                

这里,是前面网络层的连接输出特征图。这与ResNet 不同,在 ResNet 中,我们将不同的层与 element-wi se sum 结合起来。H l是一个复合函数,它定义了三种类型的 DenseNet 块(只显示了两种):

密集块:降维层(虚线)是 DenseNet-B 架构的一部分,而 DenseNet-A 没有。不显示 DenseNet-C

让我们定义它们:

  • DenseNet-A:这是基本块,其中H l由批归一化、激活和 3 × 3 卷积组成:

  • DenseNet-B:作者还介绍了第二种密集块 DenseNet-B,它在每次连接后应用降维 1×1 卷积:

  • DenseNet-C:进一步修改,在每个密集块之后添加一个下采样 1 × 1 卷积。B 和 C 的组合称为 DenseNet-BC。

密集块由其卷积层数和每层的输出体积深度指定,在本文中称为增长率。假设密集块的输入的体积深度为k 每个卷积层的输出体积深度为k。然后,由于连接,第 l 层的输入体积深度将为+ k x (l - 1)。尽管密集块的后面层具有较大的输入体积深度(由于许多连接),但 DenseNets 可以使用低至 12 的增长率值,这减少了参数的总数。为了理解为什么会这样,让我们​​将特征图视为网络的集体知识(或全局状态)。每一层都将自己的k个特征图添加到该状态,增长率决定了该层为其贡献的信息量。由于结构密集,可以从网络内的任何地方访问全局状态(因此称为全局)。换句话说,不需要像在传统网络架构中那样从一层复制到下一层,这使我们可以从较少数量的特征图开始。

为了使连接成为可能,密集块以这样一种方式使用填充,即所有输出切片的高度和宽度在整个块中都是相同的。但正因为如此,在密集块内不可能进行下采样。因此,密集网络由多个连续密集块组成,由下采样池化操作分隔。

该论文的作者提出了一个 DenseNets 家族,其整体架构类似于 ResNet:

DenseNet 网络家族。来源:https://arxiv.org/abs/1608.06993

它们具有以下特性:

  • 从 7 × 7 步长 2 下采样卷积开始。
  • 步幅为 2的进一步下采样 3 × 3 最大池化。
  • 四组 DenseNet-B 块。网络家族的不同之处在于每组中密集块的数量。
  • 下采样由 2 × 2 池化操作的过渡层处理,密集组之间的步长为 2。
  • 过渡层包含进一步的 1 × 1 瓶颈卷积,以减少特征图的数量。该卷积的压缩比由超参数θ指定,其中0 < θ ≤ 1。如果输入特征图的数量是m,那么输出特征图的数量是θm。
  • 密集块最终得到一个 7 × 7 的全局平均池化,然后是一个 1,000 单元的全连接 softmax 层。

DenseNet 的作者还发布了一个改进的 DenseNet 模型,称为 MSDNet(Multi-Scale Dense Networks for Resource Efficient Image Classificationhttps: //arxiv.org/abs/1703.09844 ),它(顾名思义)使用多尺度密集块。

通过 DenseNet,我们结束了关于传统 CNN 架构的讨论。在下一节中,我们将讨论是否可以自动化寻找最佳 NN 架构的过程。

神经架构搜索的工作原理

到目前为止,我们讨论的 NN 模型是由他们的作者设计的。但是,如果我们可以让计算机自己设计神经网络呢?进入神经架构搜索NAS)——一种自动化神经网络设计的技术。

在我们继续之前,让我们看看网络架构是由什么组成的:
  • 操作图,代表网络。正如我们在第 1 章神经网络的基本要素”中所讨论的,这些操作包括(但不限于)卷积、激活函数、全连接层、归一化等。
  • 每个操作的参数。例如,卷积参数是:类型(跨通道、深度等)、输入维度、输入和输出切片的数量、步幅和填充。
这组架构参数是 NN ML 算法的所有超参数的子集。其他参数包括学习率、小批量大小、优化算法(例如,Adam 或 SGD)。因此,我们可以将 NAS 视为一种超参数优化问题(为 ML 算法选择最优超参数的任务)。超参数优化本身是自动化机器学习 (AutoML) 的组成部分之一。这是一个更广泛的过程,旨在自动化 ML 解决方案的所有步骤。一个n AutoML 算法将首先选择算法类型(例如,决策树或 NN),然后对所选算法进行超参数优化。由于我们的书专注于神经网络,我们假设我们已经选择了算法(多么方便!),现在我们要设计我们的网络。

在本节中,我们将讨论基于梯度的 NAS 和强化学习(Neural Architecture Search with Reinforcement Learninghttps://arxiv.org/abs/1611.01578 )。在这一点上,我们不会讨论强化学习,而是专注于算法。它以我们可以将网络定义表示为字符串(标记序列)的前提开始。假设我们将生成一个仅包含卷积的顺序 CNN。

然后,部分字符串定义将如下所示:

 我们不必指定层类型,因为我们只使用卷积。 为了简单起见,我们排除了填充。为了清楚起见,第一行的下标文本被包含在内,但不会包含在算法版本中。

我们可以在下图中看到算法概述:

NAS 概述。来源:https://arxiv.org/abs/1611.01578

让我们从控制器开始。它是一个 RNN,其任务是生成新的网络架构。虽然我们还没有讨论 RNN(这个荣誉要到第 7 章理解循环网络),但我们还是会尝试解释它是如何工作的。在第 1 章神经网络的基本要素”中,我们提到 RNN 维护一个内部状态——它所有先前输入的摘要。基于该内部状态和最新的输入样本,网络生成一个新的输出,更新其内部状态,并等待下一个输入。

在这里,控制器将生成描述网络架构的字符串序列。控制器输出是序列的单个标记。这可以是过滤器高度/宽度、步幅宽度/高度或输出过滤器的数量。令牌的类型取决于当前生成的架构的长度。一旦我们有了这个令牌,我们就把它作为输入反馈给 RNN 控制器。然后,网络生成序列的下一个标记。

此过程如下图所示:

         使用 RNN 控制器生成网络架构。输出令牌作为输入反馈给控制器以生成下一个令牌。来源:https://arxiv.org/abs/1611.01578

图中的白色垂直方块代表 RNN 控制器,它由两层长短期记忆LSTM ) 单元(沿y轴)组成。尽管该图显示了 RNN 的多个实例(沿x轴),但它实际上是同一个网络;它只是及时展开,代表序列生成的过程。也就是说,沿x轴的每一步都代表网络定义的单个标记。步骤t的令牌预测由 softmax 分类器执行,然后在步骤t+1作为控制器输入。我们继续这个过程,直到生成的网络长度达到某个值。最初,这个值很小(一个短的网络),但随着训练的进行它逐渐增加(一个更长的网络)。

为了更好地理解 NAS,让我们看一下算法的逐步执行:

  1. 控制器生成新架构A
  2. 它使用所述架构构建和训练一个新网络,直到它收敛。
  3. 它在训练集的保留部分上测试新网络并测量误差R。
  4. 它使用此误差来更新控制器参数θ c。由于我们的控制器是 RNN,这意味着训练网络并调整其权重。模型参数以减少未来架构的误差R的方式更新。这是通过称为 REINFORCE 的强化学习算法实现的,这超出了本节的范围。
  5. 它重复这些步骤,直到生成的网络的误差R低于某个阈值。

控制器可以生成具有一些限制的网络架构。正如我们在本节前面提到的,最严重的是生成的网络仅由卷积层组成。为简化起见,每个卷积层都自动包含批量归一化和 ReLU 激活。但从理论上讲,控制器可以生成更复杂的架构,包括池化或标准化等其他层。我们可以通过在层类型的架构序列中添加额外的控制器步骤来实现这一点。

该论文的作者实现了一种技术,允许我们向生成的架构添加剩余的跳过连接。它与称为锚点的特殊类型的控制器步骤一起使用。第N层的锚点具有基于内容的 sigmoid。sigmoid j (j = 1, 2, 3, ..., N-1)的输出表示当前层与层j有残差连接的概率。

修改后的控制器如下图所示:

带有用于残差连接的锚点的 RNN 控制器。来源:https://arxiv.org/abs/1611.01578

如果一层有许多输入层,则所有输入都沿通道(深度)维度连接。跳过连接可能会在网络设计中产生一些问题:

  • 网络的第一个隐藏层(即未连接到任何其他输入层的隐藏层)使用输入图像作为输入层。
  • 在网络的最后,所有未连接的层输出被连接在一个最终的隐藏状态,该隐藏状态被发送到分类器。
  • 可能会发生要连接的输出具有不同大小的情况。在这种情况下,较小的特征图用零填充以匹配较大的特征图的大小。

在他们的实验中,作者使用了一个带有 2 层 LSTM 单元的控制器,每层有 35 个单元。对于每个卷积,控制器必须从值 {1, 3, 5, 7} 中选择一个过滤器的高度和宽度,并且过滤器的数量是 {24, 36, 48, 64} 之一。此外,他们还进行了两组实验——一组允许控制器在 {1,2,3} 中选择步幅,另一组固定步幅为 1。

一旦控制器生成架构,新网络将在 CIFAR-10 数据集的 45,000 张图像上训练 50 个 epoch。其余 5,000 张图像用于验证。在训练期间,控制器从 6 层的架构深度开始,然后在每 1,600 次迭代中将深度增加 2 层。表现最好的模型的验证准确率为 3.65%。它是在使用 800 个 GPU 的 12,800 个架构之后发现的(哇!)。这些高计算要求的原因是每个新网络都是从头开始训练的,只是为了产生一个准确度值,然后可以用来训练控制器。最近,新的 ENAS 算法(Efficient Neural Architecture Search via Parameter Sharinghttps://arxiv.org/abs/1802.03268)通过在生成的模型之间共享权重,可以显着减少 NAS 的计算资源。

在下一节中,我们将讨论一种新型的 NN,它试图克服我们目前讨论的 CNN 的一些限制。

介绍胶囊网络(capsule networks)

胶囊网络(胶囊之间的动态路由https://arxiv.org/abs/1710.09829 被引入作为克服标准 CNN 的一些限制的方法。要理解胶囊网络背后的想法,我们首先需要了解它们的局限性,我们将在下一节中看到

卷积网络的局限性

让我们从 Hinton 教授自己的一句话开始:

“卷积神经网络中使用的池化操作是一个大错误,它运行良好的事实是一场灾难。”

正如我们在第 2 章理解卷积网络”中提到的,CNN 是平移不变的。让我们 想象一张有一张脸的图片,位于图片的右半部分。平移不变性意味着 CNN 非常擅长告诉我们图片包含人脸,但它无法告诉我们人脸是在图像的左侧还是右侧。这种行为的罪魁祸首是池化层。每个池化层都引入了一点平移不变性。例如,最大池化路由仅转发一个输入神经元的激活,但后续层不知道路由哪个神经元。

通过堆叠多个池化层,我们逐渐增加了感受野大小。但是,检测到的对象可能位于新感受野中的任何位置,因为没有一个池化层中继此类信息。因此,我们也增加了平移不变性。起初,这似乎是件好事,因为最终的标签必须是平移不变的。但是,它带来了一个问题,因为 CNN 无法识别一个对象相对于另一个对象的位置。CNN 会将以下两幅图像都识别为人脸,因为它们都包含人脸的组成部分(鼻子、嘴巴和眼睛),而与彼此的相对位置无关。

这也称为毕加索问题,如下图所示:

卷积网络会将这两个图像都识别为人脸

但是,这还不是全部。即使面部有不同的方向,CNN也会感到困惑,例如,如果它被颠倒过来。克服这个问题的一种方法是在训练期间使用数据增强(旋转)。但是,这仅显示了网络的局限性。我们必须明确地显示不同方向的对象,并告诉 CNN 这实际上是同一个对象。

到目前为止,我们已经看到 CNN 丢弃了平移信息(过渡不变性)并且不理解对象的方向。在计算机视觉中,平移和方向的组合称为姿态。姿势足以在坐标系中唯一标识对象的属性。让我们用计算机图形来说明这一点。一个 3D 对象,比如一个立方体,完全由它的姿势和边长定义。将 3D 对象的表示转换为屏幕上的图像的过程称为渲染。只知道它的姿势和立方体的边缘长度,我们就可以从我们喜欢的任何角度渲染它。

因此,如果我们能够以某种方式训练一个网络来理解这些属性,我们就不必向它提供同一对象的多个增强版本。CNN 无法做到这一点,因为其内部数据表示不包含有关对象姿势的信息(仅有关其类型)。相比之下,胶囊网络保留了对象的类型和姿势的信息。因此,它们可以检测到可以相互转换的对象,这就是所谓的等方差。我们也可以将其视为反向图形,即根据其渲染图像重建对象的属性。

为了解决这些问题,论文的作者提出了一种新型的网络构建块,称为胶囊,而不是神经元。让我们在下一节讨论它。

 Capsules(胶囊)

胶囊的输出是一个向量,而神经元的输出 是一个标量值。胶囊输出向量具有以下含义:

  • 向量的元素表示对象的姿势和其他属性。
  • 向量的长度在 (0, 1) 范围内,表示在该位置检测到特征的概率。提醒一下,向量的长度是,其中vi是向量元素。

 让我们考虑一个检测人脸的胶囊。如果我们开始在图像上移动人脸,胶囊向量的值将发生变化以反映位置的变化。但是,它的长度将始终保持不变,因为面部的概率不会随位置而变化。

胶囊被组织在相互连接的层中,就像常规网络一样。 一层中的胶囊用作下一层胶囊的输入。而且,就像 CNN 一样,较浅的层检测基本特征,而较深的层将它们组合成更抽象和更复杂的特征。但现在,胶囊还传递位置信息,而不仅仅是检测到的物体。这使得更深的胶囊不仅可以分析特征的存在,还可以分析它们的关系。例如,胶囊层可以检测嘴、脸、鼻子和眼睛。随后的胶囊层将不仅能够验证这些特征的存在,还能够验证它们是否具有正确的空间关系。只有当这两个条件都为真时,后续层才能验证人脸是否存在。这是胶囊网络的高级概述。现在,让我们看看胶囊究竟是如何工作的。

我们可以在以下屏幕截图中看到胶囊的示意图:

一个胶囊

我们分以下几步来分析:

  1. 胶囊输入是前一层胶囊的输出向量1 , 2 , ... n
  2. 我们将每个向量i与其对应的权重矩阵ij相乘,以产生预测向量, 。权重矩阵W编码来自前一层的胶囊的低层特征与当前层的高层特征之间的空间和其他关系。例如,假设当前层的胶囊检测人脸,前一层的胶囊检测嘴(1)、眼睛(2)和鼻子(3)。然后,  是人脸的预测位置,给定嘴巴的位置。同理,根据检测到的眼睛位置预测人脸位置,根据鼻子位置预测人脸位置。如果所有三个较低级别的胶囊向量都在同一位置上一致,则当前胶囊可以确信确实存在人脸。我们仅在此示例中使用了位置,但向量可以编码特征之间的其他类型的关系,例如比例和方向。权重W是通过反向传播学习的。 
  3. 接下来,我们将向量乘以标量耦合系数ij。除了权重矩阵之外,这些系数是一组单独的参数。它们存在于任何两个胶囊之间,并指示哪些高级胶囊将从低级胶囊接收输入。但是,与通过反向传播调整的权重矩阵不同,耦合系数是在前向传播期间通过称为动态路由的过程动态计算的。我们将在下一节中描述它。
  4. 然后,我们执行加权输入向量的总和。这一步类似于神经元中的加权和,但使用向量:

                                            

         5.最后,我们将通过压缩向量j来计算胶囊的输出j。在这种情况下,压缩意味着以这样一            种方式变换向量,使其长度在 (0, 1) 范围内,而不改变其方向。如前所述,胶囊向量的长度           表示检测到的特征的概率,将其压缩在 (0, 1) 范围内反映了这一点。为此,作者提出了一个           新公式:

                        ​​​​​​​                

现在我们知道了胶囊的结构,在下一节中,我们将描述计算不同层胶囊之间的耦合系数的算法。也就是说,它们之间传递信号的机制。

动态路由

让我们描述 计算耦合系数ij的动态路由过程,如下图所示:

动态路由示例。分组的点表示彼此一致的较低级别的胶囊

我们有一个较低级别的胶囊I,它必须决定是否将其输出发送到两个较高级别的胶囊JK之一。暗点和亮点表示预测向量,JK已经从其他较低级别的胶囊中接收到。从I胶囊到JK胶囊的箭头指向IJK的预测向量. 聚集的预测向量(较浅的点)表示在高级特征方面彼此一致的低级胶囊。例如,如果K胶囊描述了一张脸,那么聚类预测将指示较低级别的特征,例如嘴巴、鼻子和眼睛。相反,分散(较暗)的点表示不同意。如果I胶囊预测车辆轮胎,它将与K中的聚类预测不一致。

 但是,如果J中的聚类预测代表前灯、挡风玻璃或挡泥板等特征,那么I的预测将与它们一致。较低级别的胶囊有一种方法可以确定它们是否属于每个较高级别胶囊的集群或分散组。如果它们落入集群组,它们将增加与该胶囊的相应耦合系数,并将其向量路由到该方向。反之,如果它们落入分散组,则系数会减小。

让我们通过作者介绍的逐步算法将这些知识形式化:

  1. 对于l层中的所有i个胶囊和(l + 1)层中的j个胶囊,我们将初始化,其中ij是等效于ij的临时变量。所有b ij的向量表示为i。在算法开始时,第i个胶囊有相同的机会将其输出路由到(l + 1)层的任何胶囊。 

  1. 重复r次迭代,其中r是一个参数:
    1. 对于l层中的所有i个胶囊: . 胶囊的所有输出耦合系数i的总和为 1(它们具有概率性质),因此是 softmax。 
    2. 对于(l + 1)层中的所有j个胶囊: . 也就是说,我们将计算(l + 1)层的所有非压缩输出向量。 
    3. 对于(l + 1)层中的所有j个胶囊,我们将计算压缩向量
    4. 对于l层中的所有i个胶囊,以及(l + 1)层中的j个胶囊:. 这里,是低级i胶囊的预测向量和高级j胶囊向量的输出向量的点积。如果点积很高,那么第i个胶囊与其他低级胶囊一致,将它们的输出路由到第j个胶囊,并且耦合系数增加。 

    作者最近发布了一种更新的动态路由算法,该算法使用一种称为期望最大化的聚类技术。您可以在原始论文Matrix capsules with EM routing ( https://ai.google/research/pubs/pub46653 ) 中阅读更多相关信息。

胶囊网络的结构

在本节中,我们将描述 胶囊网络的结构,作者用它来对 MNIST 数据集进行分类。网络的输入是28 × 28的MNIST灰度图,步骤如下:

  1. 我们将从具有 256 个 9 × 9 过滤器、步幅 1 和 ReLU 激活的单个卷积层开始。输出体积的形状是 (256, 20, 20)。
  2. 我们还有另一个卷积层,有 256 个 9 × 9 滤波器和步长 2。输出体积的形状是 (256, 6, 6)。
  3. 使用层的输出作为第一个胶囊层的基础,称为PrimaryCaps. 取 (256, 6, 6) 输出体积并将其分成 32 个单独的 (8, 6, 6) 块。也就是说,32 个块中的每一个都包含 8 个 6 × 6 切片。从每个切片中获取一个具有相同坐标的激活值,并将这些值组合到一个向量中。例如,我们可以获取切片 1 的激活 (3, 7),切片 2 的 (3, 7) 等等,并将它们组合成一个长度为 8 的向量。我们将拥有 36 个这些向量。然后,我们将每个向量转换为一个胶囊,总共 36 个胶囊。该PrimaryCaps层的输出体积的形状为 (32, 8, 6, 6)。
  1. 第二个胶囊层称为DigitCaps。它包含 10 个胶囊(每个数字 1 个),其输出是一个长度为 16 的向量。该层的输出体积的形状为 (10, 16)。在推理过程中,我们计算每个胶囊向量的长度。然后我们将具有最长向量的胶囊作为网络的预测结果。 DigitCapsDigitCaps
  2. 在训练期间,网络在 之后包括三个额外的全连接层DigitCaps,其中最后一个有 784 个神经元 (28 × 28)。在前向训练过程中,最长的胶囊向量作为这些层的输入。他们尝试从该向量开始重建原始图像。然后,将重建的图像与原始图像进行比较,差异作为反向传播的额外正则化损失。

胶囊网络是一种新的、有前途的计算机视觉方法。但是,它们还没有被广泛采用,并且在本书讨论的任何深度学习库中都没有正式的实现,但是您可以找到多个第三方实现。

概括

在本章中,我们讨论了一些流行的 CNN 架构:我们从经典的 AlexNet 和 VGG 开始。然后,我们特别关注了 ResNets,它是最著名的网络架构之一。我们还讨论了 Inception 网络的各种化身以及与之相关的 Xception 和 MobileNetV2 模型。我们还谈到了神经架构搜索令人兴奋的新机器学习领域。最后,我们讨论了胶囊网络——一种新型的 CV 网络,它试图克服一些固有的 CNN 限制。

我们已经在第 2 章理解卷积网络”中了解了如何应用这些模型,我们在迁移学习场景中使用了 ResNet 和 MobileNet 来完成分类任务。在下一章中,我们将看到如何将其中的一些应用到更复杂的任务中,例如 对象检测和图像分割。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sonhhxg_柒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值