摘自《Tensorflow:實戰Google深度學習框架》
1 import tensorflow as tf 2 from tensorflow.examples.tutorials.mnist import input_data 3 4 # MNIST 數據集相關的常數 5 INPUT_NODE = 784 # 輸入層的節點數。對於 MNIST 數據集,就等於圖片的像素 6 OUTPUT_NODE = 10 # 輸出層的節點數。這個等於類別的數目。因為在 MNIST 的數據集中需要區分的是 0~9 這 10 個數字,所以這里輸出層的節點數為 10 7 8 # 配置神經網絡的參數 9 LAYER1_NODE = 500 # 隱藏層節點數。這里使用只有一個隱藏層的網絡結構作為樣例。這個隱藏層有500個節點 10 BATCH_SIZE = 100 # 一個訓練 batch 中的訓練數據個數。數字越小時,訓練過程越接近隨機梯度下降;數字越大時,訓練越接近梯度下降 11 LEARNING_RATE_BASE = 0.8 # 基礎的學習率 12 LEARNING_RATE_DECAY = 0.99 # 學習率的衰減率 13 REGULARIZATION_RATE = 0.0001 # 描述模型復雜度的正則化項在損失函數中的系數 14 TRAINING_STEPS = 30000 # 訓練輪數 15 MOVING_AVERAGE_DECAY = 0.99 # 滑動平均衰減率 16 17 # 一個輔助函數,給定神經網絡的輸入和所有參數,計算神經網絡的前向傳播結果。在這里定義了一個使用 ReLU 激活函數的三層全連接神經網絡。 18 # 通過加入隱藏層實現了多層網絡結構, 通過 ReLU 激活函數實現了去線性化。在這個函數中也支持傳入用於計算參數平均值得類,這樣方便在 19 # 測試時使用滑動平均模型。 20 def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2): 21 # 當沒有提供滑動平均類時,直接使用參數當前的取值 22 if avg_class == None: 23 # 計算隱藏層的前向傳播結果,這里使用了 ReLU 激活函數 24 layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1) 25 26 # 計算輸出層的前向傳播結果。因為在計算損失函數時會一並計算 softmax 函數,所以這里不需要加入激活函數。 27 # 而且不加入 softmax 不會影響預測結果。因為預測時使用的是不同類別對應節點輸出值的相對大小,有沒有 28 # softmax 層對最后分類結果的計算沒有影響。於是在計算整個神經網絡的前向傳播時可以不加入最后的 softmax 層。 29 return tf.matmul(layer1, weights2) + biases2 30 else: 31 # 首先使用 avg_class.average 函數來計算得出變量的滑動平均值,然后再計算相應的神經網絡前向傳播結果。 32 layer1 = tf.nn.relu(tf.matmul(input_tensor, avg_class.average(weights1)) + avg_class.average(biases1)) 33 return tf.matmul(layer1, avg_class.average(weights2)) + avg_class.average(biases2) 34 35 # 訓練模型的過程 36 def train(mnist): 37 x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input') 38 y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-input') 39 40 # 生成隱藏層的參數 41 weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1)) 42 biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE])) 43 44 # 生成輸出層的參數 45 weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1)) 46 biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE])) 47 48 # 計算在當前參數下神經網絡前向傳播的結果。這里給出的用於計算滑動平均的類為 None,所以函數不會使用參數的滑動平均值 49 y = inference(x, None, weights1, biases1, weights2, biases2) 50 51 # 定義存儲訓練輪數的變量。這個變量不需要計算滑動平均值,所以這里指定這個變量為不可訓練的變量(trainable=False)。 52 # 在使用 Tensorflow 訓練神經網絡時,一般會將代表訓練輪數的變量指定為不可訓練的參數 53 global_step = tf.Variable(0, trainable=False) 54 55 # 給定滑動平均衰減率和訓練輪數的變量,初始化滑動平均類。在第4章中介紹過給定訓練輪數的變量可以加快訓練早期變量的更新速度 56 variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step) 57 58 # 在所有代表神經網絡參數的變量上使用滑動平均。其他輔助變量(比如 global_step )就不需要了。 59 # tf.trainable_variables 返回的就是圖上集合 GraphKeys.TRAINABLE_VARIABLES 中的元素。 60 # 這個集合的元素就是所有沒有指定 trainable=False 的參數 61 variables_averages_op = variable_averages.apply(tf.trainable_variables()) 62 63 # 計算使用了滑動平均之后的前向傳播結果。第4章中介紹過滑動品均不會改變變量本身的取值,而是會維護一個影子變量來記錄其滑動平均值。 64 # 所以當需要使用這個滑動平均值時,需要明確調用 average 函數 65 average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2) 66 67 # 計算交叉熵作為刻畫預測值和真實值之間差距的損失函數。這里使用了 Tensorflow 中提供的 sparse_softmax_cross_entropy_with_logits 68 # 函數來計算交叉熵。當分類問題只用一個正確答案時,可以使用這個函數來加速交叉熵的計算。 MNIST 問題的圖片中只包含 0~9 中的一個數字, 69 # 所以可以使用這個函數來計算交叉熵損失。這個函數的第一個參數是神經網絡不包含 softmax 層的前向傳播結果,第二個是訓練數據的正確答案。 70 # 因為標准答案是一個長度為 10 的一個數組,而該函數需要提供的是一個正確答案的數字,所以需要使用 tf.argmax 函數來得到正確答案對應的類別編號。 71 cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1)) 72 # 計算在當前 batch 中所有樣例的交叉熵品均值 73 cross_entropy_mean = tf.reduce_mean(cross_entropy) 74 75 # 計算L2正則化損失函數 76 regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE) 77 # 計算模型的正則化損失。一般只計算神經網絡邊上權重的正則化損失,而不使用偏置項。 78 regularization = regularizer(weights1) + regularizer(weights2) 79 # 總損失等於交叉熵損失和正則化損失的和 80 loss = cross_entropy_mean + regularization 81 # 設置指數衰減的學習率 82 learning_rate = tf.train.exponential_decay( 83 LEARNING_RATE_BASE, # 基礎學習率,隨着迭代的進行,更新變量時使用的學習率在這個基礎上遞減 84 global_step, # 當前迭代的輪數 85 mnist.train.num_examples / BATCH_SIZE, # 過完所有的訓練數據需要的迭代次數 86 LEARNING_RATE_DECAY) # 學習率衰減速度 87 88 # 使用 tf.train.GradientDescentOptimizer 優化算法來優化損失函數。注意這里損失函數包含了交叉熵損失和L2正則化損失 89 train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step) 90 91 # 在訓練神經網絡模型時,每過一遍數據既需要通過反向傳播來更新神經網絡中的參數,又要更新每一個參數的輪滑平均值。 92 # 為了一次完成多個操作,Tensorflow 提供了 tf.control_dependencies 和 tf.group 兩種機制。下面兩行程序和 93 # tf.group(train_step, variables_averages_op) 是等價的 94 with tf.control_dependencies([train_step, variables_averages_op]): 95 train_op = tf.no_op(name='train') 96 97 # 檢驗使用了滑動平均模型的神經網絡前向傳播結果是否正確。tf.argmax(y_, 1) 計算每一個樣例的預測答案。 98 # 其中 average_y 是一個 batch_size * 10 的二維數組,每一行表示一個樣例的前向傳播結果。tf.argmax 的第二個參數“1” 99 # 表示選取最大值的操作僅在第一個維度中進行,也就是說,只在每一行選取最大值對應的下標。於是得到的結果是一個長度 100 # 為 batch 的一維數組,這個一維數組中的值就表示了每一個樣例對應的數字識別結果。tf.equal 判斷兩個張量的每一維是否 101 # 相等,如果相等返回 True,否則返回 False。 102 correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1)) 103 # 這個運算首先將一個布爾型的數值轉換為實數型,然后計算平均值。這個平均值就是模型在這一組數據上的正確率 104 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) 105 106 # 初始化會話並開始訓練過程 107 with tf.Session() as sess: 108 tf.initialize_all_variables().run() 109 # 准備驗證數據。一般在神經網絡的訓練過程中會通過驗證數據來大致判斷停止的條件和評判訓練的效果 110 validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels} 111 # 准備測試數據。在真實的應用中,這部分數據在訓練時是不可見的,這個數據只是作為模型優劣的最后評價標准 112 test_feed = {x: mnist.test.images, y_: mnist.test.labels} 113 114 # 迭代地訓練神經網絡 115 for i in range(TRAINING_STEPS): 116 # 每1000輪輸出一次在驗證數據集上的測試結果 117 if i % 1000 == 0: 118 # 計算滑動平均模型在驗證數據上的結果。因為 MNIST 數據集比較小,所以一次可以處理所有的驗證數據。 119 # 為了計算方便,本樣例程序沒有將驗證數據划分為更小的 batch。當神經網絡模型比較復雜或驗證數據比較大時, 120 # 太大的 batch 會導致計算時間過長甚至發生內存溢出的錯誤。 121 validate_acc = sess.run(accuracy, feed_dict=validate_feed) 122 print("After %d training step(s), validation accuracy using average model is %g" % (i, validate_acc)) 123 124 # 產生這一輪使用的一個 batch 的訓練數據,並運行訓練過程。 125 xs, ys = mnist.train.next_batch(BATCH_SIZE) 126 sess.run(train_op, feed_dict={x: xs, y_: ys}) 127 128 # 在訓練結束后,在測試數據集行檢測神經網絡模型的最終正確率 129 test_acc = sess.run(accuracy, feed_dict=test_feed) 130 print("After %d training step(s), test accuracy using average model is %g" % (TRAINING_STEPS, test_acc)) 131 132 # 主程序入口 133 def main(arvg=None): 134 # 聲明處理 MNIST 數據集的類,這個類在初始化時會自動下載數據。(http://yann.lecun.com/exdb/mnist) 135 mnist = input_data.read_data_sets("/tmp/data", one_hot=True) 136 train(mnist) 137 138 # Tensorflow 提供的一個主程序入口,tf.app.run 會調用上面定義的 main 函數 139 if __name__ == '__main__': 140 tf.app.run()
運行后,得到結果:
After 0 training step(s), validation accuracy using average model is 0.1472 After 1000 training step(s), validation accuracy using average model is 0.9764 After 2000 training step(s), validation accuracy using average model is 0.9828 After 3000 training step(s), validation accuracy using average model is 0.984 After 4000 training step(s), validation accuracy using average model is 0.9846 After 5000 training step(s), validation accuracy using average model is 0.9852 After 6000 training step(s), validation accuracy using average model is 0.9858 After 7000 training step(s), validation accuracy using average model is 0.9872 After 8000 training step(s), validation accuracy using average model is 0.9866 After 9000 training step(s), validation accuracy using average model is 0.9864 After 10000 training step(s), validation accuracy using average model is 0.9858 After 11000 training step(s), validation accuracy using average model is 0.9862 After 12000 training step(s), validation accuracy using average model is 0.986 After 13000 training step(s), validation accuracy using average model is 0.9856 After 14000 training step(s), validation accuracy using average model is 0.986 After 15000 training step(s), validation accuracy using average model is 0.9858 After 16000 training step(s), validation accuracy using average model is 0.9852 After 17000 training step(s), validation accuracy using average model is 0.9854 After 18000 training step(s), validation accuracy using average model is 0.9856 After 19000 training step(s), validation accuracy using average model is 0.9854 After 20000 training step(s), validation accuracy using average model is 0.9856 After 21000 training step(s), validation accuracy using average model is 0.9856 After 22000 training step(s), validation accuracy using average model is 0.9852 After 23000 training step(s), validation accuracy using average model is 0.9856 After 24000 training step(s), validation accuracy using average model is 0.9852 After 25000 training step(s), validation accuracy using average model is 0.9858 After 26000 training step(s), validation accuracy using average model is 0.9858 After 27000 training step(s), validation accuracy using average model is 0.9856 After 28000 training step(s), validation accuracy using average model is 0.9852 After 29000 training step(s), validation accuracy using average model is 0.9854 After 30000 training step(s), test accuracy using average model is 0.9843
從第7000輪開始,模型在驗證數據集上的表現開始波動,這說明模型已經接近極小值了,所以迭代也就可以結束了。