# 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\times3^2\times C^2 = 27 C^2$$ 个参数，而如果使用 7x7 的卷积核，则需要 $$7\times 7 C^2 = 49 C^2$$ 个参数。 第二，由于卷积层中间需要插入非线性映射，叠加多个卷积层增加了非线性层的数量。

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

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

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

![](https://3632175219-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJhbbglPBd1Md8dFDXQ%2Fsync%2Fadb111d51176577538aa066358eed0e3c3515b4e.png?generation=1608284956208450\&alt=media)

其中层数最多的 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 层（后面讲）的版本。

[完整代码](https://pytorch.org/docs/stable/_modules/torchvision/models/vgg.html)

```python
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 实现了一个通用的框架，并根据不同的配置文件产生不同层数的模型结构。

```python
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'],
}
```
