文章目录
深度学习图像分类(三): VggNet
前言
一、Vgg网络模型
二、网络贡献总结
1、结构简洁
2、小卷积核和多卷积子层
3、小池化核
4、通道数多
5、层数更深、特征图更宽
三、 代码实现
总结
前言
2014年,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司的研究员一起研发出了新的深度卷积神经网络:VGGNet,并取得了ILSVRC2014比赛分类项目的第二名(第一名是GoogLeNet,也是同年提出的)和定位项目的第一名。

VGGNet探索了卷积神经网络的深度与其性能之间的关系,成功地构筑了16~19层深的卷积神经网络,证明了增加网络的深度能够在一定程度上影响网络最终的性能,使错误率大幅下降,同时拓展性又很强,迁移到其它图片数据上的泛化性也非常好。到目前为止,VGG仍然被用来提取图像特征。

VGGNet可以看成是加深版本的AlexNet,都是由卷积层、全连接层两大部分构成

一、Vgg网络模型

如上图所示是经典的Vgg16网络模型:

接收的收入是彩色图像,维度为(B, C, H, W) 分别代表(图像数量,图片色彩通道,图片高度,图片宽度)以上图实例中为(1,3,224,224)
模型在特征提取阶段,经过5次下采样(图中红颜色的层结构),下采样方式为最大池化,注意:卷积后没有改变特征图的尺寸。
最后顶层经过三个全连接层实现分类的操作。
注意:由于全连接层的存在, 网络只能接收固定大小的图像尺寸
二、网络贡献总结
1、结构简洁
VGG由5层卷积层、3层全连接层、softmax输出层构成,层与层之间使用max-pooling(最大化池)分开,所有隐层的激活单元都采用ReLU函数。

2、小卷积核和多卷积子层
VGG使用多个较小卷积核(3x3)的卷积层的堆叠代替一个卷积核较大的卷积层。这样在保证感受野的同时,一方面可以减少参数,另一方面相当于进行了更多的非线性映射,可以增加网络的拟合,表达,特征提取能力。

小卷积核是VGG的一个重要特点,虽然VGG是在模仿AlexNet的网络结构,但没有采用AlexNet中比较大的卷积核尺寸(如7x7),而是通过降低卷积核的大小(3x3),增加卷积子层数来达到同样的性能

VGG的作者认为两个3x3的卷积堆叠获得的感受野大小,相当一个5x5的卷积;而3个3x3卷积的堆叠获取到的感受野相当于一个7x7的卷积。这样可以增加非线性映射,也能很好地减少参数(例如7x7的参数为49个,而3个3x3的参数为27)

3、小池化核
相比AlexNet的3x3的池化核,VGG全部采用2x2的池化核。

4、通道数多
VGG网络第一层的通道数为64,后面每层都进行了翻倍,最多到512个通道,通道数的增加,使得更多的信息可以被提取出来。

5、层数更深、特征图更宽
由于卷积核专注于扩大通道数、池化专注于缩小宽和高,使得模型架构上更深更宽的同时,控制了计算量的增加规模

三、 代码实现
这里给出模型搭建的python代码(基于pytorch实现)。完整的代码是基于图像分类问题的(包括训练和推理脚本,自定义层等)详见我的GitHub:完整代码链接

from turtle import forward
import torch
import torch.nn as nn
from custom_layers.CustomLayers import ConvActivation
# official pretrain weights
model_urls = {
    'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth',
    'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth',
    'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth',
    'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth'
} 
# note: if use pretrain parameters, minus the mean of ImageNet(123.68, 116.78, 103.94) to normalize the dataset

cfgs_feature = {
    'vgg11': [64, 'Pooling', 128, 'Pooling', 256, 256, 'Pooling', 512, 512, 'Pooling', 512, 512, 'Pooling'],
    'vgg13': [64, 64, 'Pooling', 128, 128, 'Pooling', 256, 256, 'Pooling', 512, 512, 'Pooling', 512, 512, 'Pooling'],
    'vgg16': [64, 64, 'Pooling', 128, 128, 'Pooling', 256, 256, 256, 'Pooling', 512, 512, 512, 'Pooling', 512, 512, 512, 'Pooling'],
    'vgg19': [64, 64, 'Pooling', 128, 128, 'Pooling', 256, 256, 256, 256, 'Pooling', 512, 512, 512, 512, 'Pooling', 512, 512, 512, 512, 'Pooling'],
}

def create_feature_layers(cfgs:list, input_channels=3):
    feature_layers=[] 
    for layer in cfgs:
        if layer == 'Pooling':
            feature_layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else: 
            feature_layers += [ConvActivation(input_channels, layer, kernel_size=3, stride=1, padding=1)]
            input_channels = layer
    return nn.Sequential(*feature_layers)

class VggNet(nn.Module):
    def __init__(self, num_classes, feature_layers_type='vgg16', init_weights=True):
        super().__init__()
        assert feature_layers_type in cfgs_feature, "Warning: feature_layers_type not in cfgs dict!"

        self.feature_layers = create_feature_layers(cfgs=cfgs_feature[feature_layers_type]) 
        self.classifier_layers = nn.Sequential(*[
            nn.Linear(512*7*7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),

            nn.Linear(4096,4096),
            nn.ReLU(True),
            nn.Dropout(p=0.4),

            nn.Linear(4096, num_classes)
        ])
        if init_weights:
            self._initialize_weights()

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.xavier_uniform_(m.weight)
                if m.bias is not None:
                   nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.xavier_uniform_(m.weight)
                nn.init.constant_(m.bias, 0)
    
    def forward(self, x):
        x = self.feature_layers(x)
        x = torch.flatten(x, start_dim=1)
        x = self.classifier_layers(x)
        return x
    
 

总结

Vgg证明了对于神经网络来说,较深的层级结构可以帮助模型更好的提取特征,所谓的更好是指可以提取到蕴含语义信息更多的高级特征,这种高级语义的特征对分类任务很有帮助。