1. 程式人生 > >第一章: 利用神經網路識別手寫數字

第一章: 利用神經網路識別手寫數字

人類視覺系統是大自然的一大奇蹟。 考慮下面的手寫數字序列:

大部分人能夠毫不費力的識別出這些數字是 504192。這種簡單性只是一個幻覺。在我們大腦各半球,有一個主要的視覺皮層,即V1,它包含1.4億個神經元以及數以百億的神經元連線。而且人類不只是有V1,還有一系列的視覺皮層——V2,V3,V4和V5,它們能夠執行更加複雜的影象處理。我們可以將大腦想象成一臺超級計算機,在幾億年的進化中不斷改進,最終非常適合理解這個視覺世界。要識別手寫數字不是一件非常容易的事。然而,我們人類卻能非常驚人的通過我們的眼睛理解所看到的一切,但是幾乎所有的工作都是在不知不覺中完成的,以至於我們不會讚歎我們的視覺系統解決的問題有多麼艱難。

視覺模式識別困難在於如何讓計算機程式識別上面數字變得顯而易見。看上去非常簡單的事操作起來卻變得非常困難。我們識別這個形狀的簡單直覺是——“一個9頭上有一個圓圈,右下角有一筆豎線”——但是對於識別演算法卻不是那麼簡單。當試圖讓這些規則更加精確,你將迅速迷失在大量的例外、警告和特殊案例,而且似乎看不到解決的希望。

神經網路用不同的方法來處理這個問題。它的思想就是利用大量的手寫數字(訓練樣本),

然後開發出一套從訓練樣本中進行學習的系統。換句話說,神經網路使用樣本來自動推理出識別手寫數字的規則。此外,通過增加訓練樣本規模,神經網路能學到手寫體的更多規則從而提升它的識別精度。因此在我們像上面一樣只有100張訓練數字同時,有可能我們能通過成千上萬更多的訓練樣本來構建更好的手寫識別演算法。

本章我們將編寫一段計算機程式來實現一個能識別手寫數字的神經網路。這個程式大概有74行,而且沒有使用其他特別的神經網路庫,但是這段程式能夠具有96%的數字識別精度,而且沒有人工干預。此外,在後面的章節中我們將改進演算法達到99%以上的精度。實際上,最好的商業神經網路已經很好的應用在銀行支票處理以及郵局識別地址等。

我們專注在手寫體識別是因為它是一個很好的學習神經網路的原型問題。做為一個原型,它剛好合適:識別手寫數字是一個挑戰,不是那麼容易,而它也不需要一個極其複雜的方案或者巨大的計算能力。此外,它也是開發更多高階技術的好方法,比如深度學習。因此整本書我們將不斷重複回到手寫識別這個問題。這本書的後面,我們將討論這些演算法思想如何應用到計算機視覺的其他問題,還包括語言識別、自然語言處理和其他領域。

當然,如果本章只是寫一個識別手寫數字的計算機程式,那麼將非常短小!在編寫過程中,我們將介紹很多神經網路的關鍵思想,包括人工神經網路的兩大類別(感知器和sigmoid神經元)以及神經網路標準學習演算法,即隨機梯度下降。整個過程我們將關注在闡述為什麼這樣處理是有效的,從而讓你構建起神經網路直覺。這比只陳述基礎的機制更加冗長,但這對更深入理解所學到的內容是值得的。在這些收穫上,本章結束你將明白深度學習是什麼,且它為什麼這麼重要。

什麼是神經網路?在開始之前,我將介紹一種人工的神經元,即感知器(perceptron)。感知器是由Frank Rosenblatt 在上世紀50到60年代發明的 , 靈感來源於 Warren McCulloch 和 Walter Pitts 的早期工作。今天人工神經網路使用更加通用的其他模型,本書以及許多現代的神經網路工作中,主要的神經網路模型是sigmoid神經元。 我們將很快討論sigmoid神經元,但是為了更好了解它的原理,我們首先要理解感知器。

那麼感知器如何工作呢?一個感知器獲取幾個二進位制輸入$x_1, x_2, \ldots$,並且產生一個二進位制數出:

在這個例子中,感知器具有三個輸入$x_1, x_2, x_3$。通常它會具有更多或更少的輸入。Rosenblatt 提出了一個簡單規則來計算最後數出。他引入了權重(weights) $w_1,w_2,\ldots$,這些實數表示各個輸入對輸出的重要性。這個神經元輸出(output) $0$ 或者 $1$ 是由這些輸入的加權求和 $\sum_j w_j x_j$ 是否大於或者小於某一個閾值(threshold)。不像這些權重,閾值是這個神經元的實數引數。將它們放入更加精確的代數術語中: \begin{eqnarray} \mbox{output} & = & \left\{ \begin{array}{ll} 0 & \mbox{if } \sum_j w_j x_j \leq \mbox{ threshold} \\ 1 & \mbox{if } \sum_j w_j x_j > \mbox{ threshold} \end{array} \right. \tag{1}\end{eqnarray} 這就是一個感知器如何工作的全部內容!

這是一個基礎的數學模型。你可以這麼理解感知器,它是一個通過加權憑據來進行決策的裝置。讓我們來看這個例子,可能它不是一個真實的例子,但是非常好理解,後面我們將很快進入真實的例子。假設週末到了,你聽說在你所在的城市將有一個乳酪節,你很喜歡吃乳酪,並且正決定是否要去參加這個節日,你可能會通過以下三個方面來權衡你的決定:

  1. 天氣好嗎?
  2. 你的男(女)朋友是否願意陪你去?
  3. 是否這個活動距離公共交通很近?(你自己沒車)
我們將這三個因素用對應的二進位制變數$x_1, x_2$和$x_3$表示。比如,當天氣還不錯時,我們有$x_1 = 1$ ,天氣不好時$x_1 = 0$;相似的,如果男或女朋友願意去,$x_2 = 1$,否則$x_2 = 0$;對於公共交通$x_3$同理賦值。

現在假設乳酪是你的最愛,以致於即便你的男或女朋友不感興趣而且去那裡也不太方便,你仍然非常想去參加這個節日活動。但是也許你真的討厭壞天氣,而且如果天氣很糟,你也沒辦法去。你能使用感知器來模擬這類決策。一種決策方式是,讓天氣權重 $w_1 = 6$,其他條件權重分別為$w_2 = 2$,$w_3 = 2$。權重$w_1$值越大表示天氣影響最大,比起男或女朋友加入或者交通距離的影響都大。最後,假設你選擇5做為感知器閾值,按照這種選擇,這個感知器就能實現這個決策模型:當天氣好時候輸出$1$,天氣不好時候輸出$0$,無論你男或女朋友是否願意去,或者交通是否比較近。

通過更改權重和閾值,我們能得到不同的決策模型。例如,我們將閾值設為$3$,那麼感知器會在以下條件滿足時決定去參加活動:如果天氣很好、或者男(女)朋友願意去並且交通很近。換句話說,它將是決策的不同模型,閾值越低,表明你越想去參加這個節日活動。

顯然,這個感知器不是人類決策的完整模型!但是這個例子說明了一個感知器如何能將各種憑據進行加權和來制定決策,而且一個複雜的感知器網路能做出非常微妙的決策:

在這個網路中,第一感知器(我們稱其為第一層感知器)通過加權輸入憑據來做出三個非常簡單的決策。那第二列感知器是什麼呢?其中每一個感知器都是通過將第一列的決策結果進行加權和來做出自己的決策。通過這種方式,第二層感知器能夠比第一層感知器做出更加複雜和抽象層的決策。第三層感知器能做出更加複雜的決策,以此類推,更多層感知器能夠進行更加複雜的決策。

順便說一句,當我們定義感知器時,它們都只有一個輸出。但上面的網路中,這些感知器看上去有多個輸出。實際上,它們也仍然只有一個輸出,只不過為了更好的表明這些感知器輸出被其他感知器所使用,因此採用了多個輸出的箭頭線表示,這比起繪製一條輸出線然後分裂開更好一些。

讓我們簡化描述感知器的方式。加權求和大於閾值的條件 $\sum_j w_j x_j > \mbox{threshold}$比較麻煩,我們能用兩個符號變化來簡化它。第一個改變是將 $\sum_j w_j x_j$ 改寫成點乘方式,$w \cdot x \equiv \sum_j w_j x_j$,其中$w$ 和 $x$ 分別是權重和輸入的向量。第二個改變是將閾值放在不等式另一邊,並用偏移 $b \equiv -\mbox{threshold}$ 表示。通過使用偏移代替閾值,感知器規則能夠重寫為: \begin{eqnarray} \mbox{output} = \left\{ \begin{array}{ll} 0 & \mbox{if } w\cdot x + b \leq 0 \\ 1 & \mbox{if } w\cdot x + b > 0 \end{array} \right. \tag{2}\end{eqnarray} 你可以將偏移想象成使感知器如何更容易輸出 $1$,或者用更加生物學術語,偏移是指衡量感知器觸發的難易程度。對於一個大的偏移,感知器更容易輸出 $1$。如果偏移負值很大,那麼感知器將很難輸出 $1$。顯然,引入偏移對於描述感知器改動不大,但是我們後面將看到這將簡化符號描述。正因如此,本書剩下部分都將使用偏移,而不是用閾值。

我們已經介紹了用感知器,基於憑據(evidence)的加權求和來進行決策。感知器也能夠被用來計算基本邏輯函式,像 ANDOR, and NAND這些通常被看做是實現計算的基礎。比如說, 假設我們有一個兩個輸入的感知器,每個輸入具有權重$-2$,整個偏移為$3$,如下所示感知器:

那麼我們將看到輸入$00$會產生輸出$1$,因為$(-2)*0+(-2)*0+3=3$是正數。這裡,我們引入$*$符號來進行顯示乘法。與此類似,輸入$01$和$10$都將產生輸出$1$。但是輸入$11$將產生輸出$0$,因為$(-2)*1+(-2)*1+3=-1$是負數。因此這個感知器實現了一個NAND門!

這個NAND 門例子表明我們能夠使用感知器來計算簡單的邏輯函式。實際上,我們能夠使用感知器網路來計算任意的邏輯函式。原因是NAND 門在計算中是通用的,也就是,我們能夠用一些NAND 門來構造任意計算。比如,我們能夠用NAND門來構建兩個二進位制變數$x_1$和$x_2$的位加法電路。這需要對它們按位求和$x_1+x_2$,同時當$x_1$和$x_2$都是$1$時候進位$1$。進位就是$x_1$和$x_2$的按位乘:

為了得到相同的感知器網路,我們將NAND門用具有兩個輸入的感知器代替,每一個輸入具有權重$-2$,整體具有偏移$3$。以下是感知器網路結果,注意我們把右下角的NAND門對應的感知器向左移動了一點,以便繪製圖表中的箭頭:
值得注意的是,這個感知器網路中最左邊的感知器輸出被右下角的感知器使用了兩次。當我們定義感知器模型時,我們說這種雙倍輸出到同一個感知器是不允許的。實際上,這沒什麼關係,如果我們不允許這種情況,只需要把這兩條線合併成一條線,並且將權重設定為-4即可。(如果不這麼認為,那麼你需要停下來並證明一下它們等同性)在這點小改變後,感知器網路如下所示,沒有標籤的權重為-2,所有偏移為3,唯一權重為-4的邊進行了標記: 直到現在,我們將變數輸入$x_1$和$x_2$繪製到感知器網路的左邊。實際上,更變的應該繪製一層額外的感知器——輸入層(input layer)來編碼輸入:
這個只有一個輸出,沒有輸入的輸入感知器,
是一個簡寫。這麼看,假設我們有一個沒有輸入的感知器,那麼權重和 $\sum_j w_j x_j$ 將始終為$0$,那麼如果偏差$b > 0$,感知器將輸出$1$,否則輸出$0$。也就是,這個感知器將簡單的輸出一個固定的值,而不是希望的值(如上面的例子中$x_1$)。更好的理解輸入感知器可以將其不當作感知器,而是一個簡單定義期望值$x_1, x_2,\ldots$輸出的特殊單元。

這個加法器例子演示了感知器網路能夠模擬具有許多與非門的電路。並且由於與非門對於計算是通用的,因此感知器也是對計算通用的。

感知器的計算通用性同時讓人滿意,也讓人失望。它讓人滿意是因為感知器網路能夠像其他計算裝置那麼強大,但是也是讓人失望是因為它看起來只是一種新的與非門,不是什麼大新聞!

不過,情況比這個觀點更好一些。它能夠讓我們設計學習演算法,這種演算法能夠自動調整人工神經網路的權重和偏移。這會在響應外部刺激時候發生,而且沒有程式設計師的直接干預。這些學習演算法能讓我們用一種新的方式使用人工神經網路,它將與傳統的邏輯閘方法完全不同。替代顯示的擺放一個具有與非門和其他門的電路,我們的神經網路能夠學會簡單地解決這些問題,有些問題對於直接設計 傳統電路來說非常困難。

學習演算法聽起來好極了。但是我們如何對一個神經網路設計演算法。假設我們有一個想通過學習來解決問題的感知器網路。比如,網路的輸入可能是一些掃描來的手寫數字影象原始畫素資料。且我們希望這個網路能夠學會調權和偏移以便正確的對它們進行數字分類。為了看到學習如何進行,假設我們在網路中改變一點權重(或者偏移)。我們希望很小的權重改變能夠引起網路輸出的細微改變。後面我們將看到,這個特性將使學習成為可能。以下示意圖就是我們想要的(顯然這個網路對於手寫識別太簡單!):

如果權重或者偏移的細小改變能夠輕微影響到網路輸出,那麼我們會逐步更改權重和偏移來讓網路按照我們想要的方式發展。比如,假設網路錯誤的將數字“9”的影象識別為“8”,我們將指出如何在權重和偏移上進行細小的改動來使其更加接近於“9”。(tensorfly.cn社群原創)並且這個過程將不斷重複,不斷地改變權重和偏移來產生更好的輸出。於是這個網路將具有學習特性。

問題是在我們的感知器網路中並沒有這樣發生。實際上,任何一個感知器的權重或者偏移細小的改變有時都能使得感知器輸出徹底翻轉,從$0$到$1$。這種翻轉可能導致網路剩餘部分的行為以某種複雜的方式完全改變。因此當“9”可能被正確分類後,對於其他圖片,神經網路行為結果將以一種很難控制的方式被完全改變。這使得很難看出如何逐漸的調整權重和偏移以至於神經網路能夠逐漸按照預期行為接近。也許對於這個問題有一些聰明的方式解決,但是目前如何讓感知器網路學習還不夠清楚。

我們能通過引入一種新的人工神經元(sigmoid神經元)來克服這個問題。它和感知器類似,但是細微調整它的權重和偏移只會很細小地影響到輸出結果。這就是讓sigmoid神經元網路學習的關鍵原因。

好的,讓我們來描述一下這個sigmoid神經元。我們將用描繪感知器一樣來描繪它:

就像感知器,sigmoid神經元有輸入$x_1, x_2, \ldots$。但是輸入值不僅是$0$或者$1$,還可以是$0$$1$的任意值。因此,比如$0.638\ldots$對於sigmoid神經元是一個合理的輸入。也像感知器一樣,sigmoid神經元對每一個輸入都有對應的權重$w_1, w_2, \ldots$,還有一個全域性的偏移$b$。但是輸出不是$0$或者$1$,而是$\sigma(w \cdot x+b)$,$\sigma$是sigmoid函式**順便說一下,$\sigma$有時叫做邏輯函式,並且這種新的神經元叫做邏輯神經元。記住這個術語非常重要,因為許多研究中的神經網路都在使用它。,定義如下:\begin{eqnarray} \sigma(z) \equiv \frac{1}{1+e^{-z}}. \tag{3}\end{eqnarray}把這些更直接的放在一起,具有輸入$x_1,x_2,\ldots$,權重$w_1,w_2,\ldots$和偏移$b$的sigmoid神經元輸出為: \begin{eqnarray} \frac{1}{1+\exp(-\sum_j w_j x_j-b)}. \tag{4}\end{eqnarray}

眨眼一看,sigmoid神經元跟感知器非常不同。對於不熟悉的人來說,sigmoid函式的代數形式看起來很晦澀和令人生畏。實際上,感知器和sigmoid神經元有很多相似之處,並且sigmoid函式的代數形式展現了更多的技術細節。

為了理解和感知器模型的相似點,假設$z \equiv w \cdot x + b$是一個大的正數,那麼$e^{-z} \approx 0$且$\sigma(z) \approx 1$。換句話說,當$z = w \cdot x+b$是一個很大的正數,sigmoid神經元的輸出接近於$1$,這和感知器結果類似。另外假設$z = w \cdot x+b$是一個很小的負數,那麼$e^{-z} \rightarrow \infty$,並且$\sigma(z) \approx 0$。因此當$z = w \cdot x +b$是一個很小負數時候,sigmoid神經元和感知器的輸出也是非常接近的。只有當$w \cdot x+b$在一個適度的值,sigmoid神經元和感知器偏差才較大。

$\sigma$函式的代數形式是什麼?我們如何理解它呢?實際上,$\sigma$函式的準確形式不太重要——真正有用的是繪製的函式形狀,看看下面這個形狀:

-4-3-2-1012340.00.20.40.60.81.0zsigmoid function

這是一個階躍函式的平滑版本:

-4-3-2-1012340.00.20.40.60.81.0zstep function

如果$\sigma$函式實際上是一個階躍函式,那麼sigmoid神經元就一個感知器,因為輸出是$1$或者$0$,取決於$w\cdot x+b$是正還是負。**實際上,當$w \cdot x +b = 0$時,感知器趨於$0$,而階躍函式輸出$1$。所以,嚴格的說,我們只需要在一個點上修改階躍函式。使用上面我們說明的實際$\sigma$函式,將得到一個平滑的感知器。實際上,$\sigma$函式的平滑程度是至關重要的,而不是它的具體形式。$\sigma$函式越平滑意味著權重微調$\Delta w_j$和偏移微調$\Delta b$將對神經元產生一個細小的輸出改變$\Delta \mbox{output}$。實際上,微積分告訴我們細小的輸出$\Delta \mbox{output}$近似等於: \begin{eqnarray} \Delta \mbox{output} \approx \sum_j \frac{\partial \, \mbox{output}}{\partial w_j} \Delta w_j + \frac{\partial \, \mbox{output}}{\partial b} \Delta b, \tag{5}\end{eqnarray}其中總和基於所有的權重$w_j$,且$\partial \, \mbox{output} / \partial w_j$和$\partial \, \mbox{output} /\partial b$分別表示$\mbox{output}$基於$w_j$和$b$的偏導數。不用見到偏導就驚慌!雖然上面具有偏導運算的表示式看起來很複雜,但實際上很簡單(這是一個好訊息):輸出改變$\Delta \mbox{output}$是權重和偏移改變$\Delta w_j$和$\Delta b$的線性函式。這種線性使得權重和偏移的細微改變就能很容易使得輸出按期望方式微小改變。因此sigmoid神經元具有和感知器同樣的定性行為,同時還能方便的找出權重與偏移如何對輸出的產生影響。

如果$\sigma$函式的形狀的確起作用,而不是其精確的形式,那麼在表示式(3) 中為什麼使用這種形式呢?實際上在本書後面,我們將偶爾考慮神經元的輸出函式為$f(w \cdot x + b)$來做為啟用函式$f(\cdot)$。主要的改變是當我們使用不同的啟用函式之後,等式(5)中的偏導值將跟隨改變。結果就是當我們後面計算偏導時,使用$\sigma$函式將簡化代數計算,因為指數形式區別於其他具有更好的偏導特性。在任何情況下,$\sigma$函式在神經網路中被廣泛使用,在本書中也是我們經常使用的激勵函式。

我們如何解讀sigmoid神經元輸出?顯然,感知器和sigmoid神經元最大的區別是sigmoid神經元不是隻輸出$0$或者$1$。它能夠輸出$0$到$1$之間任意實數,如$0.173\ldots$和$0.689\ldots$都是合理的輸出。這將非常有用,比如,如果你想使用輸出值來表示神經網路中一張輸入影象畫素的平均強度。但是有時候這又非常討厭。假設我們想從神經網路中指明要麼“輸入影象是9”或者“輸入影象不是9”。顯然像感知器一樣只輸出$0$或$1$更容易處理。但實際上,我們將建立一個約定來處理這個問題,比如,將至少是$0.5$的輸出當做為“9”,其他比$0.5$小的輸出當做為“非9”。我們使用這樣約定總是明確的,因此不會產生如何混淆。

練習

  • Sigmoid神經元模擬感知器,第一部分 $\mbox{}$ 
    假設我們將感知器網路中的權重和偏移乘以一個正常數$c > 0$。證明該網路的輸出行為不會改變。
  • Sigmoid神經元模擬感知器,第二部分 $\mbox{}$ 
    假設和感知器網路最後一個問題一樣,整個輸入都設定好,不需要真實的輸入值,只需要固定輸入值。假設對於網路中某些特殊感知器輸入$x$,使得$w \cdot x + b \neq 0$。將這些感知器用神經元替換,那麼再將權重和偏移乘以一個正常數$c > 0$,可以證明當$c \rightarrow \infty$ 時,神經元網路和感知器網路的行為將是一樣的。那麼對於感知器中$w \cdot x + b = 0$的那些呢,是否不成立?

在下一區段,我將介紹用神經網路能夠很好的對手寫數字進行區分。先做點準備,讓我們命名一下網路中不同部分的術語。假如我們有如下網路:

就像先前說的,網路的最左邊一層被稱為輸入層,其中的神經元被稱為輸入神經元。最右邊及輸出層包含輸出神經元,在這個例子中,只有一個單一的輸出神經元。中間層被稱為隱含層,因為裡面的神經元既不是輸入也不是輸出。“隱含”這個術語可能聽起來很神祕——當我第一次聽到時候覺得一定有深層的哲學或者數學意義——但實際上它只表示“不是輸入和輸出”而已。上面的網路只包含了唯一個隱含層,但是一些網路可能有多層。比如,下面的4層網路具有2個隱含層:
令人困擾的是,由於一些歷史原因,這樣的多層網路有時被叫做多層感知器或者MLPs儘管它是由sigmoid神經元構成的,而不是感知器。在這本書中,我將不會使用MLP,因為我覺得它太混淆了,但是提醒你它可能在其它地方出現。

網路中的輸入和輸出層設計通常很簡單,比如,假設我們試著確定手寫圖片是否代表“9”。設計網路更自然的方式是將影象畫素強度編碼進輸入神經元。如果影象是一幅$64$ by $64$的灰度圖,那麼我們將有$4,096 = 64 \times 64$個輸入神經元,每一個是介於$0$和$1$的強度值。輸出層將只包含一個神經元,輸出值小於$0.5$表示“輸入影象不是9”,大於$0.5$表示“輸入影象是9”。

雖然神經元網路的輸入輸出層很簡單,設計好隱含層卻是一門藝術。特別是很難將隱含層的設計過程總結出簡單的經驗規則。相反,神經元研究者已經為隱含層開發出許多啟發式設計,它們能幫助大家獲取所期望行為的網路。例如,一些啟發式演算法能幫助確定如何平衡隱含層數量與網路訓練花費時間。我們將在本書後面見到一些這樣的啟發式設計。

直到現在,我們已經討論了某一層的輸出當作下一層的輸入的神經網路。這樣的網路被稱為前向反饋神經網路。這意味著在網路中沒有迴圈——資訊總是向前反饋,決不向後。如果真的有迴圈,我們將終止這種輸入依賴於輸出的$\sigma$函式。這會很難理解,因此我們不允許這樣的迴圈。

但是,人工神經網路也有一些具有迴圈反饋。這樣的模型被稱為遞迴神經網路。這樣的模型思想是讓神經元在不活躍之前激勵一段有限的時間。這種激勵能刺激其它神經元,使其也能之後激勵一小會。這就導致了更多神經元產生激勵,等過了一段時間,我們將得到神經元的級聯反應。在這樣的模型中迴圈也不會有太大問題,因為一個神經元的輸出過一會才影響它的輸入,而不是瞬間馬上影響到。

遞迴神經網路沒有前饋神經網路具有影響力,因為遞迴網路的學習演算法威力不大(至少到目前)。但是遞迴網路仍然很有趣,它們比起前饋網路更加接近於我們人腦的工作方式。並且遞迴神經網路有可能解決那些前饋網路很難解決的重要問題。但是為了限制本書的範圍,我們將集中在已經被大量使用的前饋神經網路上。

定義了神經網路後,讓我們回到手寫識別。我們能將識別手寫數字分成兩個子問題。首先,我們想辦法將一個包含很多數字的影象分成一系列獨立的影象,每張包含唯一的數字。比如我們將把下面影象

分成6個分離的小影象,

我們人類能夠很容易解決這個分段問題,但是對於計算機程式如何正確的分離影象卻是一個挑戰。然後,一旦這幅影象被分離,程式需要將各個數字進行分類。因此,比如,我們希望程式能夠將上面的第一個數字

識別為5。

我們主要關注在第二個問題,也就是,分類這些獨立的數字影象。我們這麼做是因為一旦你能夠找到一個好方式來分類獨立的影象,分離問題就不是那麼難解決了。有許多途徑能夠解決分離問題,一種途徑是試驗很多不同的分離影象方法,並採用獨立影象分類器給每個分離試驗打分。如果獨立數字分類器堅信某種分離方式,那麼打分較高。如果分類器在某些片斷上問題很多,那麼得分就低。這個思想就是如果分類器分類效果很有問題,那麼很可能是分離方式不對造成。這種想法和一些變型能夠很好解決分離問題。因此無須擔心分離,我們將集中精力開發一個神經網路,它能解決更有趣和複雜的問題,即識別獨立的手寫數字。

為了識別這些數字,我們將採用三層神經網路:

網路的輸入層含有輸入畫素編碼的神經元。跟下節討論的一樣,我們用於網路訓練的資料將包含許多$28$ by $28$畫素手寫數字掃描影象,因此輸入層包含$784 = 28 \times 28$個神經元。為了簡化,我們在上圖中忽略了許多輸入神經元。輸入畫素是灰度值,白色值是$0.0$,黑色值是$1.0$,它們之間的值表明灰度逐漸變暗的程度。

網路的第二層是隱含層。我們將其神經元的數量表示為$n$,後面還對其採用不同的$n$值進行實驗。這個例子中使用了一個小的隱含層,只包含$n = 15$個神經元。

網路的輸出層包含10個神經元。如果第一個神經元被觸發,例如它有一個$\approx 1$的輸出,那麼這就表明這個網路認為識別的數字是$0$。更精確一點,我們將神經元輸出標記為$0$到$9$,然後找出具有最大激勵值的神經元。如果這個神經元是$6$,那麼這個網路將認為輸入數字是$6$。對於其他神經元也有類似結果。

你可能想知道為什麼用$10$個輸出神經元。別忘了,網路的目標是識別出輸入影象對應的數字($0, 1, 2, \ldots, 9$)。一種看起來更自然的方法是隻用$4$個輸出神經元,每個神經元都當做一個二進位制值,取值方式取決於神經元輸出接近$0$,還是$1$。4個神經元已經足夠用來編碼輸出,因為$2^4 = 16$大於輸入數字的10種可能值。(tensorfly社群原創,qq群: 472113439)為什麼我們的網路使用$10$個神經元呢?是否這樣效率太低?最終的理由是以觀察和實驗為依據的:我們能嘗試兩種網路設計,最後結果表明對於這個特殊問題,具有$10$個輸出神經元的網路能比只有$4$個輸出神經元的網路更好的學習識別手寫數字。這使得我們想知道為什麼具有$10$個輸出神經元的網路效果更好。這裡是否有一些啟發,事先告訴我們應該用$10$個輸出神經元,而不是$4$個輸出神經元?

為了弄懂我們為什麼這麼做,從原理上想清楚神經網路的工作方式很重要。首先考慮我們使用$10$個輸出神經元的情況。讓我們集中在第一個輸出神經元上,它能試圖確定這個手寫數字是否為$0$。它通過對隱含層的憑據進行加權和求出。隱含層的神經元做了什麼呢?假設隱含層中第一個神經元目標是檢測到是否存在像下面一樣的影象:

它能夠對輸入影象與上面影象中畫素重疊部分進行重加權,其他畫素輕加權來實現。相似的方式,對隱含層的第二、三和四個神經元同樣能檢測到如下所示的影象:

你可能已經猜到,這四幅影象一起構成了我們之前看到的$0$的影象:

因此如果這四個神經元一起被觸發,那麼我們能夠推斷出手寫數字是$0$。當然,這些不是我們能推斷出$0$的憑據類別——我們也可以合理的用其他方式得到$0$(通過對上述影象的平移或輕微扭曲)。但是至少這種方式推斷出輸入是$0$看起來是安全的。

假設神經網路按這種方式工作,我們能對為什麼$10$個輸出神經元更好,而不是$4$個,給出合理的解釋。如果我們有$4$個輸出神經元,那麼第一個輸出神經元將試著確定什麼是數字影象中最重要的位。這裡沒有很容易的方式來得到如上圖所示簡單的形狀的重要位。很難想象,這裡存在任何一個很好的根據來說明數字的形狀元件與輸出重要位的相關性。

現在,就像所說的那樣,這就是一個啟發式。沒有什麼能說明三層神經網路會按照我描述的那樣運作,這些隱含層能夠確定簡單的形狀元件。可能一個更聰明的學習演算法將找到權重賦值以便我們使用$4$個輸出神經元。但是我描述的這個啟發式方法已經運作很好,能夠節約很多時間來設計更好的神經元網路架構。

練習

  • 有一個方法能夠對上面的三層網路增加一個額外層來確定數值位表示。這個額外層將先前輸出層轉換為二進位制表示,就像下圖說明一樣。找到這層新的輸出神經元權重和偏移。假定第$3$層神經元(例如,原來的輸出層)正確的輸出有至少$0.99$的激勵值,不正確的輸出有小於$0.01$的激勵值。

現在我們已經有一個設計好的神經網路,它怎樣能夠學習識別數字呢?首要的事情是我們需要一個用於學習的資料集——也叫做訓練資料集。我們將使用MNIST資料集,它包含成千上萬的手寫數字影象,同時還有它們對應的數字分類。MNIST的名字來源於NIST(美國國家標準與技術研究所)收集的兩個修改後的資料集。這裡是一些來自於MNIST的影象:

就像你看到的,事實上這些數字和本章開始展示的用於識別的影象一樣。當然,測試我們的網路時候,我們會讓它識別不在訓練集中的影象!

MNIST資料來自兩部分。第一部分包含60,000幅用於訓練的影象。這些影象是掃描250位人員的手寫樣本得到的,他們一半是美國人口普查局僱員,一半是高中學生。這些影象都是28乘28畫素的灰度圖。MNIST資料的第二部分包含10,000幅用於測試的影象。它們也是28乘28的灰度影象。我們將使用這些測試資料來評估我們用於學習識別數字的神經網路。為了得到很好的測試效能,測試資料來自和訓練資料不同的250位人員(仍然是來自人口普查局僱員和高中生)。 這幫助我們相信這個神經網路能夠識別在訓練期間沒有看到的其他人寫的數字。

我們將使用符號$x$來表示訓練輸入。可以方便的把訓練輸入$x$當作一個$28 \times 28 = 784$維的向量。每一個向量單元代表影象中一個畫素的灰度值。我們將表示對應的輸出為$y = y(x)$,其中$y$是一個$10$維向量。比如,如果一個特殊的訓練影象$x$,表明是$6$,那麼$y(x) = (0, 0, 0, 0, 0, 0, 1, 0, 0, 0)^T$就是網路期望的輸出。注意$T$是轉置運算,將一個行向量轉換為列向量。

我們想要的是一個能讓我們找到合適的權重和偏移的演算法,以便網路輸出$y(x)$能夠幾乎滿足所有訓練輸入$x$。為了量化這個匹配度目標,我們定義了一個代價函式**有時被稱為損失目標函式。我們在本書中都使用代價函式,但是你需要注意其他術語,因為它通常在一些研究文章和神經網路討論中被使用到。: \begin{eqnarray} C(w,b) \equiv \frac{1}{2n} \sum_x \| y(x) - a\|^2. \tag{6}\end{eqnarray} 這裡,$w$表示網路中的所有權重,$b$是所有偏移,$n$訓練輸入的總數,$a$是網路輸入為$x$時的輸出向量,總和是對所有輸入$x$進行的累加。當然輸出$a$取決於$x$,$w$和$b$,但是為了符號簡化,我沒有指明這種依賴關係。符號$\| v \|$只是表示向量$v$的長度。我們稱$C$為二次型代價函式;它有時候也叫做均方誤差MSE。檢驗二次型代價函式,我們能看到$C(w,b)$是一個非負值,因為求和中的每一項都是非負的。此外,對於所有訓練資料$x$,$y(x)$和輸出$a$近似相等時候,$C(w,b)$會變得很小,例如,$C(w,b) \approx 0$。因此如果我們的訓練演算法能找到合理的權重和偏移使得$C(w,b) \approx 0$,這將是一個很好的演算法。相反,如果$C(w,b)$很大——這意味著$y(x)$和大量輸出$a$相差較大。所以訓練演算法的目標就是找到合適的權重和偏移來最小化$C(w,b)$。換句話說,我們想找到一組權重和偏移集合,使得代價函式值儘可能小。我們將使用梯度下降演算法來實現。

為什麼引入二次型代價函式?畢竟,我們不是主要關注在網路對多少影象進行了正確分類?為什麼不直接最大化這個數值,而不是最小化一個像二次型代價函式一樣的間接測量值?問題就在於正確識別的影象數量不是權重和偏移的平滑的函式。對於大多數,權重和偏移的很小改變不會讓正確識別的影象數量值有任何改變。這就使得很難指出如何改變權重和偏移來提高效能。如果我們使用一個像二次型的平滑代價函式,它將很容易指出如何細微的改變權重和偏移來改進代價函式。這就是為什麼我們首先關注在最小化二次型代價函式,而且只有那樣我們才能檢測分類的準確性。

即使我們想使用一個平滑的代價函式,你可能仍然想知道為什麼在等式(6) 中選擇了二次型代價函式。它難道不是一個臨時的選擇?也許如果我們選擇一個不同的代價函式,我們將獲取到完全不同的最小化權重和偏移?這是一個很好的考慮,後面我們將回顧這個代價函式,並且進行一些改變。但是,這個等式(6) 中的二次型代價函式對理解神經網路學習基礎非常好,因此目前我們將堅持使用它。

簡要回顧一下,我們訓練神經網路的目標是找出能最小化二次型代價函式$C(w, b)$的權重和偏移。這是一個適定問題,但是它會產生許多當前提出的干擾結構——權重$w$和偏移$b$的解釋、背後隱藏的$\sigma$函式、網路架構的選擇、MNIST等等。結果說明我們能通過忽略許多這類結構來理解大量內容,且只需要集中在最小化方面。因此現在我們將忘掉所有關於代價函式的特殊形式,神經網路的連線關係等等。相反,我們將想象成只簡單的給出一個具有許多變數的函式,且我們想要最小化它的值。我們將開發一個新技術叫梯度下降,它能被用來解決這類最小化問題。然後我們將回到神經網路中這個想最小化的特殊函式。

好的,假設我們將最小化一些函式$C(v)$。它可能是具有許多變數$v = v_1, v_2, \ldots$的任意真實值函式。請注意,我們將用$v$替換符號$w$和$b$來強調它可以適合任意一個函式——我們並不是一定在神經網路的背景下考慮。為了最小化$C(v)$,可以把$C$想象成只具有兩個變數,即$v_1$和$v_2$:

我們想要找到怎樣才能使$C$達到全域性最小化。現在,當然,對上面繪製的函式,我們能看出該圖中最小化位置。從這種意義上講,我可能展示了一個簡單的函式!一個通常的函式$C$可能由許多變數構成,並且很難看出該函式最小化位置在哪裡。

一種解決辦法就是使用微積分來解析地找到這個最小值。我們能計算導數,然後使用它們來找到$C$函式最小極值的位置。當$C$只有一個或少量變數時,這個辦法可能行得通。但是當我們有許多變數時,這將變成一場噩夢。且對於神經網路,我們通常需要更多的變數——最大的神經網路在極端情況下,具有依賴數十億權重和偏移的代價函式。使用微積分來求最小值顯然行不通!

(在斷言我們通過把$C$當作只具有兩個變數的函式來獲得領悟後,我將用兩段第二次轉回來,並且說:“嘿,一個具有超過兩個變數的函式是什麼呢?”,對此很抱歉。請相信我,把$C$想象成只具有兩個變數的函式對我們理解很有幫助。有時候想象會中斷,且最後兩段將處理這種中斷。好的數學思維通常會涉及應付多個直觀想象,學會什麼時候合適使用每一個想象,什麼時候不合適。)

Ok,所以用微分解析的方法行不通。幸運的是,我們可以通過一種非常直觀的類比來找到一種“行得通”的演算法。首先將我們的函式看作是一個凹形的山谷。(瞄一眼上面的插圖,這種想法應該是很直觀的。)好,現在讓我們想象有一個小球沿著山谷的坡面向低處滾。生活經驗告訴我們這個小球最終會到達谷底。或許我們可以採用類似的思路找到函式的最小值?好,首先我們將隨機選取一個點作為這個(想象中的)球的起點,然後再模擬這個小球沿著坡面向谷底運動的軌跡。我們可以簡單地通過計算 $C$ 的偏導數(可能包括某些二階偏導數)來進行軌跡的模擬——這些偏導數蘊涵了山谷坡面區域性“形狀”的所有資訊,因此也決定了小球應該怎樣在坡面上滾動。

根據我剛才所寫的內容,你可能會以為我們將從小球在坡面上滾動的牛頓運動方程出發,然後考慮重力和坡面阻力的影響,如此等等……實際上,我們不會把“小球在坡面滾動”的這一類比太過當真——畢竟,我們我們的初衷是要設計一種最小化 $C$ 的演算法,而不是真地想精確模擬現實世界的物理定律!“小球在坡面滾動”的直觀影象是為了促進我們的理解和想象,而不應束縛我們具體的思考。所以,與其陷入繁瑣的物理細節,我們不妨這樣問自己:如果讓我們來當一天上帝,那麼我們會怎樣設計我們自己的物理定律來引導小球的運動?我們該怎樣選擇運動定律來確保小球一定會最終滾到谷底?

為了將這個問題描述得更確切,現在讓我們來想想,當我們將小球沿著 $v_1$ 方向移動一個小量$\Delta v_1$,並沿著 $v_2$ 方向移動一個小量 $\Delta v_2$ 之後會發生什麼。微分法則告訴我們,$C$ 將作如下改變: \begin{eqnarray} \Delta C \approx \frac{\partial C}{\partial v_1} \Delta v_1 + \frac{\partial C}{\partial v_2} \Delta v_2. \tag{7}\end{eqnarray} 我們將設法選擇 $\Delta v_1$ 和 $\Delta v_2$ 以確保 $\Delta C$ 是負數,換句話說,我們將通過選擇 $\Delta v_1$ 和 $\Delta v_2$ 以確保小球是向著谷底的方向滾動的。為了弄清楚該怎樣選擇 $\Delta v_1$ 和 $\Delta v_2$,我們定義一個描述 $v$ 改變的向量 $\Delta v \equiv (\Delta v_1, \Delta v_2)^T$,這裡的 $T$ 同樣是 轉置(transpose)算符,用來將一個行向量轉化為列向量。我們還將定義 $C$ 的“梯度”,它是由 $C$ 的偏導數構成的向量,$\left(\frac{\partial C}{\partial v_1}, \frac{\partial C}{\partial v_2}\right)^T$ 。我們將“梯度”向量記為 $\nabla C$,即:\begin{eqnarray} \nabla C \equiv \left( \frac{\partial C}{\partial v_1}, \frac{\partial C}{\partial v_2} \right)^T. \tag{8}\end{eqnarray} 很快我們將會用 $\Delta v$ 和梯度 $\nabla C$ 來重寫 $\Delta C$ 的表示式。在這之前,為了避免讀者可能對“梯度”產生的困惑,我想再多做一點解釋。當人們首次碰到$\nabla C$這個記號的時候,可能會想究竟該怎樣看待$\nabla$這個符號。$\nabla$的含義到底是什麼?其實,我們完全可以把$\nabla C$整體看作是單一的數學物件——按照上述公式定義的向量,僅僅是偶然寫成了用兩個符號標記的形式。根據這個觀點,$\nabla$像是一種數學形式的旗語,來提醒我們“嘿,$\nabla C$是一個梯度向量”,僅此而已。當然也有更抽象的觀點,在這種觀點下,∇被看作是一個獨立的數學實體(例如,被看作一個微分算符),不過我們這裡沒有必要採用這種觀點。

使用上述定義,$\Delta C$ 的表示式 (7) 可以被重寫為:\begin{eqnarray} \Delta C \approx \nabla C \cdot \Delta v. \tag{9}\end{eqnarray} 這個方程有助於我們理解為什麼 $\nabla C$ 被稱為“梯度”向量:$\nabla C$ 將 $v$ 的改變和 $C$ 的改變聯絡在了一起,而這正是我們對於“梯度”這個詞所期望的含義。然而,真正令我們興奮的是這個方程讓我們找到一種 $\Delta v$ 的選擇方法可以確保 $\Delta C$ 是負的。具體來說,如果我們選擇 \begin{eqnarray} \Delta v = -\eta \nabla C, \tag{10}\end{eqnarray} 這裡 $\eta$ 是一個正的小引數,被稱為“學習率”(learning rate)。這樣方程span id="margin_859162866010_reveal" class="equation_link">(9) 化為 $\Delta C \approx -\eta \nabla C \cdot \nabla C = -\eta \|\nabla C\|^2$。如果我們根據公式 (10) 指定 $v$ 的改變,由於 $\| \nabla C \|^2 \geq 0$ ,它確保了 $\Delta C \leq 0$,即 $C$ 的值將一直減小,不會反彈。(當然要在近似關係 (9) ) 成立