VGGNet的名称来源于VisualGeometry Group,它是牛津大学的计算机视觉组。VGGNet的论文题目是:VeryDeep Convolutional Networks For Large-Scale Image Recognition. 本文主要罗列一些VGGNet的创新点。
最主要的贡献点:发现使用小尺寸的卷积核能够设计比较深的网络。
训练图像的大小固定为224*224,只对训练集进行减去RGB均值的预处理操作,网络的主要结构是堆叠的卷积神经网络,并不是每一个卷积层后都进行池化,滤波器的感受野只要选择3*3,即卷积核的大小为3*3,步长为1, padding为1,其优点在于能够捕捉关键信息。同时我们也使用了1*1大小的卷积核,它可以看作是输入通道的线性变换(随后是非线性)。池化层的大小为2*2,步长为2.
那接下来我们看一下VGGNet到底是怎样的一种网络架构吧.如下图所示:
图片中表格的每一列是一个网络,因为VGGNet提供了多种不同深度的网络,所以分别命名为A,A-LRN,B,C,D,E,因为我平时用的最多的是VGGNet-16,也就是上图定义的C,那我们以C为例来分析一下VGGNet:
1) 16 weight layers表明了网络的深度为16,所以我们可以称之为VGGNet-16;那么16这个数字是怎么来的呢?它是所有的卷积层和全连接层层数的和,即2个conv3-64,2个conv3-128,2个conv3-128,1个conv1-256,2个conv3-512,1个conv1-512,3个FC,即共13个.conv3-128代表卷积核大小为3*3,通道为128个。通道的个数从64,128,256,512变化,到512后就保持不变了。
2) 网络的第一个卷积层采用两个3*3*64大小的卷积核来代替了11*11和7*7大小的卷积核,并且两个卷积层之间没有池化层,很容易看出两个3*3的卷积层的操作具有5*5大小的有效感受野.(至于3*3卷积核的好处以及为两个3*3卷积和一个5*5卷积的感受野什么相当,详见最后一部分总结)
3)注意到网络中有1*1大小的卷积核,并且其输入通道和输入通道数相同。其作用是对输入通道进行线性组合,实现了跨通道的信息组合,因为卷积后接激活函数,所以同时增加了模型的非线性能力。
4)VGGNet里面有三个全连接层,前两个都有4096个单元,最后一个有1000个单元,是一个softmax层,1000表示要对1000类别进行分类。也是因为这三个全连接层的存在,导致模型参数众多。
该部分是自己用pytorch进行的一些验证性实验,仅供参考。
1.首先来看一下VGGNet16具体的模型结构:
importtorchvision.models as models
vgg16 = models.vgg16(pretrained=True)
print(vgg16)
模型输出为
VGG(
(features): Sequential(
(0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): ReLU(inplace)
(2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(3): ReLU(inplace)
(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1,ceil_mode=False)
(5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(6): ReLU(inplace)
(7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(8): ReLU(inplace)
(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1,ceil_mode=False)
(10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1,1))
(11): ReLU(inplace)
(12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1,1))
(13): ReLU(inplace)
(14): Conv2d(256, 256, kernel_size=(3, 3),stride=(1, 1), padding=(1, 1))
(15): ReLU(inplace)
(16):MaxPool2d(kernel_size=2,stride=2,padding=0, dilation=1,ceil_mode=False)
(17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(18): ReLU(inplace)
(19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1,1))
(20): ReLU(inplace)
(21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1,1))
(22): ReLU(inplace)
(23):MaxPool2d(kernel_size=2,stride=2,padding=0, dilation=1,ceil_mode=False)
(24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1,1))
(25): ReLU(inplace)
(26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1,1))
(27): ReLU(inplace)
(28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1,1))
(29): ReLU(inplace)
(30):MaxPool2d(kernel_size=2,stride=2,padding=0, dilation=1,ceil_mode=False)
)
(classifier): Sequential(
(0): Linear(in_features=25088, out_features=4096, bias=True)
(1): ReLU(inplace)
(2): Dropout(p=0.5)
(3): Linear(in_features=4096, out_features=4096, bias=True)
(4): ReLU(inplace)
(5): Dropout(p=0.5)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
)
2.看下VGGNet-16的模型参数量
model_dict= vgg16.state_dict() # acquire dict
length = 0
for key, value in model_dict.items():
print(key)
length = length + len(value)
print(length)
在feature中的参数总量为8448,而在classifier即三个全连接层的参数总量为18384个,可见全连接层是模型参数众多的根源。
假设输入卷积层的特征图大小为n*n,我们知道特征图大小的计算公式为(输入尺寸+2*padding-卷积核尺寸)/步长+1,所以我们先分析两个3*3卷积核堆叠的情况,输入卷积层的特征图经过第一个3*3卷积层后,其大小变为(n-3)/1+1=n-2,再经过第二个3*3卷积层后变为[(n-2)-3]/1+1=n-4;然后我们再分析一个5*5卷积核的情况,其大小变为(n-5)/1+1=n-4.可见,输入特征图经过两个堆叠的3*3卷积和一个5*5的卷积后,其特征图的大小是一样的,所以说这两种具有相同的感受野.
1)通过分析两个3*3卷积的感受野和一个5*5卷积的感受野相当,我们可以知道, 三个3*3卷积的感受野和一个7*7卷积的感受野相当,当我们使用三个3*3的卷积核的情况下,我们使用了三层非线性层,而1个7*7卷积层,我们只使用了一层非线性层,所以三个3*3的卷积核使得决策函数更具判别性.
2)减少了参数量。卷积核的输入和输出之间也可以想象成是全链接的,所以输入到输出的连接方式有C*C中。在输入通道和输出通道都为C的情况下,一个7*7的卷积核的可训练参数为7*7*C*C=49*C*C,三个3*3卷积核的可训练参数为3*(3*3*C*C)=27*C*C,第一个数字3代表3层,而3*3代表卷积核的大小。
1*1大小的卷积核只与本身的像素有关,不与邻近的像素有关,其重要的作用是对不同通道上的像素点进行线性组合,然后再进行非线性操作,具有升维、降维、增加模型非线性表达能力的作用。