一文读不懂——'Very Deep Convolutional Networks For Large-Scale Image Recognition' [VGGNets]

一文读不懂——'Very Deep Convolutional Networks For Large-Scale Image Recognition' [VGGNets]

Scroll Down

一文读不懂——VGGNets

题外话: 封面是之前拍的人民公园,疫情赶紧过去吧.
自从resnet之后,这个系列的文章还没有更新过,是一个有些浮躁且有点忙的状态,趁着有时间把VGGNet论文给读一下,做个总结再复现一下提高代码能力.

1 介绍

VGG是Oxford的Visual Geometry Group的组提出的,取名也是用实验室的前几个字母组成,该网络是在ILSVRC 2014上的相关工作。在AlexNet基础上通过对网络结构进行改进和加深,证明了增加网络的深度能够在一定程度上影响网络最终的性能。VGG有两种结构,分别是VGG16和VGG19,主要是网络层数上的不同。

2 原理

1)用3x3卷积代替5x5以及7x7。网络在不断地加深,模型的参数也越来越多,给训练带来很多阻碍。VGGNet一个很重要的改进就是用更小的比如3x3的卷积层去代替5x5以及7x7的卷积,经过计算可以发现两个3x3的卷积在感受野上相当于一个5x5的卷积,但是有两方面的提升:更少的参数(2x3x3/5x5),以及多加了分线性变化层,提高模型的学习能力.这里假设输入通道和输出通道分别是C1和C2,那两个3x3的卷积就相当于2x3x3xC1xC2,同理计算其他的;
2)使用1x1卷积。这里使用的1x1卷积是在同一纬度上进行特征的融合,多引入了非线性变换,增强模型学习能力。

3 结构

结构其实也很简单,就是卷积和maxpool的堆叠,各种结构如图3.1所示.
image.png
图 3.1 VGGNet网络结构参数

4 Pytorch复现

这里虽然有很多结构的VGG,但是我们经过分析之后可以发现,都是成组的,所以可以把所有的模型集成在一起,这种写法自己平常也写的不多,学到了.

import torch.nn as nn
import torch.nn.functional as F

首先,这些包的引入就不用多说了.
接下来定义VGGNets


class VGG(nn.Module):
    def __init__(self, arch: object, num_classes=1000) -> object:
        super(VGG, self).__init__()
        self.in_channels = 3
        self.conv3_64 = self.__make_layer(64, arch[0])
        self.conv3_128 = self.__make_layer(128, arch[1])
        self.conv3_256 = self.__make_layer(256, arch[2])
        self.conv3_512a = self.__make_layer(512, arch[3])
        self.conv3_512b = self.__make_layer(512, arch[4])
        self.fc1 = nn.Linear(7*7*512, 4096)
        self.bn1 = nn.BatchNorm1d(4096)
        self.bn2 = nn.BatchNorm1d(4096)
        self.fc2 = nn.Linear(4096, 4096)
        self.fc3 = nn.Linear(4096, num_classes)

    def __make_layer(self, channels, num):
        layers = []
        for i in range(num):
            layers.append(nn.Conv2d(self.in_channels, channels, 3, stride=1, padding=1, bias=False))  # same padding
            layers.append(nn.BatchNorm2d(channels))
            layers.append(nn.ReLU())
            self.in_channels = channels
        return nn.Sequential(*layers)

    def forward(self, x):
        out = self.conv3_64(x)
        out = F.max_pool2d(out, 2)
        out = self.conv3_128(out)
        out = F.max_pool2d(out, 2)
        out = self.conv3_256(out)
        out = F.max_pool2d(out, 2)
        out = self.conv3_512a(out)
        out = F.max_pool2d(out, 2)
        out = self.conv3_512b(out)
        out = F.max_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = self.fc1(out)
        out = self.bn1(out)
        out = F.relu(out)
        out = self.fc2(out)
        out = self.bn2(out)
        out = F.relu(out)
        return F.softmax(self.fc3(out))

可以看到我们在每一个block的地方用了一个Sequential,接下来我们把不同结构网络的参数写进去.


def VGG_11():
    return VGG([1, 1, 2, 2, 2], num_classes=1000)

def VGG_13():
    return VGG([1, 1, 2, 2, 2], num_classes=1000)

def VGG_16():
    return VGG([2, 2, 3, 3, 3], num_classes=1000)

def VGG_19():
    return VGG([2, 2, 4, 4, 4], num_classes=1000)

接下来进行简单测试, 并打印模型结构

if __name__=='__main__':
    model = VGG_16()
    print(model)

另外,在看论文的时候,提到说Conv+MaxPooling 可以增强平移不变性,应该是Pooling层的一个用处,好久没看这些有点忘了,不严谨的可以理解为,Conv提取特征之后,经过MaxPooling的操作,局部的特征有些被融合了一部分.

5 总结

VGGNet在AlexNet上进行了改进,这个改进只是说依旧用卷积和最大池化堆叠的方式进行网络设计,总结其优缺点如下:

  • 优点:就是用更小更多的卷积代替大卷积,减少模型参数的同时提升模型学习能力;
  • 缺点:最后加了四层FC,参数也太多了,就很离谱,后面有人实验将其中的几个FC去掉之后,效果没有啥变化.

看论文还不是很细,理解有误的地方,欢迎交流指正~