使用神經網絡識別手寫數字

你能夠在這裏閱讀 上一篇html

neural nets
neural nets

我是薛銀亮,感謝英文原版在線書籍,這是我學習機器學習過程當中感受很是適合新手入門的一本書。鑑於知識分享的精神,我但願能將其翻譯過來,並分享給全部想了解機器學習的人,本人翻譯水平有限,歡迎讀者提出問題和發現錯誤,更歡迎大牛的指導。由於篇幅較長,文章將會分爲多個部分進行,感興趣的能夠關注個人文集,文章會持續更新。git


人類的視覺系統是世界上最美妙的系統之一。來看一下下面的手寫字:程序員

大多數人都能很容易識別出來是這些數字是:504192。在咱們的每個大腦半球,都有一個視覺皮層,也被稱做V1,它包含1.4億個神經元,在他們之間有數以百億級的聯繫。並且不光有V1,一個完整的視覺皮層還有V二、V三、V4和V5,用來處理更加複雜的圖像。這就比如咱們的大腦中有一臺通過幾百萬年進化的超級計算機,用它很是適合理解世界的景象。識別手寫字是很是困難的,可是咱們人類卻能很輕鬆的作到,並且這些全部的工做彷佛也是在無心識的狀況下被執行完成的,因此咱們一般都不會意識到咱們的視覺系統是在處理多麼複雜的問題。算法

若是你嘗試寫一個計算機程序來識別像上面那種數字的話,看起來很簡單可是在咱們動手作的時候去發現忽然變得異常的困難。就像上面的數字9,有一個圈在頂部,和右下部的一豎。當你嘗試定製這樣的程序規則來識別這個9的時候,你可能會很快迷失在各類特殊狀況和意外處理等的沼澤中(緣由是基本每一個人寫的9都是不同的),這看起來是沒有但願的。微信

然而,神經網絡處理這個問題是使用不一樣的方法: 使用大量的手寫數字做爲訓練集合,就像下面這樣的:網絡

mnist_100_digits.png
mnist_100_digits.png

而後開發一個系統,讓它從這個訓練集中學習。換句話說,就是神經網絡使用這些例子來自動推斷手寫數字的規則(就像上面的9那樣的規則)。此外,經過增長訓練集的數量,讓網絡學習更多的手寫字,來提升它的準確性。上面的圖中咱們僅僅展現了100個訓練數字,可是或許咱們須要成千上萬或者上百萬個數字來訓練咱們的網絡才能創建更出色的手寫字識別系統。機器學習

在這個章節咱們會寫一個計算機程序來實現一個神經網絡,並用它來學習手寫數字。這個程序僅僅74行代碼,而且不使用特殊的神經網絡庫。這個程序能在沒有人爲干預的狀況下識別手寫數字,其準確率超過96%。另外,在後面的章節咱們將會開發出將精度提升到99%的程序。實際上,如今有一些很好的商業用途的神經網絡例子,如銀行識別支票、郵局識別地址等。函數

咱們選擇識別手寫字的例子是由於它在學習機器學習的時候是一個傑出的例子原型,這個例子有一個優勢:識別手寫字是一個挑戰,可是它卻沒有使用極其複雜的解決方案和巨大的計算能力。另外,深度學習是一個更佳高級的開發技術。因此在這個系列的文章中咱們會常常回顧識別手寫字的問題。在後面,咱們會討論如何將這些知識應用在其餘的領域,如計算機視覺、語音識別等。工具

固然,若是這裏僅僅寫計算機識別手寫字的話,那麼內容或許會顯得有點少!因此咱們將討論不少關於神經網絡的知識,包括兩種重要的人工神經元(感知器和sigmoid 神經元),標準的神經網絡學習算法是隨機梯度降低算法。在這整個過程當中,我將注重解釋下神經網絡的原理並給你創建神經網絡的概念。這就須要花費很長的篇幅來討論,可是爲了更深層次的理解這是值得的。在本章結束的時候,你能學習到什麼是深度學習已經它的重要性。學習

感知器(Perceptrons)


什麼是神經網絡?首先,我將介紹一種人工神經元叫感知器(perceptron)。它是被科學家 Frank Rosenblatt 在1950年和1960年發明的。可是在工做中更經常使用的是另一種人工神經元,叫 sigmoid neuron。咱們會很快的瞭解到sigmoid神經元,但在這以前,爲了瞭解爲何sigmoid神經元被設計成它的樣式,先花點時間學習perceptrons是很是有必要的。

Perceptrons是如何工做的呢?一個perceptron須要幾個二進制輸入,x1,x2,…,而且產生一個二進制的輸出:

這個例子展現的perceptron有三個輸入,x1,x2,x3。但通常它會有更多或者更少的輸入。Rosenblatt(那個科學家的名字)設計了簡單的規則來計算輸出。他 引入了權重 w1,w2,…,這些值表明了每一個輸入對輸出產生影響的程度大小,也就是表明了每一個輸入的重要性。這個神經元的輸出結果是1或者0,其結果取決於輸入的全部和

大於或者小於一個(threshold)閥值(規定的一個門檻值)。就像權重同樣,閥值也是以一個真實的數字做爲神經元的參數。給出更清晰的表達式:

這就是一個perceptron工做的方式了。

這是一個基本的數學模型。你能夠認爲perceptron是一種經過權衡輸入數據來作決定的裝置。讓我舉個不是特別恰當的例子,可是很是容易理解,而且後面我會舉出更加恰當的例子。假設週末的時候,你的城市將會有一個奶酪節日。你喜歡奶酪,並且正在考慮要不要去參加這個節日,你可能經過三個因素來作出這個決定:

  • 天氣狀況
  • 你男友或女友是否陪你去
  • 是否有便利的交通工具

咱們能夠用三個二進制變量 x一、x2 和x3來表示這三個影響因素。例如,若是天氣好,咱們有x1 = 1,若是天氣很差,x1 = 0。 一樣,若是你的男友或女友想去,x2 = 1,若是不是x2 = 0。 對於x3和公共交通也一樣如此。

如今,假設你超級喜歡奶酪,那麼即便你的男友或女友不陪你去,你也很樂意去參加這個節日。但也許你真的厭惡惡劣的天氣,若是天氣很差,你不可能去參加這個節日。這時候可使用perceptron來對這種決策進行建模。一種方法是爲天氣選擇一個權重w1 = 6,其餘條件選擇w2 = 2和w3 = 2。 w1的值較大表示天氣對你來講很重要,重要性遠遠大於你的男友仍是女友是否陪你去,也大因而否有便利的交通工具。最後,假設你爲perceptron選擇了一個5的閾值。經過這些選擇,perceptron實現所需的決策模型,每當天氣好的時候輸出1,天氣很差的時候輸出0。不管你的男友仍是女友想去,或者公交是否在附近,輸出都沒有什麼不一樣。

經過改變權重和閾值,咱們能夠獲得不一樣的決策模型。例如,假設咱們選擇了一個3的門檻,那麼perceptron會決定在天氣好的時候或交通便利的時候,你的男友或女友願意陪你的時候去參加這個節日。換句話說,這將是一個不一樣的決策模式。下降門檻意味着你更願意去參加這個節日。

顯然,感知器不是一個完整的人類決策模型! 可是,這個例子說明了感知器如何權衡不一樣因素以做出決定。 而彷佛合理的是複雜的感知器網絡可能會作出至關微妙的決定:

在這個網絡中,第一列感知器 :稱之爲第一層感知器 ,它經過權衡輸入的數據,作出三個很是簡單的決定。那麼第二層感知器呢?這些感知器中的每個都經過權衡第一層決策的結果來作出決定。經過這種方式,第二層中的感知器能夠在比第一層中的感知器更復雜和更抽象的層次上作出決定。第三層感知器能夠作出更復雜的決定。經過這種方式,感知器的多層網絡能夠進行復雜的決策。

讓咱們簡化描述感知器的方式。咱們用w⋅x代替全部w和x的乘積之和,這裏的w和x是全部權重和輸入的向量。用b代替閥值threshold,使得b=-threshold。因此perceptron公式能夠寫成:

咱們已經知道,感知器是一種能夠衡量輸入因素權重並做出決定的方法。如今咱們能夠用感知器來表示計算機的基本邏輯函數,例如與非、或、與等函數。下面咱們假設有兩個輸入,每一個輸入的權重都是-2,閥值爲3的感知器:

而後咱們看到,輸入00時,輸出爲1。由於-2) 0 +( - 2) 0 + 3 = 3是正數。相似的,輸入01或10時,輸出1。可是輸入11時,輸出爲0。因此這就實現了與非門NAND的邏輯。

而咱們可使用與非門創建任何的邏輯門陣列。例如,咱們可使用與非門來創建一個電路,它增長了兩個比特x1和x2。 這須要計算按位和,x1⊕x2,當x1和x2均爲1時進位位被設置爲1,即進位位只是乘積x1x2:


爲了獲得和感知器perceptrons等效的網絡圖,咱們用感知器替換上圖中的與非門NAND,感知器包括兩個輸入,每個的權重都是-2,b=3,以下圖(注意下圖省略了一點右下方的部分NAND圖,以便更加容易畫出來):

咱們將圖中carry部分的兩個輸入變成一個,只須要將權重變爲原來的2倍便可:

咱們再將變量也變成一個輸入層:

感知器的計算廣泛性(就像上面的加法器)同時讓人放心有失望。這使人放心的是,感知器網絡能夠像其餘任何計算設備同樣強大。但也使人失望,由於它讓人以爲感知器只是一種新型的NAND門,而不是什麼大新聞!

可是,事實證實,咱們能夠設計出自動調整網絡的權重和誤差的學習算法。這種調整是響應外部刺激而發生的,沒有程序員的直接干預。這些學習算法使咱們可以以與傳統邏輯門徹底不一樣的方式使用人造神經元。咱們的神經網絡能夠簡單地學會解決問題,而不是直接設計一個NAND或其餘門電路。

Sigmoid 神經元


學習算法聽起來是很是糟糕的。可是咱們要如何爲神經網絡設計算法呢?假設咱們有一個感知器網絡,並想用它來解決一些問題。例如,輸入手寫數字圖片的像素數據,咱們但願經過神經網絡學習調整權重和b的值來正確的識別這些數字。咱們來看一下神經網絡是怎麼學習的,當咱們調整權重的值時,相應的會改變一些輸出的值。因此,目標就變得能夠學習了,學習的過程就是不斷調整權重和b的值,來改變輸出的值,使其輸出更加接近真實的值:

例如:當神經網絡錯誤的把9識別成8時,咱們能夠經過稍微調整權重和b的值,是其輸出結果爲9.咱們不斷的這麼作,直到結果更加好,這就是網絡的學習。

可是當權重和b的值有一個小的調整的時候可能會形成感知器的輸出徹底的反轉,也就是說從0到1.這可能會形成網絡的工做變的失控,例如當識別9正確了,可是卻形成了別的值識別發生很大的錯誤。或許咱們會有一些更加聰明的作法來規避這個問題。

咱們可以解決這個問題經過介紹一種新型的人工神經元叫作:sigmoid神經元。它和perceptron 是類似的,可是不一樣之處是當權重和b的值作了小的改動時對於輸出的結果也只是小小的改變。正是由於這個特性,咱們才選擇了它。

OK,讓咱們來看一下sigmoid神經元吧。讓咱們就像畫perceptions時同樣來畫一下sigmoid神經元:

同perceptron同樣,sigmoid神經元有一些輸入:x一、x2...可是不一樣的是這些輸入的值由原來的0或者1替換爲0到1之間的值。例如:0.638...等等都是有效的輸入值,sigmoid神經元對每個輸入值也有權重,w一、w2...,而且也有閥值b。可是輸出不是0或1了,而是σ(w⋅x+b),這個σ被叫作sigmoid函數的統稱,並被定義爲:

把上面的z值用咱們定義的輸入、權重和b值替換就獲得以下公式:

若是你是第一次看到相似的數學公式,可能會以爲這個超難懂,可是實際上,sigmoid神經元和perceptrons有不少的類似處,只是sigmoid神經元能夠看到更多的細節。

爲了理解sigmoid神經元和perceptrons的類似之處,假設z≡w⋅x+b是一個超大的數,那麼e^−z≈0(注意這裏的表明冪運算),也就是說σ(z)≈1。換句話說,當z=w⋅x+b這個值很大的時候,輸出就是1,相反的當z=w⋅x+b很小的時候,輸出就是0,這樣的行爲是否是和perceptrons的輸出很類似呢。那麼咱們如何理解σ函數呢?下面是它的形狀:


這個形狀是階梯函數的平滑輸出版:

若是σ是一個階梯函數,那麼sigmoid神經元就是perceptron,由於其輸出會依賴於w⋅x+b是大仍是小而變成1或者0。可是真實的sigmoid神經元能夠看做是一個平滑版的perceptron。這就是說,當試圖改變權重Δwj和b的值Δb時,輸出值也會有一些小的改變Δoutput。而不會像perceptrons那樣發生反轉突變。咱們能夠將Δoutput的變化理解成下面的公式:

上面的公式表明的是:全部的權重改變量和輸出相對於權重偏導數的乘積之和加上輸出相對於b的偏導數與 Δb的乘積。若是你對偏導數感受到難以理解也不要慌張,你能夠理解成Δoutput相對與Δwj 和 Δb的改變時線性的函數,也就是當稍微改變權重和b的時候,其輸出也會跟着有一點小的改變。

咱們應該如何理解sigmoid的輸出呢?顯然,和感知器不一樣的是,sigmoid神經元不只僅輸出0和1,它能輸出0到1之間的任何實數。這個特性很是有用,例如,咱們想使用輸出表明神經網絡中像素的強度的時候就能派上用場。但有時候也可能會帶來干擾,例如咱們想知道輸出是否是9的時候,這時候輸出是小數倒不如直接輸出0或者1來的直接。但在實踐中,咱們能夠約定一個值來處理這個問題,例如,決定把輸出大於0.5的值表明成9,而小於0.5表明不是9(這裏的9只是判斷的一個例子而已)。

搭建神經網絡


接下里的一小節我會介紹一個能很好處理識別手寫字的神經網絡。前期準備,咱們須要理解一下網絡各個部分的專業術語。例如咱們有這樣一個網絡:

最左邊一列叫作輸入層(input layer),該層的神經元稱爲輸入神經元層。最右邊或輸出層稱爲輸出神經元(output neurons),例子中只有一個輸出神經元。中間這一層叫作隱藏層(hidden layer),例子中只有一個隱藏層,可是不少網絡都有多個隱藏層。例以下面圖中的4層網絡中有2個隱藏層:

因爲歷史緣由,多層網絡也常常被稱爲多層perceptrons或者MLPs,可是這裏咱們只使用單層網絡,只是想提醒你一下多層結構的存在。

一般設計網絡中的輸入和輸出層是容易的。例如,判斷一個數字是否是9的時候。若是一個image是64*64像素的灰度圖,咱們就能夠有4,096=64×64個輸入神經元,其灰度能夠在0和1之間適當調整。輸出層會只有一個神經元。其值小於0.5表明圖片不是9,大於0.5表明圖片是9.

隱藏層的設計就不那麼容易了,不可能經過幾個簡單的經驗法則就總結出來隱藏層的設計過程。神經網絡的研究人員已經爲隱藏層提供了許多設計方案,來幫助咱們從網絡中獲取咱們想要的行爲。例如,能夠根據網絡學習的時間長短來衡量隱藏層的數量等等。

在神經網絡中,若是一層的輸出被用做下一層的輸入,這種網絡叫作前饋神經網絡(feedforward neural networks)。意思就是信息老是前饋,沒有反饋。可是仍是存在有反饋的人工神經網絡模型。這些模型被稱爲遞歸神經網絡(recurrent neural networks)。在這種模型中,神經元在必定時間內被激活。可是這裏咱們將集中精力在前饋神經網絡上。

識別手寫數字的一個簡單網絡


定義了神經網絡了,讓咱們回到手寫字的識別上。咱們把識別手寫字分紅兩個子問題。第一,咱們須要將一系列的數字圖片分紅一個個的,例如咱們想要將下面的圖片:

區分紅6部分圖片:

第二,當圖片被分開之後,程序就須要開始識別每個數字了,例如第一個數字:

咱們會將重點放在解決更難的問題上,就是識別手寫數字。咱們將會使用一個3層的神經網絡:

上圖中輸入是784=28×28的圖片的像素灰度值,可是圖中爲了表示方便省略了不少輸入神經元。輸入中的像素灰度值0表明白色,1.0表明黑色,0和1之間的值表明逐漸變黑的灰色。

第二層是隱藏層。咱們用n表明隱藏層的神經元數量,上圖中n=15.

輸出層有10個神經元。若是output=1,表明數字是0.

你可能會說表明10個數字爲何要用10個輸出神經元呢?用4個不就能夠表示了麼,由於2的4次方是16大於10,足以表示10個數字了。可是實驗代表,用10個確實比用4個的學習效果好的多。

讓咱們來看一下第一個輸出的神經元,這個神經元決定數字是否是0.它經過權衡來自隱藏層的證據來判斷這一點,那麼隱藏層是怎麼作的呢?隱藏層中的第一個神經元檢測是否存在以下的圖片:

隱藏層經過對目標區域(重疊的區域)進行加權,對其餘區域進行輕微加權來實現識別這個圖。以這種方式,隱藏層中的第2、第三和第四層來檢測下面的圖片:

這四部分就是把0分割成的四部分:

若是這個神經網絡使用這樣的方式來識別數字的,那麼咱們來解釋一下上面的問題,爲何10個輸出比4個好。若是有4個輸出,那第一次輸出的神經元將須要決定圖像和哪一個數字的某一個部位更類似,這將會很難處理,由於數字的每一部分組件的形狀很難和某個數字密切相關。

學習梯度降低


如今咱們來設計咱們本身的神經網絡,第一件事就是咱們須要收集數據。咱們會使用MNIST data set.關於這個數據集的詳解能夠去到官網瞭解。

咱們會使用MINIST中的60000個數據進行訓練,稱之爲訓練集。而後用10000個數據進行測試,稱之爲測試集。

咱們用x表明訓練輸入,它是28×28=784的多維向量。每個向量表明一張圖片的像素灰度值。用 y=y(x)表明輸出,是一個10維的向量,例如當輸入x=6時,輸出y(x)=(0,0,0,0,0,0,1,0,0,0)^T,注意T表明向量的反轉(不懂向量相似操做的讀者能夠先去學習線性代數的基礎知識),將橫向量轉換成列向量。

咱們須要怎麼樣調整權重和b的值才能使全部的輸入x對應的輸出都最接近真實的值呢?這裏咱們定義損失函數:

式子中w表示權重,b表示閥值,n表示輸入個數,a表示x的輸入時的輸出向量。輸出a依賴於w、b和x的值。爲了表示簡單, ‖v‖表明v向量的長度。咱們把C叫作二次方損失函數;它也叫作均方偏差MSE。C是非負的,若是輸出的值a越接近真實值,那麼C的值就會越小C(w,b)≈0。因此咱們的目的就是找到一種最好的權重和b值,使得C的值接近0.咱們將使用梯度降低算法來實現這一點。

Okay,咱們用C(v)代替C(w,b),目的是爲了表示這多是任何函數。爲了最小化C(v),咱們想象一下一個方程只有兩個變量,咱們稱之爲V1和V2:

咱們想要找到C的最小點,這時候咱們可使用微積分來試圖尋找這個最小點。咱們可使用導數來尋找C的極值點,對於只有幾個變量的函數這樣彷佛是行得通的,可是對於大型神經網絡的成本函數依賴於數十億的權重和極其複雜的b值,使用微積分來最小化這個是行不通的,會致使運算量極其龐大。

幸運的是,有一種算法或許可行。假設你在山谷裏,想要尋找一條下山最快的路,咱們能夠經過計算C的導數來模擬這種清況。爲了使問題簡單化,咱們來思考一下V1和V2發生一點小變化的時候咱們會在山谷裏如何移動?微積分告訴咱們C的變化以下:

咱們的目的就是選擇Δv1和Δv2讓ΔC最小。咱們把v 的變化定義爲向量, Δv≡(Δv1,Δv2)^T,那麼C的梯度的偏導數是∇C:

有了這些定義,表達式(7)就能夠寫成:

這個式子更能表示爲何∇C被稱爲梯度向量:∇C影響着v的改變對C改變的程度。可是不要忘記咱們的目的,是選擇Δv使ΔC最小,特別的,假設咱們選擇:

其中η是一個小的正參數(稱爲學習率)。 那麼等式(9)ΔC≈-η∇C⋅∇C=-η‖∇C‖2。 由於||C||2≥0,則ΔC≤0,即C老是減少,從不增長。 這正是咱們想要的目的!所以,咱們將採用方程(10)來定義咱們的梯度降低算法中球的「運動定律」。 也就是說,咱們將使用等式(10)來計算Δv的值,而後將球的位置v移動:

只要咱們不斷的迭代,就能使C一直減少,直到咱們達到最小點。

爲了使梯度降低正確的工做,咱們須要選擇學習速率η使得公式(9)足夠的小,若是咱們不這麼作,可能會形成ΔC>0,這明顯是很差的。同時咱們也不能讓η過小,那會形成算法執行太慢。

以上解釋了當C只有兩個變量時的梯度降低,可是實際中C會有不少的變量,假設有m個,v1,…,vm。這時C的變化量ΔC由 Δv=(Δv1,…,Δvm)^T產生:

梯度向量∇C是:

僅僅有兩個變量時,咱們能選擇:

爲了保證公式(12)最小,咱們遵循梯度降低的方法,發現即便是在C有不少變量的時候,也能夠經過迭代更新規則達到目的:

你能夠將此方法稱爲梯度降低算法。這種方法給咱們提供了一種經過不斷改變v尋找最小C的方法。可是這個方法不是在全部狀況都有效的,在後面咱們會提到一些狀況。可是在實踐中,咱們會發現這是一種使成本函數最小化的有效方式。

那麼咱們怎麼在神經網絡中使用梯度降低呢?在公式(6)中咱們用權重和b來替換v,梯度向量 ∇C有兩個組件 ∂C/∂wk 和 ∂C/∂bl,分開寫就是:

咱們看一下公式(6),注意一下損失函數的格式:

這是對每個訓練例子的平均消耗:

可是當訓練數據集過大的時候,計算量也會太大,消耗時間太長。

這時,咱們能夠經過隨機梯度降低來加速學習,方法是經過隨機的從訓練數據集中選擇小樣本計算梯度∇C,經過對這個小樣本平均咱們就能夠快速獲得真實的梯度∇C估計,這有助於加速梯度降低從而學習。

來讓隨機梯度降低更加的清晰點,隨機的從訓練集中挑選m個輸入X1,X2,…,Xm,咱們但願選擇的m的大小足夠可使這些數據的梯度平均值等於全部數據的梯度:

因此:


證明咱們能夠經過爲隨機選擇的小批量計算梯度來估計總體梯度。

隨機梯度降低中的權重和b的變化:

若是你認真閱讀完這篇文章,應該能夠從中學習到一些有關機器學習的基礎入門概念,那麼下一篇文章就讓咱們根據這裏學到的知識來實現識別手寫數字的神經網絡吧。

若是個人文章對你有幫助,我建議你能夠關注個人文集或者打賞,我建議打賞¥5。固然你也能夠隨意打賞。

微信
微信

支付寶
支付寶

下一篇

若是有問題,歡迎跟我聯繫討論space-x@qq.com.我會盡可能及時回覆。

相關文章
相關標籤/搜索