這篇教程是面向剛開始接觸機(jī)器學(xué)習(xí)TensorFlow的讀者。如果你已經(jīng)知道了什么是MNIST,并且知道softmax(多分類)回歸是什么罗侯,你也許會(huì)喜歡這種快節(jié)奏的教程斯碌。在開始學(xué)習(xí)這個(gè)教程之前一定要安裝TensorFlow荧琼。
有一個(gè)傳統(tǒng),當(dāng)一個(gè)人學(xué)習(xí)如何編程時(shí)即纲,第一件事情是先要打印“Hello World”篱瞎。就像編程世界有“Hello World”一樣,機(jī)器學(xué)習(xí)有MNIST达罗。
MNIST是一個(gè)簡單的計(jì)算機(jī)可視數(shù)據(jù)集坝撑。它由像這樣的手寫數(shù)字組成:
它也包含了每個(gè)圖片的標(biāo)簽,它告訴我們圖片上是那一個(gè)數(shù)字粮揉。例如巡李,對(duì)于上面圖片,標(biāo)簽是5扶认,0侨拦,4,和1.
在這篇教程中辐宾,我將訓(xùn)練一個(gè)看圖片狱从,預(yù)測(cè)圖片上的數(shù)字是什么的模型。我們的目標(biāo)不是訓(xùn)練一個(gè)真正達(dá)到最高性能的復(fù)雜模型--盡管我們隨后會(huì)會(huì)給你代碼--而是嘗試使用TensorFlow螃概。因此矫夯,我們從一個(gè)被叫做Softmax回歸的非常簡單的模型開始。
這篇教程的實(shí)際代碼非常短吊洼,核心代碼只有三行训貌。但是,理解這背后TensorFlow怎么工作和機(jī)器學(xué)習(xí)概念的核心的思想是非常重要的冒窍。由于這個(gè)原因递沪,我們將非常仔細(xì)的研究這些代碼。
關(guān)于這篇教程
這篇教程一行一行的解釋了在mnist_softmax.py中的代碼综液。
你可以用幾種不同的方式使用這篇教程款慨,包括:
在閱讀每行代碼的解釋時(shí),逐行的把每一個(gè)代碼片段復(fù)制并粘貼到你的Python環(huán)境中谬莹。
在通讀注釋之前或之后運(yùn)行整個(gè)mnist_softmax.py Python文件檩奠,并使用這個(gè)教程去理解你不清楚的代碼桩了。
我們將要在本教程中完成什么:
學(xué)習(xí)關(guān)于MNIST數(shù)據(jù)和softmax回歸。
創(chuàng)建一個(gè)函數(shù)埠戳,這個(gè)函數(shù)是一個(gè)基于查看圖片中的每一個(gè)像素來識(shí)別數(shù)字的模型井誉。
使用TensorFlow訓(xùn)練模型,讓它“看”上千個(gè)例子來識(shí)別數(shù)字(運(yùn)行我們的第一個(gè)TensorFlow session來做這點(diǎn))
使用我們的測(cè)試數(shù)據(jù)檢查模型的準(zhǔn)確率整胃。
MNIST數(shù)據(jù)
MNIST數(shù)據(jù)被托管在Yann LeCun站點(diǎn)上. 如果你要從這個(gè)教程中復(fù)制粘貼代碼颗圣,從這里開始,這兩行代碼會(huì)自動(dòng)地下載并讀取數(shù)據(jù):
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
MNIST數(shù)據(jù)被分割成了三部分:55,000條訓(xùn)練數(shù)據(jù)(nist.train)屁使, 10,000條測(cè)試數(shù)據(jù)(mnist.test)在岂,和5,000條驗(yàn)證數(shù)據(jù)(mnist.validation)。這個(gè)分割很重要:在機(jī)器學(xué)習(xí)中蛮寂,我們從不學(xué)習(xí)的數(shù)據(jù)中分離數(shù)據(jù)很關(guān)鍵蔽午,這樣我們就可以確保確實(shí)涵蓋了我們學(xué)習(xí)的數(shù)據(jù)。
正如前面提到的酬蹋,每一個(gè)MNIST數(shù)據(jù)有兩部分:一個(gè)是手寫數(shù)字的圖片祠丝,和相應(yīng)的標(biāo)簽。我們將聲明圖片為“x”,標(biāo)簽為“y”除嘹。訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集,都包含了圖片和他們相應(yīng)的標(biāo)簽岸蜗;例如訓(xùn)練圖片是mnist.train.images尉咕,訓(xùn)練標(biāo)簽是mnist.train.labels。
每個(gè)圖片都是28X28的璃岳。我們可以把它轉(zhuǎn)換成一個(gè)數(shù)字的大數(shù)組:
我們可以把這個(gè)數(shù)組展開成28x28 = 784的向量年缎,不管我們?nèi)绾握归_數(shù)組,只要我們與圖片保持一致就行铃慷。從這個(gè)角度看单芜,MNIST圖片在784維向量空間中只是一堆具有豐富結(jié)構(gòu)的數(shù)據(jù)點(diǎn)(警告:高強(qiáng)度的可視化技術(shù))。
扁平化數(shù)據(jù)丟掉了圖片的二維結(jié)構(gòu)信息犁柜。這不壞嗎洲鸠?好吧,最好的計(jì)算機(jī)視覺方法利用這種結(jié)構(gòu)馋缅,我們將在后面的教程中用到扒腕。但是我們將在這里使用最簡單的方法,softmax回歸(定義見下文)萤悴。
結(jié)果是,mnist.train.images是一個(gè)[55000, 784]的tensor(一個(gè)n-維數(shù)組)瘾腰。第一個(gè)維度是圖片列表的索引,第二個(gè)維度是每一個(gè)圖片中每個(gè)像素的索引覆履。在tensor中的每一項(xiàng)都是特定圖片之中特定像素的0到1之間的像素灰度蹋盆。
在MNIST中的每一個(gè)圖片都有一個(gè)對(duì)應(yīng)的標(biāo)簽费薄,標(biāo)簽上的一個(gè)0到9之間的數(shù)字表示圖片中畫的數(shù)字。
為了達(dá)到這篇教程的目的栖雾,我們希望我們的標(biāo)簽是“one-hot向量”楞抡。一個(gè)one-hot 向量是一個(gè)多數(shù)維度是0,一個(gè)維度是1的向量岩灭。在這案例中拌倍,第n個(gè)數(shù)字將被表示為在第n個(gè)維度是1的向量。例如噪径,3將會(huì)是[0,0,0,1,0,0,0,0,0,0]柱恤。所以,mnist.train.labels是一個(gè)[55000, 10] 浮點(diǎn)型數(shù)組.
我們現(xiàn)在準(zhǔn)備好實(shí)現(xiàn)我們的模型了找爱!
softmax回歸
我們知道在MNIST中的每一個(gè)圖片是一個(gè)0到9的數(shù)字梗顺,因此給的圖片只有10中可能。我們要通過看一個(gè)圖片并給出它是每一個(gè)數(shù)字的概率车摄。例如寺谤,我的模型看到一張9的圖片,并80%確認(rèn)他是9吮播,但是給出5%的機(jī)會(huì)是8(由于頂部的圈)变屁,并且由于不是100%的確定,所以還有所有其他數(shù)字的一點(diǎn)可能意狠。
這是一個(gè)經(jīng)典的案例粟关,其中softmax回歸是一個(gè)自然簡單的模型。如果你想把概率分配幾個(gè)不同事物中的一個(gè)环戈,softmax就是做這個(gè)的闷板,因?yàn)閟oftmax給我們一個(gè)值是0到1的列表,并且這些值加起來是1院塞。即使在以后遮晚,我們訓(xùn)練更復(fù)雜的模型,最后一步也將是softmax層拦止。
softmax回歸有兩個(gè)步驟:首先县遣,我們把我們輸入的證據(jù)累積都某個(gè)分類中,然后我們把這些證據(jù)轉(zhuǎn)換成概率创泄。
為了計(jì)算一個(gè)給定圖片是在一個(gè)特定分類內(nèi)的證據(jù)艺玲,我們計(jì)算了像素強(qiáng)度的加權(quán)和。如果具有高強(qiáng)度的像素對(duì)于圖片屬于那個(gè)分類的證據(jù)是不利的鞠抑,那么權(quán)重就是負(fù)的饭聚,如果是有利的,那么權(quán)重就是正的搁拙。
下面的圖表顯示了秒梳,我們?yōu)檫@些分類中的每一個(gè)分類學(xué)習(xí)了模型的權(quán)重法绵。紅的表示負(fù)權(quán)重,而藍(lán)的表示正權(quán)重酪碘。
我們也增加了一些額外的證據(jù)朋譬,叫做偏量。主要是兴垦,我們希望能夠得到更加獨(dú)立于輸入的東西徙赢。結(jié)果是,給定一個(gè)輸入x是分類i的證據(jù)是:
其中探越,Wi是分類i的權(quán)重狡赐,bi是分類i的偏量,j是輸入圖片x中所有像素的索引钦幔。然后我們使用
“softmax”函數(shù)把證據(jù)轉(zhuǎn)換成與我們的預(yù)測(cè)概率吻合枕屉,“softmax”函數(shù):
y=softmax(evidence)
這里softmax是做為一個(gè)“激活”或“連接”函數(shù),把我們的線性函數(shù)輸出調(diào)整成我們想要的形式——在這個(gè)案例中鲤氢,概率分布要覆蓋到10個(gè)案例搀擂。你可以把它看做是把相應(yīng)的證據(jù)轉(zhuǎn)換為我們輸入的成為每個(gè)分類的可能性,它被定義為:
softmax(x)=normalize(exp?(x))
如果你展開這個(gè)表達(dá)式卷玉,你會(huì)得到:
但是把softmax看做是第一種方式更有用:對(duì)輸入求冪哨颂,然后再把他們規(guī)范化。求冪是指給一或多個(gè)證據(jù)單元增加任何假設(shè)乘法的權(quán)重相种。并且咆蒿,相反地,至少有一個(gè)證據(jù)單元意味著一個(gè)假設(shè)得到了早期權(quán)重的一小部分蚂子。任何假設(shè)都不是零或負(fù)的權(quán)重。然后softmax規(guī)范化這些權(quán)重缭黔,讓他們加起來等于1食茎,形成有效的概率分布。(要得到一個(gè)更直觀的softmax函數(shù)馏谨,看一看Michael Nielsen的書中完成了可視化交互的章節(jié)别渔。)
盡管有很多的xs,但你可以想象一下我們的softmax回歸像下面這個(gè)樣子惧互。對(duì)于每一個(gè)輸出哎媚,我們計(jì)算了xs的加權(quán)和,加了偏量喊儡,然后使用了softmax
[圖片上傳失敗...(image-287673-1516326657579)]
如果我們把它寫成方程式拨与,我們得到:
[圖片上傳失敗...(image-5e6432-1516326657579)]
我們可以“矢量化”這個(gè)過程,把它轉(zhuǎn)換成矩陣乘法和向量加法艾猜,這樣有助于提高計(jì)算效率买喧。(這也是一種有用的思考方式)
[圖片上傳失敗...(image-21b70-1516326657580)]
更簡潔些捻悯,我們可以寫成:
y=softmax(Wx+b)
現(xiàn)在,讓我們把這些轉(zhuǎn)成Tensorflow 可以使用的淤毛。
實(shí)現(xiàn)回歸
為了在Python中進(jìn)行有效的數(shù)值計(jì)算今缚,我們通常使用如Numpy這樣的庫,它在Python之外低淡,使用其他語言實(shí)現(xiàn)更高效的代碼姓言,做一些像的矩陣乘法這樣耗時(shí)的操作。不幸的是蔗蹋,每次操作切換回Python仍然是一個(gè)很大的開銷何荚,如果你想在GPU或分布式上運(yùn)行計(jì)算,這種開銷尤其糟糕纸颜,數(shù)據(jù)的傳輸成本更高兽泣。
在Python之外,TensorFlow也做了些重量級(jí)的操作胁孙,但是為了避免這個(gè)還有更多的工作要做唠倦。為了不運(yùn)行獨(dú)立于Python的耗時(shí)操作,TensorFlow讓我們描述一個(gè)整個(gè)運(yùn)行在Python之外的交互操作的圖形涮较。(類似的方法可以在一些機(jī)器學(xué)習(xí)庫中看到稠鼻。)
為了使用TensorFlow,首先我們需要導(dǎo)入它狂票。
import tensorflow as tf
我通過控制符號(hào)變量描述了這些交互操作候齿,讓我們創(chuàng)建一個(gè):
x = tf.placeholder(tf.float32, [None, 784])
x不是一個(gè)特定的值,它是一個(gè)占位符闺属,當(dāng)我們讓TensorFlow運(yùn)行計(jì)算時(shí)慌盯,才輸入值。我們想把MNIST圖像的每一個(gè)拉平成784-維度的向量的任何數(shù)值都可以輸入掂器。我們把它表示成[None, 784]二維浮點(diǎn)數(shù)值的tensor亚皂。(這里None意味著這個(gè)維度可以是任意長度)
對(duì)于我們的模型,我們也需要權(quán)重和偏量国瓮。我可以想象一下如何對(duì)待這些額外的輸入灭必,但是TensorFlow有一個(gè)更好的方式處理它:變量。變量A是一個(gè)可修改的tensor乃摹,它處在TensorFlow的交互圖中禁漓。它可以被使用,甚至通過計(jì)算修改孵睬。對(duì)于機(jī)器學(xué)習(xí)應(yīng)用來說播歼,通常有一個(gè)模型參數(shù)是變量。
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
我們通過給定的tf.Variable創(chuàng)建變量掰读,初始化變量的值:在這個(gè)案例中荚恶,我們把W和b都初始化為都是0的tensor撩穿。由于我們要去學(xué)習(xí)W和b,所以不需要在意他的初始值是什么谒撼。
注意食寡,W的形狀是 [784, 10],是因?yàn)槲覀兿胗盟艘?84-維度的圖像向量產(chǎn)生10-維度不同類別的證據(jù)向量廓潜。b的形狀是[10],因此我們可以把它加給輸出抵皱。
現(xiàn)在我們可以實(shí)現(xiàn)我們的模型了,僅用一行就可以定義它辩蛋。
y = tf.nn.softmax(tf.matmul(x, W) + b)
首先呻畸,我們使用表達(dá)式 tf.matmul(x, W)讓W乘以x。當(dāng)我們?cè)谖覀兊姆匠淌街邢喑诉@些時(shí)有一個(gè)翻轉(zhuǎn)悼院,這里我們有Wx伤为,這是處理多個(gè)輸入的x成為2D tensor的小技巧。然后我們?cè)偌由?em>b据途,最后應(yīng)用tf.nn.softmax绞愚。
是的,經(jīng)過幾行簡短的設(shè)置后颖医,僅僅使用一行就定義了我們的模型位衩。這并不是因?yàn)門ensorFlow被設(shè)計(jì)的目的是讓softmax回歸變得極其簡單,而是它只是一個(gè)用非常靈活的方式熔萧,描述從機(jī)器學(xué)習(xí)模型到物理模型的各種類型的計(jì)算糖驴。并且一旦定義,我們的模型可以運(yùn)行在不同的設(shè)備上:你的電腦的CPU佛致,GPU贮缕,甚至是手機(jī)上!
訓(xùn)練
為了訓(xùn)練我們的模型俺榆,我們需要定義什么樣的模型是好的跷睦。實(shí)際上,在機(jī)器學(xué)習(xí)中肋演,我通常定義什么模型是壞的。我們稱之為成本或者損失烂琴,它表示了我們的模型里我們的期望有多遠(yuǎn)爹殊。我們盡量讓誤差最小,誤差越小奸绷,我們的模型越好梗夸。
一個(gè)非常常見,非常好的覺得模型的損失的功能叫“交叉熵”号醉。交叉熵來源于信息論中的信息壓縮編碼的思想反症,但它在從賭博到機(jī)器學(xué)習(xí)的許多領(lǐng)域中已成為一個(gè)重要的概念辛块。他被定義為:
這里y是我們預(yù)測(cè)的概率分布,y′是真實(shí)的分布( 數(shù)字的one-hot向量)铅碍。在某種粗略的意義上润绵,交叉熵是衡量我們的預(yù)測(cè)對(duì)描述事實(shí)的效率有多低。關(guān)于交叉熵的更多細(xì)節(jié)超出了本教程的范圍胞谈,但它很值得理解尘盼。
為了實(shí)現(xiàn)交叉熵,首先烦绳,我們需要增加一個(gè)新的占位符來輸入正確的答案:
y_ = tf.placeholder(tf.float32, [None, 10])
然后我們可以實(shí)現(xiàn)交叉熵函數(shù)卿捎, ?∑y′log?(y):
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
首先,tf.log對(duì)y的每一個(gè)元素進(jìn)行對(duì)數(shù)計(jì)算径密。接下來午阵,我們拿y_的每一個(gè)元素和相應(yīng)的tf.log(y)相乘。然后 tf.reduce_sum在y的第二個(gè)維度中把每個(gè)元素相加享扔。最終底桂, tf.reduce_mean計(jì)算了batch內(nèi)的所有例子的平均值。
(注意伪很,在源碼中,我們沒有使用公式戚啥,因?yàn)樗跀?shù)值上不穩(wěn)定。 因此我們?cè)诜菢?biāo)準(zhǔn)化數(shù)值上采用了tf.nn.softmax_cross_entropy_with_logits (例如, 在 tf.matmul(x, W) + b)上調(diào)用 softmax_cross_entropy_with_logits ,因?yàn)檫@個(gè)較為數(shù)值穩(wěn)定的功能內(nèi)部調(diào)用了softmax激活锉试。 在你的代碼中猫十,考慮使用 tf.nn.(sparse_)softmax_cross_entropy_with_logits 代替).
現(xiàn)在,我們知道了我們想要我們的模型做什么呆盖,很容易讓TensorFlow訓(xùn)練它去做這些拖云。因?yàn)門ensorFlow知道你計(jì)算的整個(gè)圖,它可以自動(dòng)的使用反向傳播算法应又,來有效的決定你的變量怎樣影響你要的最小化損失宙项。然后你可以應(yīng)用你選擇的最優(yōu)算法來修改變量,減少損失株扛。
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
在這個(gè)案例中尤筐,我們讓TensorFlow使用學(xué)習(xí)率為0.5的梯度下降算法來最小化交叉熵。梯度下降是一個(gè)簡單的算法洞就,在這里TensorFlow一點(diǎn)點(diǎn)的轉(zhuǎn)變每一個(gè)變量的方向以降低成本盆繁。但是TensorFlow也提供了其他的優(yōu)化算法:換一個(gè)就跟調(diào)整一行代碼一樣簡單。
TensorFlow在這里真正做的是什么旬蟋,在后臺(tái)油昂,是在你實(shí)現(xiàn)的反向傳播和梯度下降的圖中添加新的操作。然后,他給你返回一個(gè)單獨(dú)的操作冕碟,當(dāng)運(yùn)行時(shí)拦惋,進(jìn)行一個(gè)梯度下降訓(xùn)練,稍微調(diào)整你的變量安寺,以減少損失厕妖。
現(xiàn)在,我們要去訓(xùn)練我們的模型我衬。訓(xùn)練前的最后一件事情是叹放,我們要?jiǎng)?chuàng)建一個(gè)操作來初始化我們創(chuàng)建的變量。注意這時(shí)定義了操作挠羔,但還沒有運(yùn)行:
init = tf.global_variables_initializer()
我們現(xiàn)在可以在一個(gè)Session啟動(dòng)我們的模型井仰,現(xiàn)在,我們運(yùn)行初始化變量的操作:
sess = tf.Session()
sess.run(init)
讓我們訓(xùn)練——我們將運(yùn)行訓(xùn)練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})
循環(huán)的每一步俱恶,我們從我們的訓(xùn)練集中得到100個(gè)隨機(jī)的數(shù)據(jù)點(diǎn)“batch”。我們運(yùn)行train_step 把每個(gè)batch中數(shù)據(jù)放進(jìn)占位符中范舀。
使用小批量隨機(jī)數(shù)據(jù)被稱為隨機(jī)訓(xùn)練合是。———在這個(gè)案例中锭环,叫隨機(jī)梯度下降聪全。理想情況下,我們希望在訓(xùn)練的每一步使用我們所有的數(shù)據(jù)辅辩,因?yàn)檫@樣可以讓我們更好地了解我們應(yīng)該做什么难礼,但這很耗時(shí)。因此玫锋,我們每次使用不同的子數(shù)據(jù)集蛾茉。這樣做很方便,也有很多好處撩鹿。
評(píng)估我們的模型
我們的模型做的怎么樣谦炬?
首先,讓我們找到正確預(yù)測(cè)的標(biāo)簽节沦。tf.argmax是一個(gè)很有用的函數(shù)键思, 它給你返回在tensor中某個(gè)維度中的最高值的索引。例如甫贯,tf.argmax(y,1)是我們的模型認(rèn)為最有可能輸入的標(biāo)簽吼鳞,而tf.argmax(y_,1)是正確的標(biāo)簽。我可以使用tf.equal來檢查我們的預(yù)測(cè)是否正確获搏。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
這給我們了一個(gè)布爾列表。為了確定正確的分?jǐn)?shù),我們轉(zhuǎn)換為浮點(diǎn)數(shù)常熙,然后取平均值纬乍。例如, [True, False, True, True] 會(huì)轉(zhuǎn)換成[1,0,1,1]裸卫,然后成為0.75.
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
最后仿贬,我們得到測(cè)試數(shù)據(jù)的準(zhǔn)確性。
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
結(jié)果大約是92%
結(jié)果好嗎墓贿?不好茧泪,事實(shí)上,非常差聋袋,這是因?yàn)槎游埃覀兪褂昧朔浅:唵蔚哪P汀W鲆恍┬〉母淖冇睦眨覀兡苓_(dá)到97%嗜侮。最好的模型能超過99.7%的準(zhǔn)確率!(為了得到更多的信息啥容,看一下這個(gè)結(jié)果列表)
不管我們從這個(gè)模型中學(xué)到什么锈颗。不過,如果你對(duì)這些結(jié)果有點(diǎn)失望咪惠,看一看我們做的更好的击吱,學(xué)習(xí)怎樣使用TensorFlow構(gòu)建更復(fù)雜的下一個(gè)教程里
無戒365挑戰(zhàn)營 46