简介
进入到新世纪,第一个突破并不是在算法上的,而是工程上。2006年,研究人员成功利用GPU加速了CNN,相比CPU实现快了4倍。虽然这里没有算法的提升,但是其意义可能比一般的算法提升更大。
原论文地址:http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf
.
.
(-2012-)AlexNet:
直到2012年,这一年AlexNet的出现可以说是标志着神经网络的复苏和深度学习的崛起。在imageNet2012的图片分类任务上,AlexNet以15.3%的错误率登顶,而且以高出第二名十几个百分点的差距吊打所有其他参与者。
网络结构:
这幅图分为上下两个部分的网络,论文中提到这两部分网络是分别对应两个GPU,只有到了特定的网络层后才需要两块GPU进行交互,这种设置完全是利用两块GPU来提高运算的效率,其实在网络结构上差异不是很大。
网络总共的层数为8层,5层卷积,3层全连接层:
其实总的来看,卷积/池化操作没有太大变化,不过层数有所加深。整个网络主要的点有:
- 成功使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。其实我们之前提到这在1980年的时候就已经被应用过了,不过十几年前LeNet又因为种种原因改为tanh,直到AlexNet的出现才将其发扬光大。
- 使用了数据增强,这点在92年的Cresceptron被引入。
- 使用了小批量随机梯度下降法(mini-batch SGD),这是98年LeNet的做法。
- 在GPU上训练,这得益于06年CNN在GPU上的实现。并且是分组卷积,因为当时真的是无奈,论文里说他们的GPU只有3G显存(GTX580),所以想要在一张显卡上放下所有东西显得有些吃力,所以才发明了分组卷积。
- 训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。Dropout虽有单独的论文论述,但是AlexNet将其实用化,通过实践证实了它的效果。在AlexNet中主要是最后几个全连接层使用了Dropout。
- 提出了局部响应归一化(LRN)层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。ps:感觉这个东西现在用的人不多了,好像现在都用BatchNormalization。
- 在CNN中使用重叠的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。
tensorflow代码实现:
from datetime import datetime
import math,time
import tensorflow as tf
batch_size = 32
num_bathes = 100
'''
获取tensor信息
'''
def print_tensor_info(tensor):
print("tensor name:",tensor.op.name,"-tensor shape:",tensor.get_shape().as_list())
'''
计算每次迭代消耗时间
session:TensorFlow的Session
target:需要评测的运算算子
info_string:测试的名称
'''
def time_tensorflow_run(session,target,info_string):
#前10次迭代不计入时间消耗
num_step_burn_in = 10
total_duration = 0.0
total_duration_squared = 0.0
for i in range(num_bathes + 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 / num_bathes
#计算消耗时间的标准差
vr = total_duration_squared / num_bathes - mn * mn
std = math.sqrt(vr)
print("%s:%s across %d steps,%.3f +/- %.3f sec / batch"%(datetime.now(),info_string,num_bathes,
mn,std))
#主函数
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")
def inference(images):
#定义参数
parameters = []
#第一层卷积层
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)
#第二层卷积层
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)
#第三层卷积层
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)
return output_softmax,parameters
if __name__ == "__main__":
run_bechmark()
系列传送门:
CNN发展简史——LeNet(一)CNN发展简史——VGG(三)CNN发展简史——GoogLeNet(四)CNN发展简史——ResNet(五)CNN发展简史——DenseNet(六)