TensorFlow:实战Google深度学习框架(四)MNIST数据集识别问题
- 第5章 MNIST數字識別問題
- 5.1 MNIST數據處理
- 5.2 神經網絡的訓練以及不同模型結果的對比
- 5.2.1 TensorFlow訓練神經網絡
- 5.2.2 使用驗證數據集判斷模型的效果
- 5.2.3 不同模型效果比較
- 5.3 變量管理
- 5.4 TensorFlow模型持久化
- 5.4.1 持久化代碼實現
- 1. ckpt文件的保存
- 2. 加載已經保存的TensorFlow模型
- 5.4.2 持久化原理及數據格式
- 5.4.1 持久化代碼實現
- 5.5 最佳實踐樣例程序
- 1. 定義神經網絡的前向傳播過程
- 2. 訓練程序
- 3. 代碼
- 5.6 補充
- 5.6.1 單層神經網絡
- 5.6.2 梯度下降
- 5.6.3 學習速率衰減
- 5.6.4 過擬合和dropout
第5章 MNIST數字識別問題
5.1 MNIST數據處理
MNIST數據集是NIST數據集的一個子集,包含60000張作為訓練數據,10000張作為測試數據,圖片大小為28*28。
- TensorFlow提供了一個類來處理MINIST數據,會自動下載并轉化MINIST數據的格式,解析成訓練和測試神經網絡時的使用格式
讀取mnist數據集的程序如下
# 在Yann LeCun教授的網站中(http://yann.lecun.com/exdb/mnist ) 對MNIST數據集做出了詳細的介紹。 # 1. 讀取數據集,第一次TensorFlow會自動下載數據集到下面的路徑中 from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)# 2. 數據集會自動被分成3個子集,train、validation和test。以下代碼會顯示數據集的大小。 print("Training data size: ", mnist.train.num_examples) print("Validating data size: ", mnist.validation.num_examples) print("Testing data size: ", mnist.test.num_examples)# 3. 查看training數據集中某個成員的像素矩陣生成的一維數組和其屬于的數字標簽。 print("Example training data: ", mnist.train.images[0]) print("Example training data label: ", mnist.train.labels[0])# 4. 使用mnist.train.next_batch來實現隨機梯度下降。 batch_size = 100 xs, ys = mnist.train.next_batch(batch_size) # 從train的集合中選取batch_size個訓練數據。 print("X shape:", xs.shape) print("Y shape:", ys.shape)輸出結果
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.gzTraining data size: 55000 Validating data size: 5000 Testing data size: 10000 Example training data:[太多了,不列舉了] Example training data label: [ 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.] X shape: (100, 784) Y shape: (100, 10)5.2 神經網絡的訓練以及不同模型結果的對比
神經網絡在驗證數據集上的表現可以近似的作為評價不同神經網絡模型的標準或決定迭代次數的依據。
為了評測神經網絡模型在不同參數下的效果,一般會從訓練數據中抽取一部分作為驗證數據。使用驗證數據就可以評判不同參數取值下模型的表現。除了使用驗證數據集,還可以采用交叉驗證(cross validation)的方式來驗證模型效果,但因為神經網絡訓練實踐本身就比較長,采用cross validation會花費大量時間。所以在海量數據的情況下,一般會更多地采用驗證數據集的形式來評測模型的效果。
為了說明驗證數據在一定程度上可以作為模型效果的評判標準,我們將對比在不同迭代輪數的情況下,模型在驗證數據和測試數據上的正確率。為了同時得到同一個模型在驗證數據和測試數據上的正確率,可以在每1000輪的輸出中加入在測試數據集上的正確率。
在神經網絡結構的設計上,需要使用激活函數和多層隱藏層。在神經網絡優化時,可以使用指數衰減的學習率,加入正則化的損失函數以及滑動平均模型。
5.2.1 TensorFlow訓練神經網絡
# 《TensorFlow實戰Google深度學習框架》05 minist數字識別問題 # filename:ts05.02.py # TensorFlow訓練神經網絡--全模型import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data# 1.設置輸入和輸出節點的個數,配置神經網絡的參數 INPUT_NODE = 784 # 輸入節點個數,對于該數據集就是圖片像素總數 OUTPUT_NODE = 10 # 輸出節點,等同于類別個數 LAYER1_NODE = 500 # 隱藏層數,此處使用僅有一個隱藏層,500個節點BATCH_SIZE = 100 # 一個batch中樣本個數,數字越小,越接近隨機梯度下降,數字越大,越接近梯度下降# 模型相關的參數 LEARNING_RATE_BASE = 0.8 # 基礎學習率 LEARNING_RATE_DECAY = 0.99 # 學習率的衰減率 REGULARAZTION_RATE = 0.0001 # 描述模型復雜度的正則化項在損失函數中的系數 TRAINING_STEPS = 30000 # 訓練次數 MOVING_AVERAGE_DECAY = 0.99 # 滑動平均衰減率# 2. 定義輔助函數來計算前向傳播結果,使用ReLU做為激活函數。 def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):# 不使用滑動平均類if avg_class == None:layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)return tf.matmul(layer1, weights2) + biases2else:# 使用滑動平均類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)# 3. 定義訓練過程 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]))# 計算不含滑動平均類的前向傳播結果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_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)# 計算交叉熵及其平均值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)# 損失函數的計算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)# 反向傳播更新參數和更新每一個參數的滑動平均值with tf.control_dependencies([train_step, variables_averages_op]):train_op = tf.no_op(name='train')# 計算正確率correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))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):if i % 1000 == 0: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))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)))# 4. 主程序入口,這里設定模型訓練次數為5000次。 def main(argv=None):mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)train(mnist)if __name__=='__main__':tf.app.run()結果:
After 0 training step(s), validation accuracy using average model is 0.111 After 1000 training step(s), validation accuracy using average model is 0.977 After 2000 training step(s), validation accuracy using average model is 0.9824 After 3000 training step(s), validation accuracy using average model is 0.9832 After 4000 training step(s), validation accuracy using average model is 0.9848 After 5000 training step(s), test accuracy using average model is 0.9822 ...由以上結果可以看出,在訓練初期,隨著訓練的進行,模型在驗證數據集上的表現越來越好,但從4000輪開始,模型在驗證數據集上的表現開始波動,這說明模型已經接近極小值了,所以迭代已經可以結束了。
5.2.2 使用驗證數據集判斷模型的效果
所需初始參數:上述程序的開始,設置了初始學習率、學習率衰減率、隱藏層節點數、迭代次數、batch_size、正則項系數、滑動平均衰減數等7個不同的參數。
如何設置初始參數:一般情況需要實驗來調整
設置初始參數難點:雖然模型的最終效果是在測試數據上進行判定的,但不能直接使用測試數據,否則會過擬合,從而丟失對未知數據的判斷能力,所以要保證測試數據在訓練過程中是不可見的。
解決方法:
- 從訓練數據中抽取一部分作為驗證數據,來評判不同參數取值下模型的表現。
- 使用“交叉驗證(cross validation)”,但是神經網絡的訓練時間本身就比較長,所以采用該方法會花費大量的時間,故一般不會選用。
下面使用代碼來對比模型在不同迭代次數情況下,在驗證數據和測試數據上的正確率。
# 為了同時得到模型在測試數據和驗證數據上的正確率,可以在每1000輪的輸出中加入在測試數據集上的正確率for i in range(TRAINING_STEPS):if i % 1000 == 0:# 計算滑動平均模型在測試數據集和驗證數據集上的正確率validate_acc = sess.run(accuracy, feed_dict=validate_feed)test_acc = sess.run(accuracy, feed_dict=test_feed)print("After %d training step(s), validation accuracy using average model is %g ,test accuracy using average model is %g" % (i, validate_acc,test_acc))xs, ys = mnist.train.next_batch(BATCH_SIZE)sess.run(train_op, feed_dict={x: xs, y_: ys})結果:
After 0 training step(s), validation accuracy using average model is 0.1114 , test accuracy using average model is 0.109 After 1000 training step(s), validation accuracy using average model is 0.9778 , test accuracy using average model is 0.9741 After 2000 training step(s), validation accuracy using average model is 0.9822 , test accuracy using average model is 0.9798 After 3000 training step(s), validation accuracy using average model is 0.9838 , test accuracy using average model is 0.9816 After 4000 training step(s), validation accuracy using average model is 0.9846 , test accuracy using average model is 0.9824 After 5000 training step(s), validation accuracy using average model is 0.9852 , test accuracy using average model is 0.9823 After 6000 training step(s), validation accuracy using average model is 0.9858 , test accuracy using average model is 0.9829 After 7000 training step(s), validation accuracy using average model is 0.985 , test accuracy using average model is 0.983 After 8000 training step(s), validation accuracy using average model is 0.9854 , test accuracy using average model is 0.9831可以看出,驗證集和測試集雖然不完全一樣,但是其趨勢相同,且它們的相關系數為0.9999,意味著在MNIST數據集上完全可以通過模型在驗證數據集上的表現來判斷一個模型的優劣。
5.2.3 不同模型效果比較
在神經網絡結構的設計上,需要使用激活函數和多層隱藏層。在神經網絡優化時,可以使用指數衰減的學習率,加入正則化的損失函數以及滑動平均模型。
1. 本質影響:神經網絡的結構
2. 滑動平均模型、指數衰減的學習率、正則化對MNIST數據集影響看起來不是很大的原因:
- 因為滑動平均模型、指數衰減的學習率都在限制神經網絡的參數的更新速度,而該數據庫模型收斂的速度很快,所以影響不大。
- 但是當問題更復雜時,迭代不會很快收斂,所以滑動平均模型、指數衰減的學習率可以發揮更大的作用。
- 正則化帶來的效果更為顯著
總結:優化方法可以對模型帶來更好的效果(模型越復雜,效果越明顯)
5.3 變量管理
當神經網絡的結構復雜、參數很多時,需要更好的方式來傳遞和管理神經網絡的參數。
TensorFlow提供了通過變量名稱來創建或獲取一個變量的機制,不同函數可以直接通過變量的名字來使用變量,不需要通過參數的形式到處傳遞。
- 通過變量名稱來獲取變量:
- TensorFlow提供了7種不同的初始化函數
| tf.constant_initializer | 將變量初始化為給定常量 | 常量的取值 |
| tf.random_normal_initializer | 將變量初始化為滿足正太分布的隨機值 | 正太分布的均值和標準差 |
| tf.truncated_normal_initializer | 將變量初始化為滿足正太分布的隨機值,但若隨機出來的值偏離平均值超過兩個標準差,那么這個數將會被重新隨機 | 正太分布的均值和標準差 |
| tf.random_uniform_initializer | 將變量初始化為滿足平均分布的隨機值 | 最大,最小值 |
| tf.uniform_unit_scaling_initializer | 將變量初始化為滿足平均分布但不影響輸出數量級的隨機值 | factor(產生隨機值時乘以的系數) |
| tf.zeros_initializer | 將變量設置為全為0 | 變量維度 |
| tf.ones_initializer | 將變量設置為全為1 | 變量維度 |
- tf.get_variable:
- tf.get_variable變量名是一個必填參數,其首先會試圖去創建一個名字為v的參數,如果創建失敗(比如已經有同名的參數),那么這個程序就會報錯。這是為了避免無意識的變量復用造成的錯誤。比如在定義神經網絡參數時,第一層網絡的權重已經叫weights了,那么在創建第二層神經網絡時,如果參數名仍然叫weights,就會觸發變量重用的錯誤。否則兩層神經網絡共用一個權重會出現一些比較難以發現的錯誤。
- 如果需要通過tf.get_variable獲取一個已經創建的變量,需要通過tf.variable_scope函數來生成一個上下文管理器,并明確指定在這個上下文管理器中,tf.get_variable將直接獲取已經生成的變量。
- 下面給出一段代碼說明如何通過tf.variable_scope函數來控制tf.get_variable函數獲取已經創建過的變量。
通過 tf.variable_scope 控制 tf.get_variable 的語義:
- 如果tf.variable_scope函數使用參數 reuse=None 或者reuse=False創建上下文管理器,tf.get_variable操作將創建新的變量,如果同名的變量已經存在,則tf.get_variable函數將報錯。另外,Tensorflow中tf.variable_scope函數是可以嵌套的。
如果tf.variable_scope函數使用參數 reuse=True 生成上下文管理器時,該上下文管理器中的所有 tf.get_variable 函數會直接獲取已經創建的變量,如果變量不存在,將會報錯。
使用變量管理后,就不再需要將所有變量都作為參數傳遞到不同的函數中了,當神經網絡結構更加復雜,參數更多時,使用這種變量管理的方式將大大提高程序的可讀性。
示例代碼:
- 通過 tf.variable_scope 來管理變量命名空間:
利用上述兩個函數對5.2.1小節定義的計算前向傳播結果做了改進,就不需要將所有變量都作為參數傳遞到不同函數中了。
import tensorflow as tf def inference(input_tensor, reuse=False):# 定義第一層神經網絡的變量和前向傳播的過程with tf.variable_scope('layer', 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 layer2x = 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)5.4 TensorFlow模型持久化
為了將訓練得到的模型保存下來方便下次使用,即結果可以復用,需要將神經網絡模型持久化。
5.4.1 持久化代碼實現
TensorFlow提供了一個非常簡單的API來保存和還原神經網絡模型,該API就是tf.train.Saver類。
1. ckpt文件的保存
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")init_op=tf.global_variables_initializer() #聲明tf.train.Saver類用于保存模型 saver=tf.train.Saver()with tf.Session() as sess:sess.run(init_op)#將模型保存到文件saver.save(sess,"E:\pycharm\TensorFlow chap5\model.ckpt")Tensorflow模型一般會存在后綴為.ckpt文件中,雖然上面的程序只指定了一個文件路徑,但是在這個文件目錄下會出現三個文件,這是因為Tensorflow會將計算圖的結構和涂上的參數取值分來保存。
2. 加載已經保存的TensorFlow模型
# 2. 加載已經創建的模型 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 + v2saver = tf.train.Saver()with tf.Session() as sess:# 加載已經保存的模型,并通過已經保存的模型中的變量的值來計算加法saver.restore(sess, "E:\pycharm\TensorFlow chap5\model\model.ckpt")輸出:[ 3.]加載模型的代碼中,沒有運行變量的初始化過程,而是將變量的值通過已經保存的模型加載進來
- 如果不希望重復定義圖上的運算,也可以直接加載已經持久化的圖
如何加載部分變量
上面給出的程序中,默認保存和加載了Tensorflow計算圖上定義的全部變量。但有時候可能只需要保存或者加載部分變量,比如,可能有一個之前訓練好的五層神經網絡模型,但現在想嘗試一個六層的神經網絡,那么可以將前面五層神經網絡中的參數直接加載到新的模型,而僅僅將最后一層神經網絡重新訓練。為了保存或者加載部分變量,在聲明tf.train.Saver類時可以提供一個列表來指定需要保存或者加載的變量。比如在加載模型的代碼中使用saver=tf.train.Saver([v1])命令來構建tf.train.Saver類,那么只有變量v1會被加載進來,如果運行修改后之家在v1的代碼會得到變量未初始化的錯誤:tensorflow.python.framework.errors.FailedPreconditionError:Attempting to use uninitialized value v2
因為v2沒有被加載,所以v2在運行初始化之前是沒有值的。除了可以選取需要被加載的變量,tf.train.Saver類也支持在保存或者加載時給變量重命名。
下面給出一個簡單的程序來說明重命名時如何被調用的
在這個程序中,對變量v1和v2的名稱進行了修改,如果直接通過tf.train.Saver默認的構造函數來加載保存的模型,那么程序會報變量找不到的錯誤,因為保存時候變量的名稱和加載時變量的名稱不一致。為了解決這個問題,Tensorflow可以通過字典(dictionary)將模型保存時的變量名和需要加載的變量聯系起來。這樣做主要目的之一時方便使用變量的滑動平均值,在Tensorflow中,每一個變量的滑動平均值是通過影子變量維護的,所以要獲取變量的滑動平均值實際上就是獲取這個影子變量的取值。如果在加載模型時直接將影子變量映射到變量自身,那么在使用訓練好的模型時就不需要再調用函數來獲取變量的滑動平均值了。下面的代碼給出了一個保存滑動平均模型的樣例:
# 《TensorFlow實戰Google深度學習框架》05 minist數字識別問題 # win10 Tensorflow1.0.1 python3.5.3 # CUDA v8.0 cudnn-8.0-windows10-x64-v5.1 # filename:ts05.10.py # 滑動平均類的保存import tensorflow as tf# 1. 使用滑動平均 v = tf.Variable(0, dtype=tf.float32, name="v") for variables in tf.global_variables():print(variables.name) ''' v:0 ''' ema = tf.train.ExponentialMovingAverage(0.99) maintain_averages_op = ema.apply(tf.global_variables()) for variables in tf.global_variables():print(variables.name) ''' v:0 v/ExponentialMovingAverage:0 ''' # 2. 保存滑動平均模型 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/ExponentialMovingAverage:0這兩個變量都存下來。saver.save(sess, "Saved_model/model2.ckpt")print(sess.run([v, ema.average(v)])) ''' [10.0, 0.099999905] ''' # 3. 加載滑動平均模型 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, "Saved_model/model2.ckpt")print(sess.run(v))# 輸出:0.0999999使用tf.train.Saver會保存運行Tensorflow程序所需要的全部信息,然后有時并不需要某些信息。比如在測試或者離線預測時,只需要知道如何從神經網絡的輸入層經過前向傳播計算得到輸出層即可,而不需要類似于變量初始化,模型保存等輔助節點的信息。而且,將變量取值和計算圖結構分成不同的文件存儲有時候也不方便,于是Tensorflow提供了convert_variables_to_constants函數,通過這個函數可以將計算圖中的變量及其取值通過常量的方式保存,這樣整個Tensorflow計算圖可以統一存放在一個文件中。如下:
通過下面的程序可以直接計算定義的加法運算的結果,當只需要得到計算圖中某個節點的取值時,該方法更為簡便。
import tensorflow as tffrom tensorflow.python.platform import gfilewith tf.Session() as sess:model_filename = "/path/to/model/combined_model.pb"#讀取保存的模型文件,并將文件解析成對應的GraphDef Protocol Bufferwith gfile.FastGFile(model_filename,'rb') as f:graph_def = tf.GraphDef()graph_def.ParseFromString(f.read())#將graph_def中保存的圖加載到當前的圖中.return_elements=["add:0"]給出了返回的張量的名稱。#在保存能的時候給出的時計算節點的名稱,所以為"add",在加載的時候給出的是張量的名稱,所以時add:0result = tf.import_graph_def(graph_def,return_elements=["add:0"])print(sess.run(result))5.4.2 持久化原理及數據格式
- Tensorflow是一個通過圖的形式來表達計算的編程系統,Tensorflow程序中的所有計算都會表達為計算圖上的節點。
- Tensorflow通過元圖(MetGraph)來記錄計算圖中節點的信息以及運行計算圖中節點所需要的元數據。
- Tensorflow中元圖是由MetaGraphDef Protocol BUffer定義的,MetaGraphDef中的內容就構成了Tensorflow持久化時的第一個文件。
元圖(MetGraph)主要記錄5類信息
保存MetGraph信息的文件默認以.meta為后綴名,是一個二進制文件,無法直接查看,TensorFlow提供export_meta_graph函數來以json格式導出MetaGraphDef Protocol BUffer。
- 下面分別介紹元圖存儲的信息
- meta_info_def 屬性:記錄了計算圖中的元數據(計算圖版本號、標簽等)及程序中所有用到的運算方法信息。
- graph_def 屬性:記錄了計算圖上的節點信息,因為在meta_info_def屬性已經包含了所有運算的信息,所以graph_def只關注運算的連接結構。
- saver_def 屬性:記錄了持久化模型時需要使用的一些參數,如保存到文件的文件名、保存操作和加載操作的名稱,以及保存頻率等。
- collection_def 屬性:計算圖中維護集合的底層實現,該屬性是一個從集合名稱到集合內容的映射。
5.5 最佳實踐樣例程序
1. 定義神經網絡的前向傳播過程
# 《TensorFlow實戰Google深度學習框架》05 minist數字識別問題 # win10 Tensorflow1.0.1 python3.5.3 # CUDA v8.0 cudnn-8.0-windows10-x64-v5.1 # filename:mnist_inference.py # 定義神經網絡的前向傳播過程import tensorflow as tf# 1. 定義神經網絡結構相關的參數 INPUT_NODE = 784 OUTPUT_NODE = 10 LAYER1_NODE = 500# 2. 通過tf.get_variable函數來獲取變量 def get_weight_variable(shape, regularizer):weights = tf.get_variable("weights", shape, initializer=tf.truncated_normal_initializer(stddev=0.1))if regularizer != None:tf.add_to_collection('losses', regularizer(weights))return weights# 3. 定義神經網絡的前向傳播過程 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) + biasesreturn layer22. 訓練程序
# 《TensorFlow實戰Google深度學習框架》05 minist數字識別問題 # win10 Tensorflow1.0.1 python3.5.3 # CUDA v8.0 cudnn-8.0-windows10-x64-v5.1 # filename:mnist_train.py # 訓練程序import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import mnist_inference import os# 1. 定義神經網絡結構相關的參數 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 = "MNIST_model/" # 在當前目錄下存在MNIST_model子文件夾 MODEL_NAME = "mnist_model"# 2. 定義訓練過程 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)y = mnist_inference.inference(x, regularizer)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())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,staircase=True)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')# 初始化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})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)# 3. 主程序入口 def main(argv=None):mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)train(mnist)if __name__ == '__main__':tf.app.run()輸出:
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.gzAfter 1 training step(s), loss on training batch is 3.4301. After 1001 training step(s), loss on training batch is 0.245217. After 2001 training step(s), loss on training batch is 0.177494. After 3001 training step(s), loss on training batch is 0.137083. After 4001 training step(s), loss on training batch is 0.123793. After 5001 training step(s), loss on training batch is 0.102773. After 6001 training step(s), loss on training batch is 0.0973897. After 7001 training step(s), loss on training batch is 0.0889735. ...3. 代碼
# 《TensorFlow實戰Google深度學習框架》05 minist數字識別問題 # win10 Tensorflow1.0.1 python3.5.3 # CUDA v8.0 cudnn-8.0-windows10-x64-v5.1 # filename:mnist_eval.py # 測試程序 import time import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import mnist_inference import mnist_train# 1. 每10秒加載一次最新的模型 # 加載的時間間隔。 EVAL_INTERVAL_SECS = 10def 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}y = mnist_inference.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(mnist_train.MOVING_AVERAGE_DECAY)variables_to_restore = variable_averages.variables_to_restore()saver = tf.train.Saver(variables_to_restore)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')returntime.sleep(EVAL_INTERVAL_SECS)# 主程序 def main(argv=None):mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)evaluate(mnist)if __name__ == '__main__':tf.app.run()5.6 補充
5.6.1 單層神經網絡
MNIST數據集,圖像大小為28x28,將其進行分類的最簡單的方法就是使用784個像素作為單層神經網絡的輸入。
神經網絡中的每個神經元對其所有的輸入進行加權求和,并添加一個被稱為偏置的常數,之后通過一些非線性激活函數來反饋結果。
為了將數字分為10類,我們設計了一個具有10個輸出神經元的單層神經網絡,對于分類問題,常用的激活函數是softmax函數,該函數通過取每個元素的指數,然后歸一化向量,從而將softmax應用于向量。
softmax(Ln)=eLn||eL||softmax(Ln)=eLn||eL||
其中,LnLn為所有權重的和+偏置
softmax名稱的來源:
指數是驟增的函數,這將加大向量中每個元素的差異,也會迅速的產生一個巨大的值,然后當進行向量標準化時,支配范數(norm)的最大的元素將會被標準化為一個接近于1的數字,其他的元素將會被一個較大的值分割并標準化為一個接近于0的數字,所得到的向量清楚的顯示了哪個是其最大值,即max,但是卻又保留了其值的原始的相對排列順序,因此即為soft
假設我們有一個數組,V,Vi表示V中的第i個元素,那么這個元素的Softmax值就是
也就是說,是該元素的指數,與所有元素指數和的比值
這個定義可以說非常的直觀,當然除了直觀樸素好理解以外,它還有更多的優點,比如可以計算樣本和標注之間的差距,并且計算上非常方便。
假設輸入樣本為3,-1,3,則softmax直觀來說就是將輸入映射為(0,1)的值,且這些值的累加和為1(滿足概率性質),那么我們就可以將其理解為概率,在最后選擇輸出的時候,就可以選擇概率最大(也就是值對應最大的)結點作為預測目標。
使用矩陣乘法進行前向傳播:
我們現在將使用矩陣乘法將這個單層的神經元的行為總結進一個簡單的公式當中。直接這樣做:將包含100 個圖像的「mini-batch」作為輸入,產生 100 個預測(10 元素向量)作為輸出。
權值矩陣是784x10的大小,784對應784個像素,10對應10個神經元的輸出。
第一個神經元對應的是圖像和第一列權值相乘之和,第二個神經元是圖像和第二列權值相乘之和,以此類推,并且每個神經元都要添加一個偏置bias,
5.6.2 梯度下降
神經網絡從輸入圖像中產生預測,但是我們需要知道預測結果和真實值之間的差距,任何一種定義的距離都可以這樣操作,但是對于分類問題,一般使用交叉熵距離來度量:
crossentropy=?∑Y′i?log(Yi)crossentropy=?∑Yi′?log(Yi)
其中,Y′iYi′是真實值,YiYi是
獨熱碼(one-hot):編碼意味著你使用一個 10 個值的向量,其中除了第 6 個值為 1 以外的所有值都是 0。這非常方便,因為這樣的格式和我們神經網絡預測輸出的格式非常相似,同時它也作為一個 10 值的向量。
訓練:使用訓練圖像和標簽來調整權值和偏置,以使交叉熵損失函數最小。
梯度的數學意義在于它指向「上(up)」。因為我們想要到達一個交叉熵低的地方,那么我們就去向相反的方向。我們用一小部分的梯度更新權重和偏置并且使用下一批訓練圖像再次做同樣的事情。我們希望的是,這可以使我們到達交叉熵最小的凹點的低部。
交叉熵是一個關于權重、偏置、訓練圖像的像素和其已知標簽的函數。
如果我們相對于所有的權重和所有的偏置計算交叉熵的偏導數,我們就得到一個對于給定圖像、標簽和當前權重和偏置的「梯度」。請記住,我們有 7850 個權重和偏置,所以計算梯度需要大量的工作。
梯度下降算法遵循著一個最陡的坡度下降到局部最小值的路徑。訓練圖像在每一次迭代中同樣會被改變,這使得我們向著一個適用于所有圖像的局部最小值收斂。
學習率:在整個梯度的長度上,你不能在每一次迭代的時候都對權重和偏置進行更新。這就會像是你穿著七里靴卻試圖到達一個山谷的底部。你會直接從山谷的一邊到達另一邊。為了到達底部,你需要一些更小的步伐,即只使用梯度的一部分,通常在 1/1000 區域中。我們稱這個部分為「學習率(Learning rate)」。
神經網絡學習過程:
初始化偏置和權值→→定義損失函數→→梯度下降法更新參數→→使用下一個mini-batch優化參數
為什么使用mini-batch:
當只用一個圖像來進行參數更新的過程叫做隨機梯度下降,在 100 個樣本上都這樣做可以得到一個更好地表示由不同樣本圖像施加約束的梯度并且可能更快地朝著解決方案收斂。mini-batch 的大小是可調整的參數。還有一個更加技術化的原因:使用批處理也意味著使用較大的矩陣,而這些通常更容易在 GPU 上優化。
增加層
為了提高識別的準確度,可以將神經網絡增加更多的層,第二層神經元將計算前一層神經元輸出的加權和,而非計算像素的加權和,下面是5層全連接的神經網絡:
最后一層的激活函數仍然使用softmax,中間層使用sigmoid函數。
隨著層數的增加,神經網絡的收斂越來越難,因為梯度消失,如何來改進呢?
——修正線性單元ReLU激活函數
sigmoid函數的問題:
該函數將所有的值都歸到了(0,1)之間,是飽和函數,當層數越來越多的時候,神經元的輸入和梯度已經基本歸零了,因為變化非常小。出于歷史原因,一些現代神經網絡使用了 ReLU(修正線性單元)。
升級:
① 用ReLU替換之后,收斂更快準確度更高。
② 將你的 tf.train.GradientDescentOptimiser 替換為 tf.train.AdamOptimizer。
隨機初始化:
如果準確性一直卡在0.1,可以看看有沒有將權值初始化為隨機值了,對于偏置值,如何使用ReLU的話,最好的辦法就是將它們都初始化為小的正值,這樣神經元一開始就會工作在ReLU的非零區域內。
W = tf.Variable(tf.truncated_normal([K, L] ,stddev=0.1))B = tf.Variable(tf.ones([L])/10)③ 不定值NaN
如果準確率曲線陡然下滑,并輸出的交叉熵為NaN,則是正在計算log(0),而這個值正是NaN,
5.6.3 學習速率衰減
通過四五個中間層之后,準確度可以提升至98%,但是當迭代次數5000以上的時候,你會發現結果并非如此,而是跳動很大的曲線,也就是在全百分比范圍內跳動,這意味著學習率太大了,一個好的解決方案是開始很快隨后將學習速率指數級衰減至很小,比如0.0001。
5.6.4 過擬合和dropout
在數千次迭代之后,測試和訓練數據的交叉熵曲線開始不相連。學習算法只是在訓練數據上做工作并相應地優化訓練的交叉熵。它再也看不到測試數據了,所以這一點也不奇怪:過了一會兒它的工作不再對測試交叉熵產生任何影響,交叉熵停止了下降,有時甚至反彈回來。
它不會立刻影響你模型對于真實世界的識別能力,但是它會使你運行的眾多迭代毫無用處,而且這基本上是一個信號——告訴我們訓練已經不能再為模型提供進一步改進了。這種無法連接通常會被標明「過擬合(overfitting)」,而且當你看到這個的時候,你可以嘗試采用一種規范化(regularization)技術,稱之為「dropout」。
在 dropout 里,在每一次訓練迭代的時候,你可以從網絡中隨機地放棄一些神經元。你可以選擇一個使神經元繼續保留的概率 pkeep,通常是 50% 到 75% 之間,然后在每一次訓練的迭代時,隨機地把一些神經元連同它們的權重和偏置一起去掉。在一次迭代里,不同的神經元可以被一起去掉(而且你也同樣需要等比例地促進剩余神經元的輸出,以確保下一層的激活不會移動)。當測試你神經網絡性能的時候,你再把所有的神經元都裝回來 (pkeep=1)。
TensorFlow 提供一個 dropout 函數可以用在一層神經網絡的輸出上。它隨機地清零一些輸出并且把剩下的提升 1/pkeep。
總結
以上是生活随笔為你收集整理的TensorFlow:实战Google深度学习框架(四)MNIST数据集识别问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 原来中国科幻的背后是中国制造:《流浪地球
- 下一篇: TensorFlow:实战Google深