TensorFlow 深度学习框架(6)-- mnist 数字识别及不同模型效果比较

以下代码给出了一个在 mnist 数据集上实现 使用指数衰减学习率设置,使用正则化来避免过度拟合,以及使用滑动平均模型 功能的完整的TensorFlow 程序。

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# mnist 数据集相关常数
INPUT_NODE = 784
OUTPUT_NODE = 10  # 10个数字 0-9 分类

#配置神经网络参数
LAYER1_NODE = 500
LAYER2_NODE = 100

LEARNING_RATE_BASE = 0.8 #基础学习率
LEARNING_RATE_DECAY = 0.99 #学习衰减率
REGULARIZATION_RATE = 0.0001  #正则化系数
TRAINING_STEPS = 30000 #训练轮数
MOVING_AVERAGE_DECAY = 0.99 #滑动平均衰减率

#前向传播函数
def inference(input_tensor,avg_class,weights1,biases1,weights2,biases2):
    #当没有滑动平均时,直接使用参数当前取值
    if avg_class == None:
        layer1 = tf.nn.relu(tf.matmul(input_tensor,weights1) + biases1)
        #因为在计算损失函数时会一并计算 softmax 函数,所以这里不需要加入激活函数
        return tf.matmul(layer1,weights2) + biases2
    else:
        #首先使用 avg_class.average 函数来计算得出变量的滑动平均值
        layer1 = tf.nn.relu(tf.matmul(input_tensor,avg_class.average(weights1)) + 
                       avg_class.average(biases1))
        return tf.matmul(layer1,avg_class.average(weights2)) + avg_class.average(biases2)

#训练模型
def train(mnist):
    x = tf.placeholder(tf.float32,[None,INPUT_NODE],name = 'x-input')
    y_ = tf.placeholder(tf.float32,[None,OUTPUT_NODE],name = 'y-input')
    #隐藏层
    weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE,LAYER1_NODE],stddev = 0.1))
    biases1 = tf.Variable(tf.constant(0.1,shape = [LAYER1_NODE]))
    #输出层
    weights2 = tf.Variable(tf.truncated_normal([LATER1_NODE,OUTPUT_NODE],stddev = 0.1))
    biases2 = tf.Variable(tf.constant(0.1,shape = [OUTPUT_NODE]))
    #计算当前参数下的前向传播结果
    y = inference(x,None,weights1,biases1,weights2,biases2)
    
    #定义存储训练函数的变量。这个变量不需要计算滑动平均值,所以这里指定这个变量为不可训练的变量
    #在TensorFlow训练神经网络时,一般会将代表训练轮数的变量指定为不可训练的参数
    global_step = tf.Variable(0,trainable = False)

    #给定滑动平均衰减率和训练轮数的变量,初始化滑动平均类
    #给定训练轮数的变量可以加快训练早期变量的更新速度
    variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY,global_step)
    
    #在所有代表神经网络参数的变量上使用滑动平均,其他辅助变量(比如global_step)就不需要了
    variables_averages_op = variable_averages.apply(tf.trainable_variables())

    #计算使用了滑动平均后的前向传播结果
    #滑动平均不会改变变量本身的取值,而是会维护一个影子变量来记录其滑动平均值
    average_y = inference(x,variable_averages,weights1,biases1,weights2,biases2)

    #TensorFlow提供了 sparse_softmax_cross_entropy_with_logits 函数来计算交叉熵
    #当分类问题只有一个正确答案时,可以使用这个函数来加速交叉熵的计算
    #这个函数的第一个参数是神经网络不包括 softmax 层的前向传播结果,第二个是训练数据的正确答案
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits = y,labels = tf.argmax(y_,1))
    #计算当前batch 中所有样例的交叉熵平均值
    cross_entropy_mean = tf.reduce_mean(cross_entropy)

    #计算 L2 正则化损失函数
    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    #计算模型的正则化损失
    regularization = regularizer(weights1) + regularizer(weights2)
    loss = cross_entropy_mean + regularization
    #设置指数衰减学习率
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,  #基础学习率
        global_step,         #当前迭代的轮数
        mnist.train.num_examples/ BATCH_SIZE, #总的迭代轮数
        LEARNING_RATE_DECAY) #学习衰减速度
    #使用梯度下降优化算法来优化损失函数。注意这里损失函数包含了交叉熵损失和L2 正则化损失
    train_step = tf.train.GradientDescentOptimizer(learning_rate). \
                            minimize(loss,global_step = global_step)
    #一次完成训练参数和更新滑动平均值的操作
    with tf.control_dependencies([train_step,variables_averages_op]):
        train_op = tf.no_op(name = 'train')

    #计算正确率,tf.equal 计算两个张量的每一维是否相等,如果相等返回 True,否则返回 False
    correct_prediction = tf.equal(tf.argmax(average_y,1),tf.argmax(y_,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

    #初始化会话并开始训练过程
    with tf.Session() as sess:
        tf.initialize_all_variables().run()
        validate_feed = {x:mnist.vallidation.images,y_:mnist.validation.labels}
        test_feed = {x:mnist.test.images,y_:mnist.test.labels}
        #迭代的训练神经网络
        for i in range(TRAINING_STEPS):
            if i % 1000 == 0:
                validate_acc = sess.run(accuracy,feed_dict = validate_feed)
                print("After %d training steps,validation accuracy "
                            "using average model is %g" %(i,validate_acc))
            #产生一轮的 batch 数据并训练
            xs,ys = mnist.train.next_batch(BATCH_SIZE)
            sess.run(train_op,feed_dict = {x:xs,y_:ys})
    
        #在训练完之后,在测试数据上检测神经网络模型最终的正确率
        test_acc = sess.run(accuracy,feed_dict = test_feed)
        print("Afer %d training steps,test accuracy using average"
                "model is %g " %(TRAINING_STEPS,test_acc))

#主程序入口
def main(argv = None):
    mnist = input_data.read_data_sets("/path",one_host = True)
    train(mnist)

if __name__ == '__main__':
    tf.app.run()

以上是损失函数在训练数据上的变化。。。

不同模型效果比较

如图给出了在相同神经网络参数下,使用不同的优化方法,经过 30000 轮训练迭代后,得到的最终模型的正确率。

很明显可以看出,调整神经网络的结构对最终的正确率有非常大的影响。没有隐藏层或没有激活函数时,模型的正确率只有92.6%,这个数字远小于使用了隐藏层和激活函数的正确率。说明神经网络的结构对最终的模型效果有很大的影响。另外可以发现使用滑动平均模型和使用正则化、指数衰减学习率带来的正确率的提升并不明显。其中使用了所有优化算法的模型和不使用指数衰减学习率的模型以及不使用滑动平均的模型都可以达到大约 98.4% 的正确率。那是因为滑动平均模型和指数衰减的学习率在一定程度上都是限制神经网络中参数更新的速度,然而在mnist 数据集上,因为模型收敛的速度很快,所以这两种优化对最终模型的影响不大。

下图显示了不同迭代轮数时,使用了所有优化方法的模型正确率与平均绝对梯度的变化。

可以看出,前 4000 轮迭代对模型的改变是最大的。在4000 轮之后,因为梯度本身比较小,所以参数的改变也就比较缓慢了。于是滑动平均模型或者指数衰减的学习率作用也就没有那么突出了。

下图是使用了所有优化方法的模型的 正确率与学习率 在不同迭代轮数时的变化趋势。

从图中可以看到,学习率曲线呈现出阶梯状衰减,在前 4000 轮时,衰减后的学习率和最初的学习率差距并不大。当问题更加复杂时,迭代不会那么快接近收敛,这时滑动平均模型和指数衰减的学习率可以发挥更大的作用。

如图给出了灰色和黑色的实线分别表明 只优化交叉熵模型 和 优化总损失模型 的正确率和损失曲线。

可以看出,只优化交叉熵模型在训练数据上的交叉熵损失(灰色虚线)要比优化总损失模型(黑色虚线)更小。然而在测试数据上,优化总损失的模型(黑色实线)却要比只优化交叉熵的模型(灰色实线)较好。这就是前面介绍的过拟合问题。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值