zoukankan      html  css  js  c++  java
  • TensorFlow实战--阅读笔记part4

    Tensorflow实现经典神经网络

    一、Tensorflow实现AlexNet

     2012年,Hinton的学生Alex Krizhevsky提出了深度卷积神经网络模型AlexNet,它算是LeNet的一种更深更宽的版本。它包含了几个比较新的技术点,也首次在CNN中成功应用了ReLU,Dropout和LRN等trick.同时AlexNet也使用了GPU进行运算加速,AlexNet包括了6亿3000万个连接,6000万个参数和65万个神经元,拥有5个卷积层。

    AlexNet主要使用到的新技术有如下:

    (1)成功使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功过解决了Sigmoid在网络较深时的梯度弥散问题。虽然ReLU函数在很久之前就被提出了,但是直到AlexNet的出现才将其发扬光大

    (2)训练时使用dropout随机忽略一部分神经元,以避免模型的过拟合。dropout虽有单独的论文论述,但是AlexNet将其实用化,一般都在全连接层使用

    (3)在CNN中使用最大池化,避免了平均池化的模糊化效果。并且AlexNet提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性

    (4)提出了LRN层,对局部神经元的活动创建竞争机制,使得其中相应比较大的值变得相对更大,并抑制其他反馈小的神经元,增强了模型的泛化能力

    (5)使用CUDA加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时的大量矩阵运算

    (6)数据增强,随机地从256x256的原始图像中截取224x224大小的区域(以及水平翻转的镜像),相当于增加了(256-224)^2 x 2=2048倍的数量,仅靠原始的数据量,参数众多的CNN会陷入过拟合中,使用了数据增强后可以大大减轻过拟合,提升泛化能力。进行预测时,则是取图片的四个角加中间共五个位置,并进行左右翻转,一共获得10个图片,对他们进行预测 并对10个结果取平均值。

    因为AlexNet训练时使用了两块GPU,因此这个结构图中不少组件都被拆为了两部分。现在我们GPU的显存可以放下全部模型参数,因此只考虑一块GPU的情况就可

     

    AlexNet的每层超参数如下图所示:

      

    可以发现在前几个卷积层,虽然计算量很大,但参数量很小,都在1M左右甚至更小,只占AlexNet总参数量的很小一部分。这就是卷积层有用的地方,可以通过较小的参数量提取有效的特征。而如果前几层直接使用全连接层,那么参数量和计算量将成为天文数字,虽然每一个卷积层占整个网络的参数量的1%都不到,但是如果去掉任何一个卷积层,都会使网络的分类性能大幅地下降

    因为使用ImageNet数据集训练一个完整的AlexNet耗时非常长,所以接下来只是建立一个完整的AlexNet卷积神经网络,然后对它每一个batch的前馈计算和反馈计算的速度进行测试。

    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,接受图片作为输入,返回最后一程pool5和parameters

    通过with tf.name_scope('conv1') as scope可以将scope内生成的Variable自动命名成conv1/xxx,便于区分不同卷积层之间的组件。

    目前除了AlexNet,其他经典的卷积神经网络模型基本都放弃了LRN(主要是效果不明显),而使用LRN也会使前馈,反馈的速度大大下降(整体速度降到1/3)

    padding模式为SAME表示输入输出大小不变,图片边界外的点会进行填充,而VALID模式表示取样时不能超过边框,对边界不进行填充

    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]
    
        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)
    
        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

     inference函数已经完成了,它可以创建AlexNet的卷积部分

    接下来实现一个评估AlexNet每轮计算时间的函数time_tensorflow_run,这个函数的第一个输入是Session,第二个变量时需要评测的运算子,第三个变量时测试的名称

    先定义预热轮数num_steps_burn_in=10,它的作用是给程序热身,头几轮迭代有显存加载,cache命中等问题因此可以跳过,我们只考量10轮迭代之后的计算时间。

    同时也记录总时间total_duraiton和平方和total_duration_squared用以计算方差

    def time_tensorflow_run(session,target,info_string):
        num_steps_burn_in = 10
        total_duration = 0.0
        total_duration_squared = 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(),i-num_steps_burn_in,duration))
                total_duration += duration
                total_duration_squared += duration * duration
    
        mn = total_duration / num_batches
        vr = total_duration_squared /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))

    接下来定义主函数run_benchmark,用tf.random_normal函数构造正态分布作为输入图片

    然后使用time_tensorflow_run统计运算时间,传入的target就是pool5,backward过程的评测与forward的不同,还需要给最后的输出pool5设置一个优化目标loss.

    这里使用tf.nn.l2_loss计算pool5的loss,再使用tf.gradients求相对于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的网络结构

    然后显示的是forward的计算时间,使用的是CPU

    最后显示的是backward运算的时间

    去除LRN层,前向和后向运算时间都快3倍,而且发现backward的时间比forward时间更耗时的多

    总结

    CNN的训练过程(即backward计算)通常都比较耗时,而且不想预测过程(即forward计算),训练通常需要过很多遍数据,进行大量的迭代。因此应用CNN的主要瓶颈还是在训练,用CNN做预测问题不大。

    AlexNet中用到的寻多网络结构和Trick给深度学习的发展带来了深刻的影响。当然,我们也不能忽视ImageNet数据集给深度学习带来的贡献。训练深度神经网络,必须拥有一个像ImageNet这样超大的数据集才能避免过拟合,发挥深度学习的优势。可以说,传统机器学习模型适合学习一个小型数据集,但是对于大型数据集,我们需要有更大学习容量(Learning Capacity)的模型,即深度学习模型。

  • 相关阅读:
    77. Combinations
    76. Minimum Window Substring
    75. Sort Colors
    74. Search a 2D Matrix
    73. Set Matrix Zeroes
    72. Edit Distance
    71. Simplify Path
    70. Climbing Stairs
    线段树+欧拉函数——cf1114F
    区间dp——好题cf1132F
  • 原文地址:https://www.cnblogs.com/lainey/p/7520519.html
Copyright © 2011-2022 走看看