TensorFlow实战系列9--实战程序1

 将训练和测试分成两个独立的程序,这可以使得每一个组件更加灵活。比如训练神经网络的程序可以持续输出训练好的模型,而测试程序可以每隔一段时间检验最新模型的正确率,如果模型效果更好,则将这个模型提供给产品使用。除了可以将不同功能模块分开,本节还将前向传播的过程抽象成一个单独的库函数。因为神经网络的前向传播过程在训练和测试的过程中都会用到,所以通过库函数的方式使用起来既可以更加方便,又可以保证训练和测试过程中使用的前向传播方法一定是一致的。下面我们将给出 TensorFlow 模型训练的一个最佳实践,它使用了上文中提到的所有优化方法来解决 MNIST 问题。在这儿最佳实践中总共有三个程序,第一个是 mnist_inference.py,它定义了前向传播的过程以及神经网络中的参数。第二个是 mnist_train.py,它定义了神经网络的训练过程。第三个是 mnist_eval.py,它定义了测试过程。以下代码给出了 mnist_inference.py中的内容。

# -*- coding: utf-8 -*-
import tensorflow as tf

# 定义神经网络结构相关的参数。
INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500

# 通过tf.get_variable函数来获取变量。在训练神经网络时会创建这些变量;在测试时会通
# 过保存的模型加载这些变量的取值。而且更加方便的是,因为可以在变量加载时将滑动平均变量
# 重命名,所以可以直接通过同样的名字在训练时使用变量自身,而在测试时使用变量的滑动平
# 均值。在这个函数中,将变量的正则化损失加入损失集合。

def get_weight_variable(shape, regularizer):
    weights = tf.get_variable(
        "weights", shape,
        initializer=tf.truncated_normal_initializer(stddev=0.1))
    # 当给出了正则化生成函数时,将当前变量的正则化损失加入名字为losses的集合。在这里
    # 使用了add_to_collection函数将一个张量加入一个集合,而这个集合的名称为losses。
    # 这是自定义的集合,不在TensorFlow自动管理的集合列表中。
    if regularizer != None:
        tf.add_to_collection('losses' ,regularizer(weights))
    return  weights

# 定义神经网络向前传播过程
def inference(input_tensor , regularizer):

    #声明第一层神经网络变量并完成向前传播过程
    with tf.variable_scope('layer1'):
        # 这里通过tf.get_variable或tf.Variable没有本质区别,因为在训练或是测试中
        # 没有在同一个程序中多次调用这个函数。如果在同一个程序中多次调用,在第一次调用
        # 之后需要将reuse参数设置为True。
        weights = get_weight_variable(
            [INPUT_NODE,LAYER1_NODE],regularizer
        )
        biases = tf.get_variable(
            "biases" ,[LAYER1_NODE],
            initializer= tf.constant_initializer(0.0)
        )
        layer1 = tf.nn.relu(tf.matmul(input_tensor,weights)+biases)

        # 类似声明第二层神经网络并完成向前传播
    with tf.variable_scope('layer2'):

        weights = get_weight_variable(
            [LAYER1_NODE, OUTPUT_NODE], regularizer
        )
        biases = tf.get_variable(
            "biases", [OUTPUT_NODE],
            initializer=tf.constant_initializer(0.0)
        )
        layer2 = tf.nn.relu(tf.matmul(layer1, weights) + biases)
    return layer2

 在这段代码中定义了神经网络的前向传播算法。无论是训练时还是测试时,都可以直接调用 inference 这个函数,而不用关心具体的神经网络结构。使用定义好的前向传播过程,以下代码给出了神经网络的训练程序mnist_train.py。

# -*- coding: utf-8 -*-
import os
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 加载mnist_inference.py中定义的常量和前向传播的函数。
import mnist_inference

# 配置神经网络的参数。
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
TRAINING_STEPS = 30000
MOVING_AVARAGE_DECAY = 0.99

# 模型保存的路径和文件名
MODEL_SAVE_PATH = "file:///home/tianlei/notebook/model1"
MODEL_NAME ="model.ckpt"

def train(mnist):
    # 定义输入和输出placeholder
    x =tf.placeholder(
        tf.float32,[None , mnist_inference.INPUT_NODE],name = 'x-input'
    )
    y_ = tf.placeholder(
        tf.float32, [None, mnist_inference.OUTPUT_NODE], name='y-input'
    )


    regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
    # 直接使用mnist_inference.py中定义的前向传播过程
    y = mnist_inference(x ,regularizer)
    global_step =tf.Variable(0, trainable=False)
    # 定义损失函数、学习率、滑动平均操作以及训练过程。
    variable_avarages = tf.train.ExponentialMovingAverage(
        MOVING_AVARAGE_DECAY ,global_step
    )
    variables_averages_op = variable_avarages.apply(tf.trainable_variables())
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
        logits=y, labels=tf.argmax(y_, 1))
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))

    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)
    with tf.control_dependencies([train_step,variables_averages_op]):
        train_op = tf.no_op(name = 'train')
    #初始化tensotrflow持久化类
    saver =tf.train.Saver
    with tf.Session as sess:
        tf.global_variables_initializer().run()
        # 在训练过程中不再测试模型在验证数据上的表现,验证和测试的过程将会有一个独
        # 立的程序来完成。
        for i in range (TRAINING_STEPS):
            xs ,ys =mnist.train.next_batch(BATCH_SIZE)
            _, loss_value ,step =sess.run([train_op,loss,global_step],feed_dict= {x:xs ,y_:ys})
            #每1000轮保存一次模型
            if i % 1000 == 0:

                # 输出当前的训练情况。这里只输出了模型在当前训练batch上的损失函
                # 数大小。通过损失函数的大小可以大概了解训练的情况。在验证数据集上的
                # 正确率信息会有一个单独的程序来生成。
                print("After %d training step(s) ,loss on training" "batch is %g." % (step ,loss_value))
                #保存当前的模型。注意这里给出了global_step参数, 这样可以让每个被
                # 保存模型的文件名末尾加上训练的轮数,比如“model.ckpt-1000”表示
                # 训练1000轮之后得到的模型。
                saver.save(
                    sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME),
                    global_step=global_step)


def main(argv=None):
    mnist = input_data.read_data_sets("/home/tianlei/notebook/MNIST_data/", one_hot=True)
    train(mnist)


if __name__ == '__main__':
    tf.app.run()
$ python mnist_train.py
Extracting /home/tianlei/notebook/MNIST_data/train-images-idx3-ubyte.gz
Extracting /home/tianlei/notebook/MNIST_data/train-labels-idx1-ubyte.gz
Extracting /home/tianlei/notebook/MNIST_data/t10k-images-idx3-ubyte.gz
Extracting /home/tianlei/notebook/MNIST_data/t10k-labels-idx1-ubyte.gz
After 1 training step(s), loss on training batch is 3.46893.
After 1001 training step(s), loss on training batch is 0.172291.
After 2001 training step(s), loss on training batch is 0.197483.
After 3001 training step(s), loss on training batch is 0.153582.
After 4001 training step(s), loss on training batch is 0.117219.
After 5001 training step(s), loss on training batch is 0.121872.

After 6001 training step(s), loss on training batch is 0.0976607.

 在新的训练代码中,不再将训练和测试跑在一起。训练过程中,每1000 轮输出一次在当前训练 batch 上损失函数的大小来大致估计训练的效果。在上面的程序中,每 1000 轮保存一次训练好的模型,这样可以通过一个单独的测试程序,更加方便地在滑动平均模型上做测试。以下代码给出了测试程序 mnist_eval.py。

# -*- coding: utf-8 -*-
import time
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# 加载mnist_inference.py和mnist_train.py中定义的常量和函数
import mnist_inference
import mnist_train

# 每10秒加载一次最新的模型,并在测试数据上测试最新模型的正确率。
EVAL_INTERVAL_SECS =10

def evaluate(mnist):
    with tf.Graph().as_default() as g:
        # 定义输入输出的格式。
        x = tf.placeholder(
            tf.float32, [None, mnist_inference.INPUT_NODE], name='x-input')
        y_ = tf.placeholder(
            tf.float32, [None, mnist_inference.OUTPUT_NODE], name='y-input')
        validate_feed = {x: mnist.validation.images,
                         y_: mnist.validation.labels}
    # 直接通过调用封装好的函数来计算前向传播的结果。因为测试时不关注正则化损失的值,
    # 所以这里用于计算正则化损失的函数被设置为None。
    y  = mnist_inference.inference(x ,None)
    # 使用前向传播的结果计算正确率。如果需要对未知的样例进行分类,那么使用
    # tf.argmax(y, 1)就可以得到输入样例的预测类别了。
    correct_prediction =tf.equal(tf.argmax(y ,1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean((tf.cast(correct_prediction, tf.float32)))
    # 通过变量重命名的方式来加载模型,这样在前向传播的过程中就不需要调用求滑动平均
    # 的函数来获取平均值了。这使得我们可以完全共用mnist_inference.py中定义的
    # 前向传播过程。
    variable_averages = tf.train.ExponentialMovingAverage(
        mnist_train.MOVING_AVERAGE_DECAY
    )
    variables_to_restore = variable_averages.variables_to_restore()
    saver = tf.train.Saver(variables_to_restore)
    # 每隔EVAL_INTERVAL_SECS秒调用一次计算正确率的过程以检测训练过程中正确率的# 变化。
    while True:
        with tf.Session as sess:
            ckpt = tf.train.get_checkpoint_state(
                mnist_train.MODEL_SAVE_PATH
            )
            if ckpt and ckpt.model_checkpoint_path:
                # 加载模型
                saver.restore(sess, ckpt.model_checkpoint_path)
                # 通过文件名得到模型保存时迭代的轮数
                global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
                accuracy_score = sess.run(accuracy, feed_dict=validate_feed)
                print("After %s training step(s), validation "
                          "accuracy = %g" % (global_step, accuracy_score))
            else:
                print('No checkpoint file found')
                return
        time.sleep(EVAL_INTERVAL_SECS)


def main(argv=None):
    mnist = input_data.read_data_sets("/home/tianlei/notebook/MNIST_data/", one_hot=True)
    evaluate(mnist)

if __name__ == '__main__':
    tf.app.run()
 上面给出的 mnist_eval.py 程序会每隔 10 秒运行一次,每次运行都是读取最新保存的模型 , 并在 MNIST 验证数据集上计算模型的正确率。如果需要离线预测未知数据的类别(比如这个样例程序可以判断手写体数字图片中所包含的数字),只需要将计算正确率的部分改为答案输出即可。运行 mnist_eval.py 程序可以得到类似下面的结果。注意因为这个程序每10 秒自动运行一次,而训练程序不一定每 10 秒输出一个新模型,所以在下面的结果中会发现有些模型被测试了多次。一般在解决真实问题时,不会这么频繁地运行评测程序。

$ python mnist_eval.py

Extracting /home/tianlei/notebook/MNIST_data/train-images-idx3-ubyte.gz
Extracting /home/tianlei/notebook/MNIST_data/train-labels-idx1-ubyte.gz
Extracting /home/tianlei/notebook/MNIST_data/t10k-images-idx3-ubyte.gz
Extracting /home/tianlei/notebook/MNIST_data/t10k-labels-idx1-ubyte.gz

After 1 training step(s), validation accuracy = 0.0616
After 1001 training step(s), validation accuracy = 0.9764
After 2001 training step(s), validation accuracy = 0.9834
After 2001 training step(s), validation accuracy = 0.9834
After 3001 training step(s), validation accuracy = 0.9852
After 4001 training step(s), validation accuracy = 0.9854
After 5001 training step(s), validation accuracy = 0.986
After 6001 training step(s), validation accuracy = 0.9854
上面的程序可以将 MNIST 正确率达到 ~98.4%。


猜你喜欢

转载自blog.csdn.net/tian_qing_lei/article/details/79269851