AlexNet是Hinton的学生Alex Krizhevsky在2012年提出的深度卷积神经网络,它是LeNet一种更深更宽的版本。在AlexNet上首次应用了几个trick,ReLU、Dropout和LRN。AlexNet包含了6亿3000万个连接,6000万个参数和65万个神经元,有5个卷积层,3个全连接层。在ILSVRC 2012比赛中,AlexNet以top-5的错误率为16.4%的显著优势夺得冠军,第二名的成绩是26.2%。AlexNet的trick主要包括:
1、成功使用RELU作为CNN的激活函数,并验证其效果在较深的网络中的效果超过了sigmoid,解决了sigmoid在深层的网络中的梯度弥散的问题。
2、使用Dropout来随机使得一部分神经元失活,来避免模型的过拟合,在AlexNet中,dropout主要应用在全连接层。
3、使用重叠的最大池化,以前在卷积神经网络中大部分都采用平均池化,在AlexNet中都是使用最大池化,最大池化可以避免平均池化的模糊化效果。重叠的最大池化是指卷积核的尺寸要大于步长,这样池化层的输出之间会有重叠和覆盖,提升特征的丰富性。在AlexNet中使用的卷积核大小为3×3,横向和纵向的步长都为2。
4、使用LRN层,对局部神经元的活动创建有竞争机制,让响应较大的值变得相对更大,并抑制反馈较小的神经元,来增强模型的泛化能力。
5、使用了CUDA来加速深度神经网络的训练。
6、数据增强,随机从256×256的原始图像中截取224×224的图像以及随机翻转。如果没有数据增强,在参数众多的情况下,卷积神经网络会陷入到过拟合中,使用数据增强可以减缓过拟合,提升泛化能力。进行预测的时候,提取图片的四个角加中间位置,并进行左右翻转,一共10张图片,对它们进行预测并取10次结果的平均值。在AlexNet论文中也提到了,对图像的RGB数据进行PCA处理,并做一个标准差为0.1的高斯扰动,增加一些噪声,可以降低1%的错误率。
论文原文
第一层卷积一共有64个11×11×3大小的卷积核,横向和纵向的步长都为4,初始化权重使用截断的正态分布进行初始化,经过ReLU激活函数之后,再通过LRN层,再使用重叠的最大池化。
#第一层卷积层
with tf.name_scope("conv1") as scope:
#设置卷积核11×11,3通道,64个卷积核
kernel1 = tf.Variable(tf.truncated_normal([11,11,3,64],mean=0,stddev=0.1,
dtype=tf.float32),name="weights")
#卷积,卷积的横向步长和竖向步长都为4
conv = tf.nn.conv2d(images,kernel1,[1,4,4,1],padding="SAME")
#初始化偏置
biases = tf.Variable(tf.constant(0,shape=[64],dtype=tf.float32),trainable=True,name="biases")
bias = tf.nn.bias_add(conv,biases)
#RELU激活函数
conv1 = tf.nn.relu(bias,name=scope)
#输出该层的信息
print_tensor_info(conv1)
#统计参数
parameters += [kernel1,biases]
#lrn处理
lrn1 = tf.nn.lrn(conv1,4,bias=1,alpha=1e-3/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_tensor_info(pool1)
第二层卷积层的结构和第一层大致相似,卷积层的大小由11×11变成了5×5,有192个卷积核。
#第二层卷积层
with tf.name_scope("conv2") as scope:
#初始化权重
kernel2 = tf.Variable(tf.truncated_normal([5,5,64,192],dtype=tf.float32,stddev=0.1)
,name="weights")
conv = tf.nn.conv2d(pool1,kernel2,[1,1,1,1],padding="SAME")
#初始化偏置
biases = tf.Variable(tf.constant(0,dtype=tf.float32,shape=[192])
,trainable=True,name="biases")
bias = tf.nn.bias_add(conv,biases)
#RELU激活
conv2 = tf.nn.relu(bias,name=scope)
print_tensor_info(conv2)
parameters += [kernel2,biases]
#LRN
lrn2 = tf.nn.lrn(conv2,4,1.0,alpha=1e-3/9,beta=0.75,name="lrn2")
#最大池化
pool2 = tf.nn.max_pool(lrn2,[1,3,3,1],[1,2,2,1],padding="VALID",name="pool2")
print_tensor_info(pool2)
第三层卷积层卷积核的个数是第二层的一倍,第三层卷积层没有使用LRN层和最大池化。
#第三层卷积层
with tf.name_scope("conv3") as scope:
#初始化权重
kernel3 = tf.Variable(tf.truncated_normal([3,3,192,384],dtype=tf.float32,stddev=0.1)
,name="weights")
conv = tf.nn.conv2d(pool2,kernel3,strides=[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)
#RELU激活层
conv3 = tf.nn.relu(bias,name=scope)
parameters += [kernel3,biases]
print_tensor_info(conv3)
#第四层卷积层
with tf.name_scope("conv4") as scope:
#初始化权重
kernel4 = tf.Variable(tf.truncated_normal([3,3,384,256],stddev=0.1,dtype=tf.float32),
name="weights")
#卷积
conv = tf.nn.conv2d(conv3,kernel4,strides=[1,1,1,1],padding="SAME")
biases = tf.Variable(tf.constant(0.0,dtype=tf.float32,shape=[256]),trainable=True,name="biases")
bias = tf.nn.bias_add(conv,biases)
#RELU激活
conv4 = tf.nn.relu(bias,name=scope)
parameters += [kernel4,biases]
print_tensor_info(conv4)
#第五层卷积层
with tf.name_scope("conv5") as scope:
#初始化权重
kernel5 = tf.Variable(tf.truncated_normal([3,3,256,256],stddev=0.1,dtype=tf.float32),
name="weights")
conv = tf.nn.conv2d(conv4,kernel5,strides=[1,1,1,1],padding="SAME")
biases = tf.Variable(tf.constant(0.0,dtype=tf.float32,shape=[256]),name="biases")
bias = tf.nn.bias_add(conv,biases)
#REUL激活层
conv5 = tf.nn.relu(bias)
parameters += [kernel5,bias]
#最大池化
pool5 = tf.nn.max_pool(conv5,[1,3,3,1],[1,2,2,1],padding="VALID",name="pool5")
print_tensor_info(pool5)
#第六层全连接层
pool5 = tf.reshape(pool5,(-1,6*6*256))
weight6 = tf.Variable(tf.truncated_normal([6*6*256,4096],stddev=0.1,dtype=tf.float32),
name="weight6")
ful_bias1 = tf.Variable(tf.constant(0.0,dtype=tf.float32,shape=[4096]),name="ful_bias1")
ful_con1 = tf.nn.relu(tf.add(tf.matmul(pool5,weight6),ful_bias1))
#第七层第二层全连接层
weight7 = tf.Variable(tf.truncated_normal([4096,4096],stddev=0.1,dtype=tf.float32),
name="weight7")
ful_bias2 = tf.Variable(tf.constant(0.0,dtype=tf.float32,shape=[4096]),name="ful_bias2")
ful_con2 = tf.nn.relu(tf.add(tf.matmul(ful_con1,weight7),ful_bias2))
#
#第八层第三层全连接层
weight8 = tf.Variable(tf.truncated_normal([4096,1000],stddev=0.1,dtype=tf.float32),
name="weight8")
ful_bias3 = tf.Variable(tf.constant(0.0,dtype=tf.float32,shape=[1000]),name="ful_bias3")
ful_con3 = tf.nn.relu(tf.add(tf.matmul(ful_con2,weight8),ful_bias3))
#softmax层
weight9 = tf.Variable(tf.truncated_normal([1000,10],stddev=0.1),dtype=tf.float32,name="weight9")
bias9 = tf.Variable(tf.constant(0.0,shape=[10]),dtype=tf.float32,name="bias9")
output_softmax = tf.nn.softmax(tf.matmul(ful_con3,weight9)+bias9)
通过使用模拟出来的图片来评估模型在训练过程中的耗时。
#主函数
def run_bechmark():
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=0.1))
output,parameters = inference(images)
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
time_tensorflow_run(sess,output,"Forward")
objective = tf.nn.l2_loss(output)
grad = tf.gradients(objective,parameters)
time_tensorflow_run(sess,grad,"Forward-backward")
完整代码链接