勿满足于眼前的小小成就。你要问自己,我这辈子只有这样吗。
AlexNet模式是在2012年提出来的,并且在当年获得了ILSVRC的冠军,top-5的错误率为16.4%,比第二名的成绩26.2%高出许多,也从此开始确立了深度学习在计算机视觉的统治地位,是一块重要的里程碑。
该模型包含了几个比较新的技术点:
接下来将会使用TensorFlow搭建AlexNet,使用的数据为随机图片数据,如果你有兴趣可以自行下载ImageNet的数据进行训练兵测试
首先导入常用库并定义batch_size,num_batches
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,接受images为参数,返回第五个池化层和所有需要训练的模型参数
首先定义第一个卷积层,使用tf.truncated_normal截断的正太分布函数初始化卷积核的参数kernel,卷积核的尺寸为11X11,颜色通道为3,卷积核的数量为64。使用tf.nn.conv2d对输入的images进行卷积操作,将strides步长设置为4X4,padding模式设置为SAME,将卷积核的biases全部初始化为0,再使用tf.nn.bias_ass将conv和biases加起来,并使用激活函数tf.nn.relu对结果进行非线性处理,最后使用开始定义的print_activations将结构打印出来,将可训练的参数添加到parameters中。
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]
在第一层卷积后添加LRN层和最大池化层,先使用tf.nn.lrn对前面输出的tensor进行LRN处理,将depth_radius设为4,bias设为1,alpha设为0.001/9,beta设为0.75。对前面的输出进行最大池化,池化尺寸为3X3,padding模式设为VALID。
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)
定义后面的卷积层和池化层,步骤大体和前面相似,最后返回第五层池化和需要训练的参数,正式使用时需要自行添加3个全连接层。
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
定义一个评估AlexNet每轮计算时间的函数time_tensorflow_run,该函数的第一个输入是TensorFlow的Session,第二个变量是需要评测的运算算子,第三个变量是测试的名称。num_strps_burn_in的作用是给程序热身,前几轮迭代有显存加载、cache命中等因素所以跳过。循环结束后计算每轮迭代的平均耗时和标准差,打印输出。
def time_tensorflow_run(session, target, info_string):
num_steps_burn_in = 10
total_duration = 0.0
total_duration_squard = 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(),1-num_steps_burn_in,duration))
total_duration+=duration
total_duration_squard+=duration*duration
mn=total_duration/num_batches
vr=total_duration_squard/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))
定义主函数,首先使用with.Graph().as_default()定义默认的Graph方便后面使用,使用tf.random_normal函数构造正态分布的随机tensor代替输入的图片数据,使用之前定义的inference构建整个AlexNet网络,使用tf.Session()创建新的Session并初始化所有参数。然后进行forword计算的评测,使用tf.nn.l2_loss计算pool5d的loss,再使用tf.gardients求相对于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的网络结构,然后会打印出每轮的迭代时间消耗
conv1 [32, 56, 56, 64]
pool1 [32, 27, 27, 64]
conv2 [32, 27, 27, 192]
pool2 [32, 13, 13, 192]
conv3 [32, 13, 13, 384]
conv4 [32, 13, 13, 256]
conv5 [32, 13, 13, 256]
pool5 [32, 6, 6, 256]
2018-12-12 14:08:11.891345:step -9,duration=0.525
2018-12-12 14:08:16.898325:step -9,duration=0.501
2018-12-12 14:08:21.928768:step -9,duration=0.508