面向機(jī)器學(xué)習(xí)初學(xué)者的 MNIST 教程

我是一個很懶的人,我想試試

希望我能堅持到最后,把tensorflow的官方教程全部翻譯出來

提高自己,也幫助他人

我的博客:終身學(xué)習(xí)者

MNIST For ML Beginners

本教程面向剛開始學(xué)習(xí)機(jī)器學(xué)習(xí)和 TensorFlow 的讀者。如果你已經(jīng)知道 MNIST 是什么,以及softmax(多項(xiàng)邏輯)回歸是什么,那么你可能更適合這個快速上手教程。請在開始任何教程之前安裝好 TensorFlow。

當(dāng)一個人開始學(xué)習(xí)編程的時候,一般來說第一件事就是打印"Hello World"。正如編程有 Hello World,機(jī)器學(xué)習(xí)有 MNIST 。

MNIST 是一個簡單的機(jī)器視覺數(shù)據(jù)庫。它是由類似下列的各種手寫數(shù)字圖像組成:

img

每張圖像同時也包含了標(biāo)簽,告訴我們這個數(shù)字是多少。比如,上面的圖像的標(biāo)簽分別是5,0,4,1。

在這個教程里面,我們將要訓(xùn)練一個模型用來查看圖像并預(yù)測出上面的數(shù)字是多少。我們的目標(biāo)不是訓(xùn)練一個真實(shí)精確,有著最高性能,盡管我們接下來將提供你代碼來實(shí)現(xiàn)這個,而是簡單使用 TensorFlow 。因此,我們將從一個非常簡單的稱為 Softmax Regression 模型開始。

本教程的實(shí)現(xiàn)代碼非常的短,而且真正有意思的內(nèi)容只發(fā)生在短短的三行中。然而,去理解代碼后面的理念是非常重要的:TensorFlow 的工作原理和機(jī)器學(xué)習(xí)概念的核心。正因?yàn)槿绱耍覀儗⑼ㄟ^這些代碼小心的講解這些。

About this tutorial

本教程將逐行解釋 mnist_softmax.py 代碼所做的事情。

你可以通過幾種不同的方式來學(xué)習(xí)本教程,其中包括:

  • 通過你閱讀的每行代碼的解釋后,復(fù)制并粘貼每個代碼片段到 Python 環(huán)境中。
  • 在閱讀理解之前和之后運(yùn)行整個 mnist_softmax.py 文件,并使用本教程來理解你不清楚的代碼部分。

我們將在本教程中完成以下任務(wù):

  • 學(xué)習(xí)關(guān)于 MNIST 數(shù)據(jù)集和 softmax regressions
  • 根據(jù)查看圖像中的每個像素來創(chuàng)建一個識別數(shù)字的模型
  • 使用 TensorFlow 來 “看” 上千個數(shù)字圖像例子來訓(xùn)練模型識別數(shù)字(并執(zhí)行我們第一個 TensorFlow 會話來實(shí)現(xiàn))
  • 通過我們的測試數(shù)據(jù)來檢測我們的模型的準(zhǔn)確度

The MNIST Data

MNIST 數(shù)據(jù)托管在 Yann LeCun 的網(wǎng)站上。如果你正在復(fù)制和粘貼本教程的代碼,請先從這兩行代碼開始,它將自動下載并讀取數(shù)據(jù):

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

MNIST 數(shù)據(jù)集分為三個部分:55000 訓(xùn)練數(shù)據(jù) (mnist.train),10000 測試數(shù)據(jù) (mnist.test), 和 5000 驗(yàn)證數(shù)據(jù) (mnist.validation)。這種劃分是非常重要的:我們在還沒有學(xué)習(xí)任何東西之前劃分?jǐn)?shù)據(jù),這對于機(jī)器學(xué)習(xí)來說是非常有必要的。這樣我們就可以確認(rèn)我們的學(xué)習(xí)后的模型的實(shí)際泛化能力如何!

正如前面提到的,每個 MNIST 數(shù)據(jù)是由兩部分組成的:一個手寫數(shù)字圖像和一個對應(yīng)的標(biāo)簽。我們成圖像為“x”,標(biāo)簽為“y”。訓(xùn)練集和測試集都包含了圖像和對應(yīng)的標(biāo)簽;例如訓(xùn)練圖像是 mnist.train.images 而標(biāo)簽圖像是 mnist.train.labels 。

每個圖像是28x28像素組成。我們可以認(rèn)為這個一個很大的數(shù)字?jǐn)?shù)組:

img

我們可以將這個28x28的行列式變成一個784的數(shù)組。只要我們保證每個圖像采用相同的方式,那么我們并不在意如何把這個行列式展開。從這個角度來看,MNIST 圖像集只是一個784維矢量空間的一束 具有復(fù)雜結(jié)構(gòu)的 點(diǎn)集而已(警告:可視化是計算密集型)。

展開這些數(shù)據(jù)將丟失圖像的2D結(jié)構(gòu)信息。這當(dāng)然是不好的。最好的機(jī)器視覺方法會利用這種結(jié)構(gòu)信息,我們將在之后的教程中講解這些。但是在這里我們使用的簡單模型,一個 softmax regressions(下面將會定義) 并沒有使用這種信息。

結(jié)果是 mnist.train.images 是一個形狀為 [55000, 784] 的張量(一個N維數(shù)組)。第一維是圖像列表的一個索引,第二維是每個圖像的每個像素的索引。張量中的每個條目是表示特定圖像的特定像素的像素強(qiáng)度值,值介于0和1之間。

img

MNIST中的每個圖像都有對應(yīng)的一個標(biāo)簽數(shù)字,標(biāo)簽數(shù)字是0到9,代表了圖像中畫的數(shù)字。

為了本教程的目的,我們將要我們的標(biāo)簽作為 "one-hot vectors"。一個one-hot 向量是一個只有其中一個維度是1,其他維是0的向量。在這種情況下,向量上第n維的數(shù)字1表示第n的數(shù)字。例如3表示為[0,0,0,1,0,0,0,0,0,0]。所以 mnist.train.labels 是一個[55000, 10] 的數(shù)組。

img

現(xiàn)在我們準(zhǔn)備好實(shí)際構(gòu)造我們的模型了!

Softmax Regressions

我們知道 MNIST 里的每個圖像都是手寫數(shù)字0到9中的一個。所以對于一張圖像來說只有十種可能性。我們希望能夠查看每張圖并給出每張圖的每個數(shù)字的概率。例如,對于我們的模型,在看一張手寫數(shù)字9的圖像,它能夠80%確定這是9,但是有5%的可能是8(因?yàn)樗麄兩习氩糠侄际且粋€圓),所有其他數(shù)字的可能性加起來是另外的15%,因?yàn)榭偣哺怕适?00%。

這是 softmax regression 的一個簡單,自然的經(jīng)典模型。softmax 可以幫助你給幾個不同的事物分別分配概率,因?yàn)?softmax 可以給我們一系列的0到1之間的值,并且總和達(dá)到1。之后當(dāng)我們訓(xùn)練更復(fù)雜的模型的時候,最后一步也是要使用 softmax 層。

softmax regression 有兩個步驟:首先我們將我們輸入的證據(jù)(evidence) 加入到確定的類別中。然后我們計算對應(yīng)的證據(jù)的概率。

為了收集給定圖像在特定類別上的證據(jù),我們進(jìn)行了像素值的加權(quán)求和。如果圖像在某個類上有很高的像素強(qiáng)度的證據(jù)則權(quán)重為正,相反則為負(fù)。

下圖顯示了一個模型在學(xué)習(xí)每個類別的權(quán)重,紅色表示負(fù)權(quán)重,而藍(lán)色表示正權(quán)重。

img

我們也另外添加了一些被稱為偏差的證據(jù)?;旧希覀兿M軌蛴行〇|西獨(dú)立于輸入。結(jié)果對于給定輸入

屬于特定類
的證據(jù)可以表示為:
![][03]
其中,$W_i$ 表示權(quán)重,$b_i$ 表示類$i$的偏置,$j$ 表示輸入圖像 $x$ 的所有像素的索引。然后我們用 softmax 函數(shù)將這些證據(jù)計算出概率 $y$ :
$$
y = \text{softmax}(\text{evidence})
$$
這里的 softmax 被視為是一個激勵(activation)或者鏈接(link)函數(shù),在這種情況下,將我們的線性函數(shù)的輸出塑造成我們想要的形式——一種10個數(shù)字分類的概率分布。你可以認(rèn)為是將我們的輸入的證據(jù)轉(zhuǎn)化為我們在每個分類上的概率,定義如下:
$$
\text{softmax}(evidence) = \text{normalize}(\exp(evidence))
$$
展開等式:
$$
\text{softmax}(evidence)_i = \frac{\exp(evidence_i)}{\sum_j \exp(evidence_j)}
$$
但是把 softmax 視為激勵是有利的:將輸入值指數(shù)化,然后正則化。指數(shù)意味著每多一個單位的證據(jù)增加了假設(shè)(hypothesis)中乘數(shù)的權(quán)重。反之,每少一個單位的證據(jù)意味著減少了假設(shè)中乘數(shù)的權(quán)重。假設(shè)是不會有零或者負(fù)權(quán)重的。然后 softmax 歸一化權(quán)重,使得總權(quán)重為一,形成一個有效的概率分布。(更多有關(guān)于softmax函數(shù)的信息請查閱 Michael Nielsen 書籍的有關(guān)部分,其中包含交互式可視化的內(nèi)容。)

你可以將 softmax 回歸看成如下圖所示,有很多的輸入 $x$。對于每個輸出,我們計算 $x$ 的加權(quán)求和,加上偏置,然后在應(yīng)用于 softmax 函數(shù)中。

img

寫成等式,如下:

img

我們可以將這個過程“矢量化”,轉(zhuǎn)化為矩陣乘法和向量加法。這有助于提高計算效率。(也是一個更有用的思考方式。)

img

更簡潔的寫法如下:
$$
y = \text{softmax}(Wx + b)
$$
現(xiàn)在讓我們將其用 TensorFlow 表示。

Implementing the Regression

為了使用 Python 進(jìn)行高效的數(shù)值計算,我們通常使用諸如 NumPy這樣的的庫來做復(fù)雜的操作,例如矩陣乘法,它使用另一種高效的語言來實(shí)現(xiàn)。不幸的是,每一次運(yùn)算的結(jié)果都會返回給 Python,這會帶來許多額外的開銷。如果你希望在GPU或者以分布式計算的方式來運(yùn)行的話,這其中的轉(zhuǎn)化數(shù)據(jù)的成本將非常高,帶來的開銷將更加糟糕。

TensorFlow 也在 Python 之外進(jìn)行繁重的計算,但是為了避免這種開銷,TensorFlow 做了更進(jìn)一步的完善。TensorFlow 不在 Python 單獨(dú)運(yùn)行一個昂貴的操作,而是讓我們描述一整個交互操作的圖,然后再一起在 Python之外運(yùn)行。(這種類似的情況可以在其他的機(jī)器學(xué)習(xí)庫里看見。)

為了使用TensorFlow,首先我們需要載入它。

import tensorflow as tf

我們通過操作符號變量來描述這些交互操作。現(xiàn)在讓我們來創(chuàng)建一個:

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

x 不是一個特定的值。它是一個占位符(placeholder),當(dāng)我們讓 TensorFlow 執(zhí)行計算的時候讓我們輸入這個值。我們希望能夠輸入任意數(shù)量的 MNIST 圖像,將其展開為一個784維的矢量。我們用一個2維浮點(diǎn)數(shù)張量來表示這些圖,其形如 [None, 784]。(這里None 表示這個維度可以是任意長度的。)

對于我們的模型,我們一樣需要權(quán)重和偏置。我們也可以設(shè)想把他們當(dāng)作額外的輸入,但是 TensorFlow 有一個更好的方式來處理它:Variable。一個 Variable 是一個可變的張量,存在于 TensorFlow 的可交互的圖中。它可以在計算中被使用和被修改。對于機(jī)器學(xué)習(xí)應(yīng)用來說,通常將模型參數(shù)設(shè)置為 Variable。

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

我們通過tf.VariableVariable初始值來創(chuàng)建這些 Variable:在這里,我們用全為零的張量來初始化Wb。既然我們要學(xué)習(xí)W and b,那么他們被初始化成什么并不重要。

注意,W形如[784, 10],因?yàn)槲覀兿M艘?84維的圖像矢量來產(chǎn)生一個10維的不同類的證據(jù)的向量。b的形狀是[10],所以我們可以把它加在輸出上。

現(xiàn)在我們可以實(shí)現(xiàn)我們的模型了。只需要一行代碼來定義它!

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

首先,我們通過tf.matmul(x, W)來使W乘以x。對應(yīng)方程中的 $Wx$ ,這里 $Wx$ 作為一個小技巧來處理多項(xiàng)輸入的2維張量x。然后我們加上b,最后應(yīng)用tf.nn.softmax。

至此,經(jīng)過兩行代碼設(shè)置變量后,我們只使用了一行代碼來定義我們的模型。這不是因?yàn)?TensorFlow 是被設(shè)計為使用 softmax 回歸特別簡單:而這是一個非常靈活的方式來描述許多類型的數(shù)值計算,從機(jī)器學(xué)習(xí)模型到物理模擬仿真都是如此。而一旦定義后,我們的模型可以運(yùn)行在不同的設(shè)備中:你的電腦的CPU上,GPU上,甚至是手機(jī)上!

Training

為了訓(xùn)練我們的模型,我們需要定義什么樣的模型是好的。那么實(shí)際上,在機(jī)器學(xué)習(xí)中我們通常定義什么樣的模型是壞的。我們稱之為代價(cost),或者是損失(loss),這代表著我們的模型與我們的期望的差距有多遠(yuǎn)。我們嘗試使這個誤差最小,因?yàn)檎`差越小,模型也就越好。

一個非常常見,非常好的損失模型被稱為交叉熵("cross-entropy")。交叉熵源于信息理論里的信息壓縮編碼思想,現(xiàn)在在很多領(lǐng)域中其演變?yōu)橐环N重要的思想,從博弈論到機(jī)器學(xué)習(xí)都是如此,它被定義為:
$$
H_{y'}(y) = -\sum_i y'_i \log(y_i)
$$
其中$y$是我們的預(yù)測概率分布,$y'$是實(shí)際分布(數(shù)字標(biāo)簽的one-hot向量)。更粗糙的說話是,交叉熵是用于衡量我們的預(yù)測描述的實(shí)際的低效性。理解交叉熵更多的細(xì)節(jié)是超出本教程的范圍的,但是它是值得理解的。

為了實(shí)現(xiàn)交叉熵,我們首先增加一個占位符用來輸入正確的答案:

y_ = tf.placeholder(tf.float32, [None, 10])

然后我們可以實(shí)現(xiàn)交叉熵函數(shù),$-\sum y'\log(y)$:

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))

首先, tf.log 計算了y的每個元素的對數(shù)。然后我們將y_與相對于的tf.log(y)相乘。再然后由于reduction_indices=[1] ,tf.reduce_sum 將y的第二維元素求和。最后,tf.reduce_mean 計算批處理中所有樣本的平均值。

注意,在源代碼中,我們不適用這個公式,因?yàn)樗跀?shù)值上是不穩(wěn)定的。相反,我們應(yīng)用tf.nn.softmax_cross_entropy_with_logits在非歸一化的logits上( 例如,我們在tf.matmul(x, W) + b中調(diào)用softmax_cross_entropy_with_logits ),因?yàn)樵趕oftmax激活中的數(shù)值計算是更穩(wěn)定的。在你的代碼中,請考慮使用tf.nn.softmax_cross_entropy_with_logits替代。

現(xiàn)在我們知道我們想要我們的模型做什么了,對于 TensorFlow 來說訓(xùn)練它來做這些是非常簡單的。因?yàn)?TensorFlow 知道你所有計算的圖,它可以自動使用反向傳播算法來有效的確定你的變量如何影響你要求最小化的損失。而且它可以應(yīng)用你選擇的優(yōu)化算法來修改變量并減小損失。

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

在這種情況下,我們讓 TensorFlow 使用學(xué)習(xí)率為0.5的梯度下降(gradient descent algorithm)來最小化cross_entropy。梯度下降是一種簡單的過程,其中 TensorFlow 將每個變量往降低成本的方向微小的變化。但是 TensorFlow 也提供了許多其他的優(yōu)化算法:使用它是簡單的如同調(diào)整一條直線一樣。

TensorFlow 在這里實(shí)際上所做的是,在后臺中實(shí)現(xiàn)反向傳播和梯度下降的地方增加一個新的操作到你的圖里。然后它返回給你一個單獨(dú)的操作,當(dāng)運(yùn)行時,進(jìn)行梯度下降訓(xùn)練,微調(diào)你的變量使得損失減小。

現(xiàn)在我們可以在InteractiveSession中啟動模型:

sess = tf.InteractiveSession()

首先我們創(chuàng)建一個操作來初始化我們創(chuàng)建的變量:

tf.global_variables_initializer().run()

開始訓(xùn)練——我們執(zhí)行循環(huán)訓(xùn)練1000次:

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

在循環(huán)的每次中,我們“批量”從訓(xùn)練數(shù)據(jù)集中隨機(jī)抽取100個數(shù)據(jù)。我們用批數(shù)據(jù)替換占位符來運(yùn)行train_step 。

使用小批量隨機(jī)數(shù)據(jù)稱為隨機(jī)訓(xùn)練,在這種情況下稱為隨機(jī)梯度下降。理想情況下,我們希望在每一步的訓(xùn)練中使用所有的數(shù)據(jù),因?yàn)檫@樣我們可以更好的了解我們應(yīng)該做什么,但是這樣做將帶來很大的開銷。因此,在每一步的訓(xùn)練中我們每次使用不同的子集。這樣的開銷不會太大而且可以得到相同的好處。

Evaluating Our Model

那么我們的模型性能如何?

首先,讓我們找出那些我們預(yù)測正確的標(biāo)簽。 tf.argmax 是一個非常有用的函數(shù),它能給你一個張量中某個維度上最大值的索引。例如, tf.argmax(y,1) 是 我們模型認(rèn)為每個輸入最有可能的標(biāo)簽,其中tf.argmax(y_,1) 是正確的標(biāo)簽。我們可以使用 tf.equal 來檢驗(yàn)我們的預(yù)測與真實(shí)是否匹配。

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

這段代碼會給我們一組布爾值。為了確定正確的比例,我們將其轉(zhuǎn)化為浮點(diǎn)數(shù)。例如,[True, False, True, True] 變成 [1,0,1,1] ,這樣其正確率為0.75。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

最后,我們計算我們模型在測試集上的準(zhǔn)確性。

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

這大概為92%。

這個結(jié)果很好嗎?不怎么樣。實(shí)際上這個結(jié)果很差。這是因?yàn)槲覀兪褂昧艘粋€非常簡單的模型。做了一些小的調(diào)整后,我們將得到97%的準(zhǔn)確率。最好的模型能得到大于99.7%的準(zhǔn)確率!(想了解更多信息,請查看這個不同結(jié)果的列表 。)

重要的是我們從這個模型中所學(xué)到的東西。不過如果你對這里的結(jié)論感到一點(diǎn)失望,請查看 下一個教程 ,我們使用 TensorFlow 構(gòu)建一個更復(fù)雜的模型已得到更好的結(jié)果!

[03]:http://latex.codecogs.com/svg.latex?evidence_i=\sum_j W_{i,~ j}x_j+b_i

<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=default"></script>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容