深度学习入门笔记之VggNet网络_ysukitty的博客-程序员宝宝_vggnet

技术标签: cnn  网络  深度学习  

VGGNet是由牛津大学的视觉几何组(Visual Geometry Group)和谷歌旗下DeepMind团队的研究员共同研发提出的,获得了ILSVRC 2014( 2014年ImageNet图像分类竞赛) 的第二名,将 Top-5错误率降到7.3%, 在Top-5中取得了92.3%的正确率,同年的冠军是googlenet。
目前使用比较多的网络结构主要有ResNet(152-1000层),GooleNet(22层),VGGNet(19层),大多数模型都是基于这几个模型上改进,采用新的优化算法,多模型融合等。到目前为止,VGGNet 依然经常被用来提取图像特征。
一、简介
论文名为《Very Deep Convolutional Networks for Large-Scale Image Recognition》,论文地址:https://arxiv.org/pdf/1409.1556.pdf。 VGGNet论文给出了一个非常振奋人心的结论:卷积神经网络的深度增加和小卷积核的使用对网络的最终分类识别效果有很大的作用。
VGGNet探索了CNN的深度及其性能之间的关系,通过反复堆叠33的小型卷积核和22的最大池化层,VGGNet成功的构筑了16-19层深的CNN。
二、网络结构
网络结构如下图所示,其中D和E即为常用的VGG-16和VGG-19,前者拥有13个核大小均为3×3的卷积层、5个最大池化层和3个全连接层,后者拥有16个核大小均为3×3的卷积层、5个最大池化层和3个全连接层。本文主要针对VGG16进行解读,可以看出VGG19只是多了3个卷积层而已,其它的和VGG16没啥区别。
在这里插入图片描述
表中的卷积层(conv3-kernels,其中kernels代表卷积核的个数)全部都是大小为3x3,步距为1,padding为1的卷积操作(经过卷积后不会改变特征矩阵的高和宽)。最大池化下采样层全部都是池化核大小为2,步距为2的池化操作,每次通过最大池化下采样后特征矩阵的高和宽都会缩减为原来的一半。
VGG-16的结构图如下图:
在这里插入图片描述
VGG-16架构:13个卷积层+3个全连接层,前5段卷积网络(标号1-5),最后一段全连接网络(标号6-8),网络总共参数数量大约138M左右。。

(1) 输入层(Input):图像大小为224×224×3

(2) 卷积层1+ReLU:conv3 - 64(卷积核的数量):kernel size:3 stride:1 pad:1
像素:(224-3+2×1)/1+1=224 输出为224×224×64 64个feature maps
参数: (3×3×3)×64+64=1792

(3) 卷积层2+ReLU:conv3 - 64:kernel size:3 stride:1 pad:1
像素: (224-3+1×2)/1+1=224 输出为224×224×64 64个feature maps
参数: (3×3×64×64)+64=36928

(4) 最大池化层: pool2: kernel size:2 stride:2 pad:0
像素: (224-2)/2 = 112 输出为112×112×64,64个feature maps
参数: 0

(5) 卷积层3+ReLU:.conv3-128:kernel size:3 stride:1 pad:1
像素: (112-3+2×1)/1+1 = 112 输出为112×112×128,128个feature maps
参数: (3×3×64×128)+128=73856

(6) 卷积层4+ReLU:conv3-128:kernel size:3 stride:1 pad:1
像素: (112-3+2×1)/1+1 = 112 输出为112×112×128,128个feature maps
参数: (3×3×128×128)+128=147584

(7) 最大池化层:pool2: kernel size:2 stride:2 pad:0
像素: (112-2)/2+1=56 输出为56×56×128,128个feature maps。
参数: 0

(8) 卷积层5+ReLU:conv3-256: kernel size:3 stride:1 pad:1
像素: (56-3+2×1)/1+1=56 输出为56×56×256,256个feature maps
参数: (3×3×128×256)+256=295168

(9) 卷积层6+ReLU:conv3-256: kernel size:3 stride:1 pad:1
像素: (56-3+2×1)/1+1=56 输出为56×56×256,256个feature maps,
参数: (3×3×256×256)+256=590080

(10) 卷积层7+ReLU:conv3-256: kernel size:3 stride:1 pad:1
像素: (56-3+2×1)/1+1=56 输出为56×56×256,256个feature maps
参数: (3×3×256×256)+256=590080

(11) 最大池化层:pool2: kernel size:2 stride:2 pad:0
像素:(56 - 2)/2+1=28 输出为28×28×256,256个feature maps
参数: 0

(12) 卷积层8+ReLU:conv3-512:kernel size:3 stride:1 pad:1
像素:(28-3+2×1)/1+1=28 输出为28×28×512,512个feature maps
参数: (3×3×256×512)+512=1180160

(13) 卷积层9+ReLU:conv3-512:kernel size:3 stride:1 pad:1
像素:(28-3+2×1)/1+1=28 输出为28×28×512,512个feature maps
参数: (3×3×512×512)+512=2359808

(14) 卷积层10+ReLU:conv3-512:kernel size:3 stride:1 pad:1
像素:(28-3+2×1)/1+1=28 输出为28×28×512,512个feature maps
参数: (3×3×512×512)+512=2359808

(15) 最大池化层:pool2: kernel size:2 stride:2 pad:0,输出为14×14×512,512个feature maps。
像素:(28-2)/2+1=14 输出为14×14×512
参数: 0

(16) 卷积层11+ReLU:conv3-512:kernel size:3 stride:1 pad:1
像素:(14-3+2×1)/1+1=14 输出为14×14×512,512个feature maps
参数: (3×3×512×512)+512=2359808

(17) 卷积层12+ReLU:conv3-512:kernel size:3 stride:1 pad:1
像素:(14-3+2×1)/1+1=14 输出为14×14×512,512个feature maps
参数: (3×3×512×512)+512=2359808

(18) 卷积层13+ReLU:conv3-512:kernel size:3 stride:1 pad:1
像素:(14-3+2×1)/1+1=14 输出为14×14×512,512个feature maps,
参数: (3×3×512×512)+512=2359808

(19) 最大池化层:pool2:kernel size:2 stride:2 pad:0
像素:(14-2)/2+1=7 输出为7×7×512,512个feature maps
参数: 0

(20) 全连接层1+ReLU+Dropout:有4096个神经元或4096个feature maps
像素:1×1×4096
参数:7×7×512×4096 = 102760448

(21) 全连接层2+ReLU+Dropout:有4096个神经元或4096个feature maps
像素:1×1×4096
参数:4096×4096 = 16777216

(22) 全连接层3:有1000个神经元或1000个feature maps
像素:1×1×1000
参数:4096×1000=4096000

(23) 输出层(Softmax):输出识别结果,看它究竟是1000个可能类别中的哪一个。

train和predict的可视化结果如下图所示:
在这里插入图片描述
三、VGGNet的特点
VGG网络的特点是利用小的尺寸核代替大的卷积核,然后把网络做深。
1. 结构简洁 卷积层+ReLU、最大池化层、全连接层、Softmax输出层。
VGGNet的结构十分简洁,由5个卷积层、3个全连接层和1个softmax层构成,**层与层之间使用最大池化连接,隐藏层之间使用的激活函数全都是ReLU。**并且网络的参数也是整齐划一的,赏心悦目。
2. 使用小卷积核
VGGNet使用含有多个小型的3×3卷积核的卷积层来代替AlexNet中的卷积核较大的卷积层。**2个3×3的卷积核堆叠的感受野相当于一个5×5的卷积核的感受野,而3个3×3的卷积核堆叠的感受野则相当于一个7×7的卷积核的感受野。**因此,采用多个小型卷积核,既能减少参数的数量,又能增强网络的非线性映射从而提升网络的表达能力。
在这里插入图片描述

为什么可以增加网络的非线性?我们知道激活函数的作用就是给神经网络增加非线性因素,使其可以拟合任意的函数,每个卷积操作后都会通过ReLU激活,ReLU函数就是一个非线性函数。下图展示了为什么使用2个3x3的卷积核可以代替5×5卷积核。
在这里插入图片描述
总结一下,使用多个3×3卷积堆叠的作用有两个:一是在不影响感受野的前提下减少了参数;二是增加了网络的非线性。
3. 使用小滤波器
与AlexNet相比,VGGNet在池化层全部采用的是2×2的小滤波器,stride为2。。
4. 通道数较多
VGGNet的第一层有64个通道,后面的每一层都对通道进行了翻倍,最多达到了512个通道( 64-128-256-512-512)。由于每个通道都代表着一个feature map,这样就使更多的信息可以被提取出来。
5 图像预处理
训练采用多尺度训练(Multi-scale),将原始图像缩放到不同尺寸 S,然后再随机裁切224x224的图片,并且对图片进行水平翻转和随机RGB色差调整,这样能增加很多数据量,对于防止模型过拟合有很不错的效果。
  初始对原始图片进行裁剪时,原始图片的最小边不宜过小,这样的话,裁剪到224x224的时候,就相当于几乎覆盖了整个图片,这样对原始图片进行不同的随机裁剪得到的图片就基本上没差别,就失去了增加数据集的意义,但同时也不宜过大,这样的话,裁剪到的图片只含有目标的一小部分,也不是很好。
针对上述裁剪的问题,提出的训练图片预处理过程:
(1)训练图片归一化,图像等轴重调(最短边为S)
等轴重调剪裁时的两种解决办法:
方法一:固定最小边的尺寸为256
方法二:随机从[256,512]的确定范围内进行抽样,这样原始图片尺寸不一,有利于训练,这个方法叫做尺度抖动,有利于训练集增强。 训练时运用大量的裁剪图片有利于提升识别精确率。
(2)随机剪裁(每SGD一次)
(3)随机水平翻转
(4)RGB颜色偏移
6. 将全连接层转换为卷积层
这个特征是体现在VGGNet的测试阶段。**在进行网络测试时,将训练阶段的3个全连接层替换为3个卷积层,使测试得到的网络没有全连接的限制,能够接收任意宽和高的输入。**如果后面3个层都是全连接层,那么在测试阶段就只能将测试的图像全部缩放到固定尺寸,这样就不便于多尺度测试工作的开展。
在这里插入图片描述
为什么这样替换之后就可以处理任意尺寸的输入图像了呢?因为1×1卷积一个很重要的作用就是调整通道数。如果下一层输入的特征图需要控制通道数为N,那么设置N个1×1卷积核就可以完成通道数的调整。比如最后需要1000个神经元用于分出1000个类别,那就在最后一层的前面使用1000个1×1的卷积核,这样的到的结果就是(1, 1, 1000)正好可以匹配。
在这里插入图片描述
7 参数和内存占用分析(来源[斯坦福大学CS231课程]课件截图):
在这里插入图片描述
由上图分析可以看出:
前面部分的卷积层占用大量内存
后面的三层全连接层占用了大量的参数
四、代码(Pytorch)
在VGG网络中,有两部分,提取特征网络结构和分类网络结构。
VGG网络非常大,训练起来大概要几个小时,如果训练集样本很少比如三千多张,是没办法充分的训练VGG网络的。如果需要使用VGG网络,建议使用迁移学习方法训练自己的样本集。
model.py:

import torch.nn as nn
import torch

# 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'
}


class VGG(nn.Module):
    #
    #num_classes=1000需要分类的类别个数
    #初始化函数__init__传进来features,也就是我们刚刚通过make_features(cfg: list)函数生成的提取特征网络结构
    #init_weights=False是否对网络进行权重初始化
    def __init__(self, features, num_classes=1000, init_weights=False):
        super(VGG, self).__init__()
        self.features = features
        #生成分类网络结构
        self.classifier = nn.Sequential(
            #输入节点个数,是展平处理之后所得到的1维向量的元素个数512*7*7
            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.5),
            nn.Linear(4096, num_classes)
        )
        if init_weights:#是否要对网络进行参数的初始化
            self._initialize_weights()

    def forward(self, x):
        # N x 3 x 224 x 224
        x = self.features(x)
        # N x 512 x 7 x 7
        # 图像经过提取特征网络结构之后,得到一个7*7*512的特征矩阵,如果...
        # 要和全连接层进行全连接,要进行一个展平处理。展平之后,才能和全连接层进行全连接
        x = torch.flatten(x, start_dim=1)#展平,从第一个维度展平
        # N x 512*7*7
        x = self.classifier(x)
        return x

    def _initialize_weights(self):
        for m in self.modules():#遍历网络中的每一层
            if isinstance(m, nn.Conv2d):
                # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                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.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)

#参数是配置变量,是list类型,传入对应配置的列表就可以了
#make_features(cfg: list)这个函数用来生成提取网络结构
def make_features(cfg: list):
    layers = []#定义一个空列表,用来存放所创建的每一层结构
    in_channels = 3#输入的图片是RGB彩色图片
    for v in cfg:#通过一个for循环来遍历配置列表,能得到一个由卷积操作和池化操作组成的一个列表
        if v == "M":#如果当前的配置元素是一个M字符,说明该层是最大池化层
            # 创建一个最大池化下采样层,在VGG中,所有的最大池化下采样层的核大小都是2,stride都是2
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else:#否则是卷积层
            #in_channels表示输入的特征矩阵的深度,v是输出的特征矩阵的深度,也就是卷积核的个数
            #在VGG中,所有的卷积层padding为1,stride为1
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)#创建一个卷积层
            # 将卷积层和ReLU拼接好后,放到layers层中
            layers += [conv2d, nn.ReLU(True)]
            in_channels = v
    #将列表通过非关键字参数的形式,传入进去。layers前面的*表示我们是通过非关键字参数传入进去的
    return nn.Sequential(*layers)

#定义cfgs一个字典文件,每一个key代表一个模型的配置文件,比如vgg11代表A配置,也就是11层的一个网络
#数字比如64代表卷基层的卷积核的个数,'M'代表池化层的结构
#有了这个配置,如何生成提取特征网络结构呢?定义了一个函数make_features(cfg: list)
cfgs = {
    
    'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
    'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}

#实例化给定的配置模型
def vgg(model_name="vgg16", **kwargs):
    assert model_name in cfgs, "Warning: model number {} not in cfgs dict!".format(model_name)
    cfg = cfgs[model_name]
    #实例化VGG网络
    #**kwargs表示可变长度的字典变量,是在调用VGG函数时传入的一个字典变量
    #这个字典变量包含了分类的个数,以及是否初始化权重的布尔变量
    model = VGG(make_features(cfg), **kwargs)
    return model

#在nn.Sequential(*layers)设置一个断点,可以实例化一个类来看一下
#vgg_model=vgg(model_name='vgg13')

五、注意事项和遇到的问题
(1) 我们通常看到别人在搭建VGG网络时,图像预处理的第一步会将图像的RGB分量分别减去[123.68, 116.78, 103.94]这三个参数。这三个参数是对应着ImageNet分类数据集中所有图像的R、G、B三个通到的均值分量。如果你要使用别人在ImageNet数据集上训练好的模型参数进行fine-trian操作(也就是迁移学习)那么你需要在在图像预处理过程中减去这[123.68, 116.78, 103.94]三个分量,如果你是从头训练一个数据集(不使用在ImageNet上的预训练模型)那么就可以忽略这一步。
(2)假如输入图像大小为nn,过滤器(filter)为ff,padding为p,步长(stride)为s,则输出大小为:如果商不是整数,向下取整,即floor函数。
在这里插入图片描述
(3)在特定的层使用了预训练得到的数据进行参数的初始化。对于较浅的网络,如网络A,可以直接使用随机数进行随机初始化,而对于比较深的网络,则使用前面已经训练好的较浅的网络中的参数值对其前几层的卷积层和最后的全连接层进行初始化。
(4)11的卷积层常被用来提炼特征,即多通道的特征组合在一起,凝练成较大通道或者较小通道的输出,而每张图片的大小不变。有时11的卷积神经网络还可以用来替代全连接层。
(5)模型训练方法:使用具有动量的小批量梯度下降优化多项式逻辑回归目标函数。
对于选择softmax分类器还是k个logistics分类器,取决于所有类别之间是否互斥。所有类别之间明显互斥用softmax;所有类别之间不互斥有交叉的情况下最好用k个logistics分类器。
(6)通过逐步增加网络深度来提高性能,虽然看起来有一点小暴力,没有特别多取巧的,但是确实有效,很多pretrained的方法就是使用VGG的model(主要是16和19),VGG相对其他的方法,参数空间很大,最终的model有500多m,alnext只有200m,googlenet更少,所以train一个vgg模型通常要花费更长的时间,所幸有公开的pretrained model让我们很方便的使用。
(7)虽然每一级网络逐渐变深,但是网络的参数量并没有增长很多(相对而言),这是因为参数量主要都消耗在最后3个全连接层。前面的卷积部分虽然很深,但是消耗的参数量不大,不过训练比较耗时的部分依然是卷积,因其计算量比较大。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ysukitty/article/details/122660445

智能推荐

展示 java中System.getProperty("") 获取的系统属性_咕噜咕叽的博客-程序员宝宝

代码://Java类路径System.out.println("---Java类路径----");System.out.println(System.getProperty("java.class.path"));//加载库时搜索的路径列表System.out.println("---加载库时搜索的路径列表----");System.out.println(System.getPr...

flume学习(八):自定义source_xiao_jun_0820的博客-程序员宝宝

按照以往的惯例,还是需求驱动学习,

Oracle 11g 新特性 -- 临时表空间收缩 说明_Dave的博客-程序员宝宝

一. 临时表空间收缩 1.1 说明关于Oracle 的临时表空间,之前有整理过一篇Blog:Oracle Temp 临时表空间http://blog.csdn.net/tianlesoftware/article/details/4697417 以下操作会占用大量的temporary:    1、用户执行imp/exp 导入导出操作时,会使用大量的temporary段    2、用户在rebuil

android 7.0查看根目录,从零开始搭建一个主流项目框架(八)—— 私有目录被限制访问(Android 7.0)..._谢仁慈Mercy的博客-程序员宝宝

个人博客:haichenyi.com。感谢关注我这里只说我关心的问题,并不是说Android 7.0只增加了一个“私有目录被限制访问”,当然还有其他的变化。android 7.0的新特性中,对程序员开发造成最直接的变化就是FileUriExposedException异常,这个异常的产生就是由私有目录被限制访问造成的Andoird 7.0中为了提高文件的安全性,面向Android N或者更高版本的...

Ubuntu 16 04 编译 Caffe SSD_weixin_33713350的博客-程序员宝宝

该来的还是要来之前为了偷懒想到使用 Docker 回避 Caffe SSD 编译的难题。结果,「天道好轮回,苍天饶过谁」。Docker 镜像内无法调用 GUI 显示以及摄像头,没法跑 ssd_pascal_webcam.py 做实时 Object Detection。所以没办法又得重新尝试编译 Caffe SSD。现在就记录一下,我在编译 Caffe SSD 时候遇到的坑。如果你仅需要训练出 ...

随便推点

安卓 集成 react native_村里最靓的码农的博客-程序员宝宝

背景React Native出来已经一段时间了,相对来说也算稳定了,在很多的企业中都实际使用他们,混合开发已经是未来的一种趋势,混合开发中使用的技术很多,不外乎Html5、JS框架通过一定的技术和原始交互,目前主流混合开发React Native、Cordova、APICloud、MUI、AppCan、Sencha Touch、jQuery Mobile等等(其他的小伙伴们自己收集),目前网上

mybatis配置公共sql语句语法报错_吾爱爪哇的博客-程序员宝宝_mybatis sql语法错误

redis基础写mybatis配置查询的列名公共SQL语句 里面的sql报错,是因为Mybatis里面有语法检查,但是这个并不是sql语句而是将用的多的语句提取出来,所有想要不报错需要设置一下。

JAVA软件逆向之hxtt的Access_JDBC30.jar_gsls200808的博客-程序员宝宝_access_jdbc30.jar

前言:本文仅供学习交流,禁止用于非法用途Access_JDBC30.jar为国人参与制作的jdbc的access驱动,但是免费版存在三个限制1.查询50次限制2.查询结果1000条限制3.插入500次限制下载地址:http://www.hxtt.com/access.zip本文用到的工具:IDEA,Eclipse,JBE一、使用超过限制代码查看异常栈超过50行查询的代码如下:package com...

1+X 云计算平台运维与开发认证(初级)E_Never Settle!的博客-程序员宝宝_以下哪一项最好地描述了何时完成监控项目过程组

下面哪个是软件代码版本控制软件?(10分)A、project B、SVN (正确答案)C、notepad++D、Xshell2下面哪个阶段不是项目管理流程中的阶段?(10分)A、项目立项 B、项目开发C、项目测试D、项目质保 (正确答案)3以下哪一项最好地描述了何时完成监控项目过程组?(10分)A、整个项目中持续进行 (正确答案)B、每个可交付成果完成时C、计划里程碑或项目间隙D、每个项目阶段结束时 4以下哪一个是收尾过程的正确顺序?(10分) (

matlab下K-means Cluster 算法实现_大愚若智_的博客-程序员宝宝_cluster matlab

一、概念介绍      K-means算法是硬聚类算法,是典型的局域原型的目标函数聚类方法的代表,它是数据点到原型的某种距离作为优化的目标函数,利用函数求极值的方法得到迭代运算的调整规则。K-means算法以欧式距离作为相似度测度,它是求对应某一初始聚类中心向量V最有分类,使得评价指标J最小。算法采用误差平方和准则函数作为聚类准则函数。      K-means算法是很典型的基于距离的聚类

梯度下降法(Gradient descent)_Pchoy的博客-程序员宝宝

1. 梯度下降法简介1-1 以下是定义了一个损失函数以后,参数theta对应的损失函数J的值对应的示例图,我们需要找到使得损失函数值J取得最小值对应的theta(这里是二维平面,也就是我们的参数只有一个)在直线方程中,导数代表斜率在曲线方程中,导数代表切线斜率导数代表theta单位变化时,J相应的变化 1-2 1-3 η太小,会减慢收敛学习速...

推荐文章

热门文章

相关标签