在上一篇《TensorFlow入門之MNIST樣例代碼分析》中,我們講解了如果來用一個三層全連接網絡實現手寫數字識別。但是在實際運用中我們需要更有效率,更加靈活的代碼。在TensorFlow實戰這本書中給出了更好的實現,他將程序分為三個模塊,分別是前向傳播過程模塊,訓練模塊和驗證檢測模塊。並且在這個版本中添加了模型持久化功能,我們可以將模型保存下來,方便之后的模型檢驗,並且我們可以一邊訓練新的模型,一邊來檢驗模型,代碼更加的靈活高效。
前向傳播模塊
首先將前向傳播過程抽象出來,作為一個可以作為訓練測試共享的模塊,取名為mnist_inference.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.matmul(layer1, weights) + biases # 返回最后前向傳播的結果 return layer2
訓練模塊
將訓練模型的模塊提取出來,訓練模塊命名為mnist_train.py
,在下面的代碼中每過1000個step我們就保存一次模型。代碼如下:
# -*- 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 REGULARIZATION_RATE = 0.0001 TRAINING_STEPS = 30000 MOVING_AVERAGE_DECAY = 0.99 # 模型保存的路徑和文件名 MODEL_SAVE_PATH = "./model/" 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(REGULARIZATION_RATE) # 直接使用mnist_inference.py中定義的前向傳播過程 y = mnist_inference.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: # 輸出當前的訓練情況。這里只輸出了模型在當前訓練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("./data", one_hot=True) train(mnist) if __name__ == "__main__": tf.app.run()
驗證與測試模塊
驗證模塊與測試模塊可以對保存好的訓練模型進行驗證與測試,在下面的代碼中我們選擇每過10秒鍾驗證一個最新的模型。這樣做的好處是可以將訓練與驗證或者測試分割開來,同時進行。該模塊命名為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} # 直接通過調用封裝好的函數來計算前向傳播的結果。因為測試時不關注ze正則化損失的值 # 所以這里用於計算正則化損失的函數被設置為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: # tf.train.get_checkpoint_state函數會通過checkpoint文件自動 # 找到目錄中最新模型的文件名。 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("./data", one_hot=True) evaluate(mnist) if __name__ == "__main__": tf.app.run()
總結
這個樣例是一個非常好的可以用來理解TensorFlow的程序,特別是TensorFlow的計算圖的理解,還有模型持久化與恢復,變量的管理,滑動平均模型的實現等等。還有這種靈活的模塊分塊的思想也值得學習。