1. 程式人生 > >Tensorflow | MNIST手寫字識別

Tensorflow | MNIST手寫字識別

這次對最近學習tensorflow的總結,以理解MNIST手寫字識別案例為例來說明

0、資料解釋

資料為圖片,每個圖片是28畫素*28畫素,帶有標籤,類似於X和Y,X為28畫素*28畫素的資料,Y為該圖片的真實數字,即標籤。

1、資料的處理
以一個圖片為例
示例:1

先轉為上圖右方的矩陣,然後將矩陣攤平為1*784的向量,28*28 = 784,這裡的X有784個特徵。

標籤資料為0-9這10個數,為了方便處理,也將資料向量化,例如,3處理為[0,0,0,1,0,0,0,0,0,0],5處理為[0,0,0,0,1,0,0,0,0,0]

2、資料的讀入

用程式碼來下載資料並讀取

#載入tensorflow包 
import tensorflow as tf #載入讀取函式 from tensorflow.examples.tutorials.mnist import input_data #讀資料,one_hot表示將矩陣處理為行向量,即28*28 => 1*784 mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Extracting MNIST_data/train-images-idx3-ubyte.gz
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz

表示下載成功了。

訓練資料集有55,000 條,即X為55,000 * 784的矩陣,那麼Y為55,000 * 10的矩陣

3、模型
這裡採用softmax 迴歸的方法,下面介紹softmax模型:

我們知道MNIST的每一張圖片都表示一個數字,從0到9。我們希望得到給定圖片代表每個數字的概率。比如說,我們的模型可能推測一張包含9的圖片代表數字9的概率是80%但是判斷它是8的概率是5%(因為8和9都有上半部分的小圓),然後給予它代表其他數字的概率更小的值。

這是一個使用softmax迴歸(softmax regression)模型的經典案例。softmax模型可以用來給不同的物件分配概率。即使在之後,我們訓練更加精細的模型時,最後一步也需要用softmax來分配概率。

softmax迴歸(softmax regression)分兩步:第一步

為了得到一張給定圖片屬於某個特定數字類的證據(evidence),我們對圖片畫素值進行加權求和。如果這個畫素具有很強的證據說明這張圖片不屬於該類,那麼相應的權值為負數,相反如果這個畫素擁有有利的證據支援這張圖片屬於這個類,那麼權值是正數。

我們也需要加入一個額外的偏置量(bias),因為輸入往往會帶有一些無關的干擾量。因此對於給定的輸入圖片 x 它代表的是數字 i 的證據可以表示為

evidencei=jWi,jxj+bi

其中 Wi 代表權重, bi代表數字 i類的偏置量,j代表給定圖片 x 的畫素索引用於畫素求和。然後用softmax函式可以把這些證據轉換成概率 y:

y=softmax(evidence)

這裡的softmax可以看成是一個激勵(activation)函式或者連結(link)函式,把我們定義的線性函式的輸出轉換成我們想要的格式,也就是關於10個數字類的概率分佈。因此,給定一張圖片,它對於每一個數字的吻合度可以被softmax函式轉換成為一個概率值。softmax函式可以定義為:

softmax(x)=normalize(exp(x))

即,

softmax(x)=exp(xi)jexp(xj)

這樣得到的結果便是概率,從而獲取了是0-9這10個數的概率,然後比較概率的大小,概率最大的即為模型得到的結果類別。

圖示softmax模型:
softmax迴歸圖

轉為矩陣的形式:
softmax:矩陣形式

進而,有:
softmax:等式形式

簡化為:

y=softmax(Wx+b)

4、程式碼實現

  • 定義變數

這裡需要預定義

#定義X,浮點型,784列,None表示存在但為空值
#placeholder是佔位符,
x = tf.placeholder("float", [None, 784])

預定義引數W和b

#定義W,W為矩陣,784*10的矩陣
#Variable 表示可修改的張量
W = tf.Variable(tf.zeros([784,10]))
#預定義b,b矩陣,1*10的矩陣
b = tf.Variable(tf.zeros([10]))

注意,W的維度是[784,10],因為我們想要用784維的圖片向量乘以它以得到一個10維的證據值向量,每一位對應不同數字類。b的形狀是[10],所以我們可以直接把它加到輸出上面。

模型的實現:

y = tf.nn.softmax(tf.matmul(x,W) + b)

首先,我們用tf.matmul(​​X,W)表示x乘以W,對應之前等式裡面的Wx,這裡x是一個2維張量擁有多個輸入。然後再加上b,把和輸入到tf.nn.softmax函式裡面。

5、訓練模型

為了訓練我們的模型,我們首先需要定義一個指標來評估這個模型是好的。其實,在機器學習,我們通常定義指標來表示一個模型是壞的,這個指標稱為成本(cost)或損失(loss),然後儘量最小化這個指標。但是,這兩種方式是相同的。

一個非常常見的,非常漂亮的成本函式是“交叉熵”(cross-entropy)。交叉熵產生於資訊理論裡面的資訊壓縮編碼技術,但是它後來演變成為從博弈論到機器學習等其他領域裡的重要技術手段。它的定義如下:

Hy(y)=iyilog(yi)

y 是我們預測的概率分佈, y是實際的分佈(我們輸入的one-hot vector)。比較粗糙的理解是,交叉熵是用來衡量我們的預測用於描述真相的低效性。

為了計算交叉熵,我們首先需要新增一個新的佔位符用於輸入正確值,定義為y_ :

#為10列的矩陣
y_ = tf.placeholder("float", [None,10])

然後用ylog(y) 來計算交叉熵:

#交叉熵
cross_entropy = -tf.reduce_sum(y_*tf.log(y))

用梯度下降法來訓練模型:

TensorFlow用梯度下降演算法(gradient descent algorithm)以0.01的學習速率最小化交叉熵。梯度下降演算法(gradient descent algorithm)是一個簡單的學習過程,TensorFlow只需將每個變數一點點地往使成本不斷降低的方向移動。

#基於交叉熵值最小
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

需要新增一個操作來初始化我們建立的變數:

#初始化
init = tf.initialize_all_variables()

在一個Session裡面啟動我們的模型,並且初始化變數:

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

開始訓練模型,這裡我們讓模型迴圈訓練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})

該迴圈的每個步驟中,我們都會隨機抓取訓練資料中的100個批處理資料點,然後我們用這些資料點作為引數替換之前的佔位符來執行train_step

6、評估模型
tf.argmax(y,1),返回的是模型對於任一輸入x預測的標籤值為1的索引值。tf.argmax 是一個非常有用的函式,它能給出某個tensor物件在某一維上的其資料最大值所在的索引值。
tf.equal : 檢測我們的預測是否真實標籤匹配(索引位置一樣表示匹配)

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))

精度:

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

最後,我們計算所學習到的模型在測試資料集上面的正確率。

print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})

======================
上面便是完整的思路之一,下面給出完整的程式碼:

#載入包
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 = tf.placeholder("float", [None, 784])
#預定義W
W = tf.Variable(tf.zeros([784,10]))
#預定義b
b = tf.Variable(tf.zeros([10]))
#模型
y = tf.nn.softmax(tf.matmul(x,W) + b)
#預定義真實值
y_ = tf.placeholder("float", [None,10])
#交叉熵損失函式
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
#基於梯度的優化
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
#初始化模型的所有變數
init = tf.global_variables_initializer()
#啟動一個模型,並初始化
sess = tf.Session()
sess.run(init)
#開始訓練模型,訓練1000次,每次抽取100個批資料
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})
#真實與預測
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
#準確率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
#輸出結果
print (sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

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
0.9123

準確率為0.9123