MNIST數(shù)據(jù)集
下載數(shù)據(jù)
http://yann.lecun.com/exdb/mnist/導(dǎo)入數(shù)據(jù)
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
- 60000行的訓(xùn)練數(shù)據(jù)集和10000行的測試數(shù)據(jù)集
每一個MNIST數(shù)據(jù)單元有兩部分組成:一張包含手寫數(shù)字的圖片和一個對應(yīng)的標簽。我們把這些圖片設(shè)為“xs”娘赴,把這些標簽設(shè)為“ys”豹障。
每一張圖片包含28X28個像素點医寿。
我們把這個數(shù)組展開成一個向量钉汗,長度是 28x28 = 784亡问。
展平圖片的數(shù)字數(shù)組會丟失圖片的二維結(jié)構(gòu)信息倦挂。這顯然是不理想的楷拳,最優(yōu)秀的計算機視覺方法會挖掘并利用這些結(jié)構(gòu)信息,我們會在后續(xù)教程中介紹俩块。
- 因此黎休,在MNIST訓(xùn)練數(shù)據(jù)集中,**mnist.train.images **是一個形狀為 [60000, 784] 的張量玉凯,第一個維度數(shù)字用來索引圖片势腮,第二個維度數(shù)字用來索引每張圖片中的像素點。在此張量里的每一個元素漫仆,都表示某張圖片里的某個像素的強度值捎拯,值介于0和1之間。
- 相對應(yīng)的MNIST數(shù)據(jù)集的標簽是介于0到9的數(shù)字盲厌,用來描述給定圖片里表示的數(shù)字署照。我們使標簽數(shù)據(jù)是"one-hot vectors"。 一個one-hot向量除了某一位的數(shù)字是1以外其余各維度數(shù)字都是0吗浩。標簽0將表示成([1,0,0,0,0,0,0,0,0,0,0])建芙。mnist.train.labels 是一個 [60000, 10] 的數(shù)字矩陣。
Softmax回歸介紹
為了得到一張給定圖片屬于某個特定數(shù)字類的證據(jù)(evidence)懂扼,我們對圖片像素值進行加權(quán)求和禁荸。如果這個像素具有很強的證據(jù)說明這張圖片不屬于該類右蒲,那么相應(yīng)的權(quán)值為負數(shù),相反如果這個像素擁有有利的證據(jù)支持這張圖片屬于這個類赶熟,那么權(quán)值是正數(shù)瑰妄。
下面的圖片顯示了一個模型學習到的圖片上每個像素對于特定數(shù)字類的權(quán)值。紅色代表負數(shù)權(quán)值映砖,藍色代表正數(shù)權(quán)值间坐。
- 也需要加入一個額外的偏置量(bias),因為輸入往往會帶有一些無關(guān)的干擾量啊央。因此對于給定的輸入圖片 x 它代表的是數(shù)字 i 的證據(jù)可以表示為
- 其中Wij代表權(quán)重眶诈,bi 代表數(shù)字 i 類的偏置量,xj 代表給定圖片 x 的像素索引用于像素求和瓜饥。然后用softmax函數(shù)可以把這些證據(jù)轉(zhuǎn)換成概率 y
-
softmax可以看成是一個激活(activation)函數(shù)
- 假設(shè)模型里的權(quán)值不可以是0值或者負值逝撬。Softmax然后會正則化這些權(quán)重值,使它們的總和等于1乓土,以此構(gòu)造一個有效的概率分布宪潮。
- 可以寫成更加緊湊的方式:
實現(xiàn)回歸模型
- TensorFlow把復(fù)雜的計算放在python之外完成。Tensorflow不單獨地運行單一的復(fù)雜計算趣苏,而是讓我們可以先用圖描述一系列可交互的計算操作狡相,然后全部一起在Python之外運行。(這樣類似的運行方式食磕,可以在不少的機器學習庫中看到尽棕。)
- 導(dǎo)入tensorflow:
import tensorflow as tf
- 通過操作符號變量來描述這些可交互的操作單元,可以用下面的方式創(chuàng)建一個:
x = tf.placeholder(tf.float32, [None, 784])
x
不是一個特定的值彬伦,而是一個占位符placeholder
滔悉,我們在TensorFlow運行計算時輸入這個值。我們希望能夠輸入任意數(shù)量的MNIST圖像单绑,每一張圖展平成784維的向量回官。我們用2維的浮點數(shù)張量來表示這些圖,這個張量的形狀是[None搂橙,784 ]歉提。(這里的None表示此張量的第一個維度可以是任何長度的。)
- 我們的模型也需要權(quán)重值和偏置量区转,也可以用占位符苔巨,但是有更好的表達方式:
W = tf.Variable(tf.zeros([784,10]))#W的維度是[784,10]废离,因為我們想要用784維的圖片向量乘以它以得到一個10維的證據(jù)值向量
b = tf.Variable(tf.zeros([10]))#b的形狀是[10]侄泽,所以我們可以直接把它加到輸出上面。
一個Variable
代表一個可修改的張量厅缺,存在在TensorFlow的用于描述交互性操作的圖中蔬顾。它們可以用于計算輸入值,也可以在計算中被修改湘捎。對于各種機器學習應(yīng)用诀豁,一般都會有模型參數(shù),可以用Variable
表示窥妇。
- 現(xiàn)在舷胜,我們可以實現(xiàn)我們的模型啦。只需要一行代碼活翩!
y = tf.nn.softmax(tf.matmul(x,W) + b)#輸出的y跟x保持同樣的第一維的個數(shù)
用tf.matmul(??x烹骨,W)
表示x
乘以W
,這里x
是一個2維張量擁有多個輸入。然后再加上b
材泄,把和輸入到tf.nn.softmax
函數(shù)里面沮焕。
訓(xùn)練模型
- 為了訓(xùn)練我們的模型,我們首先需要定義一個指標來評估這個模型是好的拉宗。指標稱為成本(cost)或損失(loss)峦树,然后盡量最小化這個指標。
- 一個非常常見的旦事,非常漂亮的成本函數(shù)是“交叉熵”(cross-entropy)
y 是我們預(yù)測的概率分布, y' 是實際的分布(我們輸入的one-hot vector)魁巩。
比較粗糙的理解是,交叉熵是用來衡量我們的預(yù)測用于描述真相的低效性姐浮。
- 為了計算交叉熵谷遂,我們首先需要添加一個新的占位符用于輸入正確值:
y_ = tf.placeholder("float", [None,10]) # y_就是上面的y',是一個輸入的固定的值
- 計算交叉熵:
cross_entropy = -tf.reduce_sum(y_*tf.log(y))# 是一個y的函數(shù)
用tf.log
計算 y
的每個元素的對數(shù)卖鲤。
接下來肾扰,我們把y_
的每一個元素和tf.log(y)
的對應(yīng)元素相乘。
最后扫尖,用 tf.reduce_sum 計算張量的所有元素的總和白对。
注意,這里的交叉熵不僅僅用來衡量單一的一對預(yù)測和真實值换怖,而是所有100幅圖片的交叉熵的總和甩恼。對于100個數(shù)據(jù)點的預(yù)測表現(xiàn)比單一數(shù)據(jù)點的表現(xiàn)能更好地描述我們的模型的性能。
- 用TensorFlow來訓(xùn)練它是非常容易的沉颂。因為TensorFlow擁有一張描述你各個計算單元的圖条摸,它可以自動地使用反向傳播算法(backpropagation algorithm)來有效地確定你的變量是如何影響你想要最小化的那個成本值的。然后铸屉,TensorFlow會用你選擇的優(yōu)化算法來不斷地修改變量以降低成本钉蒲。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)#定義了一個訓(xùn)練方法,在后面作為session.run()的參數(shù)
這里TensorFlow用梯度下降算法(gradient descent algorithm)以0.01的學習速率最小化交叉熵彻坛。
當然TensorFlow也提供了其他許多優(yōu)化算法:只要簡單地調(diào)整一行代碼就可以使用其他的算法顷啼。
TensorFlow在這里實際上所做的是踏枣,它會在后臺給描述你的計算的那張圖里面增加一系列新的計算操作單元用于實現(xiàn)反向傳播算法和梯度下降算法。然后钙蒙,它返回給你的只是一個單一的操作茵瀑,當運行這個操作時,它用梯度下降算法訓(xùn)練你的模型躬厌,微調(diào)你的變量马昨,不斷減少成本。
- 現(xiàn)在扛施,我們已經(jīng)設(shè)置好了我們的模型鸿捧。在運行計算之前,我們需要添加一個操作來初始化我們創(chuàng)建的變量:
init = tf.initialize_all_variables()#定義一個初始化的函數(shù)動作疙渣,將前面定義的W和b初始化為零
- 現(xiàn)在我們可以在一個Session里面啟動我們的模型匙奴,并且初始化變量:
sess = tf.Session()#定義一個session
sess.run(init)#啟動這個session并初始化變量
會話(session):
客戶端通過創(chuàng)建 會話 (session)和 TensorFlow 系統(tǒng)進行交互。一個由會話接口提供的主要的操作就是 Run 妄荔,以需要計算的輸出名稱和替換某些輸出節(jié)點的張量的操作集合作為其參數(shù)輸入饥脑。通過控制 Run 的參數(shù),TensorFlow 的實現(xiàn)可以計算所有節(jié)點的必須執(zhí)行傳遞閉包來計算需要的輸出懦冰,然后安排執(zhí)行合適節(jié)點來保證他們的依賴關(guān)系(即如果要計算到參數(shù)那一步灶轰,前面必須的步驟會自動完成)。大多數(shù) TensorFlow 的使用都是針對一個圖啟動一個會話刷钢,然后執(zhí)行整個圖或者通過 Run 調(diào)用來執(zhí)行分離的子圖數(shù)千或者數(shù)百萬次笋颤。
- 然后開始訓(xùn)練模型,這里我們讓模型循環(huán)訓(xùn)練1000次内地!
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)#隨機抓取訓(xùn)練數(shù)據(jù)mnist中的100個批處理數(shù)據(jù)點
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})#用這些數(shù)據(jù)點作為參數(shù)替換之前的占位符來運行train_step
設(shè)計placeholder
節(jié)點的唯一的意圖就是為了提供數(shù)據(jù)供給(feeding)的方法伴澄。placeholder
節(jié)點被聲明的時候是未初始化的, 也不包含數(shù)據(jù)阱缓, 如果沒有為它供給數(shù)據(jù)非凌, 則TensorFlow運算的時候會產(chǎn)生錯誤, 所以千萬不要忘了為placeholder
提供數(shù)據(jù)荆针。
評估我們的模型
首先讓我們找出那些預(yù)測正確的標簽敞嗡。
tf.argmax
是一個非常有用的函數(shù),它能給出某個tensor
對象在某一維上的其數(shù)據(jù)最大值所在的索引值(就是對應(yīng)的數(shù)字)航背。tf.argmax(y,1)
返回的是模型對于任一輸入x
預(yù)測到的標簽值喉悴,而tf.argmax(y_,1)
代表正確的標簽,我們可以用tf.equal
來檢測我們的預(yù)測是否真實標簽匹配(索引位置一樣表示匹配)玖媚。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))#比較兩個有y和y_對應(yīng)的標簽序列箕肃,返回一個bool類型的序列
- 這行代碼會給我們一組布爾值。為了確定正確預(yù)測項的比例今魔,我們可以把布爾值轉(zhuǎn)換成浮點數(shù)勺像,然后取平均值障贸。例如,
[True, False, True, True]
會變成[1,0,1,1]
吟宦,取平均值后得到0.75
.
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))#cast()轉(zhuǎn)換類型惹想,reduce_mean()獲得平均值
- 最后,我們計算所學習到的模型在測試數(shù)據(jù)集上面的正確率督函。
print (sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
我再來解讀一下最后的run
函數(shù)。
首先函數(shù)方法是accuracy
激挪,往前看發(fā)現(xiàn)引用了一個correct_prediction
函數(shù)辰狡。查看correct_prediction
函數(shù),里面有兩個參數(shù)y
和y_
垄分。往前看宛篇,y_
是一個placeholder
,y_
確定要傳參數(shù)進來了薄湿。而y
是一個函數(shù)式叫倍,查看此函數(shù)式發(fā)現(xiàn)需要用到x
,再往前找到x
定義的地方發(fā)現(xiàn)x
是一個placeholder
,也需要傳參數(shù)進來豺瘤,就此終于形成閉包吆倦。最終是兩個placeholder
,并且次序是先x
后y_
坐求,因此用feed_dict={x: mnist.test.images, y_: mnist.test.labels}