上周我们讲了经典CNN网络AlexNet对图像分类的效果,2014年,在AlexNet出来的两年后,牛津大学提出了Vgg网络,并在ILSVRC 2014中的classification项目的比赛中取得了第2名的成绩(第一名是GoogLeNet,也是同年提出的)。在论文《Very Deep Convolutional Networks for Large-Scale Image Recognition》中,作者提出通过缩小卷积核大小来构建更深的网络。
Vgg网络结构
VGGnet是Oxford的Visual Geometry Group的team,在ILSVRC 2014上的主要工作是证明了增加网络的深度能够在一定程度上影响网络最终的性能,如下图,文章通过逐步增加网络深度来提高性能,虽然看起来有一点小暴力,没有特别多取巧的,但是确实有效,很多pretrained的方法就是使用VGG的model(主要是16和19),VGG相对其他的方法,参数空间很大,所以train一个vgg模型通常要花费更长的时间,不过公开的pretrained model让我们很方便的使用,paper中的几种模型如下:
图1 vgg网络结构
图中D和E分别为VGG-16和VGG-19,参数分别是138m和144m,是文中两个效果最好的网络结构,VGG网络结构可以看做是AlexNet的加深版,VGG在图像检测中效果很好(如:Faster-RCNN),这种传统结构相对较好的保存了图片的局部位置信息(不像GoogLeNet中引入Inception可能导致位置信息的错乱)。
我们来仔细看一下vgg16的网络结构:
图2 vgg16网络结构
从图中可以看到,每个卷积层都使用更小的3×3卷积核对图像进行卷积,并把这些小的卷积核排列起来作为一个卷积序列。通俗点来讲就是对原始图像进行3×3卷积,然后再进行3×3卷积,连续使用小的卷积核对图像进行多次卷积。
在alexnet里我们一开始的时候是用11*11的大卷积核网络,为什么在这里要用3*3的小卷积核来对图像进行卷积呢?并且还是使用连续的小卷积核?VGG一开始提出的时候刚好与LeNet的设计原则相违背,因为LeNet相信大的卷积核能够捕获图像当中相似的特征(权值共享)。AlexNet在浅层网络开始的时候也是使用9×9、11×11卷积核,并且尽量在浅层网络的时候避免使用1×1的卷积核。但是VGG的神奇之处就是在于使用多个3×3卷积核可以模仿较大卷积核那样对图像进行局部感知。后来多个小的卷积核串联这一思想被GoogleNet和ResNet等吸收。
从图1的实验结果也可以看到,VGG使用多个3x3卷积来对高维特征进行提取。因为如果使用较大的卷积核,参数就会大量地增加、运算时间也会成倍的提升。例如3x3的卷积核只有9个权值参数,使用7*7的卷积核权值参数就会增加到49个。因为缺乏一个模型去对大量的参数进行归一化、约减,或者说是限制大规模的参数出现,因此训练核数更大的卷积网络就变得非常困难了。
VGG相信如果使用大的卷积核将会造成很大的时间浪费,减少的卷积核能够减少参数,节省运算开销。虽然训练的时间变长了,但是总体来说预测的时间和参数都是减少的了。
Vgg的优势
与AlexNet相比:
相同点
整体结构分五层;
除softmax层外,最后几层为全连接层;
五层之间通过max pooling连接。
不同点
使用3×3的小卷积核代替7×7大卷积核,网络构建的比较深;
由于LRN太耗费计算资源,性价比不高,所以被去掉;
采用了更多的feature map,能够提取更多的特征,从而能够做更多特征的组合。
用PaddlePaddle实现Vgg
1.网络结构
1 #coding:utf-8 2 ''' 3 Created by huxiaoman 2017.12.12 4 vggnet.py:用vgg网络实现cifar-10分类 5 ''' 6 7 import paddle.v2 as paddle 8 9 def vgg(input): 10 def conv_block(ipt, num_filter, groups, dropouts, num_channels=None): 11 return paddle.networks.img_conv_group( 12 input=ipt, 13 num_channels=num_channels, 14 pool_size=2, 15 pool_stride=2, 16 conv_num_filter=[num_filter] * groups, 17 conv_filter_size=3, 18 conv_act=paddle.activation.Relu(), 19 conv_with_batchnorm=True, 20 conv_batchnorm_drop_rate=dropouts, 21 pool_type=paddle.pooling.Max()) 22 23 conv1 = conv_block(input, 64, 2, [0.3, 0], 3) 24 conv2 = conv_block(conv1, 128, 2, [0.4, 0]) 25 conv3 = conv_block(conv2, 256, 3, [0.4, 0.4, 0]) 26 conv4 = conv_block(conv3, 512, 3, [0.4, 0.4, 0]) 27 conv5 = conv_block(conv4, 512, 3, [0.4, 0.4, 0]) 28 29 drop = paddle.layer.dropout(input=conv5, dropout_rate=0.5) 30 fc1 = paddle.layer.fc(input=drop, size=512, act=paddle.activation.Linear()) 31 bn = paddle.layer.batch_norm( 32 input=fc1, 33 act=paddle.activation.Relu(), 34 layer_attr=paddle.attr.Extra(drop_rate=0.5)) 35 fc2 = paddle.layer.fc(input=bn, size=512, act=paddle.activation.Linear()) 36 return fc2