1. 网络的背景

VggNet以牛津大学视觉几何小组命名,并在2014年ILSVRC竞赛的定位任务的第一名和分类任务的第二名,由Karen Simonyan和Andrew Zisserman在论文Very Deep Convolutional Networks for Large-Scale Image Recognition中提出。

VGGNet的设计灵感来源于前馈神经网络中深层网络可以学习到更多的高级特征,从而提高识别准确率。VGGNet的特点是在训练过程中采用小的卷积核和池化层,并通过堆叠多个卷积层来增加网络的深度,从而实现高精度的图像识别。并且VGGNet网络共有6个配置,包括11层,13层,16层,19层,以及是否使用LRN局部响应归一化操作等,在日常使用过程中一般使用16层16层表示的权重层(可学习的参数的层,比如池化不算一层),VGG16即下图中的D所在的列。“conv3-64",“conv” 代表卷积层;“3” 表示卷积核大小为 3×3 ;“64” 表示该卷积层输出通道数为 64。

VGGNet全称是:Very Deep Convolutional Networks for Large-Scale Image Recognition。

论文地址:https://arxiv.org/abs/1409.1556

2. 网络的创新

2.1 通过堆叠3x3卷积核代替大尺度卷积核

早期CNN网络如AlexNet 使用 7×7、5×5 等大尺寸卷积核提取特征,参数量大且计算复杂。

2.1.1 VGGNet 的创新

采用堆叠多个3x3卷积核替代单一的大尺寸卷积核,如:

堆叠两个3x3的卷积核可以替代5x5的卷积核的感受野

堆叠三个3x3的卷积核可以替代7x7的卷积核的感受野

通过堆叠多个小的感受野,替代大的感受野是为了减少网络训练时参数的数量。下面介绍一下感受野?及怎么减少网络训练时参数的数量?

感受野(Receptive Field)描述了神经网络中某一层神经元的输出所依赖的输入图像区域,这个区域即该元素的感受野。

卷积神经网络中,越深层的神经元看到的输入区域越大,如下图所示,卷积核kernel size 均为3×3,stride均为1,绿色标记的是Layer2每个神经元看到的区域,黄色标记的是Layer3 看到的区域,具体地,Layer2每个神经元可看到Layer1上3×3 大小的区域,Layer3 每个神经元看到Layer2 上3×3 大小的区域,该区域可以又看到Layer1上5×5 大小的区域。

所以,感受野是个相对概念,某层feature map上的元素看到前面不同层上的区域范围是不同的,通常在不特殊指定的情况下,感受野指的是看到输入图像上的区域

感受野是可以被计算出来的,其公式为:

其中,F(i)是第i层的感受野,Stride为第i层的步距,KernelSize是卷积核或者池化核的尺寸。

根据感受野的计算公式公式,Layer3的一个1x1的区域计算输入图像所对应的区域,计算步骤如下:

计算在Layer2上的感受野:为(1-1)x1+3=3,即在Layer2上对应着3x3的感受野;

计算Layer1上的感受野:为(3-1)x1+3=5,即在Layer1上对应着5x5的感受野;

假如在Layer1前面还有一个Layer0

计算Layer0上的感受野:为(5-1)x1+3=7,即在Layer0上对应着7x7的感受野;

根据这个规律,可以得知:

二层3x3的卷积核,就可以和一层5x5的卷积核的感受野是一致的

三层3x3的卷积核,就可以和一层7x7的卷积核的感受野是一致的

2.1.1.2 怎么减少网络训练时参数的数量?

VGGNet论文中提到堆叠两个3x3的卷积核可以替代5x5的卷积核堆叠三个3x3的卷积核可以替代7x7的卷积核,那么通过多个小的感受野的堆叠,替代大的感受野是为了减少网络训练时参数的数量

这里来举个例子计算一下,一层7x7卷积核和三层3x3的卷积核的参数量(这里将输入通道数和输出通道数都用channels代替):

通过上述的计算,可以得到如下结论:堆叠三个3x3的卷积核的计算量要小于7x7的卷积核,而且这个差距会随着特征矩阵的channels的变大而扩大

总结:

以小的卷积核方式来增加网络深度,从而实现高精度的图像识别。这种方法可以有效地捕获图像中的高级特征,并通过不断拟合训练数据来提高识别准确率。

3. 网络的问题

VggNet存在的缺陷:

  1. 网络架构非常大,并且需要大量的计算资源来训练。如果想在移动设备或个人电脑上使用VggNet,会发现运行非常慢。
  2. 由于VggNet网络架构非常深,它可能会导致梯度消失或爆炸的问题。这是由于在非常深的神经网络中,梯度在传播过程中可能会变得非常小或非常大,从而导致模型无法正常训练。

4. 网络的结构

4.1 架构图

在paper中给出了网络结构的表,如下图所示:

正常情况下,更多的会选择D项,也就是平时说的VGG16这个网络,这里也分析这个结构。

VGG-16架构图如下:

注:多个卷积层堆叠是为了替代更大的卷积核

每层隐藏层后都使用了 ReLU

可以在全连接层后添加 Dropout 防止过拟合

下图中列出了VGG16的网络结构图,如下图所示:

注意:在下图中的最后一个蓝色条1x1x1000的标识是fully nected+ReLU,但是实际上最后一层全连接并不需要加ReLU激活函数,这里需要注意一下。

在paper里并没有列出卷积核和池化的某些超参数,这里将一些在计算过程中会用到的列出:

卷积的stride=1,padding=1;

池化的maxpool的size=2,stride=2。

我们结合虚拟仿真的组件,对VGG16的网络结构进行分析。

4.2 结构组件介绍

4.2.1 卷积

输入特征矩阵是(224 x 224 x 3),本层卷积核的宽、高、通道、个数是(3 x 3 x 3 x 64),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(224 x 224 x 64)。

本层之后要经过ReLU激活函数,如下图:

4.2.2 卷积

输入特征矩阵是(224 x 224 x 64),本层卷积核的宽、高、通道、个数是(3 x 3 x 64 x 64),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(224 x 224 x 64)。

本层之后要经过ReLU激活函数,如下图:

4.2.3 池化-降采样:

池化方式为MaxPool,输入特征矩阵是(224 x 224 x 64),池化核大小为2,步长为2,经过计算可知,输出特征矩阵为(112 x 112 x 64)。如下图:

4.2.4 卷积

输入特征矩阵是(112 x 112 x 64),本层卷积核的宽、高、通道、个数是(3 x 3 x 64 x 128),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(112 x 112 x 128)。

本层之后要经过ReLU激活函数,如下图:

4.2.5 卷积

输入特征矩阵是(112 x 112 x 128),本层卷积核的宽、高、通道、个数是(3 x 3 x 128 x 128),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(112 x 112 x 128)。

本层之后要经过ReLU激活函数,如下图:

4.2.6 池化-降采样:

池化方式为MaxPool,输入特征矩阵是(112 x 112 x 128),池化核大小为2,步长为2,经过计算可知,输出特征矩阵为(56 x 56 x 128)。如下图:

4.2.7 卷积

输入特征矩阵是(56 x 56 x 128),本层卷积核的宽、高、通道、个数是(3 x 3 x 128 x 256),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(56 x 56 x 256)。

本层之后要经过ReLU激活函数,如下图:

4.2.8 卷积

输入特征矩阵是(56 x 56 x 256),本层卷积核的宽、高、通道、个数是(3 x 3 x 256 x 256),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(56 x 56 x 256)。

本层之后要经过ReLU激活函数,如下图:

4.2.9 卷积

输入特征矩阵是(56 x 56 x 256),本层卷积核的宽、高、通道、个数是(3 x 3 x 256 x 256),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(56 x 56 x 256)。

本层之后要经过ReLU激活函数,如下图:

4.2.10 池化-降采样:

池化方式为MaxPool,输入特征矩阵是(56 x 56 x 256),池化核大小为2,步长为2,经过计算可知,输出特征矩阵为(28 x 28 x 256)。如下图:

4.2.11 卷积

输入特征矩阵是(28 x 28 x 256),本层卷积核的宽、高、通道、个数是(3 x 3 x 256 x 512),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(28 x 28 x 512)。

本层之后要经过ReLU激活函数,如下图:

4.2.12 卷积

输入特征矩阵是(28 x 28 x 512),本层卷积核的宽、高、通道、个数是(3 x 3 x 512 x 512),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(28 x 28 x 512)。

本层之后要经过ReLU激活函数,如下图:

4.2.13 卷积

输入特征矩阵是(28 x 28 x 512),本层卷积核的宽、高、通道、个数是(3 x 3 x 512 x 512),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(28 x 28 x 512)。

本层之后要经过ReLU激活函数,如下图:

4.2.14 池化-降采样:

池化方式为MaxPool,输入特征矩阵是(28 x 28 x 512),池化核大小为2,步长为2,经过计算可知,输出特征矩阵为(14 x 14 x 512)。如下图:

4.2.15 卷积

输入特征矩阵是(14 x 14 x 512),本层卷积核的宽、高、通道、个数是(3 x 3 x 512 x 512),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(14 x 14 x 512)。

本层之后要经过ReLU激活函数,如下图:

4.2.16 卷积

输入特征矩阵是(14 x 14 x 512),本层卷积核的宽、高、通道、个数是(3 x 3 x 512 x 512),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(14 x 14 x 512)。

本层之后要经过ReLU激活函数,如下图:

4.2.17 卷积

输入特征矩阵是(14 x 14 x 512),本层卷积核的宽、高、通道、个数是(3 x 3 x 512 x 512),步长为1,padding方式为VALID,经过计算可知,输出特征矩阵为(14 x 14 x 512)。

本层之后要经过ReLU激活函数,如下图:

4.2.18 池化-降采样:

池化方式为MaxPool,输入特征矩阵是(14 x 14 x 512),池化核大小为2,步长为2,经过计算可知,输出特征矩阵为(7 x 7 x 512)。如下图:

4.2.19 全连接:

输入节点是25088,输出节点为4096,后跟ReLU激活函数,如下图:

4.2.20 全连接:

输入节点是4096,输出节点为4096,后跟ReLU激活函数,如下图:

4.2.21 全连接:

输入节点是4096,输出节点为ImageNet分类个数1000,如下图:

4.2.22 Softmax:

最后通过Softmax实现将多分类的输出值转换为范围在[0, 1]和为1的概率分布,如下图:

4.2.23实验验证

本实验主要学习vggnet的网络的相关知识,最后需要进行点击“验证”,验证成功即代表网络结构连接是正确的。

VGG系列代码实现

import torch
import torch.nn as nn
#定义VGG网络
class VGG(nn.Module):
    def __init__(self,features,num_classes=1000):
        super(VGG,self).__init__()
        #使用nn.Sequential获得的网络
        self.features=features
        self.classifier=nn.Sequential(
            # 第一层全连接层,输入特征数512*7*7,输出特征为4096
            nn.Linear(512*7*7,4096),
            # 使用激活
            nn.ReLU(True),
            # Dropout层保留概率0.5
            nn.Dropout(p=0.5),
            # 第2层全连接层,输入特征数4096,输出特征为4096
            nn.Linear(4096, 4096),
            # ReLU激活函数层,输入的张量会原地(in-place)通过ReLU激活函数。输出张量与输入张量共享存储器(memory),不产生新的张量。
            nn.ReLU(True),
            nn.Dropout(p=0.5),
            # 第三层全连接层,输入特征数为4096,输出特征数根据num_classes设置(默认为1000)
            nn.Linear(4096, num_classes)
        )
    def forward(self,x):
        x=self.features(x)
        x=torch.flatten(x,start_dim=1)
        x=self.classifier(x)
        return x


#定义一个字典,字典的键是模型的名字,值是每个模型的网络结构配置
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']
}
#定义特征提取函数,根据给定的配置cfg创建特征提取网络
def make_features(cfg:list):
    #创建一个空列表用于保存每个卷积层的实例
    layers=[]
    # 初始化输入通道数为3
    in_channels=3
    # 遍历配置列表cfg中每个元素
    for v in cfg:
        #如果当前元素是'M',则添加一个MaxPooling2d层
        if v=='M':
            # 将nn.MaxPool2d对象添加到layers列表中,该对象代表2D最大池化层,池化窗口大小为2,步长为2
            layers=layers+[nn.MaxPool2d(kernel_size=2,stride=2)]
        # 否则,认为当前元素是一个卷积层的输出通道数,添加一个Conv2d层
        else:
            # 创建一个2D卷积层,输入通道数为in_channels,输出通道数为v,卷积核大小为3,填充为1
            cov2d=nn.Conv2d(in_channels,v,kernel_size=3,padding=1)
            # 将卷积层和ReLU激活层添加到layers列表中
            layers=layers+[cov2d]
            layers=layers+[nn.ReLU(True)]
            in_channels=v
    # *layers 解包
    return nn.Sequential(*layers)


#定义一个函数,根据给定的模型名称返回一个VGG模型
# **kwargs 是 Python 的可变关键字参数,它允许函数接收任意数量的命名参数,
# 这些参数会被收集为一个字典。在深度学习模型构建中,**kwargs 常用于传递额外的配置选项,使函数更加灵活。
def vgg(model_name='vgg11',num_classes=1000):
    cfg=cfgs[model_name]
    define_conv_pool_model=make_features(cfg)
    #将define_conv_pool_model,传入VGG组成整体的网络结构
    model=VGG(features=define_conv_pool_model,num_classes=num_classes)
    return model

model=vgg(model_name='vgg19',num_classes=2)
x=torch.rand(5,3,224,224)
y=model(x)
print(y.shape)


Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐