当前位置: 代码迷 >> 综合 >> 论文阅读:VGGNet—Very Deep Convolutional Networks For Large-Scale Image Recognition
  详细解决方案

论文阅读:VGGNet—Very Deep Convolutional Networks For Large-Scale Image Recognition

热度:33   发布时间:2024-02-24 10:06:46.0

论文阅读:VGGNet—Very Deep Convolutional Networks For Large-Scale Image Recognition

1、摘要

? 在这篇论文中,作者对卷积神经网络的深度对于其在大规模图像数据集的识别的准确性做了一个完全的评估,特点在于将CNN的网络深度提升到了16-19层,提升了大规模图像的识别准确性;同时有证明了VGG在深度视觉表征中具有较强的泛化能力。

2、引言

? 引言部分作者总结了自AlexNet以来CNN在大规模图像识别中的广泛应用,同时提出了AlextNet后续研究中提升模型性能的两个方面:

  • 使用更小的卷积核
  • 全图像多尺度地训练和测试模型

而本文提出了提升网络性能地另一条重要的途径:深度,为了实现极大的深度,本文使用了极小的卷积核(3x3).

3、分类架构

? 本文提出的VGG模型架构如下表所示:

VGG模型结构

可以看到,从A-E网络的深度逐渐增加,且网络中采用了多层连续卷积之后再进行pooling的方式。

那么,为什么使用更小的卷积核可以提升性能,加深网络结构呢?原因在于使用多层小的卷积核:

  • 获得的感受野与单层较大的卷积核一致
  • 减少参数数量
  • 多层堆叠增强非线性,增强网络地特征表达能力

举例来说,使用两个连续的3x3卷积与一层5x5卷积获得的感受野是一致的,而三个3x3连续卷积与一层7x7卷积所获得的感受野也是一致的。具体图下图所示:

多层小卷积示意

在参数压缩方面,以三层3x3卷积与一层7x7卷积为例,对于C个channel的卷积核,前者的参数数3x(3^2xC)=27C, 后者1x(7x7xC)=49C, 可以看出参数数量减少的很多。文章对提出的多个模型的参数数量进行了展示:

在这里插入图片描述

深度最深的E网络的模型参数大体上与2014年ILSVCR竞赛中获得Location第一名的网络参数一致(Seemanet 2014).

? 注意到A-LRN网络架构,这是在A模型的基础上加入了局部响应归一化(第一层卷积层之后),后面的实验证明局部响应归一化对于提升性能几乎没有作用,甚至还浪费了计算资源。另一方面,在某些特定的网络层中使用了1x1卷积,虽然输入与输出的通道数是一致的,所以本质上是在同一个空间维度中做线性变换,随后的非线性层却增加了额外的非线性。

4、训练阶段

? 训练阶段使用基于mini-batch的带Momentum的随机梯度优化,同时加上了L2正则化,实验发现相比起AlexNet,VGG收敛需要的迭代次数,原因在于隐式的正则化与对于特定层的预初始化(pre-initialization).

在这篇文章中,作者对第一个较小的A网络使用了随机初始化权重,但对于后面的几个网络结构,使用了A中训练好的权重来初始化对应的层,剩余层才使用随机初始化。

训练图像的获取

? 不同于AlextNet所进行的固定尺寸(224*224)的图像增强方式(详情见上一篇文章),这里还考虑了单尺度(Single-scale)与多尺度(Multi-scale)的输入图像。

? 首先,对于单尺度输入图像,通过设定一个最短边的值S来进行缩放,即按照将短边缩放到S的比例缩放图像。则当S=224时,相当于没有变换,当S>>224时,图像被放大,然后再被剪裁成224x224的尺寸,这样做相当于对于图像中同一个物体,在不同的尺度下有不同的大小,这也符合客观世界的规律。

? 其次,对于多尺度,则将S设定在一个合理的区间[Smin,Smax], 这称之为尺度抖动(scale jittering)然后对每一张训练图像都独立地选择S进行缩放,再剪裁成固定的224x224大小,这样相当于有多个尺度的训练图像被一起学习,提高了特征表达能力。在训练多尺度的时候,使用了单尺度下S=384的权重来初始化新的网络,加快计算。

5、测试阶段

? 在测试阶段同样引入了多尺度的概念,测试时将输入模型的图像的短边定义为Q,这个值可以与训练时的短边S不一致,对于多尺度的测试,选择不同的Q值会scale出不同尺寸的输入图像,这里并不像训练阶段对scale后的图像进行裁剪,而是将模型的后三层FC层转化为FCN(Fully-Convlutional Network),这一思想参考了(Seemanet 2014),这样做的好处在于可以处理不同尺度的输入图像。

FC与FCN

? FC层到FCN层的转化,图源(https://blog.csdn.net/qq_40027052/article/details/79015827)

对于FCN层如何处理不同尺寸数据,Seemanet论文中有如下解释:

FCN层处理尺寸不一致的数据

对于输入的尺寸为(Q,Q,C)的图像,经过一层7x7卷积,两层1x1卷积以后,输出的特征图为(N,N,1000),对于224x224的图像输入,N=1,对于N>1的情况,在最后的特征图上进行平均,得到(1,1,1000)的得分,然后将该向量传递给soft-max函数。这样的处理方式作者称之为密集评估

? 在多尺度密集评估的基础上,论文Seemanet指出虽然使用FCN层无需对输入的测试图像进行剪裁,但是使用大量的剪裁图像可以提升识别的准确性。作者认为,图像剪裁使得获得的输入图像更为精细的采样。

6、分类实验

6.1 数据集

? 整个的评估实验均在ILSVRC-2012数据集上进行,数据类别1000, 训练数据1.3M图像, 验证数据50K图像,测试数据100K图像。

6.2 单尺度评估

? 在进行单尺度评估时,当S为固定值时,Q=S, 当S有尺度变化时,Q=(Smin+Smax) / 2, 根据这个规则进行了多组实验,实验结果如下。

单尺度评估

? 通过结果可以看出:

  • 加入局部响应归一化对于模型的性能并没有提升,例如(A/A-LRN)
  • 控制其他实验条件不变的情况下,随着深度的增加模型的效果越好((A-E(256,256))
  • 深度相同的情况下,相比起使用1x1卷积的C模型,使用3x3卷积的模型D的效果更好,这表明虽然1x1卷积可以增加额外的非线性,例如(D模型比B、C好),但是利用空间上的卷积可以捕获更多的上下文
  • 尺度抖动比使用固定的最短边S更好
6.3 多尺度评估

对于固定大小的 S ,测试图片大小 Q={S-32, S, S+32} ;对于不固定大小的 S=[Smin, Smax], Q={Smin, 0.5(Smin+Smax), Smax),评估结果如下图所示:

多尺度评估

实验表明使用测试时使用尺度抖动能够获得更好的效果,与之前类似,深度较深的网络有较好的性能,使用尺度抖动比单尺度下训练模型得到的效果要好。

6.4 多剪裁评估

? 作者在密集卷积与多剪裁之间做了比较,相比起不进行剪裁,使用测试时使用大量的图像剪裁获得的效果要好上一点点,随后作者将这两种方法进行结合,获得了更显著的性能。

在这里插入图片描述

6.5 模型融合

? 与大多数模型一样,本文也对提出的多个模型进行了模型融合,做法是将多个模型预测的分类得分进行平均,多个模型互为补充,获得了更好的性能。7个模型的融合结果使得识别的错误率(top-5)下降到7.3%,这也是该团队在ILSVRC竞赛中的提交,随后,作者发现仅融合两个模型(D,E)可以获得更佳的效果。

模型融合

随后,作者将改进后的模型融合与ILSVRC分类任务中的多个模型进行对比:

模型比较

7、结论

? 本文在大规模图像数据集上评估了深层次的卷积神经网络模型的性能,结果表明深度表征对于图像分类的准确性具有重要的意义,同时也证明使用简单质朴的网络结构也可以在大规模图像分类中达到SOTA。

8、代码分析

import torch
import torch.nn as nn
from .utils import load_state_dict_from_url__all__ = ['VGG', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn','vgg19_bn', 'vgg19',
]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','vgg11_bn': 'https://download.pytorch.org/models/vgg11_bn-6002323d.pth','vgg13_bn': 'https://download.pytorch.org/models/vgg13_bn-abd245e5.pth','vgg16_bn': 'https://download.pytorch.org/models/vgg16_bn-6c64b313.pth','vgg19_bn': 'https://download.pytorch.org/models/vgg19_bn-c79401a0.pth',
}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(),    #前两层使用dropoutnn.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 xdef _initialize_weights(self):for m in self.modules():    #torch.nn.Module.modules()可以返回一个模型中所有层的迭代器if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') #kaiming初始化if m.bias is not None:nn.init.constant_(m.bias, 0)   #将模型的bias置零elif isinstance(m, nn.BatchNorm2d):    #对于batch层, 权重为1, 偏置为0nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)elif isinstance(m, nn.Linear):   #对于线性层,将权重置为一个小值,偏置为0nn.init.normal_(m.weight, 0, 0.01)nn.init.constant_(m.bias, 0)def make_layers(cfg, batch_norm=False):layers = []in_channels = 3   #第一层输入通道为3for v in cfg:if v == 'M':layers += [nn.MaxPool2d(kernel_size=2, stride=2)]  #将所有的Maxpooling层都设置为kz=2,stride=2else: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)   #使用nn.Sequential()将list中的模型串联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'],
}   #字典中值的list数字代表卷积层的输出通道数,即卷积核的个数,‘M'代表Maxpoolingdef _vgg(arch, cfg, batch_norm, pretrained, progress, **kwargs):if pretrained:kwargs['init_weights'] = False   #若使用与预训练模型,则将init_weight设置为falsemodel = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm), **kwargs)if pretrained:state_dict = load_state_dict_from_url(model_urls[arch],progress=progress)model.load_state_dict(state_dict)   #torch.nn.Module.load_state_dict(state_dict: Dict[str, tensor], strict: bool = True)return modeldef vgg11(pretrained=False, progress=True, **kwargs):r"""VGG 11-layer model (configuration "A") from`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_Args:pretrained (bool): If True, returns a model pre-trained on ImageNetprogress (bool): If True, displays a progress bar of the download to stderr"""return _vgg('vgg11', 'A', False, pretrained, progress, **kwargs)def vgg11_bn(pretrained=False, progress=True, **kwargs):r"""VGG 11-layer model (configuration "A") with batch normalization`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_Args:pretrained (bool): If True, returns a model pre-trained on ImageNetprogress (bool): If True, displays a progress bar of the download to stderr"""return _vgg('vgg11_bn', 'A', True, pretrained, progress, **kwargs)def vgg13(pretrained=False, progress=True, **kwargs):r"""VGG 13-layer model (configuration "B")`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_Args:pretrained (bool): If True, returns a model pre-trained on ImageNetprogress (bool): If True, displays a progress bar of the download to stderr"""return _vgg('vgg13', 'B', False, pretrained, progress, **kwargs)def vgg13_bn(pretrained=False, progress=True, **kwargs):r"""VGG 13-layer model (configuration "B") with batch normalization`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_Args:pretrained (bool): If True, returns a model pre-trained on ImageNetprogress (bool): If True, displays a progress bar of the download to stderr"""return _vgg('vgg13_bn', 'B', True, pretrained, progress, **kwargs)def vgg16(pretrained=False, progress=True, **kwargs):r"""VGG 16-layer model (configuration "D")`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_Args:pretrained (bool): If True, returns a model pre-trained on ImageNetprogress (bool): If True, displays a progress bar of the download to stderr"""return _vgg('vgg16', 'D', False, pretrained, progress, **kwargs)def vgg16_bn(pretrained=False, progress=True, **kwargs):r"""VGG 16-layer model (configuration "D") with batch normalization`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_Args:pretrained (bool): If True, returns a model pre-trained on ImageNetprogress (bool): If True, displays a progress bar of the download to stderr"""return _vgg('vgg16_bn', 'D', True, pretrained, progress, **kwargs)def vgg19(pretrained=False, progress=True, **kwargs):r"""VGG 19-layer model (configuration "E")`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_Args:pretrained (bool): If True, returns a model pre-trained on ImageNetprogress (bool): If True, displays a progress bar of the download to stderr"""return _vgg('vgg19', 'E', False, pretrained, progress, **kwargs)def vgg19_bn(pretrained=False, progress=True, **kwargs):r"""VGG 19-layer model (configuration 'E') with batch normalization`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_Args:pretrained (bool): If True, returns a model pre-trained on ImageNetprogress (bool): If True, displays a progress bar of the download to stderr"""return _vgg('vgg19_bn', 'E', True, pretrained, progress, **kwargs)

9、参考

  1. 论文地址:Very Deep Convolutional Networks for Large-Scale Image Recognition
  2. 知乎:深度学习卷积神经网络-VGG
  3. code
  相关解决方案