2014年,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司的研究员一起研发出了新的深度卷积神经网络:VGGNet,并取得了ILSVRC2014比赛分类项目的第二名(第一名是GoogLeNet,也是同年提出的)和定位项目的第一名。
VGGNet探索了卷积神经网络的深度与其性能之间的关系,成功地构筑了16~19层深的卷积神经网络,证明了增加网络的深度能够在一定程度上影响网络最终的性能,使错误率大幅下降,同时拓展性又很强,迁移到其它图片数据上的泛化性也非常好。到目前为止,VGG仍然被用来提取图像特征。
VGGNet可以看成是加深版本的AlexNet,都是由卷积层、全连接层两大部分构成
注意:由于全连接层的存在, 网络只能接收固定大小的图像尺寸
VGG由5层卷积层、3层全连接层、softmax输出层构成,层与层之间使用max-pooling(最大化池)分开,所有隐层的激活单元都采用ReLU函数。
VGG使用多个较小卷积核(3x3)的卷积层的堆叠代替一个卷积核较大的卷积层。这样在保证感受野的同时,一方面可以减少参数,另一方面相当于进行了更多的非线性映射,可以增加网络的拟合,表达,特征提取能力。
小卷积核是VGG的一个重要特点,虽然VGG是在模仿AlexNet的网络结构,但没有采用AlexNet中比较大的卷积核尺寸(如7x7),而是通过降低卷积核的大小(3x3),增加卷积子层数来达到同样的性能
VGG的作者认为两个3x3的卷积堆叠获得的感受野大小,相当一个5x5的卷积;而3个3x3卷积的堆叠获取到的感受野相当于一个7x7的卷积。这样可以增加非线性映射,也能很好地减少参数(例如7x7的参数为49个,而3个3x3的参数为27)
相比AlexNet的3x3的池化核,VGG全部采用2x2的池化核。
VGG网络第一层的通道数为64,后面每层都进行了翻倍,最多到512个通道,通道数的增加,使得更多的信息可以被提取出来。
由于卷积核专注于扩大通道数、池化专注于缩小宽和高,使得模型架构上更深更宽的同时,控制了计算量的增加规模
这里给出模型搭建的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证明了对于神经网络来说,较深的层级结构可以帮助模型更好的提取特征,所谓的更好是指可以提取到蕴含语义信息更多的高级特征,这种高级语义的特征对分类任务很有帮助。