【TensorFlow】笔记3:MNIST数字识别问题
文章目錄
- 一、MNIST數據處理
- 1、數據集概述
- 2、數據獲取
- 二、神經網絡模型訓練及不同模型結果對比
- 1、TF訓練神經網絡
- 2、使用驗證數據判斷模型效果
- 3、不同模型效果比較
- 三、變量管理
- 1、tf.get_variable()
- 2、tf.variable_scope()管理
- 3、前向傳播的改進
- 四、TF模型持久化
- 1、持久化代碼實現
- (1)ckpt文件的保存
- (2)加載已經保存的TF模型
- (3)保存或加載部分變量
- (4)在保存或者加載時給變量重命名
- (5)示例
- (6)統一保存
- 2、持久化原理及數據格式
- 五、TF最佳實踐樣例
- 1、mnist_inference.py
- 2、mnist_train.py
- 3、mnist_eval.py
一、MNIST數據處理
1、數據集概述
MNIST 數據集是 NIST 數據集的一個子集,它包含了 60000 張圖片作為訓練數據, 10000 張圖片作為測試數據。在 MNIST 數據集中的每一張圖片都代表了 0~9 中的一個數字。圖片的大小都為 28×2828\times2828×28 , 且數字都會出現在圖片的正中間。
2、數據獲取
from tensorflow.examples.tutorials.mnist import input_datapath = './datasets/MNIST_DATA' mnist = input_data.read_data_sets(path, one_hot=True)print("Training data size: ", mnist.train.num_examples) print("Validating data size: ", mnist.validation.num_examples) print("Testing data size: ", mnist.test.num_examples)# minist.train.next_batch函數, # 它可以從所有的訓練數據中讀取一小部分作為一個訓練batch。 batch_size = 100 xs, ys = mnist.train.next_batch(batch_size) print("X shape: ", xs.shape) print("Y shape: ", ys.shape)輸出結果
Training data size: 55000 Validating data size: 5000 Testing data size: 10000 X shape: (100, 784) Y shape: (100, 10)像素矩陣中的元素的取值范圍為[0, 1],0代表白色背景,1代表黑色前景。
通過 input_data.read_data_sets 函數生成的類提供 minist.train.next_batch函數,它可以從所有的訓練數據中讀取一小部分作為一個訓練batch。
二、神經網絡模型訓練及不同模型結果對比
1、TF訓練神經網絡
在神經網絡的結構上,深度學習一方面需要使用激活函數實現神經網絡模型的去線性化,另一方面需要使用一個或多個隱藏層使得神經網絡的結構更深,以解決復雜問題。
在訓練神經網絡時,通常使用帶指數衰減的學習率設置、使用正則化來避免過度擬合,以及使用滑動平均模型來使得最終模型更加健壯。
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_datapath = './datasets/MNIST_DATA'# MNIST 數據集相關的常數 INPUT_NODE = 784 # 28*28 OUT_NODE = 10 # 0~9# 配置神經網絡的參數 LAYER1_NODE = 500 # 一個隱藏層,節點數:500 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 # 滑動平均衰減率# 一個輔助函數,給定神經網絡的輸入和所有參數,計算神經網絡的前向傳播結果。 # 在這里定義了一個使用 ReLU 激活函數的三層全連接神經網絡。 # 通過加入隱藏層實現了多層網絡結構,通過 ReLU 激活函數實現了去線性化。 # 在這個函數中也支持傳入用于計算參數平均值的類,這樣方便在測試時使用滑動平均模型。 def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):# 當沒有提供滑動平均類時,直接使用參數當前的取值if avg_class == None:# 計算隱藏層的前向傳播結果,這里使用了 ReLU i版活函數layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)# 計算輸出層的前向傳播結果。# 因為在計算損失函數時會一并計算 softmax 函數,所以這里不需要加入激活函數。# 而且不加入 softmax 不會影響預測結果。# 因為預測時使用的是不同類別對應節點輸出值的相對大小,有沒有 softmax 層對最后分類結果的# 計算沒有影響。于是在計算整個神經網絡的前向傳播時可以不加入最后的 softmax 層 。return tf.matmul(layer1, weights2) + biases2else:# 首先使用 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, OUT_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, OUT_NODE], stddev=0.1))biases2 = tf.Variable(tf.constant(0.1, shape=[OUT_NODE]))# 計算在當前參數下神經網絡前向傳播的結果。這里給出的用于計算滑動平均的類為 None,# 所以的數不會使用參數的滑動平均值。y = inference(x, None, weights1, biases1, weights2, biases2)# 定義在儲訓練輪數的變量。這個變量不需要計算滑動平均值,所以這里指定這個變量為# 不可訓練的變量(trainable=Fasle)。在使用 TensorFlow 訓練神經網絡時,# -般會將代表訓練輪數的變量指定為不可訓練的參數。global_step = tf.Variable(0, trainable=False)# 給定消動平均哀減率和訓練輪數的變量,初始化滑動平均類。# 給定訓練輪數的變量可以加快訓練早期變量的更新速度。variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)# 在所有代表神經網絡參數的變量上使用滑動平均。# tf.trainable_variables 返回的就是圖上集合GraphKeys.TRAINABLE_VARIABLES 中的元索。# 也就是這個集合的元索就是所有沒有指定 trainable=False 的參數。variable_avergaes_op = variable_averages.apply(tf.trainable_variables())# 計算使用了滑動平均之后的前向傳播結果.# 滑動平均不會改變變量本身的取值,而是會維護一個影子變量來記錄其滑動平均值。# 所以當面要使用這個滑動平均值時,需要明確調用 average 函數。average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)# 計算交叉熵損失函數:spare_softmax_cross_entropy_with_logits()# 第一個參數是神經網絡不包括 softmax 層的前向傳播結果,第二個是訓練數據的正確答案。# 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(REGULARIZATION_RATE)# 計算模型的正則化損失。一般只計算神經網絡邊上權重的正則化損失,而不使用bias。regularization = regularizer(weights1) + regularizer(weights2)# loss funtionsloss = cross_entropy_mean + regularization# 設置指數衰減的學習率 。learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE, # 基礎的學習率,隨著迭代的進行,更新變量時使用的學習率在這個基礎上遞減 。global_step, # 當前迭代的輪數 mnist.train.num_examples / BATCH_SIZE, # 過完所有的訓練數據的迭代輪數LEARNING_RATE_DECAY) # 學習率衰減速度 。# 使用 tf.train.GradientDescentOptimizer 優化算法來優化損失函數train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step = global_step)# 在訓練、神經網絡模型時,每過一遍數據既需要通過反向傳播來更新神經網絡中的參數,# 又要更新每一個參數的滑動平均值。# 為了一次完成多個操作,TF提供了tf.control_dependencies和tf.group兩種機制。# train_op = tf.group(train_step, variable_avergaes_op) # 等價with tf.control_dependencies([train_step, variable_avergaes_op]):train_op = tf.no_op(name='train') # 檢驗使用了滑動平均模型的神經網絡前向傳播結果是否正確。# tf.argmax(average_y , 1)計算每一個樣例的預測答案。# 其中 average_y 是一個 batch_size*10 的二維數組,每一行表示一個樣例的前向傳播結果。# tf.argmax 的第二個參數"1”表示選取最大值的操作僅在第一個維度中進行,# 也就是說,只在每一行選取最大值對應的下標。# 得到長度為batch一維數組,數組中的值表示每個樣例對應的數字識別結果。# tf.equal判斷兩個張量的每一維是否相等,如果相等返回 True ,否則返回 False 。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):# 每 1000 輪輸出一次在驗證數據集上的測試結果if i % 1000 == 0:# 計算滑動平均模型在驗證數據上的結果。因為 MNIST 數據集比較小,所以一次# 可以處理所有的驗證數據。為了計算方便,本樣例程序沒有將驗證數據劃分為更# 小的 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 = input_data.read_data_sets(path, one_hot=True)train(mnist)if __name__ == "__main__":tf.app.run()輸出結果
WARNING:tensorflow:From /home/jie/Jie/codes/tf/mnist.py:166: read_data_sets (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version. Instructions for updating: Please use alternatives such as official/mnist/dataset.py from tensorflow/models. WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:260: maybe_download (from tensorflow.contrib.learn.python.learn.datasets.base) is deprecated and will be removed in a future version. Instructions for updating: Please write your own downloading logic. WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:262: extract_images (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version. Instructions for updating: Please use tf.data to implement this functionality. Extracting ./datasets/MNIST_DATA/train-images-idx3-ubyte.gz WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:267: extract_labels (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version. Instructions for updating: Please use tf.data to implement this functionality. Extracting ./datasets/MNIST_DATA/train-labels-idx1-ubyte.gz WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:110: dense_to_one_hot (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version. Instructions for updating: Please use tf.one_hot on tensors. Extracting ./datasets/MNIST_DATA/t10k-images-idx3-ubyte.gz Extracting ./datasets/MNIST_DATA/t10k-labels-idx1-ubyte.gz WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:290: DataSet.__init__ (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version. Instructions for updating: Please use alternatives such as official/mnist/dataset.py from tensorflow/models. 2019-03-14 11:30:04.019232: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 2019-03-14 11:30:04.104098: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:897] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero 2019-03-14 11:30:04.105001: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1405] Found device 0 with properties: name: GeForce GTX 1060 major: 6 minor: 1 memoryClockRate(GHz): 1.6705 pciBusID: 0000:01:00.0 totalMemory: 5.94GiB freeMemory: 5.50GiB 2019-03-14 11:30:04.105016: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1484] Adding visible gpu devices: 0 2019-03-14 11:30:04.324445: I tensorflow/core/common_runtime/gpu/gpu_device.cc:965] Device interconnect StreamExecutor with strength 1 edge matrix: 2019-03-14 11:30:04.324486: I tensorflow/core/common_runtime/gpu/gpu_device.cc:971] 0 2019-03-14 11:30:04.324493: I tensorflow/core/common_runtime/gpu/gpu_device.cc:984] 0: N 2019-03-14 11:30:04.324817: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1097] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 5264 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060, pci bus id: 0000:01:00.0, compute capability: 6.1) After 0 training step(s), validation accuracy using average model is 0.1332 After 1000 training step(s), validation accuracy using average model is 0.9774 After 2000 training step(s), validation accuracy using average model is 0.9806 After 3000 training step(s), validation accuracy using average model is 0.982 After 4000 training step(s), validation accuracy using average model is 0.983 After 5000 training step(s), validation accuracy using average model is 0.9836 After 6000 training step(s), validation accuracy using average model is 0.9834 After 7000 training step(s), validation accuracy using average model is 0.9828 After 8000 training step(s), validation accuracy using average model is 0.9832 After 9000 training step(s), validation accuracy using average model is 0.9826 After 10000 training step(s), validation accuracy using average model is 0.9824 After 11000 training step(s), validation accuracy using average model is 0.9822 After 12000 training step(s), validation accuracy using average model is 0.9842 After 13000 training step(s), validation accuracy using average model is 0.9836 After 14000 training step(s), validation accuracy using average model is 0.9842 After 15000 training step(s), validation accuracy using average model is 0.9834 After 16000 training step(s), validation accuracy using average model is 0.9838 After 17000 training step(s), validation accuracy using average model is 0.9844 After 18000 training step(s), validation accuracy using average model is 0.984 After 19000 training step(s), validation accuracy using average model is 0.9834 After 20000 training step(s), validation accuracy using average model is 0.9838 After 21000 training step(s), validation accuracy using average model is 0.9844 After 22000 training step(s), validation accuracy using average model is 0.984 After 23000 training step(s), validation accuracy using average model is 0.984 After 24000 training step(s), validation accuracy using average model is 0.9844 After 25000 training step(s), validation accuracy using average model is 0.9848 After 26000 training step(s), validation accuracy using average model is 0.9838 After 27000 training step(s), validation accuracy using average model is 0.9844 After 28000 training step(s), validation accuracy using average model is 0.984 After 29000 training step(s), validation accuracy using average model is 0.9842 After 30000 training step(s), test accuracy using average model is 0.9842在訓練初期,模型在驗證數據集上表現越來越好,后來出現波動,說明模型已經接近極小值,迭代結束。
2、使用驗證數據判斷模型效果
所需初始的超參數:上述程序的開始,設置了初始學習率、學習率衰減率、隱藏層節點數、迭代次數、batch_size、正則項系數、滑動平均衰減數等7個不同的參數。
如何設置初始的超參數:一般情況需要實驗來調整
難點:雖然模型的最終效果是在測試數據上進行判定的,但不能直接使用測試數據,否則會過擬合,從而丟失對未知數據的判斷能力,所以要保證測試數據在訓練過程中是不可見的。
解決方法:
- 從訓練數據中抽取一部分作為驗證數據,來評判不同參數取值下模型的表現。在海量數據的情況下,一般會更多地采用驗證數據集的形式來評測模型的效果。
- 使用“交叉驗證(cross validation)”,但是神經網絡的訓練時間本身就比較長,所以采用該方法會花費大量的時間,故一般不會選用。
不同迭代輪次下,模型在驗證數據和測試數據上的正確率。
==》每1000輪的輸出滑動平均的模型在驗證數據和測試數據上的正確率。
輸出結果
After 0 training step(s), validation accuracy using average model is 0.0592, test accuracy using average model is 0.058 After 1000 training step(s), validation accuracy using average model is 0.9754, test accuracy using average model is 0.9764 After 2000 training step(s), validation accuracy using average model is 0.981, test accuracy using average model is 0.981 After 3000 training step(s), validation accuracy using average model is 0.982, test accuracy using average model is 0.9824 After 4000 training step(s), validation accuracy using average model is 0.9842, test accuracy using average model is 0.9825 After 5000 training step(s), validation accuracy using average model is 0.984, test accuracy using average model is 0.9841 After 6000 training step(s), validation accuracy using average model is 0.9852, test accuracy using average model is 0.984 .... After 25000 training step(s), validation accuracy using average model is 0.9842, test accuracy using average model is 0.9839 After 26000 training step(s), validation accuracy using average model is 0.984, test accuracy using average model is 0.983 After 27000 training step(s), validation accuracy using average model is 0.9848, test accuracy using average model is 0.9841 After 28000 training step(s), validation accuracy using average model is 0.9846, test accuracy using average model is 0.9837 After 29000 training step(s), validation accuracy using average model is 0.9846, test accuracy using average model is 0.9845分析:
驗證數據和測試數據上的準確率趨勢基本一致,且他們的相關系數(correlation coefficient)大于0.9999。意味著可以通過模型在驗證數據上的表現來判斷一個模型的優劣。
==》前提:驗證數據分布可以很好代表測試數據分布。
3、不同模型效果比較
不同的優化方法,需要使用多層和激活函數的網絡結構。此外,可以使用指數衰減學習率、加入正則化的損失函數以及滑動平均模型。
在神經網絡結構的設計上,需要使用激活函數和多層隱藏層。在神經網絡優化時,可以使用指數衰減的學習率,加入正則化的損失函數以及滑動平均模型。
不同優化方法的影響:
- 因為滑動平均模型、指數衰減的學習率都在限制神經網絡的參數的更新速度,而該數據庫模型收斂的速度很快,所以影響不大。
- 但是當問題更復雜時,迭代不會很快收斂,所以滑動平均模型、指數衰減的學習率可以發揮更大的作用。
- 正則化帶來的效果更為顯著
總結:優化方法可以對模型帶來更好的效果(模型越復雜,效果越明顯)
三、變量管理
當神經網絡的結構復雜、參數很多時,需要更好的方式來傳遞和管理神經網絡的參數。
TensorFlow提供了通過變量名稱來創建或獲取一個變量的機制,不同函數可以直接通過變量的名字來使用變量,不需要通過參數的形式到處傳遞。
==》實現:tf.get_variable()和tf.variable_scope()
1、tf.get_variable()
tf.get_variable() 用于創建或獲取變量。
通過變量名稱來獲取變量。創建變量時,基本等價于tf.Variable()
v = tf.get_variable("v", shape=[1], initializer=tf.constant_initializer(1.0)) v = tf.Variable(tf.constant(1.0, shape=[1]), name="v")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的參數,如果創建失敗(比如已經有同名的參數),那么這個程序就會報錯。這是為了避免無意識的變量復用造成的錯誤。
- 如果需要通過tf.get_variable獲取一個已經創建的變量,需要通過tf.variable_scope函數來生成一個上下文管理器,并明確指定在這個上下文管理器中,tf.get_variable將直接獲取已經生成的變量。
2、tf.variable_scope()管理
下面給出一段代碼說明如何通過tf.variable_scope函數來控制tf.get_variable函數獲取已經創建過的變量。
#在名字為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 v == v1 #輸出為True,代表v,v1是相同的Tensorflow中的變量#將參數reuse設置為True時,tf.variable_scope將只能獲取已經創建的變量,因為在命名空間bar中還沒有創建變量v,所以下面的代碼將會報錯: with tf.variable_scope("bar",reuse=True):v = tf.get_variable("v",[1])通過 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 函數會直接獲取已經創建的變量,如果變量不存在,將會報錯。
- 使用變量管理后,就不再需要將所有變量都作為參數傳遞到不同的函數中了,當神經網絡結構更加復雜,參數更多時,使用這種變量管理的方式將大大提高程序的可讀性。
嵌套示例代碼:
with tf.variable_scope("root"):# 獲取當前上下文管理器中 reuse 的取值。print(tf.get_variable_scope().reuse)with tf.variable_scope("foo", reuse=True):print(tf.get_variable_scope().reuse)with tf.variable_scope("bar"):print(tf.get_variable_scope().reuse)with tf.variable_scope("bar1"):print(tf.get_variable_scope().reuse)print(tf.get_variable_scope().reuse)輸出結果
False True True True False結論:在嵌套中,若指定reuse參數為True則輸出為True;當未指定reuse參數時,這時的reuse取值與外面一層保持一致,若為最后層,則為False。
通過 tf.variable_scope 來管理變量命名空間:
v1 = tf.get_variable("v", 1) print(v1.name) # output: v:0 # "v"表示變量名稱; # “0”表示該變量是生成變量這個運算的第一個結果with tf.variable_scope("foo"):v2 = tf.get_variable("v", [1])print(v2.name)# output: foo/v:0with tf.variable_scope("foo"):with tf.variable_scope("bar"):v3 = tf.get_variable("v", [1])print(v3.name)# output: foo/bar/v:0# 名稱會加入命名空間的名稱v4 = tf.get_variable("v1", [1])print(v4.name)# output: foo/v1:0with tf.variable_scope("", reuse=True):v5 = tf.get_variable("foo/bar/v", [1])print(v5 == v3)# output: Truev6 = tf.get_variable("foo/v1", [1])print(v6 == v4)# output: True3、前向傳播的改進
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)# 定義第 2 層神經網絡的變量和前向傳播過程。with tf.variable_scope('layer2', reuse=reuse):weights = tf.get_variable("weights", [LAYER1_NODE, OUT_NODE],initializer=tf.truncated_normal_initializer(stddev=0.1))biases = tf.get_variable("biases", [OUT_NODE],initializer=tf.constant_initializer(0.0))layer2 = tf.matmul(layer1, weights) + biasesreturn 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)四、TF模型持久化
為了將訓練得到的模型保存下來方便下次使用,即結果可以復用,需要將神經網絡模型持久化。
1、持久化代碼實現
實現: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") result = v1 + v2init_op = tf.global_variables_initializer() # 聲明 tf.train.Saver 類用于保存模型。 saver = tf.train.Saver()with tf.Session() as sess:sess.run(init_op)# 將模型保存到model.ckpt 文件。saver.save(sess, "./model.ckpt")Tensorflow模型一般會存在后綴為 .ckpt 文件中,雖然上面的程序只指定了一個文件路徑,但是在這個文件目錄下會出現三個文件,這是因為Tensorflow會將計算圖的結構和圖上的參數取值分來保存。
- 第一個文件為model.ckpt.meta,它保存了Tensorflow計算圖的結構,
- 第二個文件為model.ckpt,這個文件保存了Tensorflow程序中每一個變量的取值,
- 最后一個文件為checkpoint文件,這個文件保存了一個目錄下所有的模型文件列表。
(2)加載已經保存的TF模型
import tensorflow as tf# load the model 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, "./model.ckpt")print(sess.run(result))# 輸出:[ 3.]加載模型的代碼中,沒有運行變量的初始化過程,而是將變量的值通過已經保存的模型加載進來.
如果不希望重復定義圖上的運算,也可以直接加載已經持久化的圖。
import tensorflow as tf# 加載計算圖 saver = tf.train.import_meta_graph("./model.ckpt.meta") with tf.Session() as sess:# 加載全部變量saver.restore(sess, "./model.ckpt")# 通過張量名稱來獲取張量print(sess.run(tf.get_default_graph().get_tensor_by_name("add:0")))# 輸出:[ 3.](3)保存或加載部分變量
情景:可能有一個之前訓練好的五層神經網絡模型,但現在想嘗試一個六層的神經網絡,那么可以將前面五層神經網絡中的參數直接加載到新的模型,而僅僅將最后一層神經網絡重新訓練。
實現:在聲明 tf.train.Saver 類時可以提供一個列表來指定需要保存或者加載的變量。比如在加載模型的代碼中使用saver=tf.train.Saver([v1])命令來構建tf.train.Saver類,那么只有變量v1會被加載進來,如果運行修改后之家在v1的代碼會得到變量未初始化的錯誤:
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value v2(4)在保存或者加載時給變量重命名
v11 = tf.Variable(tf.constant(1.0, shape=[1]), name="other-v1") v22 = tf.Variable(tf.constant(2.0, shape=[1]), name="other-v2")# 如果直接使用tf.train.Saver類來加載模型會報變量找不到的錯誤。# 使用字典來重命名變量即可加載原來的模型 # 字典指定了原來名稱為v1的變量現在加載到變量v11中(名稱為other-v1) saver = tf.train.Saver({"v1":v11, "v2":v22})主要目的:方便使用變量的滑動平均值,在Tensorflow中,每一個變量的滑動平均值是通過影子變量維護的,所以要獲取變量的滑動平均值實際上就是獲取這個影子變量的取值。如果在加載模型時直接將影子變量映射到變量自身,那么在使用訓練好的模型時就不需要再調用函數來獲取變量的滑動平均值了。
(5)示例
一個保存滑動平均模型的樣例。
import tensorflow as tf# 1. 使用滑動平均 vv = tf.Variable(0, dtype=tf.float32, name="v") # 在沒有申明滑動平均模型時只有一個變量 v,所以以下語句只會輸出“v:0” for variables in tf.global_variables():print(variables.name)# v:0ema = tf.train.ExponentialMovingAverage(0.99) maintain_average_op = ema.apply(tf.global_variables()) # 在申明滑動平均模型之后, TensorFlow 會自動生成一個影子變量 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(vv, 10))sess.run(maintain_average_op)# 保存的時候會將v:0 v/ExponentialMovingAverage:0這兩個變量都存下來。saver.save(sess, "./model/model1.ckpt")print(sess.run([vv, ema.average(vv)])) ''' [10.0, 0.099999905] '''# 3. 加載滑動平均模型 vv1 = tf.Variable(0, dtype=tf.float32, name="v") # 通過變量命名將原來變量v的滑動平均值直接賦值給vv saver = tf.train.Saver({"v/ExponentialMovingAverage":vv1}) with tf.Session() as sess:saver.restore(sess, "./model/model1.ckpt")print(sess.run(vv1))在加載時重命名滑動平均變量。使用tf.train.ExponentialMovingAverage類提供的variables_to_restore函數生成tf.train.Saver類所需的變量命名字典
import tensorflow as tfv = tf.Variable(0, dtype=tf.float32, name="v") ema = tf.train.ExponentialMovingAverage(0.99)print(ema.variables_to_restore()) # {'v/ExponentialMovingAverage': <tf.Variable 'v:0' shape=() dtype=float32_ref>}saver = tf.train.Saver(ema.variables_to_restore()) with tf.Session() as sess:saver.restore(sess, "./model/model1.ckpt")print(sess.run(v))# 0.099999905(6)統一保存
將變量取值和計算圖結構統一保存:在TF提供了 convert_variables_to_constants 函數,可以將計算圖中的變量及其取值通過變量的方式保存到一個文件中。
import tensorflow as tf from tensorflow.python.framework import graph_utilv1 = tf.Variable(tf.constant(1.0, shape=[1]), name="v1") v2 = tf.Variable(tf.constant(2.0, shape=[1]), name="v2") result = v1 + v2init_op = tf.global_variables_initializer() with tf.Session() as sess:sess.run(init_op)# 導出當前計算圖的GraphDef部分,只需此部分就可完成從輸入層到輸出層的計算過程graph_def = tf.get_default_graph().as_graph_def()# 將圖中的變量及其取值轉化為常量,同時將圖中不必要的節點去掉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())# load the model import tensorflow as tf from tensorflow.python.platform import gfilewith tf.Session() as sess:model_filename = "./model/combined_model.pb"# 保存的模型文件,并將文件解析成對應的GrapDef 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))# [array([3.], dtype=float32)]2、持久化原理及數據格式
- Tensorflow是一個通過圖的形式來表達計算的編程系統,Tensorflow程序中的所有計算都會表達為計算圖上的節點。
- Tensorflow通過元圖(MetGraph)來記錄計算圖中節點的信息以及運行計算圖中節點所需要的元數據。
- Tensorflow中元圖是由MetaGraphDef Protocol Buffer定義的,MetaGraphDef中的內容就構成了Tensorflow持久化時的第一個文件。==》.meta文件
元圖(MetGraph)主要記錄5類信息
保存MetGraph信息的文件默認以.meta為后綴名,是一個二進制文件,無法直接查看,TensorFlow提供export_meta_graph函數來以json格式導出MetaGraphDef Protocol Buffer。
import tensorflow as tf from tensorflow.python.framework import graph_utilv1 = 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() saver.export_meta_graph("./model/model1.ckpt.meda.json", as_text=True)下面分別介紹元圖存儲的信息 :
- meta_info_def 屬性:記錄了計算圖中的元數據(計算圖版本號、標簽等)及程序中所有用到的運算方法信息。
- graph_def 屬性:記錄了計算圖上的節點信息,因為在meta_info_def屬性已經包含了所有運算的信息,所以graph_def只關注運算的連接結構。
- saver_def 屬性:記錄了持久化模型時需要使用的一些參數,如保存到文件的文件名、保存操作和加載操作的名稱,以及保存頻率等。
- collection_def 屬性:計算圖中維護集合的底層實現,該屬性是一個從集合名稱到集合內容的映射。
五、TF最佳實踐樣例
- 將不同的功能模塊分開:將訓練和測試分成兩個獨立的程序,這可以使得每一個組件更加靈活。比如訓練神經網絡的程序可以持續輸出訓練好的模型,而測試程序可以每隔一段實踐檢驗最新模型的正確率,如果模型效果更好,則將這個模型提供給產品使用。除了將不同的功能模塊分開;
- 本節還將前向傳播的過程抽象成一個單獨的庫函數。因為神經網絡的前向傳播過程在訓練和測試的過程中都會用到,所以通過庫函數的方式使用起來既方便又可以保證訓練和測試過程中使用的前向傳播方法是一致的。
1、mnist_inference.py
定義了前向傳播的過程以及神經網絡中的參數。
# -*- coding:utf-8 -*- import tensorflow as tf# 1. 定義神經網絡結構相關參數 INPUT_NODE = 784 # 28*28 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))# 當給出正則化生成函數,將正則化損失加入lossesif 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 = 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 = get_variable("biases", [OUTPUT_NODE], initializer=tf.constant_initializer(0.0))layer2 = tf.matmul(layer1, weights) + biasesreturn layer22、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# 1. 定義神經網絡結構相關參數 BATCH_SIZE = 100 LEARNING_RATE_BASE = 0.8 LEARNING_RATE_DECAY = 0.99 REGULARAZTION_RATE = 0.0001 TRANING_STEPS = 30000 MOVING_AVERAGE_DECAY = 0.99MODEL_SAVE_PATH = "./model/" MODEL_NAME = "model_mnist.ckpt"# 2. 定義訓練過程 def train(mnist):# 定義輸入輸出placeholderx = 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)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()# trainfor i in range(TRANING_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)def main(argv=None):mnist = input_data.read_data_sets("./datasets/MNIST_DATA", one_hot=True)train(mnist)if __name__ == '__main__':tf.app.run()輸出結果
5216 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060, pci bus id: 0000:01:00.0, compute capability: 6.1) After 1 training step(s), loss on training batch is 2.84809. After 1001 training step(s), loss on training batch is 0.326219. After 2001 training step(s), loss on training batch is 0.179183. After 3001 training step(s), loss on training batch is 0.131504. After 4001 training step(s), loss on training batch is 0.113809. After 5001 training step(s), loss on training batch is 0.10388. ... After 22001 training step(s), loss on training batch is 0.0408138. After 23001 training step(s), loss on training batch is 0.0397483. After 24001 training step(s), loss on training batch is 0.0349427. After 25001 training step(s), loss on training batch is 0.0382821. After 26001 training step(s), loss on training batch is 0.0368644. After 27001 training step(s), loss on training batch is 0.0366197. After 28001 training step(s), loss on training batch is 0.0426658. After 29001 training step(s), loss on training batch is 0.0358222.3、mnist_eval.py
定義了測試過程。
# -*- coding:utf-8 -*- import time import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_dataimport mnist_inference import mnist_train# 每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}# 測試時不關注正則化損失的值,所以設置為Noney = 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))# 通過變量重命名的方式來加載模型variable_averages = tf.train.ExponentialMovingAverage(mnist_train.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(mnist_train.MODEL_SAVE_PATH)if ckpt and ckpt.model_checkpoint_path:# load the modelsaver.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("./datasets/MNIST_DATA", one_hot=True)evaluate(mnist)if __name__ == '__main__':tf.app.run()輸出結果
After 29001 training step(s), validation accuracy = 0.985 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的【TensorFlow】笔记3:MNIST数字识别问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【TensorFlow】笔记2:深层神经
- 下一篇: 【LeetCode】130.被围绕的区域