原文:《ImageNet Classification with Deep Convolutional Neural Networks》
我没有读原文,这个已经很老的文章了,分类领域应用CNN的经典文章。
先看结构:
咋一看像是两个网络,实际上并不是这样,文章中是用两个GPU来训练的,所以华城这样了,实际上就是一系列卷积池化和全连接层构成的,具体的网络结构列表:
这是个参数列表,实际训练或者检测的时候是从下往上走的,显示11-11的卷积层。
有几个点要说一下:
前面的两个大卷积使用了一个叫做LRN(local response nornalization)的方法进行局部标准化,一般来说Relu激活函数是不需要对输入进行标准化的,但是实验之后确实发现使用这种方法可以提高性能,在这之后LRN用的就很少了,基本上被BN替代掉。
overlap pooling
带overlap的pooling就是说pooling的模板核的大小要大于移动的步长,这样的话就会出现重叠,一般而言pooling是不带overlap的。
减少过拟合
- 数据增强:对图片进行变换可以增大数据集,第一种方法就是原图大小是256-256,在图片上随机选取224-224的小块进行训练,还可以对这些块进行水平翻转进一步增强数据量。
- dropout:这个可以翻译做:随机失活,用的比较多,可以考虑在训练的时候使用这个,能够有效减少过拟合。
其他的就没有什么说的了,比较简单,这个框架基本可以不用修改可以用到一些典型的图像分类任务之中,并且可以得到比较好的性能。
贴一下代码,写了详细的注释:
#实现AlexNet,基本是按照tensorflow那本书来写的,然后我加详细的注释。
#导入库文件
from datetime import datetime
import math
import time
import tensorflow as tf
#batch和num_batch设置
batch_size=32
batch_num=100
#定义一个函数,输出每一层的结构 t是一个tensor,作为输入
def print_activations(t):
print(t.op.name,'',t.get_shape().as_list)
'''
接下来是设计AlexNet的结构,我们定义一个函数(inferen),接受image作为输入,
返回最后一层pool(第五个池化层),以及所有需要训练的模型参数,这个函数比较大,
包括多个卷积层和池化层。每一层我们利用name_scope命名。
'''
def inference(image):
parameters=[] #存参数
#第一层,11*11*3的卷积核,一共有64个,原文是96个,
with tf.name_scope('conv1') as scope:
kernel=tf.Variable(tf.truncated_normal([11,11,3,64],dtype=tf.float32,stddev=0.1),name='weights')
conv=tf.nn.conv2d(image,kernel,[1,4,4,1],padding='SAME') #四个维度上的步长,分别是batch_num,height,width,纵向一般为1
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参数
parameters+=[kernel,biases] #参数存储
lrn1=tf.nn.lrn(conv1,bias=1.0,alpha=0.001/9,beta=0.75,name='lrn1') #local response normalization--局部响应标准化
pool1=tf.nn.max_pool(lrn1,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID',name='pool1')
print_activations(pool1)
#最大值pool,池化核3*3,池化步长2*2
#第二层和第一层基本一样,不同的是卷积核和步长设置
with tf.name_scope('conv2') as scope:
kernel=tf.Variable(tf.truncated_normal([5,5,64,192],dtype=tf.float32,stddev=0.1),name='weights')
conv=tf.nn.conv2d(pool1,kernel,[1,1,1,1],padding='SAME') #四个维度上的步长,分别是batch_num,height,width,纵向一般为1
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)
print_activations(conv2) #打印tensor参数
parameters+=[kernel,biases]
lrn2 = tf.nn.lrn(conv2, bias=1.0, alpha=0.001 / 9, beta=0.75, name='lrn2') # local response normalization--局部响应标准化
pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1')
print_activations(pool2)
with tf.name_scope('conv3') as scope:
kernel=tf.Variable(tf.truncated_normal([3,3,192,384],dtype=tf.float32,stddev=0.1),name='weights')
conv=tf.nn.conv2d(pool2,kernel,[1,1,1,1],padding='SAME') #四个维度上的步长,分别是batch_num,height,width,纵向一般为1
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)
print_activations(conv3) #打印tensor参数
parameters+=[kernel,biases]
with tf.name_scope('conv4') as scope:
kernel=tf.Variable(tf.truncated_normal([3,3,384,256],dtype=tf.float32,stddev=0.1),name='weights')
conv=tf.nn.conv2d(conv3,kernel,[1,1,1,1],padding='SAME') #四个维度上的步长,分别是batch_num,height,width,纵向一般为1
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)
print_activations(conv4) #打印tensor参数
parameters+=[kernel,biases]
with tf.name_scope('conv2') as scope:
kernel=tf.Variable(tf.truncated_normal([3,3,256,256],dtype=tf.float32,stddev=0.1),name='weights')
conv=tf.nn.conv2d(conv4,kernel,[1,1,1,1],padding='SAME') #四个维度上的步长,分别是batch_num,height,width,纵向一般为1
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)
print_activations(conv5) #打印tensor参数
parameters+=[kernel,biases]
pool5=tf.nn.max_pool(conv5,ksize=[1,3,3,1],strides=[1,2,2,1],padding='SAME',name='pool5')
print_activations(pool5)
return pool5,parameters
#三个全链接层,4096-4096-1000,接受一个pool层,返回1*1000个向量(评分)
def FC(pool,parameters): #全连接层函数,接受卷积层最后池化输出的tensor
with tf.name_scope('FC'):
with tf.name_scope('FC1'):
pool_x=tf.reshape(pool,[-1,7*7*256])
W_FC1=tf.Variable(tf.truncated_normal([7*7*256,4096],dtype=tf.float32,stddev=0.1),name='W_FC1')
b_1=tf.Variable(tf.constant(0.01,shape=[4096]),dtype=tf.float32,trainable=True,name='b_1')
FC1=tf.nn.relu(tf.matmul(pool_x,W_FC1)+b_1)
parameters+=[W_FC1,b_1]
print_activations(FC1)
with tf.name_scope('FC2'):
W_FC2=tf.Variable(tf.truncated_normal([4096,4096],dtype=tf.float32,stddev=0.1,name='W_FC2'))
b_2=tf.Variable(tf.constant(0.01,shape=[4096]),dtype=tf.float32,trainable=True,name='b_2')
FC2=tf.nn.relu(tf.matmul(FC1,W_FC2)+b_2)
parameters += [W_FC2, b_2]
print_activations(FC2)
with tf.name_scope('FC3'):
W_FC3=tf.Variable(tf.truncated_normal([4096,1000],dtype=tf.float32,stddev=0.1,name='W_FC3'))
b_3=tf.Variable(tf.constant(0.01,shape=[1000]),dtype=tf.float32,trainable=True,name='b_2')
FC3=tf.nn.relu(tf.matmul(FC2,W_FC3)+b_3)
parameters += [W_FC3, b_3]
print_activations(FC3)
return FC3,parameters
'''
session 是tensorflow的session,第二个参数是需要评测的运算算子,第三个变量是测试的名称
我们通过batch_num+num_step_burn_in来计算次数,使用time.time()来计算时间,每轮Session.run(target
来执行,初始热身的num_step_burn_in次迭代后,每十轮显示当前迭代所需要的时间,同时每轮将total_duration
和total_duration_squared累加,以便后面计算每轮平均耗时和耗时标准差。
'''
def time_tensorflow_run(session,target,info_string):
num_step_burn_in=10
total_duration=0.0
total_duration_squared=0.1
for i in range(batch_num+num_step_burn_in):
start_time=time.time()
_=session.run(target)
duration=time.time()-start_time
if(i>=num_step_burn_in):
if not i%10: #每十个显示一次
print('%s:step %d, duration=%.3f' %(datetime.now(),i-num_step_burn_in,duration))
total_duration+=duration
total_duration_squared+=duration*duration
mn=total_duration/batch_num #平均时间
vr=total_duration_squared/batch_num-mn*mn #计算方差
sd=math.sqrt(vr) #计算标准差
#print('sd:'+str(sd))
#print(sd)
print(str(datetime.now())+'\t'+info_string+'\t'+'\t:'+'Ave_time:'+str(mn)+'\tsd:\t'+str(sd))
'''
接下来我们定义主函数 run_benchmark,首先我们使用 with tf.Graph.as_default()来定义默认的Graph来方便后面使用,
首先我们先不使用ImageNet来进行训练,只是测试其前馈和反馈的耗时,我们使用tf.randon_normal来随机生成一些图像数据,
然后使用前面的inference和FC函数来构建整个AlexNet网络,得到一个输出层和两个参数(卷积参数和全脸阶层参数),接下来
我们利用tf.sesion()来创建新的session并初始化所有参数。
'''
def run_benchmark():
with tf.Graph().as_default():
image_size=224
images=tf.Variable(tf.random_normal(
[batch_size,
image_size,
image_size,
3], #batch_sz和图像大小224*224*3
dtype=tf.float32,
stddev=1e-1
))
pool5,parameters=inference(images)
FC3,parameters=FC(pool5,parameters)
init=tf.global_variables_initializer()
sess=tf.Session()
sess.run(init)
'''
下面是会模型的评测,直接使用time_tensorflow_run来统计运行时间,传入的target是FC3,即全连接的最后一个输出层,然后进行反馈
即训练过程的评测,和前馈不同的是我们需要给最后的输出设置一个loss,一般的loss需要用到数据损失和模型损失,我们这里不传入labels,所以
只计算模型损失,使用L2来计算,
'''
time_tensorflow_run(sess,FC3,'forward')
objecttive=tf.nn.l2_loss(FC3)
grad=tf.gradients(objecttive,parameters)
time_tensorflow_run(sess,grad,'forward-backward')
run_benchmark()
网友评论