使用重复元素的网络(VGG)

AlexNet 在 LeNet 的基础上增加了三个卷积层。但作者对它们的卷积窗口、通道数和构造顺序均做了大量的调整。虽然 AlexNet 指明了深度卷积神经网络可以取得很高的结果,但并没有提供简单的规则来告诉后来的研究者如何设计新的网络。我们将在接下来数个小节里介绍几种不同的网络设计思路。

本节我们介绍 VGG [1],它名字来源于论文作者所在实验室 Visual Geometry Group。VGG 提出了可以通过重复使用简单的基础块来构建深层模型。

VGG 块

VGG 模型的基础组成规律是:连续使用数个相同的填充为 1 的 \(3\times 3\) 卷积层后接上一个步幅为 2 的 \(2\times 2\) 最大池化层。卷积层保持输入高宽,而池化层则对其减半。我们使用 vgg_block 函数来实现这个基础块,它可以指定使用卷积层的数量和其输出通道数。

In [1]:
import sys
sys.path.append('..')
import gluonbook as gb
from mxnet import nd, init, gluon
from mxnet.gluon import loss as gloss, nn

def vgg_block(num_convs, num_channels):
    blk = nn.Sequential()
    for _ in range(num_convs):
        blk.add(nn.Conv2D(
            num_channels, kernel_size=3, padding=1, activation='relu'))
    blk.add(nn.MaxPool2D(pool_size=2, strides=2))
    return blk

VGG 模型

VGG 网络同 AlexNet 和 LeNet 一样是由卷积层模块后接全连接层模块构成。卷积层模块串联数个 vgg_block,其超参数由 conv_arch 定义,用于指定每个块里卷积层个数和输出通道数。全连接模块则跟 AlexNet 一样。

现在我们构造一个 VGG 网络。它有 5 个卷积块,前三块使用单卷积层,而后两块使用双卷积层。第一块的输出通道是 64,之后每次对输出通道数翻倍。因为这个网络使用了 8 个卷积层和 3 个全连接层,所以经常被称为 VGG 11。

In [2]:
conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))

下面我们根据架构实现 VGG 11。

In [3]:
def vgg(conv_arch):
    net = nn.Sequential()
    # 卷积层部分。
    for (num_convs, num_channels) in conv_arch:
        net.add(vgg_block(num_convs, num_channels))
    # 全连接层部分。
    net.add(nn.Dense(4096, activation='relu'), nn.Dropout(0.5),
            nn.Dense(4096, activation='relu'), nn.Dropout(0.5),
            nn.Dense(10))
    return net

net = vgg(conv_arch)

然后我们打印每个卷积块的输出变化。

In [4]:
net.initialize()
X = nd.random.uniform(shape=(1, 1, 224, 224))
for blk in net:
    X = blk(X)
    print(blk.name, 'output shape:\t', X.shape)
sequential1 output shape:        (1, 64, 112, 112)
sequential2 output shape:        (1, 128, 56, 56)
sequential3 output shape:        (1, 256, 28, 28)
sequential4 output shape:        (1, 512, 14, 14)
sequential5 output shape:        (1, 512, 7, 7)
dense0 output shape:     (1, 4096)
dropout0 output shape:   (1, 4096)
dense1 output shape:     (1, 4096)
dropout1 output shape:   (1, 4096)
dense2 output shape:     (1, 10)

可以看到每次我们将长宽减半,直到最后高宽变成 7 后进入全连接层。与此同时输出通道数每次都翻倍。因为每个卷积层的窗口大小一样,所以每层的模型参数大小和计算复杂度跟 高 \(\times\)\(\times\) 输入通道数 \(\times\) 输出通道数 成正比。VGG 这种高宽减半和通道翻倍的设计使得每个卷积层都有相同的模型参数大小和计算复杂度。

模型训练

因为 VGG 11 计算上比 AlexNet 更加复杂,出于测试的目的我们构造一个通道数更小,或者说更窄的网络来训练 FashionMNIST。

In [5]:
ratio = 4
small_conv_arch = [(pair[0], pair[1] // ratio) for pair in conv_arch]
net = vgg(small_conv_arch)

除了使用了稍大些的学习率,模型训练过程跟上一节的 AlexNet 类似。

In [6]:
lr = 0.05
num_epochs = 5
batch_size = 128
ctx = gb.try_gpu()
net.initialize(ctx=ctx, init=init.Xavier())
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': lr})
train_iter, test_iter = gb.load_data_fashion_mnist(batch_size=batch_size,
                                                   resize=224)
loss = gloss.SoftmaxCrossEntropyLoss()
gb.train_ch5(net, train_iter, test_iter, loss, batch_size, trainer, ctx,
             num_epochs)
training on gpu(0)
epoch 1, loss 0.9110, train acc 0.668, test acc 0.859, time 137.2 sec
epoch 2, loss 0.4008, train acc 0.853, test acc 0.887, time 132.9 sec
epoch 3, loss 0.3281, train acc 0.881, test acc 0.900, time 142.9 sec
epoch 4, loss 0.2851, train acc 0.897, test acc 0.908, time 139.9 sec
epoch 5, loss 0.2553, train acc 0.906, test acc 0.915, time 133.1 sec

小结

VGG 通过 5 个可以重复使用的卷积块来构造网络。根据每块里卷积层个数和输出通道数的不同可以定义出不同的 VGG 模型。

练习

  • VGG 的计算比 AlexNet 慢很多,也需要很多的 GPU 内存。请分析下原因。
  • 尝试将 FashionMNIST 的高宽由 224 改成 96,实验其带来的影响。
  • 参考 [1] 里的表 1 来构造 VGG 其他常用模型,例如 VGG16 和 VGG19。

扫码直达 讨论区

参考文献

[1] Simonyan, K., & Zisserman, A. (2014). Very deep convolutional networks for large-scale image recognition. arXiv preprint arXiv:1409.1556.