天天看點

tensorfllow MNIST機器學習入門MNIST機器學習入門深入MNIST

當我們開始學習程式設計的時候,第一件事往往是學習列印"Hello World"。就好比程式設計入門有Hello World,機器學習入門有MNIST。

MNIST是一個入門級的計算機視覺資料集,它包含各種手寫數字圖檔:

它也包含每一張圖檔對應的标簽,告訴我們這個是數字幾。比如,上面這四張圖檔的标簽分别是5,0,4,1。

在此教程中,我們将訓練一個機器學習模型用于預測圖檔裡面的數字。我們的目的不是要設計一個世界一流的複雜模型 -- 盡管我們會在之後給你源代碼去實作一流的預測模型 -- 而是要介紹下如何使用TensorFlow。是以,我們這裡會從一個很簡單的數學模型開始,它叫做Softmax Regression。

對應這個教程的實作代碼很短,而且真正有意思的内容隻包含在三行代碼裡面。但是,去了解包含在這些代碼裡面的設計思想是非常重要的:TensorFlow工作流程和機器學習的基本概念。是以,這個教程會很詳細地介紹這些代碼的實作原理。

下載下傳下來的資料集被分成兩部分:60000行的訓練資料集(<code>mnist.train</code>)和10000行的測試資料集(<code>mnist.test</code>)。這樣的切分很重要,在機器學習模型設計時必須有一個單獨的測試資料集不用于訓練而是用來評估這個模型的性能,進而更加容易把設計的模型推廣到其他資料集上(泛化)。

正如前面提到的一樣,每一個MNIST資料單元有兩部分組成:一張包含手寫數字的圖檔和一個對應的标簽。我們把這些圖檔設為“xs”,把這些标簽設為“ys”。訓練資料集和測試資料集都包含xs和ys,比如訓練資料集的圖檔是 <code>mnist.train.images</code> ,訓練資料集的标簽是 <code>mnist.train.labels</code>。

每一張圖檔包含28像素X28像素。我們可以用一個數字數組來表示這張圖檔:

展平圖檔的數字數組會丢失圖檔的二維結構資訊。這顯然是不理想的,最優秀的計算機視覺方法會挖掘并利用這些結構資訊,我們會在後續教程中介紹。但是在這個教程中我們忽略這些結構,所介紹的簡單數學模型,softmax回歸(softmax regression),不會利用這些結構資訊。

是以,在MNIST訓練資料集中,<code>mnist.train.images</code> 是一個形狀為 <code>[60000, 784]</code> 的張量,第一個次元數字用來索引圖檔,第二個次元數字用來索引每張圖檔中的像素點。在此張量裡的每一個元素,都表示某張圖檔裡的某個像素的強度值,值介于0和1之間。

相對應的MNIST資料集的标簽是介于0到9的數字,用來描述給定圖檔裡表示的數字。為了用于這個教程,我們使标簽資料是"one-hot vectors"。 一個one-hot向量除了某一位的數字是1以外其餘各次元數字都是0。是以在此教程中,數字n将表示成一個隻有在第n次元(從0開始)數字為1的10維向量。比如,标簽0将表示成([1,0,0,0,0,0,0,0,0,0,0])。是以, <code>mnist.train.labels</code> 是一個 <code>[60000, 10]</code> 的數字矩陣。

現在,我們準備好可以開始建構我們的模型啦!

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

這是一個使用softmax回歸(softmax regression)模型的經典案例。softmax模型可以用來給不同的對象配置設定機率。即使在之後,我們訓練更加精細的模型時,最後一步也需要用softmax來配置設定機率。

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

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

下面的圖檔顯示了一個模型學習到的圖檔上每個像素對于特定數字類的權值。紅色代表負數權值,藍色代表正數權值。

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

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

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

展開等式右邊的子式,可以得到:

對于softmax回歸模型可以用下面的圖解釋,對于輸入的<code>xs</code>權重求和,再分别加上一個偏置量,最後再輸入到softmax函數中:

如果把它寫成一個等式,我們可以得到:

我們也可以用向量表示這個計算過程:用矩陣乘法和向量相加。這有助于提高計算效率。(也是一種更有效的思考方式)

更進一步,可以寫成更加緊湊的方式:

為了用python實作高效的數值計算,我們通常會使用函數庫,比如NumPy,會把類似矩陣乘法這樣的複雜運算使用其他外部語言實作。不幸的是,從外部計算切換回Python的每一個操作,仍然是一個很大的開銷。如果你用GPU來進行外部計算,這樣的開銷會更大。用分布式的計算方式,也會花費更多的資源用來傳輸資料。

TensorFlow也把複雜的計算放在python之外完成,但是為了避免前面說的那些開銷,它做了進一步完善。Tensorflow不單獨地運作單一的複雜計算,而是讓我們可以先用圖描述一系列可互動的計算操作,然後全部一起在Python之外運作。(這樣類似的運作方式,可以在不少的機器學習庫中看到。)

使用TensorFlow之前,首先導入它:

我們通過操作符号變量來描述這些可互動的操作單元,可以用下面的方式建立一個:

<code>x</code>不是一個特定的值,而是一個占位符<code>placeholder</code>,我們在TensorFlow運作計算時輸入這個值。我們希望能夠輸入任意數量的MNIST圖像,每一張圖展平成784維的向量。我們用2維的浮點數張量來表示這些圖,這個張量的形狀是<code>[None,784 ]</code>。(這裡的<code>None</code>表示此張量的第一個次元可以是任何長度的。)

我們的模型也需要權重值和偏置量,當然我們可以把它們當做是另外的輸入(使用占位符),但TensorFlow有一個更好的方法來表示它們:<code>Variable</code> 。 一個<code>Variable</code>代表一個可修改的張量,存在在TensorFlow的用于描述互動性操作的圖中。它們可以用于計算輸入值,也可以在計算中被修改。對于各種機器學習應用,一般都會有模型參數,可以用<code>Variable</code>表示。

我們賦予<code>tf.Variable</code>不同的初值來建立不同的<code>Variable</code>:在這裡,我們都用全為零的張量來初始化<code>W</code>和<code>b</code>。因為我們要學習<code>W</code>和<code>b</code>的值,它們的初值可以随意設定。

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

現在,我們可以實作我們的模型啦。隻需要一行代碼!

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

至此,我們先用了幾行簡短的代碼來設定變量,然後隻用了一行代碼來定義我們的模型。TensorFlow不僅僅可以使softmax回歸模型計算變得特别簡單,它也用這種非常靈活的方式來描述其他各種數值計算,從機器學習模型對實體學模拟仿真模型。一旦被定義好之後,我們的模型就可以在不同的裝置上運作:計算機的CPU,GPU,甚至是手機!

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

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

為了計算交叉熵,我們首先需要添加一個新的占位符用于輸入正确值:

然後我們可以用  計算交叉熵:

首先,用 <code>tf.log</code> 計算 <code>y</code> 的每個元素的對數。接下來,我們把 <code>y_</code> 的每一個元素和 <code>tf.log(y_)</code> 的對應元素相乘。最後,用 <code>tf.reduce_sum</code> 計算張量的所有元素的總和。(注意,這裡的交叉熵不僅僅用來衡量單一的一對預測和真實值,而是所有100幅圖檔的交叉熵的總和。對于100個資料點的預測表現比單一資料點的表現能更好地描述我們的模型的性能。

TensorFlow在這裡實際上所做的是,它會在背景給描述你的計算的那張圖裡面增加一系列新的計算操作單元用于實作反向傳播算法和梯度下降算法。然後,它傳回給你的隻是一個單一的操作,當運作這個操作時,它用梯度下降算法訓練你的模型,微調你的變量,不斷減少成本。

現在,我們已經設定好了我們的模型。在運作計算之前,我們需要添加一個操作來初始化我們建立的變量:

現在我們可以在一個<code>Session</code>裡面啟動我們的模型,并且初始化變量:

然後開始訓練模型,這裡我們讓模型循環訓練1000次!

該循環的每個步驟中,我們都會随機抓取訓練資料中的100個批處理資料點,然後我們用這些資料點作為參數替換之前的占位符來運作<code>train_step</code>。

使用一小部分的随機資料來進行訓練被稱為随機訓練(stochastic training)- 在這裡更确切的說是随機梯度下降訓練。在理想情況下,我們希望用我們所有的資料來進行每一步的訓練,因為這能給我們更好的訓練結果,但顯然這需要很大的計算開銷。是以,每一次訓練我們可以使用不同的資料子集,這樣做既可以減少計算開銷,又可以最大化地學習到資料集的總體特性。

那麼我們的模型性能如何呢?

首先讓我們找出那些預測正确的标簽。<code>tf.argmax</code> 是一個非常有用的函數,它能給出某個tensor對象在某一維上的其資料最大值所在的索引值。由于标簽向量是由0,1組成,是以最大值1所在的索引位置就是類别标簽,比如<code>tf.argmax(y,1)</code>傳回的是模型對于任一輸入x預測到的标簽值,而 <code>tf.argmax(y_,1)</code> 代表正确的标簽,我們可以用 <code>tf.equal</code> 來檢測我們的預測是否真實标簽比對(索引位置一樣表示比對)。

這行代碼會給我們一組布爾值。為了确定正确預測項的比例,我們可以把布爾值轉換成浮點數,然後取平均值。例如,<code>[True, False, True, True]</code> 會變成 <code>[1,0,1,1]</code> ,取平均值後得到 <code>0.75</code>.

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

這個最終結果值應該大約是91%。

TensorFlow是一個非常強大的用來做大規模數值計算的庫。其所擅長的任務之一就是實作以及訓練深度神經網絡。

在本教程中,我們将學到建構一個TensorFlow模型的基本步驟,并将通過這些步驟為MNIST建構一個深度卷積神經網絡。

在建立模型之前,我們會先加載MNIST資料集,然後啟動一個TensorFlow的session。

這裡,<code>mnist</code>是一個輕量級的類。它以Numpy數組的形式存儲着訓練、校驗和測試資料集。同時提供了一個函數,用于在疊代中獲得minibatch,後面我們将會用到。

Tensorflow依賴于一個高效的C++後端來進行計算。與後端的這個連接配接叫做session。一般而言,使用TensorFlow程式的流程是先建立一個圖,然後在session中啟動它。

為了在Python中進行高效的數值計算,我們通常會使用像NumPy一類的庫,将一些諸如矩陣乘法的耗時操作在Python環境的外部來計算,這些計算通常會通過其它語言并用更為高效的代碼來實作。

但遺憾的是,每一個操作切換回Python環境時仍需要不小的開銷。如果你想在GPU或者分布式環境中計算時,這一開銷更加可怖,這一開銷主要可能是用來進行資料遷移。

TensorFlow也是在Python外部完成其主要工作,但是進行了改進以避免這種開銷。其并沒有采用在Python外部獨立運作某個耗時操作的方式,而是先讓我們描述一個互動操作圖,然後完全将其運作在Python外部。這與Theano或Torch的做法類似。

在這一節中我們将建立一個擁有一個線性層的softmax回歸模型。在下一節,我們會将其擴充為一個擁有多層卷積網絡的softmax回歸模型。

我們通過為輸入圖像和目标輸出類别建立節點,來開始建構計算圖。

這裡的<code>x</code>和<code>y</code>并不是特定的值,相反,他們都隻是一個<code>占位符</code>,可以在TensorFlow運作某一計算時根據該占位符輸入具體的值。

輸入圖檔<code>x</code>是一個2維的浮點數張量。這裡,配置設定給它的<code>shape</code>為<code>[None, 784]</code>,其中<code>784</code>是一張展平的MNIST圖檔的次元。<code>None</code>表示其值大小不定,在這裡作為第一個次元值,用以指代batch的大小,意即<code>x</code>的數量不定。輸出類别值<code>y_</code>也是一個2維張量,其中每一行為一個10維的one-hot向量,用于代表對應某一MNIST圖檔的類别。

雖然<code>placeholder</code>的<code>shape</code>參數是可選的,但有了它,TensorFlow能夠自動捕捉因資料次元不一緻導緻的錯誤。

我們現在為模型定義權重<code>W</code>和偏置<code>b</code>。可以将它們當作額外的輸入量,但是TensorFlow有一個更好的處理方式:<code>變量</code>。一個<code>變量</code>代表着TensorFlow計算圖中的一個值,能夠在計算過程中使用,甚至進行修改。在機器學習的應用過程中,模型參數一般用<code>Variable</code>來表示。

我們在調用<code>tf.Variable</code>的時候傳入初始值。在這個例子裡,我們把<code>W</code>和<code>b</code>都初始化為零向量。<code>W</code>是一個784x10的矩陣(因為我們有784個特征和10個輸出值)。<code>b</code>是一個10維的向量(因為我們有10個分類)。

Before <code>Variable</code>s can be used within a session, they must be initialized using that session. This step takes the initial values (in this case tensors full of zeros) that have already been specified, and assigns them to each <code>Variable</code>. This can be done for all <code>Variables</code> at once.

<code>變量</code>需要通過seesion初始化後,才能在session中使用。這一初始化步驟為,為初始值指定具體值(本例當中是全為零),并将其配置設定給每個<code>變量</code>,可以一次性為所有<code>變量</code>完成此操作。

現在我們可以實作我們的回歸模型了。這隻需要一行!我們把向量化後的圖檔<code>x</code>和權重矩陣<code>W</code>相乘,加上偏置<code>b</code>,然後計算每個分類的softmax機率值。

可以很容易的為訓練過程指定最小化誤差用的損失函數,我們的損失函數是目标類别和預測類别之間的交叉熵。

注意,<code>tf.reduce_sum</code>把minibatch裡的每張圖檔的交叉熵值都加起來了。我們計算的交叉熵是指整個minibatch的。

這一行代碼實際上是用來往計算圖上添加一個新操作,其中包括計算梯度,計算每個參數的步長變化,并且計算出新的參數值。

傳回的<code>train_step</code>操作對象,在運作時會使用梯度下降來更新參數。是以,整個模型的訓練可以通過反複地運作<code>train_step</code>來完成。

每一步疊代,我們都會加載50個訓練樣本,然後執行一次<code>train_step</code>,并通過<code>feed_dict</code>将<code>x</code> 和 <code>y_</code>張量<code>占位符</code>用訓練訓練資料替代。

注意,在計算圖中,你可以用<code>feed_dict</code>來替代任何張量,并不僅限于替換<code>占位符</code>。

這裡傳回一個布爾數組。為了計算我們分類的準确率,我們将布爾值轉換為浮點數來代表對、錯,然後取平均值。例如:<code>[True, False, True, True]</code>變為<code>[1,0,1,1]</code>,計算出平均值為<code>0.75</code>。

最後,我們可以計算出在測試資料上的準确率,大概是91%。

在MNIST上隻有91%正确率,實在太糟糕。在這個小節裡,我們用一個稍微複雜的模型:卷積神經網絡來改善效果。這會達到大概99.2%的準确率。雖然不是最高,但是還是比較讓人滿意。

為了建立這個模型,我們需要建立大量的權重和偏置項。這個模型中的權重在初始化時應該加入少量的噪聲來打破對稱性以及避免0梯度。由于我們使用的是ReLU神經元,是以比較好的做法是用一個較小的正數來初始化偏置項,以避免神經元節點輸出恒為0的問題(dead neurons)。為了不在建立模型的時候反複做初始化操作,我們定義兩個函數用于初始化。

TensorFlow在卷積和池化上有很強的靈活性。我們怎麼處理邊界?步長應該設多大?在這個執行個體裡,我們會一直使用vanilla版本。我們的卷積使用1步長(stride size),0邊距(padding size)的模闆,保證輸出和輸入是同一個大小。我們的池化用簡單傳統的2x2大小的模闆做max pooling。為了代碼更簡潔,我們把這部分抽象成一個函數。

現在我們可以開始實作第一層了。它由一個卷積接一個max pooling完成。卷積在每個5x5的patch中算出32個特征。卷積的權重張量形狀是<code>[5, 5, 1, 32]</code>,前兩個次元是patch的大小,接着是輸入的通道數目,最後是輸出的通道數目。 而對于每一個輸出通道都有一個對應的偏置量。

為了用這一層,我們把<code>x</code>變成一個4d向量,其第2、第3維對應圖檔的寬、高,最後一維代表圖檔的顔色通道數(因為是灰階圖是以這裡的通道數為1,如果是rgb彩色圖,則為3)。

We then convolve <code>x_image</code> with the weight tensor, add the bias, apply the ReLU function, and finally max pool. 我們把<code>x_image</code>和權值向量進行卷積,加上偏置項,然後應用ReLU激活函數,最後進行max pooling。

為了建構一個更深的網絡,我們會把幾個類似的層堆疊起來。第二層中,每個5x5的patch會得到64個特征。

現在,圖檔尺寸減小到7x7,我們加入一個有1024個神經元的全連接配接層,用于處理整個圖檔。我們把池化層輸出的張量reshape成一些向量,乘上權重矩陣,加上偏置,然後對其使用ReLU。

為了減少過拟合,我們在輸出層之前加入dropout。我們用一個<code>placeholder</code>來代表一個神經元的輸出在dropout中保持不變的機率。這樣我們可以在訓練過程中啟用dropout,在測試過程中關閉dropout。 TensorFlow的<code>tf.nn.dropout</code>操作除了可以屏蔽神經元的輸出外,還會自動處理神經元輸出值的scale。是以用dropout的時候可以不用考慮scale。

最後,我們添加一個softmax層,就像前面的單層softmax regression一樣。

這個模型的效果如何呢?

為了進行訓練和評估,我們使用與之前簡單的單層SoftMax神經網絡模型幾乎相同的一套代碼,隻是我們會用更加複雜的ADAM優化器來做梯度最速下降,在<code>feed_dict</code>中加入額外的參數<code>keep_prob</code>來控制dropout比例。然後每100次疊代輸出一次日志。

以上代碼,在最終測試集上的準确率大概是99.2%。

目前為止,我們已經學會了用TensorFlow快捷地搭建、訓練和評估一個複雜一點兒的深度學習模型。

本文轉自張昺華-sky部落格園部落格,原文連結:http://www.cnblogs.com/bonelee/p/7903038.html,如需轉載請自行聯系原作者