續集請點擊我:tensorflow學習筆記——使用TensorFlow操做MNIST數據(2)html
本節開始學習使用tensorflow教程,固然從最簡單的MNIST開始。這怎麼說呢,就比如編程入門有Hello World,機器學習入門有MNIST。在此節,我將訓練一個機器學習模型用於預測圖片裏面的數字。node
MNIST 是一個很是有名的手寫體數字識別數據集,在不少資料中,這個數據集都會被用作深度學習的入門樣例。而Tensorflow的封裝讓MNIST數據集變得更加方便。MNIST是NIST數據集的一個子集,它包含了60000張圖片做爲訓練數據,10000張圖片做爲測試數據。在MNIST數據集中的每一張圖片都表明了0~9中的一個數字。圖片的大小都爲28*28,且數字都會出如今圖片的正中間,以下圖:python
在上圖中右側顯示了一張數字1的圖片,而右側顯示了這個圖片所對應的像素矩陣,MNIST數據集提供了四個下載文件,在tensorflow中能夠將這四個文件直接下載放到一個目錄中並加載,以下代碼input_data.read_data_sets所示,若是指定目錄中沒有數據,那麼tensorflow會自動去網絡上進行下載。下面代碼介紹瞭如何使用tensorflow操做MNIST數據集。git
MNIST數據集的官網是Yann LeCun's website。在這裏,咱們提供了一份python源代碼用於自動下載和安裝這個數據集。你能夠下載這份代碼,而後用下面的代碼導入到你的項目裏面,也能夠直接複製粘貼到你的代碼文件裏面。github
import tensorflow.examples.tutorials.mnist.input_data as input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
這裏,我直接下載了數據集,並放在了個人代碼裏面。測試以下:web
# _*_coding:utf-8_*_ from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True) print("OK") # 打印「Training data size: 55000」 print("Training 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) # 打印「Example training data: [0. 0. 0. ... 0.380 0.376 ... 0.]」 print("Example training data: ", mnist.train.images[0]) # 打印「Example training data label: [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]」 print("Example training data label: ", mnist.train.labels[0]) batch_size = 100 # 從train的集合中選取batch_size個訓練數據 xs, ys = mnist.train.next_batch(batch_size) # 輸出「X shape:(100,784)」 print("X shape: ", xs.shape) # 輸出"Y shape:(100,10)" 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.gz OK Training data size: 55000 Validating data size: 5000 Testing data size: 10000 X shape: (100, 784) Y shape: (100, 10)
從上面的代碼能夠看出,經過input_data.read_data_sets函數生成的類會自動將MNIST數據集劃分紅train,validation和test三個數據集,其中train這個集合內含有55000張圖片,validation集合內含有5000張圖片,這兩個集合組成了MNIST自己提供的訓練數據集。test集合內有10000張圖片,這些圖片都來自與MNIST提供的測試數據集。處理後的每一張圖片是一個長度爲784的一維數組,這個數組中的元素對應了圖片像素矩陣中的每個數字(28*28=784)。由於神經網絡的輸入是一個特徵向量,因此在此把一張二維圖像的像素矩陣放到一個一維數組中能夠方便tensorflow將圖片的像素矩陣提供給神經網絡的輸入層。像素矩陣中元素的取值範圍爲[0, 1],它表明了顏色的深淺。其中0表示白色背景,1表示黑色前景。編程
咱們知道MNIST的每一張圖片都表示一個數字,從0到9。咱們但願獲得給定圖片表明每一個數字的機率。好比說,咱們的模型可能推測一張包含9的圖片表明數字9 的機率爲80%可是判斷它是8 的機率是5%(由於8和9都有上半部分的小圓),而後給予它表明其餘數字的機率更小的值。api
這是一個使用softmax regression模型的經典案例。softmax模型能夠用來給不一樣的對象分配機率。即便在以後,咱們訓練更加精細的模型的時候,最後一步也是須要softmax來分配機率。數組
爲了獲得一張給定圖片屬於某個特定數字類的證據(evidence),咱們對圖片像素值進行加權求和。若是這個像素具備很強的證聽說明這種圖片不屬於該類,那麼相應的權值爲負數,相反若是這個像素擁有很強的證聽說明這張圖片不屬於該類,那麼相應的權值爲負數,相反若是這個像素擁有有力的證據支持這張圖片屬於這個類,那麼權值是正數。
下面的圖片顯示了一個模型學習到的圖片上每一個像素對於特定數字類的權值,紅色表明負數權值,藍色表明正數權值。
咱們也須要加入一個額外的偏置量(bias),由於輸入每每會帶有一些無關的干擾量。由於對於給定的輸入圖片 x 它表明的是數字 i 的證據能夠表示爲:
其中,Wi表明權重,bi表明數字 i 類的偏置量, j 表明給定圖片 x 的像素索引用於像素求和。而後用 softmax 函數能夠把這些證據轉換成機率 y:
這裏的softmax能夠當作是一個激勵(activation)函數或者連接(link)函數,把咱們定義的線性函數的輸出轉換成咱們想要的格式,也就是關於10個數字類的機率分佈。所以,給定一張圖片,它對於每個數字的吻合度能夠被softmax函數轉換成一個機率值。softmax函數能夠定義爲:
展開等式右邊的子式,能夠獲得:
可是更多的時候把softmax模型函數定義爲前一種形式:把輸入值當作冪指函數求值,再正則化這些結果值。這個冪運算表示,更大的證據對應更大的假設模型(hypothesis)裏面的乘數權重值。反之,擁有更少的證據意味着在假設模型裏面擁有更小的乘數係數。假設模型裏面的權值不能夠是0值或者負值。Softmax而後會正則化這些權重值,使他們的總和等於1,以此構造一個有效的機率分佈。
對於softmax迴歸模型能夠用下面的圖解釋,對於輸入的 xs 加權求和,再分別加上一個偏置量,最後再輸入到 softmax 函數中:
若是把它寫成一個等式,咱們能夠獲得:
咱們也能夠用向量表示這個計算過程:用矩陣乘法和向量相加。這有助於提升計算效率:
更進一步,能夠寫成更加緊湊的方式:
接下來就是使用Tensorflow實現一個 Softmax Regression。其實在Python中,當尚未TensorFlow時,一般使用Numpy作密集的運算操做。由於Numpy是使用C和一部分 fortran語言編寫的,而且調用了 openblas,mkl 等矩陣運算庫,所以效率很好。其中每個運算操做的結果都要返回到Python中,但不一樣語言之間傳輸數據可能會帶來更大的延遲。TensorFlow一樣將密集的複雜運算搬到Python外面執行,不過作的更完全。
首先載入TensorFlow庫,並建立一個新的 InteractiveSession ,使用這個命令會將這個session 註冊爲默認的session,以後的運算也默認跑在這個session裏,不一樣session之間的數據和運算應該都是相互獨立的。接下來建立一個 placeholder,即輸入數據的地方。Placeholder 的第一個參數是數據類型,第二個參數 [None, 784] 表明 tensor 的 shape,也就是數據的尺寸,這裏的None表明不限條數的輸入, 784表明每條數據的是一個784維的向量。
sess = tf.InteractiveSession() x = tf.placeholder(tf.float32, [None, 784])
接下來要給Softmax Regression模型中的 weights和 biases 建立 Variable 對象,Variable是用來存儲模型參數的,不一樣於存儲數據的 tensor 一旦使用掉就會消失,Variable 在模型訓練迭代中是持久的,它能夠長期存在而且在每輪迭代中被更新。咱們把 weights 和 biases 所有初始化爲0,由於模型訓練時會自動學習適合的值,因此對這個簡單模型來講初始值不過重要,不過對於複雜的卷積網絡,循環網絡或者比較深的全鏈接網絡,初始化的方法就比較重要,甚至能夠說相當重要。注意這裏的W的shape是 [784, 10] 中 , 784是特徵的維數,然後面的10 表明有10類,由於Label在 one-hot編碼後是10維的向量。
W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10]))
接下來要實現 Softmax Regression 算法,咱們將上面提到的 y = softmax(Wx+b),改成 TensorFlow的語言就是下面的代碼:
y = tf.nn.softmax(tf.matmul(x, W) + b)
Softmax是 tf.nn 下面的一個函數,而 tf.nn 則包含了大量的神經網絡的組件, tf.matmul是TensorFlow中矩陣乘法函數。咱們就定義了簡單的 Softmax Regression,語法和直接寫數學公式很像。然而Tensorflow最厲害的地方還不是定義公式,而是將forward和backward的內容都自動實現。因此咱們接下來定義好loss,訓練時將會自動求導並進行梯度降低,完成對 Softmax Regression模型參數的自動學習。
爲了訓練模型,咱們首先須要定義一個指標來評估這個模型是好的。其實,在機器學習中,咱們一般定義指標來表示一個模型是壞的,這個指標稱爲成本(cost)或者損失(loss),而後近鄰最小化這個指標。可是這兩種方式是相同的。
一個很是常見的的成本函數是「交叉熵(cross-entropy)」。交叉熵產生於信息論裏面的信息壓縮編碼技術,可是後來他演變爲從博弈論到機器學習等其餘領域裏的重要技術手段,它的定義以下:
Y是咱們預測的機率分佈, y' 是實際的分佈(咱們輸入的 one-hot vector)。比較粗糙的理解是,交叉熵是用來衡量咱們的預測用於描述真相的低效性。
爲了計算交叉熵,咱們首先須要添加一個新的佔位符用於輸入正確值:
y_ = tf.placeholder("float", [None,10])
而後咱們能夠用交叉熵的公式計算交叉熵:
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
或者
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
(這裏咱們 y_ * tf.log(y)是前面公式的交叉熵公式,而 tf.reduce_sum就是求和,而tf.reduce_mean則是用來對每一個 batch 的數據結果求均值。)
首先,用 tf.log 計算 y 的每一個元素的對數。接下來,咱們把 y_ 的每個元素 和 tf.log(y) 的對應元素相乘。最後用 tf.reduce_sum 計算張量的全部元素總和。(注意:這裏的交叉熵不只僅用來衡量單一的一對預測和真實值,而是全部的圖片的交叉熵的總和。對於全部的數據點的預測表現比單一數據點的表現能更好的描述咱們的模型的性能)。
當咱們知道咱們須要咱們的模型作什麼的時候,用TensorFlow來訓練它是很是容易的。由於TensorFlow擁有一張描述咱們各個計算單元的圖,它能夠自動的使用反向傳播算法(backpropagation algorithm)來有效的肯定變量是如何影響想要最小化的那個成本值。而後TensorFlow會用選擇的優化算法來不斷地修改變量以下降成本。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
在這裏,咱們要求TensorFlow用梯度降低算法(gradient descent alogrithm)以0.01的學習速率最小化交叉熵。梯度降低算法(gradient descent algorithm)是一個簡單的學習過程,TensorFlow只需將每一個變量一點點地往使成本不斷下降的方向移動。固然TensorFlow也提供了其餘許多優化算法:只要簡單地調整一行代碼就可使用其餘的算法。
TensorFlow在這裏實際上所作的是,它會在後臺給描述你的計算的那張圖裏面增長一系列新的計算操做單元用於實現反向傳播算法和梯度降低算法。而後,它返回給你的只是一個單一的操做,當運行這個操做時,它用梯度降低算法訓練你的模型,微調你的變量,不斷減小成本。
如今,咱們已經設置好了咱們的模型,在運行計算以前,咱們須要添加一個操做來初始化咱們建立的變量並初始化,可是咱們能夠在一個Session裏面啓動咱們的模型:
with tf.Session() as sess: tf.global_variables_initializer().run()
而後開始訓練模型,這裏咱們讓模型訓練訓練1000次:
for i in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
或者
for i in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) train_step.run({x: batch_xs, y:batch_ys})
該訓練的每一個步驟中,咱們都會隨機抓取訓練數據中的100個批處理數據點,而後咱們用這些數據點做爲參數替換以前的佔位符來運行 train_step。並且咱們能夠經過 feed_dict 將 x 和 y_ 張量 佔位符用訓練數據替代。(注意:在計算圖中,咱們能夠用 feed_dict 來替代任何張量,並不只限於替換佔位符)
使用一小部分的隨機數據來進行訓練被稱爲隨機訓練(stochastic training)- 在這裏更確切的說是隨機梯度降低訓練。在理想狀況下,咱們但願用咱們全部的數據來進行每一步的訓練,由於這能給咱們更好的訓練結果,但顯然這須要很大的計算開銷。因此,每一次訓練咱們可使用不一樣的數據子集,這樣作既能夠減小計算開銷,又能夠最大化地學習到數據集的整體特性。
如今咱們已經完成了訓練,接下來就是對模型的準確率進行驗證,下面代碼中的 tf.argmax是從一個 tensor中尋找最大值的序號, tf.argmax(y, 1)就是求各個預測的數字中機率最大的一個,而 tf.argmax(y_, 1)則是找到樣本的真實數字類別。而 tf.equal方法則是用來判斷預測數字類別是否就是正確的類別,最後返回計算分類是否正確的操做 correct_predition。
爲了獲得能夠用來後續恢復模型以進一步訓練或評估的檢查點文件(checkpoint file),咱們實例化一個 tf.train.Saver。
saver = tf.train.Saver()
在訓練循環中,將按期調用 saver.save() 方法,向訓練文件夾中寫入包含了當前全部可訓練變量值的檢查點文件。
saver.save(sess, FLAGS.train_dir, global_step=step)
這樣,咱們之後就可使用 saver.restore() 方法,重載模型的參數,繼續訓練。
saver.restore(sess, FLAGS.train_dir)
那麼咱們的模型性能如何呢?
首先讓咱們找出那些預測正確的標籤。tf.argmax 是一個很是有用的函數,它能給出某個 tensor 對象在某一維上的其數據最大值所在的索引值。因爲標籤向量是由0, 1 組成,因此最大值1所在的索引位置就是類別標籤,好比 tf.argmax(y, 1) 返回的是模型對於任一輸入 x 預測到的標籤值,而 tf.argmax(y_, 1) 表明正確的標籤,咱們能夠用 tf.equal 來檢測咱們的預測是否真實標籤匹配(索引位置同樣表示匹配)。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
這裏返回一個布爾數組,爲了計算咱們分類的準確率,咱們使用cast將布爾值轉換爲浮點數來表明對,錯,而後取平均值。例如: [True, False, True, True] 變爲 [1, 0, 1, 1],計算出平均值爲 0.75。
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
最後,咱們能夠計算出在測試數據上的準確率,大概是91%。
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
下面咱們總結一下實現了上面簡單機器學習算法 Softmax Regression,這能夠算做是一個沒有隱含層的最淺的神經網絡,咱們來總結一下整個流程,咱們作的事情分爲下面四部分:
這幾個步驟是咱們使用Tensorflow進行算法設計,訓練的核心步驟,也將會貫穿全部神經網絡。
代碼以下:
#_*_coding:utf-8_*_ import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data # 首先咱們直接加載TensorFlow封裝好的MNIST數據 28*28=784 mnist = input_data.read_data_sets('MNIST_data/', one_hot=True) print(mnist.train.images.shape, mnist.train.labels.shape) print(mnist.test.images.shape, mnist.test.labels.shape) print(mnist.validation.images.shape, mnist.validation.labels.shape) ''' (55000, 784) (55000, 10) (10000, 784) (10000, 10) (5000, 784) (5000, 10) ''' sess = tf.InteractiveSession() x = tf.placeholder(tf.float32, [None, 784]) W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10])) y = tf.nn.softmax(tf.matmul(x, W) + b) y_ = tf.placeholder(tf.float32, [None, 10]) cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1])) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) tf.global_variables_initializer().run() for i in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) train_step.run({x: batch_xs, y_: batch_ys}) correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
傳統的神經網絡訓練代碼:
# _*_coding:utf-8_*_ import tensorflow as tf import numpy as np from tensorflow.examples.tutorials.mnist import input_data BATCH_SIZE = 100 # 訓練批次 INPUT_NODE = 784 # 784=28*28 輸入節點 OUTPUT_NODE = 10 # 輸出節點 LAYER1_NODE = 784 # 層級節點 TRAIN_STEP = 10000 # 訓練步數 LEARNING_RATE = 0.01 L2NORM_RATE = 0.001 def train(mnist): # define input placeholder # None表示此張量的第一個維度能夠是任何長度的 input_x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='input_x') input_y = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='input_y') # define weights and biases w1 = tf.Variable(tf.truncated_normal(shape=[INPUT_NODE, LAYER1_NODE], stddev=0.1)) b1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE])) w2 = tf.Variable(tf.truncated_normal(shape=[LAYER1_NODE, OUTPUT_NODE], stddev=0.1)) b2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE])) layer1 = tf.nn.relu(tf.nn.xw_plus_b(input_x, w1, b1)) y_hat = tf.nn.xw_plus_b(layer1, w2, b2) print("1 step ok!") # define loss cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y_hat, labels=input_y) cross_entropy_mean = tf.reduce_mean(cross_entropy) regularization = tf.nn.l2_loss(w1) + tf.nn.l2_loss(w2) + tf.nn.l2_loss(b1) + tf.nn.l2_loss(b2) loss = cross_entropy_mean + L2NORM_RATE * regularization print("2 step ok") # define accuracy correct_predictions = tf.equal(tf.argmax(y_hat, 1), tf.argmax(input_y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_predictions, tf.float32)) print("3 step ok") # train operation global_step = tf.Variable(0, trainable=False) train_op = tf.train.AdamOptimizer(LEARNING_RATE).minimize(loss, global_step=global_step) print("4 step ok ") with tf.Session() as sess: tf.global_variables_initializer().run() # 初始化建立的變量 print("5 step OK") # 開始訓練模型 for i in range(TRAIN_STEP): xs, ys = mnist.train.next_batch(BATCH_SIZE) feed_dict = { input_x: xs, input_y: ys, } _, step, train_loss, train_acc = sess.run([train_op, global_step, loss, accuracy], feed_dict=feed_dict) if (i % 100 == 0): print("After %d steps, in train data, loss is %g, accuracy is %g." % (step, train_loss, train_acc)) test_feed = {input_x: mnist.test.images, input_y: mnist.test.labels} test_acc = sess.run(accuracy, feed_dict=test_feed) print("After %d steps, in test data, accuracy is %g." % (TRAIN_STEP, test_acc)) if __name__ == '__main__': mnist = input_data.read_data_sets("data", one_hot=True) print("0 step ok!") train(mnist)
結果以下:
Extracting data\train-images-idx3-ubyte.gz Extracting data\train-labels-idx1-ubyte.gz Extracting data\t10k-images-idx3-ubyte.gz Extracting data\t10k-labels-idx1-ubyte.gz 0 step ok! 1 step ok! 2 step ok 3 step ok 4 step ok 5 step OK After 1 steps, in train data, loss is 5.44352, accuracy is 0.03. After 101 steps, in train data, loss is 0.665012, accuracy is 0.95. ... ... After 9901 steps, in train data, loss is 0.304703, accuracy is 0.96. After 10000 steps, in test data, accuracy is 0.9612.
佔位符
咱們經過爲輸入圖像和目標輸出類別建立節點,來開始構建計算圖:
x = tf.placeholder("float", [None, 784])
x 不是一個特定的值,而是一個佔位符 placeholder,咱們在TensorFlow運行計算時輸入這個值咱們但願可以輸入任意數量的MNIST圖像,每張圖展平爲784維(28*28)的向量。咱們用二維的浮點數張量來表示這些圖,這個張量的形狀是 [None, 784],這裏的None表示此張量的第一個維度能夠是任意長度的。
雖然palceholder 的shape參數是可選的,可是有了它,TensorFlow可以自動捕捉因數據維度不一致致使的錯誤。
變量
一個變量表明着TensorFlow計算圖中的一個值,可以在計算過程當中使用,甚至進行修改。在機器學習的應用過程當中,模型參數通常用Variable來表示。
w1 = tf.Variable(tf.truncated_normal(shape=[INPUT_NODE, LAYER1_NODE], stddev=0.1)) b1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
咱們的模型也須要權重值和偏置量,固然咱們能夠將其當作是另外的輸入(使用佔位符),這裏使用Variable。一個Variable表明一個可修改的張量,它能夠用於計算輸入值,也能夠在計算中被修改。對於各類機器學習英語,通常都會有模型參數,能夠用Variable來表示。
變量須要經過session初始化後,才能在session中使用,這一初始化的步驟爲,爲初始化指定具體值,並將其分配給每一個變量,能夠一次性爲全部變量完成此操做。
sess.run(tf.initialize_all_variables())
在MNIST上只有96%的正確率是在是糟糕,因此這裏咱們用一個稍微複雜的模型:卷積神經網絡來改善效果,這裏大概會達到100%的準確率。
卷積神經網絡的訓練代碼以下:
# _*_coding:utf-8_*_ import tensorflow as tf import numpy as np from tensorflow.examples.tutorials.mnist import input_data BATCH_SIZE = 100 # 訓練批次 INPUT_NODE = 784 # 784=28*28 輸入節點 OUTPUT_NODE = 10 # 輸出節點 LAYER1_NODE = 784 # 層級節點 TRAIN_STEP = 10000 # 訓練步數 LEARNING_RATE = 0.01 L2NORM_RATE = 0.001 def train(mnist): # define input placeholder # None表示此張量的第一個維度能夠是任何長度的 input_x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='input_x') input_y = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='input_y') # define weights and biases w1 = tf.Variable(tf.truncated_normal(shape=[INPUT_NODE, LAYER1_NODE], stddev=0.1)) b1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE])) w2 = tf.Variable(tf.truncated_normal(shape=[LAYER1_NODE, OUTPUT_NODE], stddev=0.1)) b2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE])) layer1 = tf.nn.relu(tf.nn.xw_plus_b(input_x, w1, b1)) y_hat = tf.nn.xw_plus_b(layer1, w2, b2) print("1 step ok!") # define loss cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y_hat, labels=input_y) cross_entropy_mean = tf.reduce_mean(cross_entropy) regularization = tf.nn.l2_loss(w1) + tf.nn.l2_loss(w2) + tf.nn.l2_loss(b1) + tf.nn.l2_loss(b2) loss = cross_entropy_mean + L2NORM_RATE * regularization print("2 step ok") # define accuracy correct_predictions = tf.equal(tf.argmax(y_hat, 1), tf.argmax(input_y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_predictions, tf.float32)) print("3 step ok") # train operation global_step = tf.Variable(0, trainable=False) train_op = tf.train.AdamOptimizer(LEARNING_RATE).minimize(loss, global_step=global_step) print("4 step ok ") with tf.Session() as sess: tf.global_variables_initializer().run() # 初始化建立的變量 print("5 step OK") # 開始訓練模型 for i in range(TRAIN_STEP): xs, ys = mnist.train.next_batch(BATCH_SIZE) feed_dict = { input_x: xs, input_y: ys, } _, step, train_loss, train_acc = sess.run([train_op, global_step, loss, accuracy], feed_dict=feed_dict) if (i % 100 == 0): print("After %d steps, in train data, loss is %g, accuracy is %g." % (step, train_loss, train_acc)) test_feed = {input_x: mnist.test.images, input_y: mnist.test.labels} test_acc = sess.run(accuracy, feed_dict=test_feed) print("After %d steps, in test data, accuracy is %g." % (TRAIN_STEP, test_acc)) if __name__ == '__main__': mnist = input_data.read_data_sets("data", one_hot=True) print("0 step ok!") train(mnist)
訓練結果以下:
Extracting data\train-images-idx3-ubyte.gz Extracting data\train-labels-idx1-ubyte.gz Extracting data\t10k-images-idx3-ubyte.gz Extracting data\t10k-labels-idx1-ubyte.gz 0 step ok! 1 step ok! 2 step OK! 3 step OK! 4 step OK! 5 step ok! 6 step OK 7 step ok! 8 step OK! 9, step OK! step:1 ,train loss:10.3222, train_acc:0.17 step:101 ,train loss:0.727434, train_acc:0.93 step:201 ,train loss:0.829298, train_acc:0.84 step:301 ,train loss:0.630073, train_acc:0.95 step:401 ,train loss:0.53797, train_acc:0.95 step:501 ,train loss:0.624309, train_acc:0.92 step:601 ,train loss:0.412459, train_acc:1 step:701 ,train loss:0.392073, train_acc:1 ... ... step:2601 ,train loss:0.173203, train_acc:1 step:2701 ,train loss:0.226434, train_acc:0.99 step:2801 ,train loss:0.172851, train_acc:0.99 step:2901 ,train loss:0.227201, train_acc:0.96 After 3000 training steps, in test dataset, loss is 0.165731, acc is 0.989
初始化權重
爲了避免在創建模型的時候反覆作初始化的操做,咱們定義兩個函數用於初始化。
# 定義W初始化函數 def get_weights(shape): form = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(form) # 定義b初始化函數 def get_biases(shape): form = tf.constant(0.1, shape=shape) return tf.Variable(form)
爲了建立此模型,咱們須要建立大量的權重和偏置項。這個模型中的權重在初始化時應該加入少許的噪聲來打破對稱性以及避免0梯度。因爲咱們使用的是ReLU神經元,所以比較好的作法是用一個較小的正數來初始化偏置項,以免神經元節點輸出恆爲0 的問題(dead neurons)。爲了避免在創建模型的時候反覆作初始化操做,咱們定義兩個函數用於初始化。
卷積和池化
TensorFlow在卷積和池化上有很強的靈活性,那麼如何處理邊界?步長應該設置多大?這裏咱們卷積使用的是1步長(stride size),0邊距(padding size)。保證輸出和輸出的大小相同,咱們的池化是用簡單的2*2大小的模板作 max pooling 。
第一層卷積
如今咱們開始實現第一層卷積,它是由一個卷積接一個 max pooling 完成。卷積在每一個5*5的patch中算出 32個特徵。卷積的權重張量形狀是 [5, 5, 1, 32],前兩個維度是patch的大小,接着是輸出通道的數目,最後是輸出的通道數目。爲了使用這一層卷積,咱們將x 變成一個4d向量,其第二,第三對應圖片的寬,高。最後一維表明輸出的通道數目。
最後咱們將x 和權重向量進行卷積,加上偏置項,而後應用ReLU激活函數,代碼以下:
# 第一層:卷積層conv1 ''' input: [-1, 28, 28, 1] filter: [5, 5, 32] output: [-1, 28, 28, 32] ''' with tf.name_scope("conv1"): w = get_weights([FILTER1_SIZE, FILTER1_SIZE, 1, FILTER1_NUM]) b = get_biases([FILTER1_NUM]) conv1_op = tf.nn.conv2d( input=input_x, filter=w, strides=[1, 1, 1, 1], padding="SAME", name='conv1_op' ) conv1 = tf.nn.relu(tf.nn.bias_add(conv1_op, b), name='relu')
第二層池化
前面也說過,咱們的池化用簡單傳統的2*2大小模板作 max pooling .
# 第二層:池化層pooling2 ''' input: [-1, 28, 28, 32] output: [-1. 14, 14, 32] ''' with tf.name_scope('pooling2'): pooling2 = tf.nn.max_pool( value=conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME', name='pooling1' )
第三層卷積
爲了構建一個更深的網絡,咱們會把幾個相似的層堆疊起來,第二層中,每一個5*5的patch 會獲得64個特徵,代碼以下:
# 第三次:卷積層conv3 ''' input" [-1, 14, 14, 32] filter: [5, 5, 64] output: [-1, 14, 14, 64] ''' with tf.name_scope('conv3'): w = get_weights([FILTER3_SIZE, FILTER3_SIZE, FILTER1_NUM, FILTER3_NUM]) b = get_biases([FILTER3_NUM]) conv3_op = tf.nn.conv2d( input=pooling2, filter=w, strides=[1, 1, 1, 1], padding="SAME", name='conv3_op' ) conv3 = tf.nn.relu(tf.nn.bias_add(conv3_op, b), name='relu')
第四層池化
而後進行池化,隨機減小特徵:
# 第四層:池化層pooling4 ''' input: [-1, 14, 14, 64] output: [-1, 7, 7, 64] ''' with tf.name_scope("pooling4"): pooling4 = tf.nn.max_pool( value=conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME", name="pooling4" )
全鏈接層
如今圖片的尺寸減小到7*7,咱們加入一個3136個神經元的全鏈接層,用於處理整個圖片。咱們先把池化層輸出的張量reshape成一些向量,也就是展開它。而後輸入其權重公式,偏置項公式,而後求其L2_Loss函數,最後代碼以下:
# 池化結果展開 ''' input: [-1, 7,7,64] output: [-1, 3136] ''' pooling4_flat = tf.reshape(pooling4, [-1, FLAT_SIZE]) print("5 step ok!") # 第五層:全鏈接層 FC5 ''' input: [-1, 3136] (56*56=3136) output: [-1, 512] ''' with tf.name_scope('fc5'): w = get_weights([FLAT_SIZE, FC5_SIZE]) b = get_biases([FC5_SIZE]) fc5 = tf.nn.relu(tf.nn.xw_plus_b(pooling4_flat, w, b, name='fc5'), name='relu') fc5_drop = tf.nn.dropout(fc5, droput_keep_prob) l2_loss += tf.nn.l2_loss(w) + tf.nn.l2_loss(b) print("6 step OK") # 第六層:全鏈接層(輸出) ''' input: [-1, 512] output: [-1, 10] ''' with tf.name_scope('fc6'): w = get_weights([FC5_SIZE, OUTPUT_SIZE]) b = get_biases([OUTPUT_SIZE]) y_hat = tf.nn.xw_plus_b(fc5_drop, w, b, name='y_hat') l2_loss += tf.nn.l2_loss(w) + tf.nn.l2_loss(b) print("7 step ok!") cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y_hat, labels=input_y) cross_entropy_mean = tf.reduce_mean(cross_entropy) loss = cross_entropy_mean + L2NORM_RATE * l2_loss
下面利用TensorFlow使用MNIST數據集訓練並評估一個用於識別手寫數字的簡易前饋神經網絡(feed-forward neural network)。
其中說明一下下面兩個文件的目的:
full_connected_mnist.py代碼以下:
# _*_coding:utf-8_*_ """Builds the MNIST network. # 爲模型構建實現推理 損失 訓練的模型 Implements the inference/loss/training pattern for model building. # 推理 根據運行網絡的須要 構建模型向前作預測 1. inference() - Builds the model as far as required for running the network forward to make predictions. # 將生成loss所需的層添加到推理模型中 2. loss() - Adds to the inference model the layers required to generate loss. # 訓練 將生成和所需的ops添加到損失模型中並應用梯度 3. training() - Adds to the loss model the Ops required to generate and apply gradients. This file is used by the various "fully_connected_*.py" files and not meant to be run. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import math import tensorflow as tf # The MNIST dataset has 10 classes representing the digits 0 through 9 NUM_CLASSES = 10 # The MNIST images are always 28*28 pixels IMAGES_SIZE = 28 IMAGES_PIXELS = IMAGES_SIZE * IMAGES_SIZE def inference(images, hidden1_units, hidden2_units): """Build the MNIST model up to where it may be used for inference. Args: images: Images placeholder, from inputs(). hidden1_units: Size of the first hidden layer. hidden2_units: Size of the second hidden layer. Returns: softmax_linear: Output tensor with the computed logits. """ # hidden1 with tf.name_scope('hidden1'): weights = tf.Variable( tf.truncated_normal([IMAGES_PIXELS, hidden1_units], stddev=1.0 / math.sqrt(float(IMAGES_PIXELS))), name='weights' ) biases = tf.nn.relu(tf.matmul([hidden1_units]), name='weights') hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases) # hidden2 with tf.name_scope('hidden2'): weights = tf.Variable( tf.truncated_normal([hidden1_units, hidden2_units], stddev=1.0 / math.sqrt(float(hidden1_units))), name='weights' ) biases = tf.Variable(tf.zeros([hidden2_units]), name='biases') hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases) # Linear with tf.name_scope('softmax_linear'): weights = tf.Variable( tf.truncated_normal([hidden2_units, NUM_CLASSES], stddev=1.0 / math.sqrt(float(hidden2_units))), name='weights' ) biases = tf.nn.relu(tf.zeros([NUM_CLASSES]), name='biases') logits = tf.matmul(hidden2, weights) + biases return logits def loss(logits, labels): """Calculates the loss from the logits and the labels. Args: logits: Logits tensor, float - [batch_size, NUM_CLASSES]. labels: Labels tensor, int32 - [batch_size]. Returns: loss: Loss tensor of type float. """ labels = tf.to_int64(labels) return tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits) def training(loss, learning_rate): """Sets up the training Ops. Creates a summarizer to track the loss over time in TensorBoard. Creates an optimizer and applies the gradients to all trainable variables. The Op returned by this function is what must be passed to the `sess.run()` call to cause the model to train. Args: loss: Loss tensor, from loss(). learning_rate: The learning rate to use for gradient descent. Returns: train_op: The Op for training. """ # add a scalar summary for the snapshot loss tf.summary.scalar('loss', loss) # create the gradient descent optimizer with the given learning rate optimizer = tf.train.GradientDescentOptimizer(learning_rate) # create a variable to track the global step global_step = tf.Variable(0, name='global_step', trainable=False) # Use the optimizer to apply the gradients that minimize the loss # (and also increment the global step counter) as a single training step. train_op = optimizer.minimize(loss, global_step=global_step) return train_op def evaluation(logits, labels): """Evaluate the quality of the logits at predicting the label. Args: logits: Logits tensor, float - [batch_size, NUM_CLASSES]. labels: Labels tensor, int32 - [batch_size], with values in the range [0, NUM_CLASSES). Returns: A scalar int32 tensor with the number of examples (out of batch_size) that were predicted correctly. """ # For a classifier model, we can use the in_top_k Op. # It returns a bool tensor with shape [batch_size] that is true for # the examples where the label is in the top k (here k=1) # of all logits for that example. correct = tf.nn.in_top_k(logits, labels, 1) # Return the number of true entries. return tf.reduce_sum(tf.cast(correct, tf.int32))
full_connected_feed.py代碼以下:
#_*_coding:utf-8_*_ '''Trains and Evaluates the MNIST network using a feed dictionary''' from __future__ import absolute_import from __future__ import division from __future__ import print_function # pulint:disable=missing-docstring import argparse import os import sys import time from six.moves import xrange # pylint: disable=redefined-builtin import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data from tensorflow.examples.tutorials.mnist import mnist # Basic model parameters as external flags FLAGS = None def placeholder_inputs(batch__size): ''' 生成一個佔位符變量來表述輸入張量 這些佔位符被模型構建的其他部分用做輸入代碼,並將從下面 .run() 循環中下載數據提供 :param batch__size: :return: 返回一個 圖像的佔位符,一個標籤佔位符 ''' # Note that the shapes of the placeholders match the shapes of the full # image and label tensors, except the first dimension is now batch_size # rather than the full size of the train or test data sets. images_placeholder = tf.placeholder(tf.float32, shape=(batch__size, mnist.IMAGE_PIXELS)) labels_placeholder = tf.placeholder(tf.int32, shape=(batch__size)) return images_placeholder, labels_placeholder def fill_feed_dict(data_set, images_pl, labels_pl): """Fills the feed_dict for training the given step. A feed_dict takes the form of: feed_dict = { <placeholder>: <tensor of values to be passed for placeholder>, .... } Args: data_set: The set of images and labels, from input_data.read_data_sets() images_pl: The images placeholder, from placeholder_inputs(). labels_pl: The labels placeholder, from placeholder_inputs(). Returns: feed_dict: The feed dictionary mapping from placeholders to values. """ # Create the feed_dict for the placeholders filled with the next # `batch size` examples. images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size, FLAGS.fake_data) feed_dict = { images_pl: images_feed, labels_pl: labels_feed, } return feed_dict def do_evaluation(sess, eval_correct, images_placeholder, labels_placeholder, data_set): ''' 對陣個數據進行評估 :param sess: The session in which the model has been trained. :param eval_correct: The Tensor that returns the number of correct predictions. :param images_placeholder: The images placeholder. :param labels_placeholder: The labels placeholder. :param data_set: The set of images and labels to evaluate, from input_data.read_data_sets(). :return: ''' # and run one epoch of eval true_count = 0 # counts the number of correct predictions steps_per_epoch = data_set.num_examples // FLAGS.batch_size num_examples = steps_per_epoch * FLAGS.batch_size for step in range(steps_per_epoch): feed_dict = fill_feed_dict(data_set, images_placeholder, labels_placeholder) true_count += sess.run(eval_correct, feed_dict=feed_dict) precision = float(true_count) / num_examples print('Num examples: %d Num correct: %d Precision @ 1: %0.04f' % (num_examples, true_count, precision)) def run_training(): '''Train MNIST for a number of steps''' # get the sets of images and labels for training validation and test on MNIST data_sets = input_data.read_data_sets(FLAGS.input_data_dir, FLAGS.fake_data) # Tell Tensorflow that the model will be built into the default graph with tf.Graph().as_default(): #Generate placeholders for the images and labels images_placeholder, labels_placeholder = placeholder_inputs(FLAGS.batch_size) # build a graph that computes predictions from the inference model logits = mnist.inference(images_placeholder, FLAGS.hidden1, FLAGS.hidden2) # add to graph the ops for loss calculation loss = mnist.loss(logits, labels_placeholder) # add to the graph the ops that calculate and apply gradients train_op = mnist.training(loss, FLAGS.learning_rate) # add the Op to compare the logits to the labels during evaluation eval_correct = mnist.evaluation(logits, labels_placeholder) # bulid the summary Tensor based on the TF collection of Summaries summary = tf.summary.merge_all() # add the variable initializer Op init = tf.global_variables_initializer() # create a saver fro writing training checkpoint saver = tf.train.Saver() # create a session for running Ops on the Graph sess = tf.compat.v1.Session() # Instantiate a SummaryWriter to output summaries and the graph summary_writer = tf.summary.FileWriter(FLAGS.log_dir, sess.graph) # and then after everything is built # run the Op to initialize the variables sess.run(init) # start the training loop for step in range(FLAGS.max_steps): start_time = time.time() # Fill a feed dictionary with the actual set of images and labels # for this particular training step. feed_dict = fill_feed_dict(data_sets.train, images_placeholder, labels_placeholder) # Run one step of the model. The return values are the activations # from the `train_op` (which is discarded) and the `loss` Op. To # inspect the values of your Ops or variables, you may include them # in the list passed to sess.run() and the value tensors will be # returned in the tuple from the call. _, loss_value = sess.run([train_op, loss], feed_dict=feed_dict) duration = time.time() - start_time # write the summaries and print an overview fairly often if step % 100 == 0: # Print status to stdout. print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration)) # Update the events file. summary_str = sess.run(summary, feed_dict=feed_dict) summary_writer.add_summary(summary_str, step) summary_writer.flush() # Save a checkpoint and evaluate the model periodically. if (step + 1) % 1000 == 0 or (step + 1) == FLAGS.max_steps: checkpoint_file = os.path.join(FLAGS.log_dir, 'model.ckpt') saver.save(sess, checkpoint_file, global_step=step) # Evaluate against the training set. print('Training Data Eval:') do_evaluation(sess, eval_correct, images_placeholder, labels_placeholder, data_sets.train) # Evaluate against the validation set. print('Validation Data Eval:') do_evaluation(sess, eval_correct, images_placeholder, labels_placeholder, data_sets.validation) # Evaluate against the test set. print('Test Data Eval:') do_evaluation(sess, eval_correct, images_placeholder, labels_placeholder, data_sets.test) def main(): if tf.gfile.Exists(FLAGS.log_dir): tf.gfile.DeleteRecursively(FLAGS.log_dir) tf.gfile.MakeDirs(FLAGS.log_dir) run_training() def main(_): if tf.gfile.Exists(FLAGS.log_dir): tf.gfile.DeleteRecursively(FLAGS.log_dir) tf.gfile.MakeDirs(FLAGS.log_dir) run_training() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument( '--learning_rate', type=float, default=0.01, help='Initial learning rate.' ) parser.add_argument( '--max_steps', type=int, default=2000, help='Number of steps to run trainer.' ) parser.add_argument( '--hidden1', type=int, default=128, help='Number of units in hidden layer 1.' ) parser.add_argument( '--hidden2', type=int, default=32, help='Number of units in hidden layer 2.' ) parser.add_argument( '--batch_size', type=int, default=100, help='Batch size. Must divide evenly into the dataset sizes.' ) parser.add_argument( '--input_data_dir', type=str, default=os.path.join(os.getenv('TEST_TMPDIR', ''), 'input_data'), help='Directory to put the input data.' ) parser.add_argument( '--log_dir', type=str, default=os.path.join(os.getenv('TEST_TMPDIR', ''), 'fully_connected_feed'), help='Directory to put the log data.' ) parser.add_argument( '--fake_data', default=False, help='If true, uses fake data for unit testing.', action='store_true' ) FLAGS, unparsed = parser.parse_known_args() tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
注意事項:
此處參數注意修改!!
結果展現:
Extracting data\train-images-idx3-ubyte.gz Extracting data\train-labels-idx1-ubyte.gz Extracting data\t10k-images-idx3-ubyte.gz Extracting data\t10k-labels-idx1-ubyte.gz Step 0: loss = 2.30 (0.114 sec) Step 100: loss = 2.11 (0.002 sec) Step 200: loss = 1.92 (0.002 sec) Step 300: loss = 1.49 (0.002 sec) Step 400: loss = 1.22 (0.002 sec) Step 500: loss = 0.95 (0.003 sec) Step 600: loss = 0.73 (0.003 sec) Step 700: loss = 0.58 (0.004 sec) Step 800: loss = 0.58 (0.002 sec) Step 900: loss = 0.50 (0.002 sec) Training Data Eval: Num examples: 55000 Num correct: 47310 Precision @ 1: 0.8602 Validation Data Eval: Num examples: 5000 Num correct: 4365 Precision @ 1: 0.8730 Test Data Eval: Num examples: 10000 Num correct: 8628 Precision @ 1: 0.8628 Step 1000: loss = 0.51 (0.017 sec) Step 1100: loss = 0.46 (0.104 sec) Step 1200: loss = 0.50 (0.002 sec) Step 1300: loss = 0.40 (0.003 sec) Step 1400: loss = 0.57 (0.003 sec) Step 1500: loss = 0.40 (0.003 sec) Step 1600: loss = 0.42 (0.003 sec) Step 1700: loss = 0.44 (0.003 sec) Step 1800: loss = 0.41 (0.002 sec) Step 1900: loss = 0.37 (0.000 sec) Training Data Eval: Num examples: 55000 Num correct: 49375 Precision @ 1: 0.8977 Validation Data Eval: Num examples: 5000 Num correct: 4529 Precision @ 1: 0.9058 Test Data Eval: Num examples: 10000 Num correct: 8985 Precision @ 1: 0.8985
準確率比起卷積神經網絡仍是差點,可是效果仍是不錯的。這裏咱們主要是學習這個過程,瞭解深度學習訓練模型的過程,因此還好。
下載
在run_training() 方法的一開始, input_data.read_data_sets() 函數會確保咱們的本地訓練文件夾中,是否已經下載了正確的數據,而後將這些數據解壓並返回一個含有DataSet實例的字典。
data_sets = input_data.read_data_sets(FLAGS.train_dir, FLAGS.fake_data)
注意:fake_data標記是用於單元測試的。
數據展現以下:
輸入與佔位符(inputs and placeholders)
placeholder_inputs() 函數將生成兩個 tf.placeholder 操做,定義傳入圖表中的 shape 參數, shape參數中包括 batch_size 值,後續還會將實際的訓練用例傳入圖表。
images_placeholder = tf.placeholder(tf.float32, shape=(batch_size, IMAGE_PIXELS)) labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))
在訓練循環(training loop)的後續步驟中,傳入的整個圖像和標籤數據集會被切片,以符合每個操做所設置的batch_size
值,佔位符操做將會填補以符合這個batch_size
值。而後使用feed_dict
參數,將數據傳入sess.run()
函數。
構建圖表(Build the Graph)
在爲數據建立佔位符以後,就能夠運行full_connected_mnist.py文件,通過三階段的模式函數操做: inference() loss() training() 圖表就構建完成了。
推理(Inference)
inference()函數會盡量的構建圖表,作到返回包含了預測結果(output prediction)的Tensor。
它接受圖像佔位符爲輸入,在此基礎上藉助ReLU(Rectified Linear Units)激活函數,構建一對徹底鏈接層(layers),以及一個有着十個節點(node),指明瞭輸出 logits 模型的線性層。
每一層都建立於一個惟一的 tf.name_scope之下,建立於該做用域之下的全部元素都將帶有其前綴。
with tf.name_scope('hidden1') as scope:
在定義的做用域中,每一層所使用的權重和誤差都在 tf.Variable實例中生成,而且包含了各自指望的shape。
weights = tf.Variable( tf.truncated_normal([IMAGE_PIXELS, hidden1_units], stddev=1.0 / math.sqrt(float(IMAGE_PIXELS))), name='weights') biases = tf.Variable(tf.zeros([hidden1_units]), name='biases')
例如,當這些層是在hidden1 做用域下生成時,賦予權重變量的獨特名稱將會是「hidden1/weights」。
每一個變量在構建時,都會得到初始化操做(initializer ops)。
在這種最多見的狀況下,經過tf.truncated_normal
函數初始化權重變量,給賦予的shape則是一個二維tensor,其中第一個維度表明該層中權重變量所鏈接(connect from)的單元數量,第二個維度表明該層中權重變量所鏈接到的(connect to)單元數量。對於名叫hidden1
的第一層,相應的維度則是[IMAGE_PIXELS, hidden1_units]
,由於權重變量將圖像輸入鏈接到了hidden1
層。tf.truncated_normal
初始函數將根據所獲得的均值和標準差,生成一個隨機分佈。
而後,經過tf.zeros
函數初始化誤差變量(biases),確保全部誤差的起始值都是0,而它們的shape則是其在該層中所接到的(connect to)單元數量。
圖表的三個主要操做,分別是兩個tf.nn.relu
操做,它們中嵌入了隱藏層所需的tf.matmul
;以及logits模型所需的另一個tf.matmul
。三者依次生成,各自的tf.Variable
實例則與輸入佔位符或下一層的輸出tensor所鏈接。
hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases) hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases) logits = tf.matmul(hidden2, weights) + biases
最後,程序會返回包含了輸出結果的logits Tensor。
損失(loss)
loss()函數經過添加所須要的損失操做,進一步構建圖表。
首先,labels_placeholder中的值,將被編碼爲一個含有1-hot values 的Tensor。例如,若是類標識符爲「3」,那麼該值就會被轉換爲:[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
batch_size = tf.size(labels) labels = tf.expand_dims(labels, 1) indices = tf.expand_dims(tf.range(0, batch_size, 1), 1) concated = tf.concat(1, [indices, labels]) onehot_labels = tf.sparse_to_dense( concated, tf.pack([batch_size, NUM_CLASSES]), 1.0, 0.0)
以後,又添加一個 tf.nn.softmax_cross_entropy_with_logits操做,用來比較 inference() 函數與 1-hot 標籤所輸出的 logits Tensor。
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits, onehot_labels, name='xentropy')
而後,使用tf.reduce_mean() 函數,計算 batch 維度(第一維度)下交叉熵(cross entropy)的平均值,將該值做爲總損失。
loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')
最後程序會返回包含了損失值的Tensor。
訓練training()
training() 函數添加了經過梯度降低(gradient descent)將損失最小化所須要的操做。
首先,該函數從loss() 函數中得到損失Tensor,將其交給 tf.scalar_summary,後者在於SummaryWriter配合使用時,能夠向事件文件(events file)中生成彙總值(summary values)。每次寫入彙總值時,它都會釋放損失Tensor的當前值(snapshot value)。
tf.scalar_summary(loss.op.name, loss)
接下來,咱們實例化一個tf.train.GradientDescentOptimizer
,負責按照所要求的學習效率(learning rate)應用梯度降低法(gradients)。
optimizer = tf.train.GradientDescentOptimizer(FLAGS.learning_rate)
以後,咱們生成一個變量用於保存全局訓練步驟(global training step)的數值,並使用minimize()
函數更新系統中的三角權重(triangle weights)、增長全局步驟的操做。根據慣例,這個操做被稱爲 train_op
,是TensorFlow會話(session)誘發一個完整訓練步驟所必須運行的操做(見下文)。
global_step = tf.Variable(0, name='global_step', trainable=False) train_op = optimizer.minimize(loss, global_step=global_step)
最後,程序返回包含了訓練操做(training op)輸出結果的Tensor。
圖表
在 run_training() 這個函數的一開始,是一個python語言的with命令,這個命令代表全部已經構建的操做都要與默認的 tf.Graph 全局實例關聯起來。
with tf.Graph().as_default():
tf.Graph
實例是一系列能夠做爲總體執行的操做。TensorFlow的大部分場景只須要依賴默認圖表一個實例便可。
會話
完成所有的構建準備,生成所有所需的操做以後,咱們就能夠建立一個tf.Session,用於運行圖表。
sess = tf.Session()
固然,咱們也能夠利用with 代碼塊生成Session,限制做用域:
with tf.Session() as sess:
Session
函數中沒有傳入參數,代表該代碼將會依附於(若是尚未建立會話,則會建立新的會話)默認的本地會話。
生成會話以後,全部tf.Variable
實例都會當即經過調用各自初始化操做中的sess.run()
函數進行初始化。
init = tf.initialize_all_variables() sess.run(init)
sess.run()
方法將會運行圖表中與做爲參數傳入的操做相對應的完整子集。在初次調用時,init
操做只包含了變量初始化程序tf.group
。圖表的其餘部分不會在這裏,而是在下面的訓練循環運行。
訓練循環
完成會話中變量的初始化以後,就能夠開始訓練了。
訓練的每一步都是經過用戶代碼控制,而能實現有效訓練的最簡單循環就是:
for step in range(max_steps): sess.run(train_op)
可是咱們這裏須要更復雜一點,由於咱們必須把輸入的數據根據每一步的狀況進行切分,以匹配以前生成的佔位符。
執行每一步時,咱們的代碼會生成一個反饋字典(feed dictionary),其中包含對應步驟中訓練所要使用的例子,這些例子的哈希鍵就是其所表明的佔位符操做。
fill_feed_dict
函數會查詢給定的DataSet
,索要下一批次batch_size
的圖像和標籤,與佔位符相匹配的Tensor則會包含下一批次的圖像和標籤。
images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size)
而後,以佔位符爲哈希鍵,建立一個python字典對象,鍵值則是其表明的反饋Tensor。
feed_dict = { images_placeholder: images_feed, labels_placeholder: labels_feed, }
這個字典隨後做爲feed_dict
參數,傳入sess.run()
函數中,爲這一步的訓練提供輸入樣例。
在運行sess.run
函數時,要在代碼中明確其須要獲取的兩個值:[train_op, loss]
。
for step in range(FLAGS.max_steps): feed_dict = fill_feed_dict(data_sets.train, images_placeholder, labels_placeholder) _, loss_value = sess.run([train_op, loss], feed_dict=feed_dict)
由於要獲取這兩個值,sess.run()
會返回一個有兩個元素的元組。其中每個Tensor
對象,對應了返回的元組中的numpy數組,而這些數組中包含了當前這步訓練中對應Tensor的值。因爲train_op
並不會產生輸出,其在返回的元祖中的對應元素就是None
,因此會被拋棄。可是,若是模型在訓練中出現誤差,loss
Tensor的值可能會變成NaN,因此咱們要獲取它的值,並記錄下來。
假設訓練一切正常,沒有出現NaN,訓練循環會每隔100個訓練步驟,就打印一行簡單的狀態文本,告知用戶當前的訓練狀態。
if step % 100 == 0: print 'Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration)
爲了釋放TensorBoard所使用的事件文件(events file),全部的即時數據(在這裏只有一個)都要在圖表構建階段合併至一個操做(op)中。
summary_op = tf.merge_all_summaries()
在建立好會話(session)以後,能夠實例化一個tf.train.SummaryWriter
,用於寫入包含了圖表自己和即時數據具體值的事件文件。
summary_writer = tf.train.SummaryWriter(FLAGS.train_dir, graph_def=sess.graph_def)
最後,每次運行summary_op
時,都會往事件文件中寫入最新的即時數據,函數的輸出會傳入事件文件讀寫器(writer)的add_summary()
函數。
summary_str = sess.run(summary_op, feed_dict=feed_dict) summary_writer.add_summary(summary_str, step)
sess = tf.compat.v1.Session() AttributeError: module 'tensorflow.python.util.compat' has no attribute 'v1'
解決方法:
sess = tf.Session()
參考文獻:https://blog.csdn.net/GeForce_GTX1080Ti/article/details/80119194
TensorFlow官網(http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html)
http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_tf.html
此處是作以筆記,記錄學習過程,如有侵權,請聯繫我