在前一個博客中,咱們已經對MNIST 數據集和TensorFlow 中MNIST 數據集的載入有了基本的瞭解。本節將真正以TensorFlow 爲工具,寫一個手寫體數字識別程序,使用的機器學習方法是Softmax 迴歸。python
Softmax 迴歸是一個線性的多類分類模型,實際上它是直接從Logistic迴歸模型轉化而來的。區別在於Logistic 迴歸模型爲兩類分類模型,而Softmax 模型爲多類分類模型。git
在手寫體識別問題中, 一共有10 個類別( 0~9 ),咱們但願對輸入的圖像計算它屬於每一個類別的機率。如屬於9 的機率爲70% ,屬於1 的機率爲10%等。最後模型預測的結果就是機率最大的那個類別。github
先來了解什麼是Softmax 函數。Softmax 函數的主要功能是將各個類別的「打分」轉化成合理的機率值。例如,一個樣本可能屬於三個類別:第一個類別的打分爲a,第二個類別的打分爲b,第三個類別的打分爲c。打分越高表明屬於這個類別的機率越高,可是打分自己不表明機率,由於打分的值能夠是負數,也能夠很大,但機率要求值必須在0~ 1 ,而且三類的機率加起來應該等於1 。那麼,如何將(a, b, c)轉換成合理的機率值呢?方法就是使用Softmax 函數。例如,對(a, b, c)使用Softmax 函數後,相應的值會變成數組
也就是說,第一類的機率能夠用第一個值表示,第二類的機率能夠用第二個值表示,第三類的機率能夠用第三個值表示。顯然,這三個數值都在0~1之間,而且加起來正好等於1,是合理的機率表示。網絡
假設x 是單個樣本的特徵, W、b 是Softmax 模型的參數。在MNIST 數據集中, x 就表明輸入圖片,它是一個784 維的向量,而W 是一個矩陣, 它的形狀爲(784, 10),b 是一個10 維的向量, 10 表明的是類別數。Softmax 模型的第一步是經過下面的公式計算各個類別的Logit:Logit = WTx + b。機器學習
Logit 一樣是一個10 維的向量,它實際上能夠當作樣本對應於各個類別的「打分」 。接下來使用Softmax 函數將包轉換成各個類別的機率值:y = Softmax(Logit)函數
Softmax 模型輸出的y 表明各個類別的機率,還能夠直接用下面的式子來表示整個Softmax 模型:y = Softmax(Logit = WTx + b)工具
本節對應的程序爲softmax_regression.py ,在該程序中,使用TensorFlow定義了一個Softmax 模型,實現了MNIST 數據集的分類。首先導入TensorFlow 模塊:學習
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)
下面的步驟是很是關鍵的幾步,先來看代碼:測試
# 建立x,x是一個佔位符(placeholder),表明待識別的圖片 x = tf.placeholder(tf.float32,[None,784]) # w是Softmax模型的參數,將一個784維的輸入轉換爲10維的輸出 # 在TensorFlow中,模型的參數用tf.Variable表示 w = tf.Variable(tf.zeros([784,10])) # b是又一個Softmax模型的參數,通常叫作「偏置頂」(bias) b = tf.Variable(tf.zeros([10])) # y 表示模型的輸出 y = tf.nn.softmax(tf.matmul(x,w)+b) # y_是實際的圖像標籤,一樣以佔位符表示 y_ = tf.placeholder(tf.float32,[None,10])
這裏定義了一些佔位符和變量(Variable )。在TensorFlow 中,不管是佔位符仍是變量, 它們實際上都是「 Tensor」。從TensorFlow 的名字中,就能夠看出Tensor 在整個系統中處於核心地位。TensorFlow 中的Tensor 並非具體的數值, 它只是一些咱們「但願」 TensorFlow 系統計算的「節點’。
這裏的佔位符和變量是不一樣類型的Tensor 。先來說解佔位符。佔位符不依賴於其餘的Tensor ,它的值由用戶自行傳遞給TensorFlow ,一般用來存儲樣本數據和標籤。如在這裏定義了x = tf.placeholder(tf. float32, [None, 784]),它是用來存儲訓練圖片數據的佔位符。它的形狀爲[None, 784], None 表示這一維的大小能夠是任意的,也就是說能夠傳遞任意張訓練圖片給這個佔位符,每張圖片用一個784 維的向量表示。一樣的, y_= tf.placeholder(tf.float32,[None, 10])也是個佔位符,它存儲訓練圖片的實際標籤。
再來看什麼是變量。變量是指在計算過程當中能夠改變的值,每次計算後變量的值會被保存下來,一般用變量來存儲模型的參數。如這裏建立了兩個變量: W = tf.Variable(tf.zeros([784, 10 ])) 、b = tf.Variable(tf. zeros([10])) 。它們都是Softmax 模型的參數。建立變量時一般須要指定某些初始值。這裏W的初始值是一個784 × 10 的全零矩陣, b的初始值是一個10 維的0向量。
除了變量和佔位符以外,還建立了一個y = tf.nn.softmax(tf.matmul(x, W)+ b) 。這個y 就是一個依賴x、 W 、b 的Tensor 。若是要求TensorFlow 計算y的值,那麼系統首先會獲取x、 W、b 的值,再去計算y 的值。
y 實際上定義了一個Softmax 迴歸模型,在此能夠嘗試寫出y 的形狀。假設輸入x 的形狀爲(N, 784),其中N 表示輸入的訓練圖像的數目。W 的形狀爲(784,10), b 的形狀爲(10,) 。那麼, Wx + b 的形狀是(N, 10)。Softmax函數不改變結果的形狀,因此獲得y 的形狀爲(N, 10) 。也就是說, y 的每一行是一個10 維的向量,表示模型預測的樣本對應到各個類別的機率。
模型的輸出是y ,而實際的標籤爲y_ , 它們應當越類似越好。在Softmax迴歸模型中,一般使用「交叉熵」損失來衡量這種類似性。損失越小,模型的輸出就和實際標籤越接近,模型的預測也就越準確。
在TensorFlow 中,這樣定義交叉熵損失:
# 至此,咱們獲得了兩個重要的Tensor:y和y_。 # y是模型的輸出,y_是實際的圖像標籤,不要忘了y_是獨熱表示的 # 下面咱們就會根據y和y_構造損失 # 根據y和y_構造交叉熵損失 cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y)))
構造完損失以後,下面一步是如何優化損失,讓損失減少。這裏使用梯度降低法優化損失,定義爲:
# 有了損失,咱們就能夠用隨機梯度降低針對模型的參數(w和b)進行優化 train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
TensorFlow 默認會對全部變量計算梯度。在這裏只定義了兩個變量W和b ,所以程序將使用梯度降低法對W 、b 計算梯度並從新它們的值。tf.train.GradientDescentOptimizer(0.01)中的0.01 是梯度降低優化器使用的學習率( Learning Rate )。
# 建立一個Session。只有在Session中才能運行優化步驟train_step sess = tf.InteractiveSession() # 運行以前必需要初始化全部變量,分配內存 tf.global_variables_initializer().run() print("start training...")
會話(Session)是Tensor Flow 的又一個核心概念。前面提到Tensor 是「但願」Tensor Flow 進行計算的結點。而會話就能夠當作對這些結點進行計算的上下文。以前還提到過,變量是在計算過程當中能夠改變值的Tensor ,同時變量的值會被保存下來。事實上,變量的值就是被保存在會話中的。在對變量進行操做前必須對變量進行初始化,其實是在會話中保存變量的初始值。初始化所高變量的語句是tf.global_variables initializer().run()。
有了會話,就能夠對變量W 、b 進行優化了,優化的程序以下:
# 進行1000步梯度降低 for _ in range(1000): # 在mnist.train中取100個訓練數據 # batch_xs是形狀爲(100, 784)的圖像數據,batch_ys是形如(100, 10)的實際標籤 # batch_xs, batch_ys對應着兩個佔位符x和y_ batch_xs,batch_ys = mnist.train.next_batch(100) # 在Session中運行train_step,運行時要傳入佔位符的值 sess.run(train_step,feed_dict={x:batch_xs,y_:batch_ys})
每次不使用所有訓練數據,而是每次提取100 個數據進行訓練,共訓練1000 次。batch_xs, batch _ys 分別是100 個訓練圖像及其對應的標籤。在訓練時,須要把它們放入對應的佔位符x, y_中,對應的語句是feed_dict={x:batch_ xs, y_:batch_ys} 。
在會話中,不須要系統計算佔位符的值,而是直接把佔位符的值傳遞給會話。與變量不一樣的是,佔位符的值不會被保存,每次能夠給佔位符傳遞不一樣的值。
運行完梯度降低後,能夠檢測模型訓練的結果,對應的代碼以下:
# 正確的預測結果 correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_,1)) # 計算預測準確率,它們都是Tensor accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32)) # 在Session中運行Tensor能夠獲得Tensor的值 # 這裏是獲取最終模型的準確率 print(sess.run(accuracy,feed_dict={x:mnist.test.images,y_:mnist.test.labels})) # 0.9149
模型預測y 的形狀是(N,10), 而實際標籤y_的形狀是(N, 10),其中N 爲輸入模型的樣本個數。tf. argmax(y, 1) 、tf. argmax(y_, 1)的功能是取出數組中最大值的下標,能夠用來將獨熱表示以及模型輸出轉煥爲數字標籤。假設傳入四個樣本,它們的獨熱表示y_爲(須要經過sess.run(y_)才能獲取此Tensor的值,下同):
[[1,0,0,0,0,0,0,0,0,0], [0,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0]]
tf.argmax(y_,1)就是:
[0,2,9,0]
也就是說,取出每一行最大值對應的下標位置, 它們是輸入樣本的實際標籤。假設此時模型的預測輸出y 爲:
[[0.91, 0.01, 0.01, 0.01, 0.01 , 0.01, 0.01, 0.01, 0.01, 0.01], [0.91, 0.01, 0.01, 0.01, 0.01 , 0.01, 0.01, 0.01, 0.01, 0.01], [0.91, 0.01, 0.01, 0.01, 0.01 , 0.01, 0.01, 0.01, 0.01, 0.01], [0.91, 0.01, 0.01, 0.01, 0.01 , 0.01, 0.01, 0.01, 0.01, 0.01]]
tf.argmax(y_,1)就是:
[0,0,0,0]
獲得了預測的標籤和實際標籤,接下來經過tf.equal 函數來比較它們是否相等,並將結果保存到correct_prediction 中。在上述例子中,correct_prediction 就是:
[True,False,False,True]
即第一個樣本和最後一個樣本預測是正確的,另外兩個樣本預測錯誤。能夠用tf.cast(correct_prediction, tf. float32)將比較值轉換成float32 型的變量,此時True 會被轉換成1, False 會被轉接成0 。在上述例子中,tf.cast(correct_prediction, tf.float32)的結果爲:
[1., 0., 0., 1.]
最後,用tf.reduce_mean 能夠計算數組中的全部元素的平均值,至關於獲得了模型的預測準確率,如[1., 0., 0., 1.]的平均值爲0.5 ,即50% 的分類準確率。
在程序softmax_regression. py 中,傳入佔位符的值是feed_dict={x:moist.test.images, y_: mnist.test.labels}。也就是說,使用全體測試樣本進行測試。測試圖片一共高10000 張,運行的結果爲0.9149 ,即91.49% 的準確率。由於Softmax 迴歸是一個比較簡單的模型,這裏預測的準確率並不高,假如使用卷積神經網絡的話會將預測的準確率提升到99% 。
這篇博文主要來自《21個項目玩轉深度學習》這本書裏面的第一章,內容有刪減,還有本書的一些代碼的實驗結果。隨書附贈的代碼庫連接爲:https://github.com/hzy46/Deep-Learning-21-Examples