邏輯迴歸與MNIST手寫識別代碼詳解

文章來源:https://cloud.tencent.com/developer/article/1004867?utm_source=qq&utm_medium=social&utm_oi=55659114528768

線性迴歸通常用於對於連續值預測,比如根據房價走勢,邏輯迴歸就是離散值的預測,比如醫學圖像的是否患病,手寫數字0-9等,是個多分類問題。

MNIST手寫識別來講述下如何實現邏輯迴歸,MNIST手寫識別的例子也算是機器學習祖傳例子了,大家基本上都是拿這個例子入門的。

當然照慣例先上代碼

#coding=utf-8
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

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)

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(64)
  sess.run(train_step, feed_dict={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(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

1.

from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

這兩行是導入MNIST的數據,如果本地沒有數據,會自動從網絡上下載。因爲有監督的學習任務,所以還有對應的標籤(也就是圖像對應的真實數字),這部分位於(mnist.train.labels),標籤也是以one-hot(one-hot就是有一個長度爲N的數組,只有一位是1表示是某一個分類,其他位都是0)的方式表示,即這個數組共有10位,第2位1就是證明這個圖像是數字1的圖像。

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])

這裏定義

x = tf.placeholder(tf.float32, [None, 784])

784是因爲MNIST的輸入圖像是2828的,所以轉換成列是784,前面的None代表任意多個輸入。 意思就是我們可以輸入一個矩陣N784,代表N張MNIST圖像數據。 定義

W = tf.Variable(tf.zeros([784, 10]))

這裏有個小技巧,雖然模型是y = W x + b 但實際上我們寫代碼的是都會寫成y = x W + b; 這個地方我覺得是兩個原因

1.我們輸入input_size的數據,輸出一個output_size的預測結果,如果是x W的,我們可以讓 x = [N, input_size];W = [input_size, output_size] 這樣xXW 得到就是[N,output_size]矩陣,每一橫行都代表一個輸入數據的預測結果,比較直觀。如果我們使用W x的形式的話,定義的矩陣就沒有這麼直觀。

2.我還搜索了一下,發現有些資料說x * W的形式計算導數更加容易。所以我們這裏定義的W便是[input_size, output_size],因爲MNIST輸入的圖像是28*28所以input_size=784,我們要分類成0-9這十個數字,所以outpu_size=10,因此b便是[10]

b = tf.Variable(tf.zeros([10]))

因爲輸出的size是10,所以這裏定義一個長度爲10的數組。

y = tf.nn.softmax(tf.matmul(x, W) + b)
y_ = tf.placeholder(tf.float32, [None, 10])

y是我們預測的輸出,y_是真實的結果。 我們對y進行一下講解,tf.matmul(x, W) + b是應用y = x * W + b模型,前面我們說過我們每次預測是很難100%確定我們預測的東西是什麼,所以我們會輸出一個概率數組,每一項標識是某一分類的概率。那怎麼才能得到這個概率數組呢?這裏我們使用softmax函數,那什麼是softmax函數呢?我們簡單介紹下,讓大家有個簡單的認識~ 首先我們先引用下維基百科上的定義

簡單的說softmax把一個k維的向量(a1,a2,a3,a4….aK)映射成(b1,b2,b3,b4….)其中bi是一個0-1的常數,然後可以根據bi的大小來進行多分類的任務,如取權重最大的一維。也有一個激活函數叫sigmoid通常也用於二分類任務。 這裏有人可能要提問了,爲什麼不直接用y = x * W + b的輸出來判斷,而是再套一層softmax?

這裏我覺有兩個原因: 1.我們在訓練的時候往往是通過改變W和b的值來擬合的,如果我們直接用y = x * W + b的輸出結果,可能W和b值的一點小變化就會導致輸出值產生比較大的波動,這樣損失函數的波動也會比較大,這對我們進行預測是不利的,所以我們套一層softmax這樣可以確保W和b的小幅改動時輸出結果也是小幅改動,這樣更利於訓練。 2.通常直接的輸出結果是不太直觀的,比如輸出[3.3, 1, 3 , 5]我們要進行一些轉換,可以更好的將輸出結果和輸入結果進行結合,比如我們可以轉換成[0, 0, 0, 1],表示我們預測的是第四個分類,但這個轉換不太好不是1就是0,很多波動都沒表現出來,同樣不利於訓練,所以我們還是採用softmax,這樣輸出結果就不會都爲0,利於訓練。當然跟我們採用的損失函數是交叉熵也有一定關係,下面還有介紹。

假如我們預測的[1]這個圖像 它的 one-hot 向量是 [0,1,0,0,0,0,0,0,0,0],然後我們得到的y_是 [1.3, 33, 2, 1.2, 3.2, 0.5, 3, 9.2, 1],繪製如下

然後我們使用softmax公式進行概率轉換

上面的公式還是比較簡單的,如 a = [a1, a2, a3] 則

softmax([1.3, 33, 2, 1.2, 3.2, 0.5, 3, 9.2, 1])如下圖所示

softmax(y)圖形在形狀上與 prediction (y) 相似,且所有項相加和爲1,繪製如下

那怎麼判斷softmax後的結果和真實結果的相近程度呢?

也許有人想說想使用歐幾里德距離,餘弦距離等等前面這些也是可以的,但這裏最適合是交叉熵(cross-entropy)。相關數學證明我們這裏就不詳細說明了。 交叉熵的公式

其中p(x)是真實的值,q(x)是我們預測的值 套用我們的y_和softmax(y)得

爲了便於理解我們對這個公式分爲3部分

藍:實際圖像類(y’)對應的 one-hot 圖

紅:由預測向量元素(y)經過softmax(y),-log(softmax(y))一系列變化而來:

綠:每一圖片類別 i,其中,i = 0, 1, 2, …, 9, 紅藍部分相乘的結果

於是有了下面這行代碼

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1])) reduction_indices的默認值時None,即把input_tensor降到0維,也就是一個數。 對於2維input_tensor,reduction_indices=0時,按列;reduction_indices=1時,按行。因爲我們每行都是一組輸入,所以這裏按行。

3.

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) 這裏我們仍舊採用梯度下降的方法,學習速率0.5,目的是減小損失函數cross_entropy。

4.

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(64)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

這裏開始輸入數據進行訓練,分爲1000個迭代,每次迭代輸入64組數據,是用的小批梯度下降 (Mini-Batch Gradient Descent),每次迭代TF都會自動學習W和b的值。

5.

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1)) correct_prediction便是我們預測的正確與否的數組。tf.argmax 是一個非常有用的函數,它能給出某個tensor對象在某一維上的其數據最大值所在的索引值。後面的1也是代表按行。 我們實例運行輸出一下y看看值 [ 5.85600610e-05 3.37338224e-09 9.09965966e-05 1.22266507e-03 1.30357535e-06 1.67362687e-05 7.03774949e-09 9.98453379e-01 1.52235698e-05 1.41092940e-04] 我們對其tf.argmax便可以得到7,因爲下標爲7的位置概率最大(從0開始)。這樣每一行數據都會得到一個數表明該行數據預測的值是多少。

tf.equal會將相應位置的值進行比較相同True,不同得到False。 經過上面的運算correct_prediction便得到了一個對所有數據預測正確與否的數組。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.cast(correct_prediction, tf.float32)是將我們前面得到的[True,False,True…]數組轉換爲[1.0,0,1.0….]

tf.reduce_mean對轉換得到的數組求平均數,這個值便是預測正確率。

文章來源:https://cloud.tencent.com/developer/article/1004867?utm_source=qq&utm_medium=social&utm_oi=55659114528768

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章