MNIST数字识别问题

MNIST数字识别问题

专栏文章汇总


文章结构如下:

1: MNIST数据处理

2: TensorFlow 训练神经网络

3: 变量管理

4: 模型持久化

5: TensorFlow 最佳实践样例程序


深层神经网络介绍了训练神经网络模型时需要考虑的主要问题以及解决这些问题的常用方法。这篇将通过一个实际问题来验证里面介绍的方法。


1: MNIST数据处理

MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges数据集下载地址,当然也可以用下面代码下载。

from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('/MNIST_data/',one_hot=True)


#输出:Train data size:  55000
print('Train data size: ', mnist.train.num_examples)
#输出:Validating data size:  5000
print('Validating data size: ', mnist.validation.num_examples)
#输出:Testing data size:  10000
print('Testing data size: ', mnist.test.num_examples)
#输出:。。。
print('Example training data: ', mnist.train.images[0])
#输出:Example training label:  [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
print('Example training label: ', mnist.train.labels[0])


#input_data.read_data_sets函数里的mnist.train.next_batch函数
batch_size = 100
xs, ys = mnist.train.next_batch(batch_size)
#从train的集合中选取batch_size个训练数据
print('X shape', xs.shape)
#输出:X shape (100, 784)
print('Y shape', ys.shape)
#输出:Y shape (100, 10)


2: TensorFlow 训练神经网络

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

#MNIST数据集相关常数
INPUT_NODE = 784     #输入节点,等于图片的像素
OUTPUT_NODE = 10     #输出节点,等于类别的数目

#配置神经网络参数
LAYER1_NODE = 500    #隐藏层节点数,这里使用只有一个隐藏层的网络结构作为样例

BATCH_SIZE = 100     #一个训练batch中的训练数据个数

LEARNING_RATE_BASE = 0.8     #基础学习率
LEARNING_RATE_DECAY = 0.99   #学习率的衰减率

REGULARIZATION_RATE = 0.0001   #正则化
TRAINING_STEPS = 6000         #训练轮数
MOVING_AVERAGE_DECAY = 0.99    #滑动平均衰减率

#一个辅助函数,给的那个神经网络的输入和所有参数,计算神经网络的前向传播结果,
#定义Relu激活函数的三层全连接神经网络,通过加入隐藏层实现了多层网络结构
#这个函数也支持传入用于计算参数平均值的类,这样方便在测试时使用滑动平均模型
def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
    # 不使用滑动平均类
    if avg_class == None:
        #计算输出层的前向传播结果,因为在计算损失函数时会一并计算softmax函数,
        #所以这里不需加入激活函数
        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)

    #定义存储训练轮数的变量,这个变量不需要计算滑动平均值,所以指定为不可训练的变量
    global_step = tf.Variable(0, trainable=False)

    #给定滑动平均衰减率和训练轮数的变量,初始化滑动平均类,
    variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)

    #在所有代表神经网络参数的变量上使用滑动平均。
    variables_averages_op = variable_averages.apply(tf.trainable_variables())

    #计算使用滑动平均之后的前向传播结果
    #因为滑动平均不会改变变量本身的取值,而是会维护一个影子变量来记录其滑动平均值
    #所以在当需要使用这个滑动平均值时,需要用average函数
    average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)

    #计算交叉熵作为刻画预测值和真实值之间差距的损失函数
    #sparse_softmax_cross_entropy_with_logits第一个参数是不包含softmax层的前向传播结果
    #第二个参数是训练数据的正确值
    #因为标准答案是一个长度为10的一个一维数组,而该函数需要提供的是一个正确答案的数字
    #所以需要tf.argmax函数来得到正确答案对应的类别编号
    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(REGULARAZTION_RATE)
    #计算模型的正则化损失,一般只计算权重项
    regularaztion = regularizer(weights1) + regularizer(weights2)
    #总损失
    loss = cross_entropy_mean + regularaztion

    # 设置指数衰减的学习率。
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        mnist.train.num_examples / BATCH_SIZE,
        LEARNING_RATE_DECAY,
        staircase=True)

    # 优化损失函数
    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)

    #在训练模型时,每过一遍数据既需要通过反向传播来更新神经网络中的参数,
    #又要更新每一次参数的滑动平均值
    #为了一次完成多个操作,Tensorflow提供了tf.control_dependencies和tf.group两种机制,下面两行和
    #train_op = tf.group(train_step, variables_average_op)是等价的
    with tf.control_dependencies([train_step, variables_averages_op]):
        train_op = tf.no_op(name='train')

    #检验使用了滑动平均模型的神经网络的前向传播是否正确,
    #tf.argmax(average_y, 1)计算每一个样例的预测答案
    #其中average_y是一个batch_size*10的二维数组,每一行表示一个样例前向传播结果
    #tf.argmax的第二个参数'1'表示选取最大值的操作仅在一个维度进行,也就是说,只在每一行选取最大值对应的下标
    #tf.equal返回bool类型
    correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))
    #tf.cast将bool类型转为实数型,然后计算平均值
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    # 初始化会话,并开始训练过程。
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        #准备验证数据,通过验证数据来大致判断停止条件
        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):
            #每迭代1000轮输出一次在验证集上的测试结果
            if i % 1000 == 0:
                #本例没将验证数据划分为更小的batch,当神经网络比较复杂或验证数据比价大时,
                #太大的batch会导致计算时间过长甚至发生内存溢出
                validate_acc = sess.run(accuracy, feed_dict=validate_feed)
                print("After %d training step(s), 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(("After %d training step(s), test accuracy using average model is %g" %(TRAINING_STEPS, test_acc)))

#主程序入口
def main(argv=None):
    #声明处理MNIST数据集的类,这个类在初始化时会自动下载数据
    mnist = input_data.read_data_sets("/MNIST_data", one_hot=True)
    train(mnist)

#Tensorflow提供的一个主程序入口,tf.app.run会调用上面定义的main函数
if __name__=='__main__':
    main()


Extracting /MNIST_data\train-images-idx3-ubyte.gz
Extracting /MNIST_data\train-labels-idx1-ubyte.gz
Extracting /MNIST_data\t10k-images-idx3-ubyte.gz
Extracting /MNIST_data\t10k-labels-idx1-ubyte.gz
After 0 training step(s), validation accuracy using average model is 0.0922 
After 1000 training step(s), validation accuracy using average model is 0.976 
After 2000 training step(s), validation accuracy using average model is 0.9802 
After 3000 training step(s), validation accuracy using average model is 0.9816 
After 4000 training step(s), validation accuracy using average model is 0.9834 
After 5000 training step(s), validation accuracy using average model is 0.9828 
After 6000 training step(s), test accuracy using average model is 0.9832

3: 变量管理

Tensorflow入门介绍了通过tf.Variable函数来创建一个变量。除了tf.Variable函数,TensorFlow还提供了tf.get_variable函数来创建或者获得一个变量。当tf.get_variable函数用于创建一个变量时,它和tf.Variable函数基本等价的。

#以下这两个定义是等价的
v = tf.get_varlable('v', shape=[1], initializer=tf.constant_initializer(1.0))
v = tf.Variabels(tf.constant(1.0, shape=[1]), name='v')


initializer函数:

tf.get_variable函数与tf.Variable函数最大的区别在于指定变量名称的参数。对于tf.Variable函数,变量名称是个可选参数,通过name='v'形式给出。但是对于tf.get_variable函数,变量名称是个必填参数。tf.get_variable会根据这个名字取创建或者获取变量。当用tf.get_variable函数创建已经有同名的参数,就会报错。如果需要通过tf.get_variable获取一个已经创建的变量,需要通过tf.variable_scope函数生成一个上下文管理器,并明确指定这个上下文管理器中,tf.get_variabl将直接获取已经生成的变量。

#在名字为foo的命名空间内创建名字为v的变量
with tf.variable_scope("foo"):
    v = tf.get_variable('v', [1], initializer=tf.constant_initializer(1.0))

#因为在命名空间foo中已经存在名字为v的变量,所以下面报错
with tf.variable_scope('foo'):
    v = tf.get_variable('v', [1])

#在生成上下文管理器时,将参数reuse设置成True,这样tf.get_variable函数将直接获取已声明的变量
with tf.variable_scope('foo', reuse=True):
    v1 = tf.get_variable('v', [1])
    print(v1==v)   
    #输出为True

#将参数reuse设置为True时,tf.variable_scope将只能获取已经创建过的变量。
#因为在命名空间bar中还没有创建变量,所以下面代码报错
with tf.variable_scope('bar', reuse=True):
    v = tf.get_variable('v', [1])


TensorFlow中的tf.variable_scope函数是可以嵌套的

with tf.variable_scope('root'):
    #可以通过tf.get_variable_scope().reuse函数来去当前上下文管理器中reuse参数的取值
    print(tf.get_variable_scope().reuse) #输出False

    with tf.variable_scope('foo', reuse=True): #新的一个上下文管理器
        print(tf.get_variable_scope().reuse)   #输出为True
        with tf.variable_scope('bar'):        #新建一个嵌套的上下文管理器,当不指定reuse
                                              #这时取值和外面一层保持一致
            print(tf.get_variable_scope().reuse)   #输出为True
    print(tf.get_variable_scope().reuse)   #输出为False,


tf.variable_scope函数生成的上下文管理器也会创建一个Tensorflow中的命名空间,在命名空间内创建的变量名称都会带上这个命名空间作为前缀。所以 tf.variable_scope函数除了控制tf.get_variable执行功能,这个函数也提供了一个管理变量命名空间的方式。

#通过variable_scope来管理变量
v1 = tf.get_variable("v", [1])   #输出v:0,'v'是变量的名称,'0'表示这个变量是生成变量这个运算的第一个结果
print(v1.name)    

with tf.variable_scope("foo",reuse=True):
    v2 = tf.get_variable("v", [1])
print(v2.name)        #输出foo/v:0

with tf.variable_scope("foo"):
    with tf.variable_scope("bar"):
        v3 = tf.get_variable("v", [1])
        print(v3.name)           #输出:foo/bar/v:0

v4 = tf.get_variable("v1", [1])
print(v4.name)       #输出:v1:0

#创建一个空命名空间,并设置reuse=True
with tf.variable_scope("",reuse=True):
    v5 = tf.get_variable("foo/bar/v", [1])   #可以通过变量的名称来获取变量
    print(v5 == v3)         #输出为:True
    v6 = tf.get_variable("v1", [1])     
    print(v6 == v4)         #输出为:True


下面对第二节的inference(input_tensor, avg_class, weights1, biases1, weights2, biases2)做些改进

def inference(input_tensor, reuse=False):
    #定义第一层神经网络的变量和前向传播过程
    with tf.variable_scope('layer1', reuse=reuse):
        #根据传进来的reuse来判断是创建变量还是使用自己的变量。
        #在第一个构造网络时需要创建变量,以后每次调用这个函数都是直接使用reuse=True
        #就不要每次变量传进来了
        weights = tf.get_variable('weights', [INPUT_NODE, LAYER1_NODE],
                                 initializer=tf.truncated_normal_initializer(stddev=0.1))
        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', reuse=reuse):
        weights = tf.get_variable('weights', [LAYER1_NODE, OUTPUT_NODE],
                                 initializer=tf.truncated_normal_initializer(stddev=0.1))
        biases = tf.get_variable('biases', [OUTPUT_NODE], 
                                initializer=tf.constant_initializer(0.0))
        layer2 = tf.matmul(layer1, weights) + biases
    return layer2

x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y = inference(x)

#在程序中需要使用神经网络进行推导时,可以直接使用inference(new_x, True)
new_x = ...
new_y = inference(new_x, True)

4: 模型持久化

import tensorflow as tf

#声明两个变量并计算它们的和
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name='v1')
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name='v2')
result = v1 + v2

init_op = tf.global_variables_initializer()
#声明tf.train.Saver类用于保存模型
saver = tf.train.Saver()

with tf.Session() as sess:
    sess.run(init_op)
    #将模型保存到/model/model.ckpt文件
    saver.save(sess, 'model/model.ckpt')

会生成下列文件:

.ckpt.meta文件,保存tensorflow模型的计算图结构。

.ckpt.data文件,保存计算图下所有变量的取值。

.checkpoint文件,保存目录下所有模型文件列表。

.ckpt.index文件保存了当前变量名

#通过下列代码加载这个保存好的模型
import tensorflow as tf
tf.reset_default_graph()

#使用和保存模型代码中一样的方法来声明变量
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name='v1')
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name='v2')
result = v1 + v2

saver = tf.train.Saver()

with tf.Session() as sess:
    #加载已经保存的模型,并通过已经保存的模型中的变量的值来计算加法
    saver.restore(sess, 'model/model.ckpt')
    print(sess.run(result))


这段代码基本上和保存模型的代码相似。在加载时也是先定义了计算图上的所有运算,并声明一个tf.train.Saver()类,不同的是,在加载模型时是没有运行变量的初始化,而是将变量的值通过已经保存的模型加载进来。如果不希望重复定义图上的运算,也可以直接加载已经持久化的图。

import tensorflow as tf
#直接加载持久化的图
saver = tf.train.import_meta_graph('model/model.ckpt.meta')

with tf.Session() as sess:
    saver.restore(sess, 'model/model.ckpt')
    #通过张量的名称来获取张量
    print(sess.run(tf.get_default_graph().get_tensor_by_name('add:0')))


上面的程序默认保存和加载了TensorFlow计算图上定义的全部变量。但有时只需要保存和加载部分变量。tf.train.Saver类可以提供一个列表来指定需要保存的或者加载的变量。如saver = tf.train.Saver([v1])命令来构建tf.train.Saver类,那么只有变量v1会被加载进来,

除了可以寻去需要被加载的变量,tf.train.Saver类也支持在保存或加载时给变量名重命名。

v1 = tf.Variable(tf.constant(1.0, shape=[1]), name='other-v1')
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name='other-v2')

#如果直接使用tf.train.Saver()来加载模型会报变量找不到的错误
#使用一个字典来重命名变量就可以加载原来的模型了
#字典指定了原来名称为v1的变量现在加载到变量v1中(名称为other-v1),名称为v2的变量加载到变量v2中,名称为(other-v2)
saver = tf.train.Saver({'v1': v1, 'v2': v2})


因为每一个变量的滑动平均值是通过影子变量维护的,所以要获取变量的滑动平均值实际上就是获取这个影子变量的取值。如果在加载模型时直接将影子变量映射到变量的自身,那么在使用训练好的模型时就不需要调用函数来获取变量的滑动平均值了。

import tensorflow as tf

v = tf.Variable(0, dtype=tf.float32, name='v')
#在没有声明滑动平均模型时只有一个变量v,所以下语句只会输出'v:0'
for variables in tf.global_variables():
    print(variables.name)   

ema = tf.train.ExponentialMovingAverage(0.99)
maintain_averages_op = ema.apply(tf.global_variables())
#在声明滑动平均模型后,Tensorflow会自动生成一个影子变量 v/ExponentialMovingAverages
#输出:v:0,v/ExponentialMovingAverage:0
for variables in tf.global_variables():
    print(variables.name)

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

    sess.run(tf.assign(v, 10))
    sess.run(maintain_averages_op)
    #保存时,v:0和v/ExponentialMovingAverages:0两个变量都会存下来,
    saver.save(sess, 'model/model.ckpt')
    print(sess.run([v, ema.average(v)]))   #输出:[10.0, 0.099999905]


v = tf.Variable(0, dtype=tf.float32, name='v')
#通过变量重命名将原来变量v的滑动平均值直接赋值给v
saver = tf.train.Saver({'v/ExponentialMovingAverage': v})
with tf.Session() as sess:
    saver.restore(sess, 'model/model.ckpt')
    print(sess.run(v))    #输出:0.099999905


为了方便加载时重命名滑动平均变量,tf.train.ExponentialMovingAverage类提供了variables_to_restore函数来生成tf.train.Saver类所需的变量重命名字典。

import tensorflow as tf

v = tf.Variable(0, dtype=tf.float32, name='v')
ema = tf.train.ExponentialMovingAverage(0.99)

#通过使用variables_to_restore函数可以直接生成上面代码提供的字典
#{'v/ExponentialMovingAverage': v}
#输出:{'v/ExponentialMovingAverage': <tf.Variable 'v:0' shape=() dtype=float32_ref>}
print(ema.variables_to_restore())  

saver = tf.train.Saver(ema.variables_to_restore())

with tf.Session() as sess:
    saver.restore(sess, 'model/model.ckpt')
    print(sess.run(v))   #输出:0.099999905


#pb文件的保存方法
import tensorflow as tf
from tensorflow.python.framework import graph_util

v1 = tf.Variable(tf.constant(1.0, shape=[1]), name = "v1")
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name = "v2")
result = v1 + v2

init_op = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init_op)
    #导出计算图的GraphDef部分,只需要这一部分就可以完成从输入层到输出层的计算过程
    graph_def = tf.get_default_graph().as_graph_def()

    #将图中的变量及其取值化为常量,同时将图中不必要的节点去掉,
    #下面一行代码,最后一个参数['add']给出了需要保存的节点名称
    output_graph_def = graph_util.convert_variables_to_constants(sess, graph_def, ['add'])
    with tf.gfile.GFile("model/combined_model.pb", "wb") as f:
           f.write(output_graph_def.SerializeToString())


#加载pb文件
from tensorflow.python.platform import gfile
with tf.Session() as sess:
    model_filename = "model/combined_model.pb"

    with gfile.FastGFile(model_filename, 'rb') as f:
        graph_def = tf.GraphDef()
        graph_def.ParseFromString(f.read())

    result = tf.import_graph_def(graph_def, return_elements=["add:0"])
    print(sess.run(result)) #输出:[array([ 3.], dtype=float32)]

5: TensorFlow 最佳实践样例程序

将程序拆分为三个程序:mnist_inference.py,它定义了前向传播的过程以及神经网络中的参数;第二个是mnist_train.py,它定义了神经网络的训练过程;第三个是mnist_eval.py,它定义了测试过程。

#mnist_inference.py

import tensorflow as tf

#1. 定义神经网络结构相关的参数
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的集合
    if regularizer != None:
        tf.add_to_collection('losses', regularizer(weights))
    return weights

def inference(input_tensor, regularizer):
    #声明第一层神经网络的变量并完成前向传播过程
    with tf.variable_scope('layer1'):
        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.matmul(layer1, weights) + biases

    return layer2


##mnist_train.py

import os
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
#import mnist_inference

#定义神经网络结构相关的参数
BATCH_SIZE = 100 
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 6000
MOVING_AVERAGE_DECAY = 0.99 
MODEL_SAVE_PATH = "MNIST_model/"
MODEL_NAME = "mnist_model"

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

    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    #y = mnist_inference.inference(x, regularizer)
    y = inference(x, regularizer)
    global_step = tf.Variable(0, trainable=False)

    # 定义损失函数、学习率、滑动平均操作以及训练过程。
    variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    variable_averages_op = variable_averages.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, variable_averages_op]):
        train_op = tf.no_op(name='train')

    # 初始化TensorFlow持久化类。
    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:
                print("After %d training step(s), loss on training batch is %g." % (step, loss_value))
                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('MNIST_data/', one_hot=True)
    train(mnist)

if __name__ == '__main__':
    main()


##mnist_eval.py

import time
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
#import mnist_inference
#import mnist_train

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

def evaluate(mnist):
    with tf.Graph().as_default() as g:   #新生成的图作为整个 tensorflow 运行环境的默认图
        #定义输入格式
        x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
        y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-input')
        validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels}

        #计算前向传播结果,因为测试时关心正则化损失,
        y = inference(x, None)

        correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

        variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY)
        variable_to_restore = variable_averages.variables_to_restore()
        saver = tf.train.Saver(variable_to_restore)

        #每隔EVAL_INTERVAL_SECS秒调用一次计算正确率的过程以检测训练过程中正确率的变换
        while True:
            with tf.Session() as sess:
                #tf.train.get_checkpoint_state函数会通过checkpoint文件自动找到目录中最新的模型的文件名
                ckpt = tf.train.get_checkpoint_state(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("MNIST_data", one_hot=True)
    evaluate(mnist)

if __name__ == '__main__':
    main()

编辑于 2018-06-23 22:06