VGGNet

VGGNet 是牛津大学的视觉几何研究组(Visual Geometry Group)提出的一系列网络模型。

VGG 团队凭借 VGGNet 在 2014 年的 ImageNet 挑战赛上以 7.3% 的 top-5 错误率取得了图像分类比赛的第二名。比赛结束后,团队进一步将将错误率降低至 6.8% 。

论文:VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION

网络结构

VGGNet 最大的创新点在于使用多个 3x3 的卷积层叠加替代 AlexNet 等前代网络中 5x5 或 7x7 的卷积层。

这种做法有一些好处。 首先,使用多个 3x3 卷积堆叠可以在保持相同感受野的情况下降低参数的数量。假设所有层的通道数都为 C,则每个卷积层都包含 C 个 C 通道卷积核。 如果使用 3 层 3x3 的卷积核,只需要 3×32×C2=27C23\times3^2\times C^2 = 27 C^2 个参数,而如果使用 7x7 的卷积核,则需要 7×7C2=49C27\times 7 C^2 = 49 C^2 个参数。 第二,由于卷积层中间需要插入非线性映射,叠加多个卷积层增加了非线性层的数量。

通过叠加 3x3 的卷积层,VGGNet 在控制参数量的情况下加深了网络的层数,提高了网络的表达能力。

而实验也证明了作者的观点。

在论文中,作者给出了 5 种网络结构,如下图所示。

其中层数最多的 E 配置达到了 19 层,相比于 AlexNet 总共 8 层的结构深了很多。

配合多尺度的训练方法,该模型可以达到 8% 的 top-5 错误率。而 2013 年 ILSVRC 的最好成绩只有 11% 。

为了能达到更高的精度,比赛团队还是用了多尺度训练、多尺度测试,并借助了集成学习方法。由于这部分内容并不是 VGGNet 自身的特点,感兴趣的读者可以查阅论文以了解更多细节。

由于这几种模型分别包含 11、13、16、19 层,因此 A、B、D、E 四个模型结构也被称为 VGG-11,VGG-13,VGG-16 和 VGG-19 。在大规模视觉任务中,使用较多的是 VGG16 和 VGG-19。 而包含 LRN 和 1x1 卷积的变体 A-LRN 和 C 配置由于在实验中表现不好,通常并不采用。

与 AlexNet 类似,VGGNet 在最后一个卷积层之后叠加了三个全连接层,前两个为 4096 个神经元的隐层,第三个为 1000 个神经元的输出层。同样,VGGNet 在两个全连接层上使用了 DrouOut ,以防止因参数过多导致的过拟合。

Pytorch 实现

torchvision 提供了 VGG-11、13、16、19 四个模型,并提供了包含 Batch Normalization 层(后面讲)的版本。

完整代码

class VGG(nn.Module):

    def __init__(self, features, num_classes=1000, init_weights=True):
        super(VGG, self).__init__()
        self.features = features
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7))
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, num_classes),
        )
        if init_weights:
            self._initialize_weights()

    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

由于这些模型在结构上非常类似,只是在层数上有所区别,因此 torchvision 实现了一个通用的框架,并根据不同的配置文件产生不同层数的模型结构。

def make_layers(cfg, batch_norm=False):
    layers = []
    in_channels = 3
    for v in cfg:
        if v == 'M':
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else:
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            if batch_norm:
                layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
            else:
                layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    return nn.Sequential(*layers)


cfgs = {
    'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
    'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}

Last updated