一、AlexNet网络介绍
AlexNet包含了6亿3000万个连接,6000万个参数和65万个神经元,拥有5个卷积层,其中3个卷积层后面连接了最大池化层,最后有3个全连接层。
二、AlexNet优点
1、成功使用RELU作为CNN的激活函数,并验证其效果在较深的网络超过了sigmoid,成功解决了sigmoid在网络较深时的梯度弥散问题。
2、训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。
3、在CNN中使用重叠的最大池化。
4、提出了LRN层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强模型的泛化能力。
5、数据增强,增大了数据集的量。
三、实现
我们主要建立一个完整的AlexNet卷积神经网络,然后对它的每个batch的forward和backward的速度进行测试。
定义一个用来显示网络每一层结构的函数print_actications,这个函数接受一个tensor作为输入,并显示其名称和尺寸。
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())
接下来设计AlexNet的网络结构。这个inference函数将会很大,包括多个卷积和池化层。
def inference(images): parameters = [] with tf.name_scope('conv1') as scope: #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) #将这一层最后输出的tensor conv1的结构打印出来,并将这一层可训练的参数kernel、biases添加到parameters中 parameters += [kernel, biases] lrn1 = tf.nn.lrn(conv1, 4, bias=1.0, alpah=0.001/9, beta=0.75, name='lrn1')#depth_radius 设为4, pool1 = tf.nn.max_pool(lrn1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1')#VALID为取样时不能超过边框,不像SAME模式那样可以填充边界外的点。 print_activations(pool1)#将输出结果pool1的结构打印出来 #第二层卷积层 with tf.name_scope('conv2') as scope: # 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, alpah=0.001/9, beta=0.75, name='lrn2')#depth_radius 设为4, pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool2')#VALID为取样时不能超过边框,不像SAME模式那样可以填充边界外的点。 print_activations(pool2)#将输出结果pool2的结构打印出来 #第三层 with tf.name_scope('conv3') as scope: # 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: # 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: # 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) conv3 = 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
接下来实现一个评估AlexNet每轮计算时间的函数time_tensorflow_run。这个函数的第一个输入时tensorflow的session,第二个变了是需要评测的运算算子,第三个是测试的名称。
先定义预热轮数num_steps_burn_in=10,作用时给程序热身,头几轮迭代有显存加载等问题可以跳过。所以我们只需考量10轮迭代之后的计算时间。同时,也记录总时间total_duration和平方和total_duration_squared用来计算方差。
#time_tensorflow_run函数时评估AlexNet每轮计算时间,用来计算某个算子的运行时间 def time_tensorflow_run(session, target, info_string): num_steps_burn_in = 10 #轮数 total_duration = 0.0 #总时间 total_duration_squared = 0.0 #平方和
我们进行num_batches + num_steps_burn_in次迭代计算,使用time.time()记录时间,每次迭代通过session.run(target)执行。在初始热身的num_steps_burn_in次迭代后,每10轮迭代显示当前迭代所需要的时间。同时每轮将total_duration和total_duration_squared累加以便后面计算每轮耗时的均值和标准差。
在循环结束后,计算每轮迭代的平均耗时mn和标准差sd,最后将结果显示出来。这样就完成了计算每轮迭代耗时的评测函数time_tensorflow_run。
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和标准差sd,最后将结果显示出来。 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。首先使用with tf.Graph().as_default()定义默认的Graph方便后面使用。
使用tf.random_normal函数构造正态分布(标准差为0.1)的随机tensor,第一个维度是batch_size,第二个和第三个维度时图片的尺寸 image_size=224,第四个维度是图片的颜色通道数。
用inference函数构建整个AlexNet网络,得到最后一个池化层的输出pool5和网络中需要训练的参数的集合parameters.
使用tf.Session创建新的Session,并通过tf.global_variables_initializer()初始化所有参数。
接着进行AlexNet的forward计算的评测,这里直接使用time_tensorflow_run统计运行时间,传入的target就是pool5,即卷积网络最后一个池化层的输出。然后进行backward,这里需要给最后的输出pool5设置一个优化目标loss。我们使用tf.nn.l2_loss计算pool5的loss,在使用tf.gradients求相对于loss的所有模型参数的过程。
最后使用time_tensorflow_run统计backward的运算时间,这里的target就是求整个网络梯度gard的操作。
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") object = tf.nn.l2_loss(pool5) grad = tf.gradients(objective, parameters) time_tensorflow_run(sess, grad, "Forward-backward")
最后执行主函数
run_benchmark()