1、VGGNet 模型简介

VGG Net由牛津大学的视觉几何组(Visual Geometry Group)和 Google DeepMind公司的研究员一起研发的的深度卷积神经网络,在 ILSVRC 2014 上取得了第二名的成绩,将 Top-5错误率降到7.3%。它主要的贡献是展示出网络的深度(depth)是算法优良性能的关键部分。目前使用比较多的网络结构主要有ResNet(152-1000层),GooleNet(22层),VGGNet(19层),大多数模型都是基于这几个模型上改进,采用新的优化算法,多模型融合等。到目前为止,VGG Net 依然经常被用来提取图像特征。

2、VGGNet的特点 

1) 结构简洁
VGG由5层卷积层、3层全连接层、softmax输出层构成,层与层之间使用max-pooling(最大化池)分开,所有隐层的激活单元都采用ReLU函数。
2) 小卷积核和多卷积子层
VGG使用多个较小卷积核(3x3)的卷积层代替一个卷积核较大的卷积层,一方面可以减少参数,另一方面相当于进行了更多的非线性映射,可以增加网络的拟合/表达能力。 小卷积核是VGG的一个重要特点,虽然VGG是在模仿AlexNet的网络结构,但没有采用AlexNet中比较大的卷积核尺寸(如7x7),而是通过降低卷积核的大小(3x3),增加卷积子层数来达到同样的性能(VGG:从1到4卷积子层,AlexNet:1子层)。 VGG的作者认为两个3x3的卷积堆叠获得的感受野大小,相当一个5x5的卷积;而3个3x3卷积的堆叠获取到的感受野相当于一个7x7的卷积。这样可以增加非线性映射,也能很好地减少参数(例如7x7的参数为49个,而3个3x3的参数为27)。
3) 小池化核
相比AlexNet的3x3的池化核,VGG全部采用2x2的池化核。
4) 通道数多
VGG网络第一层的通道数为64,后面每层都进行了翻倍,最多到512个通道,通道数的增加,使得更多的信息可以被提取出来。
5) 层数更深、特征图更宽
由于卷积核专注于扩大通道数、池化专注于缩小宽和高,使得模型架构上更深更宽的同时,控制了计算量的增加规模。
6) 全连接转卷积(测试阶段)
这也是VGG的一个特点,在网络测试阶段将训练阶段的三个全连接替换为三个卷积,使得测试得到的全卷积网络因为没有全连接的限制,因而可以接收任意宽或高为的输入,这在测试阶段很重要。

3、VGGNet的网络结构

分别使用了A、A-LRN、B、C、D、E这6种网络结构进行测试,这6种网络结构相似,都是由5层卷积层、3层全连接层组成,其中区别在于每个卷积层的子层数量不同,从A至E依次增加(子层数量从1到4),总的网络深度从11层到19层(添加的层以粗体显示),表格中的卷积层参数表示为“conv⟨感受野大小⟩-通道数⟩”,例如con3-128,表示使用3x3的卷积核,通道数为128。为了简洁起见,在表格中不显示ReLU激活功能。 其中,网络结构D就是著名的VGG16,网络结构E就是著名的VGG19。

网络结构D(VGG16)为例,介绍其处理过程如下:

1)输入224x224x3的图片,经64个3x3的卷积核作两次卷积+ReLU,卷积后的尺寸变为224x224x64
2)作max pooling(最大化池化),池化单元尺寸为2x2(效果为图像尺寸减半),池化后的尺寸变为112x112x64
3)经128个3x3的卷积核作两次卷积+ReLU,尺寸变为112x112x128
4)作2x2的max pooling池化,尺寸变为56x56x128
5)经256个3x3的卷积核作三次卷积+ReLU,尺寸变为56x56x256
6)作2x2的max pooling池化,尺寸变为28x28x256
7)经512个3x3的卷积核作三次卷积+ReLU,尺寸变为28x28x512
8)作2x2的max pooling池化,尺寸变为14x14x512
9)经512个3x3的卷积核作三次卷积+ReLU,尺寸变为14x14x512
10)作2x2的max pooling池化,尺寸变为7x7x512
11)与两层1x1x4096,一层1x1x1000进行全连接+ReLU(共三层)
12)通过softmax输出1000个预测结果

 A、A-LRN、B、C、D、E这6种网络结构比较:

A、A-LRN、B、C、D、E这6种网络结构的深度虽然从11层增加至19层,但参数量变化不大,这是由于基本上都是采用了小卷积核(3x3,只有9个参数),这6种结构的参数数量(百万级)并未发生太大变化,这是因为在网络中,参数主要集中在全连接层。

A、A-LRN、B、C、D、E这6种网络结构进行单尺度的评估,错误率结果如下:

从上表可以看出:

1)LRN层无性能增益(A-LRN) VGG作者通过网络A-LRN发现,AlexNet曾经用到的LRN层(local response normalization,局部响应归一化)并没有带来性能的提升,因此在其它组的网络中均没再出现LRN层。
2)随着深度增加,分类性能逐渐提高(A、B、C、D、E) 从11层的A到19层的E,网络深度增加对top1和top5的错误率下降很明显。
多个小卷积核比单个大卷积核性能好(B) VGG作者做了实验用B和自己一个不在实验组里的较浅网络比较,较浅网络用conv5x5来代替B的两个conv3x3,结果显示多个小卷积核比单个大卷积核效果要好。

总结:

1)通过增加深度能有效地提升性能;
2)最佳模型:VGG16,从头到尾只有3x3卷积与2x2池化,简洁优美;
3)卷积可代替全连接,可适应各种尺寸的图片。

4  VGGNet 模型TensorFlow实现

开发环境: Python - 3.0、TensorFlow - 1.4.0、无GPU

# -*- coding: utf-8 -*-
"""
Created on 2017
@author: 黄文坚、唐源
""" from datetime import datetime
import math
import time
import tensorflow as tf def conv_op(input_op, name, kh, kw, n_out, dh, dw, p):
n_in = input_op.get_shape()[-1].value with tf.name_scope(name) as scope:
#定义卷积层参数:前两个为尺寸 ?*?、第三个为当前层节点矩阵的深度 、第四个为卷积层的深度
kernel = tf.get_variable(scope+"w",
shape=[kh, kw, n_in, n_out],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
#tf.nn.conv2d 提供了一个方便的卷积层前向传播函数
#参数1:当前层的节点矩阵,四维矩阵,第一维度对应一个输入batch,如第一张图片,第二张图片..
#参数2:卷积层参数
#参数3:不同维度上的步长(第一维、最后一维必须为1)
#参数4:提供'SAME'和'VALLD'选择,'SAME'为添加全0填充,'VALLD'为不添加
conv = tf.nn.conv2d(input_op, kernel, (1, dh, dw, 1), padding='SAME')
#定义偏置项,及下一层节点矩阵的深度 (参数共享)
bias_init_val = tf.constant(0.0, shape=[n_out], dtype=tf.float32)
biases = tf.Variable(bias_init_val, trainable=True, name='b')
#tf.nn.bias_add提供给每个conv节点加上偏置项
z = tf.nn.bias_add(conv, biases)
#将计算结果通过ReLU激活函数完成去线性化
activation = tf.nn.relu(z, name=scope)
p += [kernel, biases]
return activation def fc_op(input_op, name, n_out, p):
n_in = input_op.get_shape()[-1].value with tf.name_scope(name) as scope:
kernel = tf.get_variable(scope+"w",
shape=[n_in, n_out],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
biases = tf.Variable(tf.constant(0.1, shape=[n_out], dtype=tf.float32), name='b')
activation = tf.nn.relu_layer(input_op, kernel, biases, name=scope)
p += [kernel, biases]
return activation def mpool_op(input_op, name, kh, kw, dh, dw):
#tf.nn.max_pool 提供了一个方便的最大池化层的前向传播过程。
#tf.nn.avg_pool 提供了一个方便的平均池化层的前向传播过程,两者参数一致。
#参数1:四维矩阵,第一维度对应一个输入batch,如第一张图片,第二张图片..
#参数2:ksize为过滤器参数,常为[1, 2, 2, 1]、[1, 3, 3, 1]
#参数3:不同维度上的步长(第一维、最后一维必须为1)
#参数4:提供'SAME'和'VALLD'选择,'SAME'为添加全0填充,'VALLD'为不添加
return tf.nn.max_pool(input_op,
ksize=[1, kh, kw, 1],
strides=[1, dh, dw, 1],
padding='SAME',
name=name) def inference_op(input_op, keep_prob):
p = []
# assume input_op shape is 224x224x3 # block 1 -- outputs 112x112x64
conv1_1 = conv_op(input_op, name="conv1_1", kh=3, kw=3, n_out=64, dh=1, dw=1, p=p)
conv1_2 = conv_op(conv1_1, name="conv1_2", kh=3, kw=3, n_out=64, dh=1, dw=1, p=p)
pool1 = mpool_op(conv1_2, name="pool1", kh=2, kw=2, dw=2, dh=2) # block 2 -- outputs 56x56x128
conv2_1 = conv_op(pool1, name="conv2_1", kh=3, kw=3, n_out=128, dh=1, dw=1, p=p)
conv2_2 = conv_op(conv2_1, name="conv2_2", kh=3, kw=3, n_out=128, dh=1, dw=1, p=p)
pool2 = mpool_op(conv2_2, name="pool2", kh=2, kw=2, dh=2, dw=2) # # block 3 -- outputs 28x28x256
conv3_1 = conv_op(pool2, name="conv3_1", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
conv3_2 = conv_op(conv3_1, name="conv3_2", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
conv3_3 = conv_op(conv3_2, name="conv3_3", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
pool3 = mpool_op(conv3_3, name="pool3", kh=2, kw=2, dh=2, dw=2) # block 4 -- outputs 14x14x512
conv4_1 = conv_op(pool3, name="conv4_1", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
conv4_2 = conv_op(conv4_1, name="conv4_2", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
conv4_3 = conv_op(conv4_2, name="conv4_3", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
pool4 = mpool_op(conv4_3, name="pool4", kh=2, kw=2, dh=2, dw=2) # block 5 -- outputs 7x7x512
conv5_1 = conv_op(pool4, name="conv5_1", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
conv5_2 = conv_op(conv5_1, name="conv5_2", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
conv5_3 = conv_op(conv5_2, name="conv5_3", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
pool5 = mpool_op(conv5_3, name="pool5", kh=2, kw=2, dw=2, dh=2) # flatten
#将第五段卷积网络的输出结果扁平化,转化为 7*7*512=25088的一维向量
shp = pool5.get_shape()
flattened_shape = shp[1].value * shp[2].value * shp[3].value
resh1 = tf.reshape(pool5, [-1, flattened_shape], name="resh1") # fully connected
#连接一个隐含节点为4096的全连接层,激活函数为ReLU,然后连接一个Dropout层
fc6 = fc_op(resh1, name="fc6", n_out=4096, p=p)
fc6_drop = tf.nn.dropout(fc6, keep_prob, name="fc6_drop") #连接一个隐含节点为4096的全连接层,激活函数为ReLU,然后连接一个Dropout层
fc7 = fc_op(fc6_drop, name="fc7", n_out=4096, p=p)
fc7_drop = tf.nn.dropout(fc7, keep_prob, name="fc7_drop") #连接一个隐含节点为4096的全连接层,激活函数为ReLU,然后连接一个Softmax进行处理得到分类输出概率
fc8 = fc_op(fc7_drop, name="fc8", n_out=1000, p=p)
softmax = tf.nn.softmax(fc8)
predictions = tf.argmax(softmax, 1)
return predictions, softmax, fc8, p def time_tensorflow_run(session, target, feed, info_string):
num_steps_burn_in = 10
total_duration = 0.0
total_duration_squared = 0.0
for i in range(num_batches + num_steps_burn_in):
start_time = time.time()
_ = session.run(target, feed_dict=feed)
duration = time.time() - start_time
if i >= num_steps_burn_in:
if not i % 10:
print ('%s: step %d, duration = %.3f' %
(datetime.now(), i - num_steps_burn_in, duration))
total_duration += duration
total_duration_squared += duration * duration
mn = total_duration / num_batches
vr = total_duration_squared / num_batches - mn * mn
sd = math.sqrt(vr)
print ('%s: %s across %d steps, %.3f +/- %.3f sec / batch' %
(datetime.now(), info_string, num_batches, mn, sd)) def run_benchmark():
with tf.Graph().as_default():
image_size = 224
images = tf.Variable(tf.random_normal([batch_size,
image_size,
image_size, 3],
dtype=tf.float32,
stddev=1e-1)) keep_prob = tf.placeholder(tf.float32)
predictions, softmax, fc8, p = inference_op(images, keep_prob) init = tf.global_variables_initializer() config = tf.ConfigProto()
config.gpu_options.allocator_type = 'BFC'
sess = tf.Session(config=config)
sess.run(init) time_tensorflow_run(sess, predictions, {keep_prob:1.0}, "Forward") objective = tf.nn.l2_loss(fc8)
grad = tf.gradients(objective, p)
time_tensorflow_run(sess, grad, {keep_prob:0.5}, "Forward-backward") if __name__ == "__main__":
batch_size=32
num_batches=100
run_benchmark()

参考文献

[1] https://my.oschina.net/u/876354/blog/1634322

[2] Simonyan K , Zisserman A . Very Deep Convolutional Networks for Large-Scale Image Recognition[J]. Computer Science, 2014.

[3] 黄文坚、唐源等. TensorFlow 实战 [M] , 北京:电子工业出版社,2017.

CNN-3: VGGNet 卷积神经网络模型的更多相关文章

  1. 使用PyTorch简单实现卷积神经网络模型

    这里我们会用 Python 实现三个简单的卷积神经网络模型:LeNet .AlexNet .VGGNet,首先我们需要了解三大基础数据集:MNIST 数据集.Cifar 数据集和 ImageNet 数 ...

  2. 利用Tensorflow实现卷积神经网络模型

    首先看一下卷积神经网络模型,如下图: 卷积神经网络(CNN)由输入层.卷积层.激活函数.池化层.全连接层组成,即INPUT-CONV-RELU-POOL-FC池化层:为了减少运算量和数据维度而设置的一 ...

  3. 手写数字识别 ----卷积神经网络模型官方案例注释(基于Tensorflow,Python)

    # 手写数字识别 ----卷积神经网络模型 import os import tensorflow as tf #部分注释来源于 # http://www.cnblogs.com/rgvb178/p/ ...

  4. CNN-1: LeNet-5 卷积神经网络模型

    1.LeNet-5模型简介 LeNet-5 模型是 Yann LeCun 教授于 1998 年在论文 Gradient-based learning applied to document      ...

  5. 【TensorFlow/简单网络】MNIST数据集-softmax、全连接神经网络,卷积神经网络模型

    初学tensorflow,参考了以下几篇博客: soft模型 tensorflow构建全连接神经网络 tensorflow构建卷积神经网络 tensorflow构建卷积神经网络 tensorflow构 ...

  6. CNN-2: AlexNet 卷积神经网络模型

    1.AlexNet 模型简介 由于受到计算机性能的影响,虽然LeNet在图像分类中取得了较好的成绩,但是并没有引起很多的关注. 知道2012年,Alex等人提出的AlexNet网络在ImageNet大 ...

  7. caffe中LetNet-5卷积神经网络模型文件lenet.prototxt理解

    caffe在 .\examples\mnist文件夹下有一个 lenet.prototxt文件,这个文件定义了一个广义的LetNet-5模型,对这个模型文件逐段分解一下. name: "Le ...

  8. CNN-4: GoogLeNet 卷积神经网络模型

    1.GoogLeNet 模型简介 GoogLeNet 是2014年Christian Szegedy提出的一种全新的深度学习结构,该模型获得了ImageNet挑战赛的冠军. 2.GoogLeNet 模 ...

  9. 吴裕雄--天生自然python Google深度学习框架:经典卷积神经网络模型

    import tensorflow as tf INPUT_NODE = 784 OUTPUT_NODE = 10 IMAGE_SIZE = 28 NUM_CHANNELS = 1 NUM_LABEL ...

随机推荐

  1. 用python写一个GitHub Trending Api

    GitHub 给了开发者相当丰富的 API 接口 https://developer.github.com/v3/,包括认证,搜索,活动等接口,但就是没有提供获取 Trending 的接口.因此,需要 ...

  2. exam9.3

    #   用  户  名  公园 计划 抽卡   总分  19 859乔屹 100 03:15:05 40 03:14:01   140 03:15:05 emm 怎么讲 T2我把自己优化掉了40分 优 ...

  3. Gluon学习02-使用GPU

    小书匠kindle 目录,方便快速定位: 1.安装cuda与cudnn 2.安装mxnet-gpu 本机环境介绍: 系统:Linuxmint Python版本:Python3 1.安装cuda与cud ...

  4. spring boot 扫描 其他jar包里面的 mapper xml

    启动类配置扫描 goods.mapper为当前项目mapper路径 ,common.mpper为其他jar包. 1. 2.mybatis.mapper-locations=classpath*:map ...

  5. javascript的this与prototype的区别

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. JavaScript--自定义事件Event

    在开发过程中,js原生事件不足以满意开发需求,需要开发者自定义事件. 一.Event Event()构造函数创建一个新的Event. event = new Event(typeArg,eventIn ...

  7. ORACLE批量导入图片到BLOB字段

    要插入图片的表不是固定的,而且是批量插入很多张,还要考虑到因为图片的文件名错误,修改后要再次插入,此时应避免已经插入的重复执行操作, 浪费时间. 所以就选择先用一张临时表来暂时保存从文件系统读取的照片 ...

  8. ICEM rpl文件简要讲解【转载】

    转载自:http://blog.sina.com.cn/s/blog_90affd9801016xti.html 很多人问ICEM的rpl怎样录制的问题,为什么CFX调用时老是报错,这里开个帖子简单讲 ...

  9. @AUTORELEASEPOOL

    Swift 在内存管理上使用的是自动引用计数 (ARC) 的一套方法,在 ARC 中虽然不需要手动地调用像是 retain,release 或者是 autorelease 这样的方法来管理引用计数,但 ...

  10. kafka简介&使用

    架构 几个角色概念 broker 缓存代理,Kafa集群中的一台或多台服务器统称为broker.kafka集群有多个kafka实例组成,每个实例(server)成为broker.每个类型的消息被定义为 ...