【深度学习】VGGNet原理解析及实现

VGGNet由牛津大学的视觉几何组(Visual Geometry Group)和Google DeepMind公司的研究员共同提出,是ILSVRC-2014中定位任务第一名和分类任务第二名。其突出贡献在于证明使用很小的卷积(3*3),增加网络深度可以有效提升模型的效果,而且VGGNet对其他数据集具有很好的泛化能力。到目前为止,VGGNet依然经常被用来提取图像特征

VGGNet探索了CNN的深度及其性能之间的关系,通过反复堆叠3*3的小型卷积核和2*2的最大池化层,VGGNet成功的构筑了16-19层深的CNN。

一、VGGNet结构

 VGGNet有A-E七种结构,从A-E网络逐步变深,但是参数量并没有增长很多(图6-7),原因为:参数量主要消耗在最后3个全连接层,而前面的卷积层虽然层数多,但消耗的参数量不大。不过,卷积层的训练比较耗时,因为其计算量大。

其中,D和E是常说的VGGNet-16和VGGNet-19。C很有意思,相比于B多了几个1*1的卷积层,1*1卷积的意义在于线性变换,而输入的通道数和输出的通道数不变,没有发生降维。

VGG的性能:

VGGNet网络特点:

1. VGGNet拥有5段卷积,每段卷积内有2-3个卷积层,同时每段尾部都会连接一个最大池化层(用来缩小图片)。

2. 每段内的卷积核数量一样,越后边的段内卷积核数量越多,依次为:64-128-256-512-512

3. 越深的网络效果越好。(图6-9)

4. LRN层作用不大(作者结论)

5. 1*1的卷积也是很有效的,但是没有3*3的卷积好,大一些的卷积核可以学习更大的空间特征。

为什么一个段内有多个3*3的卷积层堆叠?

这是个非常有用的设计。如下图所示,2个3*3的卷积层串联相当于1个5*5的卷积层,即一个像素会跟周围5*5的像素产生关联,可以说感受野大小为5*5。而3个3*3的卷积层相当于1个7*7的卷积层。并且,两个3*3的卷积层的参数比1个5*5的更少,前者为2*3*3=18,后者为1*5*5=25。

更重要的是,2个3*3的卷积层比1个5*5的卷积层有更多的非线性变换(前者可使用2次ReLu函数,后者只有两次),这使得CNN对特征的学习能力更强。

所以3*3的卷积层堆叠的优点为:

(1)参数量更小

(2)小的卷积层比大的有更多的非线性变换,使得CNN对特征的学习能力更强。

与其他网络对比:

与ILSVRC-2012和ILSVRC-2013最好结果相比,VGGNet优势很大。与GoogLeNet对比,虽然7个网络集成效果不如GoogLeNet,但是单一网络测试误差好一些,而且只用2个网络集成效果与GoogLeNet的7网络集成差不多。

二、VGGNet实现

tensorboard可视化的VGGNet结构:

           

1.卷积层操作

    因为要多次实现卷积操作,所以写了一个单独的卷积层实现。

def conv_op(input,kh,kw,n_out,dh,dw,parameters,name):"""定义卷积层的操作:param input: 输入的tensor:param kh:卷积核的高:param kw:卷积核的宽:param n_out:输出通道数(即卷积核的数量):param dh:步长的高:param dw:步长的宽:param parameters:参数列表:param name:层的名字:return:返回卷积层的结果"""n_in = input.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())conv=tf.nn.conv2d(input,kernel,[1,dh,dw,1],padding='SAME')biases=tf.Variable(tf.constant(0.0,shape=[n_out],dtype=tf.float32),trainable=True,name='b')z=tf.nn.bias_add(conv,biases) # wx+bactivation =tf.nn.relu(z,name=scope)parameters +=[kernel,biases]return activation

2.全连接层操作

def fc_op(input,n_out,parameters,name):"""定义全连接层操作注意:卷积层的结果要做扁平化才能和fc层相连接;此全连接操作带着RELU:param input: 输入的tensor:param n_out: 输出通道数(即神经元的数量):param parameters: 参数列表:param name: 层的名字:return: 返回全连接层的结果"""


n_in=input.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), trainable=True, name='b') activation=tf.nn.relu(tf.matmul(input,kernel)+biases, name=scope) #ReLU parameters +=[kernel,biases] return activation

3.最大池化操作

def maxPool_op(input,kh,kw,dh,dw,name):return tf.nn.max_pool(input,ksize=[1,kh,kw,1],strides=[1,dh,dw,1],padding='SAME',name=name)

4.VGGNet实现

def vggNet(input,keep_prob):parameters =[]#conv1段conv1_1 =conv_op(input,kh=3,kw=3,n_out=64,dh=1,dw=1,parameters=parameters,name='conv1_1')conv1_2 =conv_op(conv1_1, kh=3, kw=3, n_out=64, dh=1, dw=1,parameters=parameters, name='conv1_2')pool1 =maxPool_op(conv1_2,kh=2,kw=2,dh=2,dw=2,name='pool1')# conv2段conv2_1 = conv_op(pool1, kh=3, kw=3, n_out=128, dh=1, dw=1,parameters=parameters, name='conv2_1')conv2_2 = conv_op(conv2_1, kh=3, kw=3, n_out=128, dh=1, dw=1,parameters=parameters, name='conv2_2')pool2 = maxPool_op(conv2_2, kh=2, kw=2, dh=2, dw=2, name='pool2')# conv3段conv3_1 = conv_op(pool2, kh=3, kw=3, n_out=256, dh=1, dw=1,parameters=parameters, name='conv3_1')conv3_2 = conv_op(conv3_1, kh=3, kw=3, n_out=256, dh=1, dw=1,parameters=parameters, name='conv3_2')conv3_3 = conv_op(conv3_2, kh=3, kw=3, n_out=256, dh=1, dw=1,parameters=parameters, name='conv3_3')pool3 = maxPool_op(conv3_3, kh=2, kw=2, dh=2, dw=2, name='pool3')# conv4段conv4_1 = conv_op(pool3, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv4_1')conv4_2 = conv_op(conv4_1, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv4_2')conv4_3 = conv_op(conv4_2, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv4_3')pool4 = maxPool_op(conv4_3, kh=2, kw=2, dh=2, dw=2, name='pool4')# conv5段conv5_1 = conv_op(pool4, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv5_1')conv5_2 = conv_op(conv5_1, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv5_2')conv5_3 = conv_op(conv5_2, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv5_3')pool5 = maxPool_op(conv5_3, kh=2, kw=2, dh=2, dw=2, name='pool5')#将最后一个卷积层的结果扁平化:每个样本占一行conv_shape=pool5.get_shape()col=conv_shape[1].value *conv_shape[2].value * conv_shape[3].valueflat=tf.reshape(pool5, [-1,col],name='flat')# fc6段fc6 =fc_op(input=flat,n_out=4096,parameters=parameters,name='fc6')fc6_dropout =tf.nn.dropout(fc6,keep_prob,name='fc6_drop')# fc7段fc7 = fc_op(input=fc6_dropout, n_out=4096, parameters=parameters, name='fc7')fc7_dropout = tf.nn.dropout(fc7, keep_prob, name='fc7_drop')# fc8段:最后一个全连接层,使用softmax进行处理得到分类输出概率fc8=fc_op(input=fc7_dropout,n_out=1000,parameters=parameters,name='fc8')softmax =tf.nn.softmax(fc8)predictions =tf.arg_max(softmax,1)return predictions,softmax,fc8,parameters

5.测评:前向和反向用时测评

def time_compute(session, target, feed,info_string):num_batch = 100 #100num_step_burn_in = 10  # 预热轮数,头几轮迭代有显存加载、cache命中等问题可以因此跳过total_duration = 0.0  # 总时间total_duration_squared = 0.0for i in range(num_batch + num_step_burn_in):start_time = time.time()_ = session.run(target,feed_dict=feed )duration = time.time() - start_timeif i >= num_step_burn_in:if i % 10 == 0:  # 每迭代10次显示一次durationprint("%s: step %d,duration=%.5f " % (datetime.now(), i - num_step_burn_in, duration))total_duration += durationtotal_duration_squared += duration * durationtime_mean = total_duration / num_batchtime_variance = total_duration_squared / num_batch - time_mean * time_meantime_stddev = math.sqrt(time_variance)# 迭代完成,输出print("%s: %s across %d steps,%.3f +/- %.3f sec per batch " %(datetime.now(), info_string, num_batch, time_mean, time_stddev))

6.运行结果

为了节约时间,设置 batch_size = 2 ,运行结果如下:前向预测和反向学习

前向预测:

反向学习

反向是前向用时的3-4倍。

【附录】整体代码

# -*- coding:utf-8 -*-
"""
@author:Lisa
@file:VggNet.py.py
@note:from<tensorflow实战>
@time:2018/6/25 0025下午 7:29
"""import tensorflow as tf
import math
import time
from datetime import datetimedef conv_op(input,kh,kw,n_out,dh,dw,parameters,name):"""定义卷积层的操作:param input: 输入的tensor:param kh:卷积核的高:param kw:卷积核的宽:param n_out:输出通道数(即卷积核的数量):param dh:步长的高:param dw:步长的宽:param parameters:参数列表:param name:层的名字:return:返回卷积层的结果"""n_in = input.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())conv=tf.nn.conv2d(input,kernel,[1,dh,dw,1],padding='SAME')biases=tf.Variable(tf.constant(0.0,shape=[n_out],dtype=tf.float32),trainable=True,name='b')z=tf.nn.bias_add(conv,biases) # wx+bactivation =tf.nn.relu(z,name=scope)parameters +=[kernel,biases]return activationdef fc_op(input,n_out,parameters,name):"""定义全连接层操作注意:卷积层的结果要做扁平化才能和fc层相连接:param input: 输入的tensor:param n_out: 输出通道数(即神经元的数量):param parameters: 参数列表:param name: 层的名字:return: 返回全连接层的结果"""n_in=input.get_shape()[-1].valuewith 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),trainable=True, name='b')activation=tf.nn.relu(tf.matmul(input,kernel)+biases, name=scope)parameters +=[kernel,biases]return activationdef maxPool_op(input,kh,kw,dh,dw,name):return tf.nn.max_pool(input,ksize=[1,kh,kw,1],strides=[1,dh,dw,1],padding='SAME',name=name)def vggNet(input,keep_prob):parameters =[]#conv1段conv1_1 =conv_op(input,kh=3,kw=3,n_out=64,dh=1,dw=1,parameters=parameters,name='conv1_1')conv1_2 =conv_op(conv1_1, kh=3, kw=3, n_out=64, dh=1, dw=1,parameters=parameters, name='conv1_2')pool1 =maxPool_op(conv1_2,kh=2,kw=2,dh=2,dw=2,name='pool1')# conv2段conv2_1 = conv_op(pool1, kh=3, kw=3, n_out=128, dh=1, dw=1,parameters=parameters, name='conv2_1')conv2_2 = conv_op(conv2_1, kh=3, kw=3, n_out=128, dh=1, dw=1,parameters=parameters, name='conv2_2')pool2 = maxPool_op(conv2_2, kh=2, kw=2, dh=2, dw=2, name='pool2')# conv3段conv3_1 = conv_op(pool2, kh=3, kw=3, n_out=256, dh=1, dw=1,parameters=parameters, name='conv3_1')conv3_2 = conv_op(conv3_1, kh=3, kw=3, n_out=256, dh=1, dw=1,parameters=parameters, name='conv3_2')conv3_3 = conv_op(conv3_2, kh=3, kw=3, n_out=256, dh=1, dw=1,parameters=parameters, name='conv3_3')pool3 = maxPool_op(conv3_3, kh=2, kw=2, dh=2, dw=2, name='pool3')# conv4段conv4_1 = conv_op(pool3, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv4_1')conv4_2 = conv_op(conv4_1, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv4_2')conv4_3 = conv_op(conv4_2, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv4_3')pool4 = maxPool_op(conv4_3, kh=2, kw=2, dh=2, dw=2, name='pool4')# conv5段conv5_1 = conv_op(pool4, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv5_1')conv5_2 = conv_op(conv5_1, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv5_2')conv5_3 = conv_op(conv5_2, kh=3, kw=3, n_out=512, dh=1, dw=1,parameters=parameters, name='conv5_3')pool5 = maxPool_op(conv5_3, kh=2, kw=2, dh=2, dw=2, name='pool5')#将最后一个卷积层的结果扁平化:每个样本占一行conv_shape=pool5.get_shape()col=conv_shape[1].value *conv_shape[2].value * conv_shape[3].valueflat=tf.reshape(pool5, [-1,col],name='flat')# fc6段fc6 =fc_op(input=flat,n_out=4096,parameters=parameters,name='fc6')fc6_dropout =tf.nn.dropout(fc6,keep_prob,name='fc6_drop')# fc7段fc7 = fc_op(input=fc6_dropout, n_out=4096, parameters=parameters, name='fc7')fc7_dropout = tf.nn.dropout(fc7, keep_prob, name='fc7_drop')# fc8段:最后一个全连接层,使用softmax进行处理得到分类输出概率fc8=fc_op(input=fc7_dropout,n_out=1000,parameters=parameters,name='fc8')softmax =tf.nn.softmax(fc8)predictions =tf.arg_max(softmax,1)return predictions,softmax,fc8,parametersdef time_compute(session, target, feed,info_string):num_batch = 100 #100num_step_burn_in = 10  # 预热轮数,头几轮迭代有显存加载、cache命中等问题可以因此跳过total_duration = 0.0  # 总时间total_duration_squared = 0.0for i in range(num_batch + num_step_burn_in):start_time = time.time()_ = session.run(target,feed_dict=feed )duration = time.time() - start_timeif i >= num_step_burn_in:if i % 10 == 0:  # 每迭代10次显示一次durationprint("%s: step %d,duration=%.5f " % (datetime.now(), i - num_step_burn_in, duration))total_duration += durationtotal_duration_squared += duration * durationtime_mean = total_duration / num_batchtime_variance = total_duration_squared / num_batch - time_mean * time_meantime_stddev = math.sqrt(time_variance)# 迭代完成,输出print("%s: %s across %d steps,%.3f +/- %.3f sec per batch " %(datetime.now(), info_string, num_batch, time_mean, time_stddev))def main():with tf.Graph().as_default():"""仅使用随机图片数据 测试前馈和反馈计算的耗时"""image_size = 224batch_size = 2   #32images = tf.Variable(tf.random_normal([batch_size, image_size, image_size, 3],dtype=tf.float32, stddev=0.1))keep_prob=tf.placeholder(tf.float32)predictions,softmax,fc8, parameters = vggNet(images,keep_prob)init = tf.global_variables_initializer()sess = tf.Session()sess.run(init)"""AlexNet forward 计算的测评传入的target:fc8(即最后一层的输出)优化目标:loss使用tf.gradients求相对于loss的所有模型参数的梯度AlexNet Backward 计算的测评target:grad"""time_compute(sess, target=fc8, feed={keep_prob:1.0},info_string="Forward")obj = tf.nn.l2_loss(fc8)grad = tf.gradients(obj, parameters)time_compute(sess, grad, feed={keep_prob:0.5},info_string="Forward-backward")if __name__ == "__main__":main()

------------------------------------------------------         END      ----------------------------------------------------------

参考:

《tensorflow实战》黄文坚(本文内容及代码大多源于此书,感谢!)

大牛论文《VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION 》Karen Simonyan 等

VGGNet笔记   https://blog.csdn.net/muyiyushan/article/details/62895202(翻译)

【深度学习】VGGNet原理解析及实现相关推荐

  1. [深度学习主流框架解析一] Caffe

    [深度学习主流框架解析一] Caffe 1.模型文件协议解析 重点解析caffe.proto模型文件中的内容,整体的模型文件可见下图. 模型结构相关特性: 含有训练和测试推理的开关和相关的必要参数 不 ...

  2. Python大数据综合应用 :零基础入门机器学习、深度学习算法原理与案例

    机器学习.深度学习算法原理与案例实现暨Python大数据综合应用高级研修班 一.课程简介 课程强调动手操作:内容以代码落地为主,以理论讲解为根,以公式推导为辅.共4天8节,讲解机器学习和深度学习的模型 ...

  3. TensorFlow深度学习算法原理与编程实战 人工智能机器学习技术丛书

    作者:蒋子阳 著 出版社:中国水利水电出版社 品牌:智博尚书 出版时间:2019-01-01 TensorFlow深度学习算法原理与编程实战 人工智能机器学习技术丛书 ISBN:97875170682 ...

  4. 深度学习算法原理_用于对象检测的深度学习算法的基本原理

    深度学习算法原理 You just got a new drone and you want it to be super smart! Maybe it should detect whether ...

  5. python原理书籍_python书籍推荐:《深入浅出深度学习:原理剖析与Python实践》

    在过去的这十年,深度学习已经席卷了整个科技界和工业界,2016年谷歌阿尔法狗打败围棋世界冠军李世石,更是使其成为备受瞩目的技术焦点. 今日,小编就为大家推荐一本能让初学者和"老司机" ...

  6. 没人说得清深度学习的原理 只是把它当作一个黑箱来使

    没人说得清深度学习的原理 只是把它当作一个黑箱来使 人类正在慢慢接近世界的本质--物质只是承载信息模式的载体.人脑之外的器官都只是保障这一使命的给养舰队. 自从去年AlphaGo 完虐李世乭,深度学习 ...

  7. 深度学习经典网络解析图像分类篇(二):AlexNet

    深度学习经典网络解析图像分类篇(二):AlexNet 1.背景介绍 2.ImageNet 3.AlexNet 3.1AlexNet简介 3.2AlexNet网络架构 3.2.1第一层(CONV1) 3 ...

  8. [深度学习主流框架解析一] Onnx

    [深度学习主流框架解析一] Onnx 1.模型文件协议解析 Onnx同样也是采用了Protobuf协议进行模型文件的整体构建,与Caffe相比,Onnx使用了多proto文件描述模型.因此,解析Onn ...

  9. [深度学习] 面试常见问题+解析汇总

    什么是深度学习,与传统机器学习算法的区别? (1)传统机器学习算法,在一开始,性能(识别率)会随着数据的增加而增加,但一段时间后,它的性能会进入平台期.这些模型无法处理海量数据. (2)最近20年来, ...

最新文章

  1. Spring Boot 2.x基础教程:事务管理入门
  2. linux关闭方防火墙的命令
  3. 清华大学计算机专业学生埃朗读村,《朗读者~矣晓沅(清华大学计算机研究生  彝族)》...
  4. linux下的各种shell介绍(bash和dash转换)
  5. 最后解密的两弹元勋,众帅之帅朱光亚
  6. [CTO札记]Google数字图书馆对中国版权的威胁
  7. Kryo为什么比Hessian快
  8. MySql的基本操作流程-SAE的MySQL-PHP
  9. 国产操作系统往事:四十年激变,终再起风云
  10. new hashmap 初始大小_你们要的HashMap工作原理!它来了!!!
  11. 国庆期间新闻回顾:微软智斗盗版 华为出手3Com
  12. python小绵羊怎么画_使用Python的turtle画小绵羊
  13. php获取 微信unionid,微信授权登录获取openId和unionId
  14. ISIS协议原理与配置
  15. Some file crunching failed, see logs for details解决办法
  16. 微观交通仿真软件分析比较
  17. SQL中COUNT的用法
  18. 送给孩子的10句人生箴言
  19. 计算锋生的函数 frontogenesis
  20. 淘宝粉丝增加技巧!京东店铺粉丝可以买?

热门文章

  1. 用OLED显示屏显示文字
  2. 工欲利其事,常用软件使用感受交流分享
  3. Linux文件、目录
  4. 赵志博陕师大计算机科学学院,肖 冰
  5. 夫妻驾驶途中打瞌睡 车辆定速巡航120迈飞下四米高速路
  6. 计算机专业女生节祝福语,2017年女生节祝福语大全 3.7女生节祝福语微信短信推荐...
  7. Docker 搭建容器合集
  8. spark on yarn falling back to uploading libraries under SPARK_HOME.
  9. 网站排名SEO优化方案(2022最新)
  10. Python+pyc文件编译和运行