深度学习-推荐系统-CV-NLP大数据,机器学习,人工智能机器学习、深度学习与人工智能

卷积神经网络之AlexNet

2019-08-05  本文已影响9人  Ice_spring

AlexNet网络结构剖析

2012年AlexNet在ImageNet大赛上一举夺冠,展现了深度CNN在图像任务上的惊人表现,掀起了CNN研究的热潮,深度学习自此引爆,在AlexNet之前,深度学习已经沉寂了很久。该网络是以论文的第一作者 Alex Krizhevsky 的名字命名的,另外两位合著者是 Ilya Sutskever 和 Geoffery Hinton。
该网络结构如下(为了讲清楚AlexNet结构,找了不同的示意图):

原论文AlexNet AlexNet2 AlexNet3

需要注意的是,论文发表时的GPU运算能力达不到该网络的训练需求,所以使用的是两个GPU并行,中间为了提高模型效果使用了不少交互相关的Trick。我们下面只关注网络结构,不关注两个GPU处理之间的交互。

下面对该结构做详细介绍,数据预处理首先将所有图像都处理为256×256×3的图像,随后AlexNet通过随机采样的方式从中选取一张227×227×3的图片作为输入(原论文AlexNet结构图示写的是224×224×3,AlexNet2是吴恩达老师讲义中的图示,经计算确实应该是227)。
注:以下卷积操作由于卷积核通道数和上一层次输出FeatureMap个数相同,故没有写进Kernel中,Kernel的最后一个参数是卷积核个数。

在原论文的双GPU并行计算结构下,卷积层 Conv2,Conv4,Conv5中的卷积核只和位于同一GPU的上一层的FeatureMap相连,于是需要训练的参数数量是:

卷积层的参数 = 卷积核的数量 * 卷积核 + 偏置
Conv1: 96个11×11×3的卷积核,96×11×11×3+96=34848
Conv2: 2组,每组128个5×5×48的卷积核,(128×5×5×48+128)×2=307456
Conv3: 384个3×3×256的卷积核,3×3×256×384+384=885120
Conv4: 2组,每组192个3×3×192的卷积核,(3×3×192×192+192)×2=663936
Conv5: 2组,每组128个3×3×192的卷积核,(3×3×192×128+128)×2=442624
FuulC6: 4096个6×6×256的卷积核,6×6×256×4096+4096=37752832
FuulC7: 4096∗4096+4096=16781312
output(FuulC8): 4096∗1000=4096000

而实际如果Conv2,Conv4,Conv5也合在一块计算的话,这3个层次中相应的参数数量会翻倍。
不过,从上面也可以看出,参数大多数集中在全连接层,在卷积层由于局部连接和权值共享,权值参数相对较少。

AlexNet的特点及其解释

AlexNet首次在CNN中成功应用了ReLU和Dropout等Trick,同时也使用了GPU进行运算加速。

sigmoid

所谓梯度饱和就是当输入的数字很大(或很小)时,其导数值接近于0。这样在深层次网络结构中进行反向传播时,由于链式法则,很多个很小的sigmoid导数相乘,导致其结果趋于0,权值更新将非常缓慢。
而ReLU是一个分段线性函数:
ReLu=max(x,0)

ReLu

相比于Sigmoid不仅运算更快,且导数是恒定值,一般不会发生严重的梯度饱和。另外Relu会使一部分神经元的输出为0,这样就形成了网络的稀疏性,减少了参数的相互依赖,从而一定程度抑制了过拟合。

AlexNet逻辑的TensorFlow实现

由于是用个人的PC跑程序,相当于只有一个运算中心,所以不可能实现论文最原始的网络了,我们只实现AlexNet的核心逻辑。另外,后来的实验发现LRN会让前向传播和反向传播的速度降低,但最终对模型效果提升却不明显,所以只有AlexNet用LRN,其之后的模型都放弃了。这里实现的网络中虽然加入了LRN,但为了训练速度可以自行删除LRN过程:

# -*- coding:utf-8 -*-
import tensorflow as tf
import time
import math
from datetime import datetime
 
batch_size=32
num_batch=100
keep_prob=0.5
def print_architecture(t):
    
    print(t.op.name," ",t.get_shape().as_list()) 
def inference(images):
    '''构建网络'''
    parameters=[]  #储存参数
 
    with tf.name_scope('conv1') as scope:
 
        kernel=tf.Variable(tf.truncated_normal([11,11,3,96],
                           dtype=tf.float32,stddev=0.1),name="weights")
        conv=tf.nn.conv2d(images,kernel,[1,4,4,1],padding='SAME')
        biases=tf.Variable(tf.constant(0.0, shape=[96],  dtype=tf.float32),
                           trainable=True,name="biases")
        bias=tf.nn.bias_add(conv,biases) # w*x+b
        conv1=tf.nn.relu(bias,name=scope) # reLu
        print_architecture(conv1)
        parameters +=[kernel,biases]
 
        #添加LRN层和max_pool层
        lrn1=tf.nn.lrn(conv1,depth_radius=4,bias=1,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_architecture(pool1)
 
    with tf.name_scope('conv2') as scope:
        kernel = tf.Variable(tf.truncated_normal([5, 5, 96, 256],
                                                 dtype=tf.float32, stddev=0.1), name="weights")
        conv = tf.nn.conv2d(pool1, 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)  # w*x+b
        conv2 = tf.nn.relu(bias, name=scope)  # reLu
        parameters += [kernel, biases]
        # 添加LRN层和max_pool层
        lrn2 = tf.nn.lrn(conv2, depth_radius=4, bias=1, alpha=0.001 / 9, beta=0.75, name="lrn1")
        pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
                               padding="VALID", name="pool2")
        print_architecture(pool2)
 
    with tf.name_scope('conv3') as scope:
        kernel = tf.Variable(tf.truncated_normal([3, 3, 256, 384],
                                                 dtype=tf.float32, stddev=0.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)  # w*x+b
        conv3 = tf.nn.relu(bias, name=scope)  # reLu
        parameters += [kernel, biases]
        print_architecture(conv3)
 
    with tf.name_scope('conv4') as scope:
      
        kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 384],
                                                 dtype=tf.float32, stddev=0.1), name="weights")
        conv = tf.nn.conv2d(conv3, 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)  # w*x+b
        conv4 = tf.nn.relu(bias, name=scope)  # reLu
        parameters += [kernel, biases]
        print_architecture(conv4)
 
    with tf.name_scope('conv5') as scope:
        kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 256],
                                                 dtype=tf.float32, stddev=0.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)  # w*x+b
        conv5 = tf.nn.relu(bias, name=scope)  # reLu
        pool5 = tf.nn.max_pool(conv5, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
                               padding="VALID", name="pool5")
        parameters += [kernel, biases]
        print_architecture(pool5)
 
    #全连接层6
    with tf.name_scope('fc6') as scope:
        kernel = tf.Variable(tf.truncated_normal([6*6*256,4096],
                                                 dtype=tf.float32, stddev=0.1), name="weights")
        biases = tf.Variable(tf.constant(0.0, shape=[4096], dtype=tf.float32),
                             trainable=True, name="biases")
        # 输入数据变换
        flat = tf.reshape(pool5, [-1, 6*6*256] )  # 整形成m*n,列n为7*7*64
        # 进行全连接操作
        fc = tf.nn.relu(tf.matmul(flat, kernel) + biases,name='fc6')
        # 防止过拟合  nn.dropout
        fc6 = tf.nn.dropout(fc, keep_prob)
        parameters += [kernel, biases]
        print_architecture(fc6)
 
    # 全连接层7
    with tf.name_scope('fc7') as scope:
        kernel = tf.Variable(tf.truncated_normal([4096, 4096],
                                                 dtype=tf.float32, stddev=0.1), name="weights")
        biases = tf.Variable(tf.constant(0.0, shape=[4096], dtype=tf.float32),
                             trainable=True, name="biases")
        # 进行全连接操作
        fc = tf.nn.relu(tf.matmul(fc6, kernel) + biases, name='fc7')
        # 防止过拟合  nn.dropout
        fc7 = tf.nn.dropout(fc, keep_prob)
        parameters += [kernel, biases]
        print_architecture(fc7)
 
    # 全连接层8
    with tf.name_scope('fc8') as scope:
        kernel = tf.Variable(tf.truncated_normal([4096, 1000],
                                                 dtype=tf.float32, stddev=0.1), name="weights")
        biases = tf.Variable(tf.constant(0.0, shape=[1000], dtype=tf.float32),
                             trainable=True, name="biases")
        # 进行全连接操作
        fc8 = tf.nn.xw_plus_b(fc7, kernel, biases, name='fc8')
        parameters += [kernel, biases]
        print_architecture(fc8)
 
    return fc8,parameters
 
def time_compute(session,target,info_string):
    num_step_burn_in=10 
    total_duration=0.0   #总时间
    total_duration_squared=0.0
    for i in range(num_batch+num_step_burn_in):
        start_time=time.time()
        _ = session.run(target)
        duration= time.time() -start_time
        if i>= num_step_burn_in:
            if i%10==0: #每迭代10次显示一次duration
                print("%s: step %d,duration=%.5f "% (datetime.now(),i-num_step_burn_in,duration))
            total_duration += duration
            total_duration_squared += duration *duration
    time_mean=total_duration /num_batch
    time_variance=total_duration_squared / num_batch - time_mean*time_mean
    time_stddev=math.sqrt(time_variance)
    #迭代完成,输出
    print("%s: %s across %d steps,%.3f +/- %.3f sec per batch "%
              (datetime.now(),info_string,num_batch,time_mean,time_stddev))
 
def main():
    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 ) )
        fc8,parameters=inference(images)
 
        init=tf.global_variables_initializer()
        sess=tf.Session()
        sess.run(init)
        time_compute(sess,target=fc8,info_string="Forward")
 
        obj=tf.nn.l2_loss(fc8)
        grad=tf.gradients(obj,parameters)
        time_compute(sess,grad,"Forward-backward")
if __name__=="__main__":
    main()

原数据集太大,随机生成图片进行测试。

参考文章1:深入理解AlexNet网络
参考文章2:再谈AlexNet
参考文章3:CNN之AlexNet

上一篇下一篇

猜你喜欢

热点阅读