TensorFlow实战--阅读笔记part4
Posted Lainey❤
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TensorFlow实战--阅读笔记part4相关的知识,希望对你有一定的参考价值。
Tensorflow实现经典神经网络
一、Tensorflow实现AlexNet
2012年,Hinton的学生Alex Krizhevsky提出了深度卷积神经网络模型AlexNet,它算是LeNet的一种更深更宽的版本。它包含了几个比较新的技术点,也首次在CNN中成功应用了ReLU,Dropout和LRN等trick.同时AlexNet也使用了GPU进行运算加速,AlexNet包括了6亿3000万个连接,6000万个参数和65万个神经元,拥有5个卷积层。
AlexNet主要使用到的新技术有如下:
(1)成功使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功过解决了Sigmoid在网络较深时的梯度弥散问题。虽然ReLU函数在很久之前就被提出了,但是直到AlexNet的出现才将其发扬光大
(2)训练时使用dropout随机忽略一部分神经元,以避免模型的过拟合。dropout虽有单独的论文论述,但是AlexNet将其实用化,一般都在全连接层使用
(3)在CNN中使用最大池化,避免了平均池化的模糊化效果。并且AlexNet提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性
(4)提出了LRN层,对局部神经元的活动创建竞争机制,使得其中相应比较大的值变得相对更大,并抑制其他反馈小的神经元,增强了模型的泛化能力
(5)使用CUDA加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时的大量矩阵运算
(6)数据增强,随机地从256x256的原始图像中截取224x224大小的区域(以及水平翻转的镜像),相当于增加了(256-224)^2 x 2=2048倍的数量,仅靠原始的数据量,参数众多的CNN会陷入过拟合中,使用了数据增强后可以大大减轻过拟合,提升泛化能力。进行预测时,则是取图片的四个角加中间共五个位置,并进行左右翻转,一共获得10个图片,对他们进行预测 并对10个结果取平均值。
因为AlexNet训练时使用了两块GPU,因此这个结构图中不少组件都被拆为了两部分。现在我们GPU的显存可以放下全部模型参数,因此只考虑一块GPU的情况就可
AlexNet的每层超参数如下图所示:
可以发现在前几个卷积层,虽然计算量很大,但参数量很小,都在1M左右甚至更小,只占AlexNet总参数量的很小一部分。这就是卷积层有用的地方,可以通过较小的参数量提取有效的特征。而如果前几层直接使用全连接层,那么参数量和计算量将成为天文数字,虽然每一个卷积层占整个网络的参数量的1%都不到,但是如果去掉任何一个卷积层,都会使网络的分类性能大幅地下降
因为使用ImageNet数据集训练一个完整的AlexNet耗时非常长,所以接下来只是建立一个完整的AlexNet卷积神经网络,然后对它每一个batch的前馈计算和反馈计算的速度进行测试。
from datetime import datetime import math import time import tensorflow as tf batch_size = 32 num_batches = 100 def print_activations(t): print(t.op.name," ",t.get_shape().as_list())
定义函数inference,接受图片作为输入,返回最后一程pool5和parameters
通过with tf.name_scope(\'conv1\') as scope可以将scope内生成的Variable自动命名成conv1/xxx,便于区分不同卷积层之间的组件。
目前除了AlexNet,其他经典的卷积神经网络模型基本都放弃了LRN(主要是效果不明显),而使用LRN也会使前馈,反馈的速度大大下降(整体速度降到1/3)
padding模式为SAME表示输入输出大小不变,图片边界外的点会进行填充,而VALID模式表示取样时不能超过边框,对边界不进行填充
def inference(images): parameters = [] with tf.name_scope(\'conv1\') as scope: kernel = tf.Variable(tf.truncated_normal([11,11,3,64],dtype=tf.float32,stddev=1e-1),name=\'weights\') conv = tf.nn.conv2d(images,kernel,[1,4,4,1],padding=\'SAME\') biases = tf.Variable(tf.constant(0.0,shape=[64],dtype=tf.float32),trainable=True,name=\'biases\') bias = tf.nn.bias_add(conv,biases) conv1 = tf.nn.relu(bias,name=scope) print_activations(conv1) parameters += [kernel,biases] lrn1 = tf.nn.lrn(conv1,4,bias=1.0,alpha=0.001/9,beta=0.75,name=\'lrn1\') pool1 = tf.nn.max_pool(lrn1,ksize=[1,3,3,1],strides=[1,2,2,1],padding=\'VALID\',name=\'pool1\') print_activations(pool1) with tf.name_scope(\'conv2\') as scope: kernel = tf.Variable(tf.truncated_normal([5,5,64,192],dtype=tf.float32,stddev=1e-1),name=\'weights\') conv = tf.nn.conv2d(pool1,kernel,[1,1,1,1],padding=\'SAME\') biases = tf.Variable(tf.constant(0.0,shape=[192],dtype=tf.float32),trainable=True,name=\'biases\') bias = tf.nn.bias_add(conv,biases) conv2 = tf.nn.relu(bias,name=scope) parameters += [kernel,biases] print_activations(conv2) lrn2 = tf.nn.lrn(conv2,4,bias=1.0,alpha=0.001/9,beta=0.75,name=\'lrn2\') pool2 = tf.nn.max_pool(lrn2,ksize=[1,3,3,1],strides=[1,2,2,1],padding=\'VALID\',name=\'pool2\') print_activations(pool2) with tf.name_scope(\'conv3\') as scope: kernel = tf.Variable(tf.truncated_normal([3,3,192,384],dtype=tf.float32,stddev=1e-1),name=\'weights\') conv = tf.nn.conv2d(pool2,kernel,[1,1,1,1],padding=\'SAME\') biases = tf.Variable(tf.constant(0.0,shape=[384],dtype=tf.float32),trainable=True,name=\'biases\') bias = tf.nn.bias_add(conv,biases) conv3 = tf.nn.relu(bias,name=scope) parameters += [kernel,biases] print_activations(conv3) with tf.name_scope(\'conv4\') as scope: kernel = tf.Variable(tf.truncated_normal([3,3,384,256],dtype=tf.float32,stddev=1e-1),name=\'weights\') conv = tf.nn.conv2d(conv3,kernel,[1,1,1,1],padding=\'SAME\') biases = tf.Variable(tf.constant(0.0,shape=[256],dtype=tf.float32),trainable=True,name=\'biases\') bias = tf.nn.bias_add(conv,biases) conv4 = tf.nn.relu(bias,name=scope) parameters += [kernel,biases] print_activations(conv4) with tf.name_scope(\'conv5\') as scope: kernel = tf.Variable(tf.truncated_normal([3,3,256,256],dtype=tf.float32,stddev=1e-1),name=\'weights\') conv = tf.nn.conv2d(conv4,kernel,[1,1,1,1],padding=\'SAME\') biases = tf.Variable(tf.constant(0.0,shape=[256],dtype=tf.float32),trainable=True,name=\'biases\') bias = tf.nn.bias_add(conv,biases) conv5 = tf.nn.relu(bias,name=scope) parameters += [kernel,biases] print_activations(conv5) pool5 = tf.nn.max_pool(conv5,ksize=[1,3,3,1],strides=[1,2,2,1],padding=\'VALID\',name=\'pool5\') print_activations(pool5) return pool5,parameters
inference函数已经完成了,它可以创建AlexNet的卷积部分
接下来实现一个评估AlexNet每轮计算时间的函数time_tensorflow_run,这个函数的第一个输入是Session,第二个变量时需要评测的运算子,第三个变量时测试的名称
先定义预热轮数num_steps_burn_in=10,它的作用是给程序热身,头几轮迭代有显存加载,cache命中等问题因此可以跳过,我们只考量10轮迭代之后的计算时间。
同时也记录总时间total_duraiton和平方和total_duration_squared用以计算方差
def time_tensorflow_run(session,target,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) 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))
接下来定义主函数run_benchmark,用tf.random_normal函数构造正态分布作为输入图片
然后使用time_tensorflow_run统计运算时间,传入的target就是pool5,backward过程的评测与forward的不同,还需要给最后的输出pool5设置一个优化目标loss.
这里使用tf.nn.l2_loss计算pool5的loss,再使用tf.gradients求相对于loss的所有模型参数的梯度,这样就模拟了一个训练的过程。
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)) pool5,parameters = inference(images) init = tf.global_variables_initializer() sess = tf.Session() sess.run(init) time_tensorflow_run(sess,pool5,"Forward") objective = tf.nn.l2_loss(pool5) grad = tf.gradients(objective,parameters) time_tensorflow_run(sess,grad,"Forward-backward") run_benchmark()
程序显示的结果又三段,首先是AlexNet的网络结构
然后显示的是forward的计算时间,使用的是CPU
最后显示的是backward运算的时间
去除LRN层,前向和后向运算时间都快3倍,而且发现backward的时间比forward时间更耗时的多
总结
CNN的训练过程(即backward计算)通常都比较耗时,而且不想预测过程(即forward计算),训练通常需要过很多遍数据,进行大量的迭代。因此应用CNN的主要瓶颈还是在训练,用CNN做预测问题不大。
AlexNet中用到的寻多网络结构和Trick给深度学习的发展带来了深刻的影响。当然,我们也不能忽视ImageNet数据集给深度学习带来的贡献。训练深度神经网络,必须拥有一个像ImageNet这样超大的数据集才能避免过拟合,发挥深度学习的优势。可以说,传统机器学习模型适合学习一个小型数据集,但是对于大型数据集,我们需要有更大学习容量(Learning Capacity)的模型,即深度学习模型。
以上是关于TensorFlow实战--阅读笔记part4的主要内容,如果未能解决你的问题,请参考以下文章
《TensorFlow实战Google深度学习框架(第二版)》学习笔记及书评