【深度学习】VGGNet原理解析及实现
【深度学习】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原理解析及实现相关推荐
- [深度学习主流框架解析一] Caffe
[深度学习主流框架解析一] Caffe 1.模型文件协议解析 重点解析caffe.proto模型文件中的内容,整体的模型文件可见下图. 模型结构相关特性: 含有训练和测试推理的开关和相关的必要参数 不 ...
- Python大数据综合应用 :零基础入门机器学习、深度学习算法原理与案例
机器学习.深度学习算法原理与案例实现暨Python大数据综合应用高级研修班 一.课程简介 课程强调动手操作:内容以代码落地为主,以理论讲解为根,以公式推导为辅.共4天8节,讲解机器学习和深度学习的模型 ...
- TensorFlow深度学习算法原理与编程实战 人工智能机器学习技术丛书
作者:蒋子阳 著 出版社:中国水利水电出版社 品牌:智博尚书 出版时间:2019-01-01 TensorFlow深度学习算法原理与编程实战 人工智能机器学习技术丛书 ISBN:97875170682 ...
- 深度学习算法原理_用于对象检测的深度学习算法的基本原理
深度学习算法原理 You just got a new drone and you want it to be super smart! Maybe it should detect whether ...
- python原理书籍_python书籍推荐:《深入浅出深度学习:原理剖析与Python实践》
在过去的这十年,深度学习已经席卷了整个科技界和工业界,2016年谷歌阿尔法狗打败围棋世界冠军李世石,更是使其成为备受瞩目的技术焦点. 今日,小编就为大家推荐一本能让初学者和"老司机" ...
- 没人说得清深度学习的原理 只是把它当作一个黑箱来使
没人说得清深度学习的原理 只是把它当作一个黑箱来使 人类正在慢慢接近世界的本质--物质只是承载信息模式的载体.人脑之外的器官都只是保障这一使命的给养舰队. 自从去年AlphaGo 完虐李世乭,深度学习 ...
- 深度学习经典网络解析图像分类篇(二):AlexNet
深度学习经典网络解析图像分类篇(二):AlexNet 1.背景介绍 2.ImageNet 3.AlexNet 3.1AlexNet简介 3.2AlexNet网络架构 3.2.1第一层(CONV1) 3 ...
- [深度学习主流框架解析一] Onnx
[深度学习主流框架解析一] Onnx 1.模型文件协议解析 Onnx同样也是采用了Protobuf协议进行模型文件的整体构建,与Caffe相比,Onnx使用了多proto文件描述模型.因此,解析Onn ...
- [深度学习] 面试常见问题+解析汇总
什么是深度学习,与传统机器学习算法的区别? (1)传统机器学习算法,在一开始,性能(识别率)会随着数据的增加而增加,但一段时间后,它的性能会进入平台期.这些模型无法处理海量数据. (2)最近20年来, ...
最新文章
- Spring Boot 2.x基础教程:事务管理入门
- linux关闭方防火墙的命令
- 清华大学计算机专业学生埃朗读村,《朗读者~矣晓沅(清华大学计算机研究生 彝族)》...
- linux下的各种shell介绍(bash和dash转换)
- 最后解密的两弹元勋,众帅之帅朱光亚
- [CTO札记]Google数字图书馆对中国版权的威胁
- Kryo为什么比Hessian快
- MySql的基本操作流程-SAE的MySQL-PHP
- 国产操作系统往事:四十年激变,终再起风云
- new hashmap 初始大小_你们要的HashMap工作原理!它来了!!!
- 国庆期间新闻回顾:微软智斗盗版 华为出手3Com
- python小绵羊怎么画_使用Python的turtle画小绵羊
- php获取 微信unionid,微信授权登录获取openId和unionId
- ISIS协议原理与配置
- Some file crunching failed, see logs for details解决办法
- 微观交通仿真软件分析比较
- SQL中COUNT的用法
- 送给孩子的10句人生箴言
- 计算锋生的函数 frontogenesis
- 淘宝粉丝增加技巧!京东店铺粉丝可以买?
热门文章
- 用OLED显示屏显示文字
- 工欲利其事,常用软件使用感受交流分享
- Linux文件、目录
- 赵志博陕师大计算机科学学院,肖 冰
- 夫妻驾驶途中打瞌睡 车辆定速巡航120迈飞下四米高速路
- 计算机专业女生节祝福语,2017年女生节祝福语大全 3.7女生节祝福语微信短信推荐...
- Docker 搭建容器合集
- spark on yarn falling back to uploading libraries under SPARK_HOME.
- 网站排名SEO优化方案(2022最新)
- Python+pyc文件编译和运行