MNIST手写数字识别(三)应用优化

本篇的主要内容

  • 应用三种优化方式,对之前的模型进行优化
  • 介绍一些在程序中用到的函数

学习于《TensorFlow实战Google深度学习框架》一书

程序

相比于第一次的简单逻辑回归模型,这一次的调整了网络结构,添加了一个500个节点的隐藏层,在结构中,设置了动态学习率,添加了正则化项,并使用了滑动平均模型稳定整个模型。
整体的结构流程如下:

输入数据
输入层
隐藏层
输出层
输出数据

这次的代码比较长,所以划分了几个函数,这样结构比较清楚一些。

代码:
第一部分:
必要的包以及要使用到的一些常量 直接定义在外边

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

# MNIST 数据集相关的常数
# 输入层的几点疏对于MNIST来说就是是图片展平之后的像素数量
INPUT_NODE = 784
# 输出层的节点数量 等分类类别的数量 需要进行区分的是10个数字 所以输出节点数目是10
OUTPUT_NODE = 10

# 配置神经网络的参数
# 隐藏层的结点数 建立的模型只有一个隐藏层结构
LAYER1_NODE = 500  # 这个隐藏层有500个节点
# 一个训练的batch中训练数据个数 数字越小 训练过程越接近随机梯度下降(SGD)
# 数字越大,训练越接近梯度下降
BATCH_SIZE = 100

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

第一个函数,定义了网络的结构,传入所需的权重以及偏置,计算前向传播的结果,在前面的过程中,这就是主体部分了,这个函数输出的结果就是对于每张图,输出的未进行softmax回归的1*10的向量,这里有两个返回,后一个是有滑动平均输入时候的结果。

# 使用的激活函数是 ReLU 激活函数

def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
    # 如果没有提供滑动平均类 直接使用当前的参数取值
    if avg_class is None:
        # 计算隐藏层的前向传播结果 使用ReLU哈数进行激活
        layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
        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([LAYER1_NODE, OUTPUT_NODE], stddev=0.1)
    )
    biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))


    # 计算当前参数下神经网络前向传播的结果  指定计算滑动平均的类为 None
    # 也就是函数不会使用参数的滑动平均值
    y = inference(x, None, weights1, biases1, weights2, biases2)
    # 定义存储训练额轮数的变量 此处的这个变量不需要计算滑动平均值
    # 所以设定为  不可训练  (trainable = False)
    global_step = tf.Variable(0, trainable=False)

    # 初始化 滑动平均类
    variable_averages = tf.train.ExponentialMovingAverage(
        MOVING_AVERAGE_DECAY, global_step
    )
    # 计算所有可训练的变量的滑动平均
    # tf.trainable_variables() 是所有的 可以训练的变量(参数)
    variable_averages_op = variable_averages.apply(tf.trainable_variables())

    # 计算使用了滑动平均之后的前向传播结果 指定了 variable_average
    average_y = inference(
        x, variable_averages, weights1, biases1, weights2, biases2
    )

    # 使用的损失函数是交叉熵 这里的交叉熵与之前的调用不太一样
    # 简单来说 就是将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                     # 学习率衰减速度
    )

    # 使用梯度下降法来优化
    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)

    # 在训练模型的过程中 需要反向传播来更新参数 也需要更新参数的滑动平均值
    # 使用这种方式可以同时运行这两个操作
    train_op = tf.group(train_step, variable_averages_op)

    # 检验使用了滑动平均模型的神经网络前向传播结果是否正确 #
    # average_y是模型的输出结果  y_ 是输入正确的标签集合
    corrext_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))

    # 这个与之前的程序一样 都是计算一组数据上的准确率
    accuracy = tf.reduce_mean(tf.cast(corrext_prediction, tf.float32))

    with tf.Session() as sess:
        init_op = tf.global_variables_initializer()
        sess.run(init_op)

        # 准备训练数据集 和 测试数据集
        validate_feed = {x: mnist.validation.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:        # 1000轮输出一次结果
                validate_acc = sess.run(accuracy, feed_dict=validate_feed)
                print("After %d training steps, validation accuracy using average model is %g " %(i, validate_acc))

            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("After %d training step, test accuracy using average model is %g" % (TRAINING_STEPS, test_acc))

接下来就是简单调用上面的函数实现运行就可以了:

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

# TensorFlow 定义的主程序入口 tf.app.run() 方法会调用main函数
if __name__ == '__main__':
    tf.app.run()

结果这样的优化,准确率相比之前提高到了98%左右,输出结果(部分),可以看到,模型收敛的速度比较快,在2000次左右的时候就已经趋于稳定了。

After 0 training steps, validation accuracy using average model is 0.0674 
After 1000 training steps, validation accuracy using average model is 0.9738 
After 2000 training steps, validation accuracy using average model is 0.98 
After 3000 training steps, validation accuracy using average model is 0.9818 
After 4000 training steps, validation accuracy using average model is 0.9858 
After 5000 training steps, validation accuracy using average model is 0.9844 
After 6000 training steps, validation accuracy using average model is 0.9842 
After 7000 training steps, validation accuracy using average model is 0.9852 
After 8000 training steps, validation accuracy using average model is 0.9852 
After 9000 training steps, validation accuracy using average model is 0.9854 
After 10000 training steps, validation accuracy using average model is 0.9848 

如果我们改动上面的代码,上面的代码应用了三种优化方式,其实对于准确率的提高,最重要的是正则化处理,如果去掉滑动平均模型部分,可以看到准确率的变化不大,也就是说明我们模型的参数变化不大,如果去掉动态学习率部分,可以明显看出来在准确率上,达到同样的准确率,确实花费的训练时间要多,:

After 0 training steps, validation accuracy using average model is 0.106 
After 1000 training steps, validation accuracy using average model is 0.9666 
After 2000 training steps, validation accuracy using average model is 0.9728 
After 3000 training steps, validation accuracy using average model is 0.977 
After 4000 training steps, validation accuracy using average model is 0.9794 
After 5000 training steps, validation accuracy using average model is 0.9806 
After 6000 training steps, validation accuracy using average model is 0.981 
After 7000 training steps, validation accuracy using average model is 0.9806 
After 8000 training steps, validation accuracy using average model is 0.9808 
After 9000 training steps, validation accuracy using average model is 0.9816 
After 10000 training steps, validation accuracy using average model is 0.9816 

在去掉正则化处理之后,可以看到正确率是在不太到98%的地方浮动的,相比上面是有变化的,相比之前的模型,准确率上升当然还是得益于模型结构的调整:

After 0 training steps, validation accuracy using average model is 0.0968 
After 1000 training steps, validation accuracy using average model is 0.9636 
After 2000 training steps, validation accuracy using average model is 0.9706 
After 3000 training steps, validation accuracy using average model is 0.9758 
After 4000 training steps, validation accuracy using average model is 0.9774 
After 5000 training steps, validation accuracy using average model is 0.979 
After 6000 training steps, validation accuracy using average model is 0.98 
After 7000 training steps, validation accuracy using average model is 0.9788 
After 8000 training steps, validation accuracy using average model is 0.98 
After 9000 training steps, validation accuracy using average model is 0.9794 
After 10000 training steps, validation accuracy using average model is 0.9806 

正则化对整体模型的准确度确实起到了很大的作用。

新的交叉熵函数

第一个就是交叉熵函数的变化,之前的模型中,我们首先拿到模型的输出结果,进行softmax回归处理,然后使用计算与正确的函数之间的交叉熵,最后优化这个函数,得到最后的模型,在这里,使用了一个新的函数,其实本质上,这个函数就是将 softmax回归和交叉熵结合在一起了,加速整个训练过程,具体的结构如下:

def sparse_softmax_cross_entropy_with_logits(_sentinel: Any = None,
                                             labels: Any = None,
                                             logits: Any = None,
                                             name: Any = None) -> Union[Tensor, None, IndexedSlices, SparseTensor]

我们在使用的时候只需注意 labels 和 logits即可,labels 传入的是正确的类别,在上面的程序中,也就是已知的y部分,但是要注意的与之前不同的是,这里的输入要求是非稀疏的(就拿这个例子来说,对于一张图片的label,在数据中给的是 [1, 0, 0, 0, …]这样的形式,这就是稀疏的,如果明确指出这个数字是 1,2…,就是非稀疏的),也就是输入的shape会是 b a t c h s i z e × 1 batch size \times 1 batchsize×1的形式,所以在上面的程序中,我们使用了 argmax()函数将原本的label转化为列为1的矩阵输入, logits就是我们模型中输出的结果,这与之前是一样的 shape是 b a t c h s i z e × 10 batch size \times 10 batchsize×10。此外,这里会一块进行softmax回归,所以我们在函数 inference()部分没有计算softmax。
在TensorFlow中,有一个与这个函数很相似的函数:tf.nn.softmax_cross_entropy_with_entropy,这个函数唯一的区别就是label部分是稀疏的,一般图像处理的结果都是非稀疏的,所以这个函数没有之前那个一个应用多。

同时运行多个操作

在TensorFlow中提供了同时运行多个操作的函数,有两种方式:

  • tf.control_dependencies([a, b, c…], train_op = tf.no_op(name=‘XXXX’))
  • train_op = tf.group(a, b, c…)

在上面,采用的是第二种方式。

通过调整结构和添加优化,模型的准确率达到了98.5%左右,接下来继续学习CNN,之后用新的网络结构急需解决这个问题。

以上~

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值