1. 程式人生 > >Python實戰五.簡單神經網路

Python實戰五.簡單神經網路

異或問題

何為異或問題?對於給定的兩個二進位制輸入,我們通過異或邏輯閘得到一個預測輸出,這一過程即為異或問題。注意,輸入不相等時輸出為 1,否則為 0。表 1 展示了異或函式的所有可能的輸出結果:

24139WX20180115-101655@2x.png

那麼現在我們就畫出資料分佈圖來探究它的本質。

        def plot_data(data, labels):
    """
    argument:
    data: np.array containing the input value
    labels: 1d numpy array containing the expected label
    """
    positives = data[labels == 1
, :] negatives = data[labels == 0, :] plt.scatter(positives[:, 0], positives[:, 1], color='red', marker='+', s=200) plt.scatter(negatives[:, 0], negatives[:, 1], color='blue', marker='_', s=200) positives = np.array([[1, 0], [0, 1]]) negatives = np.array([[0
, 0], [1, 1]]) data = np.concatenate([positives, negatives]) labels = np.array([1, 1, 0, 0]) plot_data(data, labels)

8443811.png

看到上圖後,我們或許會反思,這真的是一個簡單問題麼?

如你所見,我們的資料並非線性可分的,因此,一些常用的線性模型,例如 logistic 迴歸可能就不太適合分類我們的資料了。為了給你一個更直觀的理解,我用一個簡單的線性模型畫出瞭如下圖的決策邊界。

0789812.png

微調 logistic 迴歸模型構造決策邊界

上面的圖清楚的告訴我們,我們需要一個更好的分類器來分離非線性資料。SVM 結合它的核心技巧就是一個不錯的選擇。但是,在本文中,我們打算重新構建一個神經網路而非 SVM,並帶你領略下神經網路解決異或問題的風采。

何為神經網路?

神經網路就是就是找到一個可以模擬人腦工作行為的表現良好的近似函式。圖 1 對人類神經元與人工智慧網路作了類比。

87478WX20180115-101932@2x.png

圖 1:(a)人腦神經元結構(b)從生理神經網路類比得出的人工智慧網路—圖片摘自 cs231n.github.io

你不需要了解太多生物學知識,我會從高度形象的角度來解釋人體神經元如何處理資訊。

人體的神經元通過樹突接受訊號。這些資訊或訊號隨後被傳遞到腦細胞或細胞體。在細胞體內部,所有的資訊將被加工生成一個輸出。當該輸出結果達到某一閾值時,神經元就會興奮,並通過軸突傳遞資訊,然後通過突觸傳遞到其他相連的神經元。神經元間傳輸的訊號量取決於連線的強度。

前面提到的整個流程某種程度上適用於人工智慧網路。你可以把樹突想象成人工智慧網路中基於突觸感知器的被賦予了權重的輸入。然後,該輸入在人工智慧網路的『細胞體』中相加。如果得出的輸出值大於閾值單元,那麼神經元就會『興奮』,並將輸出傳遞到其它神經元。

這樣你就可以理解人工智慧網路就是借鑑基本的生物神經元工作原理建模的吧。

神經網路究竟如何工作?

為了瞭解神經網路是如何工作的,我們先來看看一個叫感知機的簡單的人工神經網路。

對我而言,感知機是我見過的機器學習中最優雅的演算法之一。它於 1950 年代被提出,儘管很簡單,但它可以說是很多重要的機器學習演算法的起點了,例如 logistic 迴歸、支援向量機甚至深度神經網路。

那麼感知機怎麼工作暱?我們以圖 2 展開討論。

70963WX20180115-102101@2x.png

圖 2:感知機

圖 2 展示了給定三個輸入 x_1、x_2 和 x_3 以及一個可以計算輸出值的神經元的感知機演算法。Rosenblatt 通過引入權重的概念介紹這一簡單的規則,用於生成輸出值。權重通常是表示輸入對應於輸出的重要性的實數。上圖中的神經元將會得到兩個可能的結果,0 或 1,是由每個輸入的加權和 ∑wjxj 決定的大於或小於閾值的結果。因此,感知機的主要思想就是去學到一些可以決定神經元興奮還是抑制的與輸入特徵相乘的權重 w。我們可以寫出一個如下的數學表示式:

09845WX20180115-102138@2x.png

我們現在可以做兩件事來修改上述公式:第一,我們把權重相加操作轉變成兩個向量的點乘。

w (權重) 和 x (輸入), 其中 w⋅x ≡ ∑wjxj。接下來,我們可以把閾值移到不等式的另一端並取個新變數名為偏置 b,b 恆等於閾值的負數。通過這些改動,感知機公式重寫如下:

3283114.png

現在我們把這些公式套進我們的感知機架構,這樣就有了如下所示的完整的單層感知機架構:

7447915.png

圖 3:單層感知機架構

通常情況下,單層感知機模型都會使用階躍函式作為啟用函式將結果轉化成 0 或 1,因此將輸入歸到 0 或 1 類。如圖 4 所示,負數的輸出為 0,正數的輸出為 1。

2707216.png

圖 4:階躍函式的圖示

對於輸入資料線性可分的分類任務,階躍函式十分有用。但是,由於我們的目的是找到一個用於分離非線性資料的分類器,單層感知機與階躍函式就毫無意義了。稍後幾節,我們將會看到使用非線性啟用函式的多層感知機網路模型。

關於我們為什麼不用使用階躍函式,這裡有兩個主要原因:

1. 目前,結合反向傳播使用梯度下降(通過loss反向調節引數,引數)演算法是訓練一個多層神經網路的有效方法之一(我們稍後會簡短的介紹一下)。反向傳播的必要條件是使用的啟用函式必須可微。然而階躍函式在 x=0 處不可導,且其它位置導數均為 0。如此一來就無法運用梯度下降法更新權重了。

2. 回想下,神經網路的主要目的就是學習到使預測儘可能接近真實值的權重和偏置。為了達到這一目的,如同很多優化問題,我們希望對權重或偏置上作一個小的改變,在網路輸出中只產生一個相對小的變化。而這是一個僅能生成 0 或 1 的函式難以企及的。

啟用函式

啟用函式是神經網路的一個重要組成部分。一般來說,我們最少有三個需要啟用函式的理由:

  • 它幫助神經元學習和理解一些非常複雜的東西。
  • 它們為網路引入非線性屬性
  • 我們希望對權重或偏差上作一個小的改變,以便在網路輸出中只產生一個相對小的變化

我們已經看到以階躍函式作啟用函式的例子,然而,在這一節,我們將要探討一些深度學習中常用的非線性啟用函式。順便提一下,若要深入瞭解啟用函式,包括每一個啟用函式的利弊,你可以參考 Avinash Sharma 和 Karpathy 寫的文章。

  •  Avinash Sharma :https://medium.com/the-theory-of-everything/understanding-activation-functions-in-neural-networks-9491262884e0 
  • Karpathy:https://medium.com/@karpathy/yes-you-should-understand-backprop-e2f06eab496b 

Sigmoid 函式(在上一節,邏輯迴歸中使用過,是連續函式可導且可以相對小變化)

sigmoid 函式,也即 logistic 函式,對於任意輸入,它的輸出範圍是 (0,1)。公式如下:

3207217.png

sigmoid 的數學公式

5597618.png

圖 5:sigmoid 函式圖

圖 5 畫出了 sigmoid 函式的圖形。如你所見,它很像平滑版的階躍函式。但是,sigmoid 有很多好處,例如:

1. 它是非線性的

2. 不同於二值化輸出,sigmoid 可以輸入 0 到 1 之間的任意值。對,跟你猜的一樣,這可以用來表示概率值。

3. 與 2 相關,sigmoid 的輸出值在一個範圍內,這意味著它不會輸出無窮大的數。

但是,sigmoid 啟用函式並不完美:

梯度消失。如上圖所示,當輸入值 z 趨近負無窮時,sigmoid 函式的輸出幾乎為 0 . 相反,當輸入 z 趨近正無窮時,輸出值幾乎為 1 . 那麼這意味著什麼?

在這兩個極端情況下,對應的梯度很小,甚至消失了。梯度消失在深度學習中是一個十分重要的問題,我們在深度網路中加了很多層這樣的非線性啟用函式,這樣的話,即使第一層的引數有很大的變化,也不會對輸出有太大的影響。換句話講,就是網路不再學習了,通常訓練模型的過程會變得越來越慢,尤其是使用梯度下降演算法時

sigmoid 的另一個弊端就是實際運用中指數運算開銷太大。儘管有人說,與矩陣乘法或卷積相比,啟用函式在深度網路的計算是非常小的一部分,所以這可能不會成為一個大問題。不過,我認為這值得一提。

Tanh 函式

Tanh 或雙曲正切是另一個深度神經網路中常用的啟用函式。類似於 sigmoid 函式,它也將輸入轉化到良好的輸出範圍內。具體點說就是對於任意輸入,tanh 將會產生一個介於 -1 與 1 之間的值。

4497919.png

Tanh 函式的數學公式

0355220.png

圖 6:tanh 函式圖

如前面提及的,tanh 啟用函式有點像 sigmoid 函式。非線性且輸出在某一範圍,此處為 (-1, 1)。不必意外,它也有跟 sigmoid 一樣的缺點。從數學表示式就可以看出來,它也有梯度消失的問題,以及也需要進行開銷巨大的指數運算。

ReLU(修正線性單元)

終於講到了 Relu,人們起初並不覺得它的效果會好過 sigmoid 和 tanh。但是,實戰中它確實做到了。事實上,cs231n 課程甚至指出,應該預設使用 Relu 函式。

ReLU 從數學表示式來看,運算十分高效。對於某一輸入,當它小於 0 時,輸出為 0,否則不變。下面是 ReLU 的函式表示式。

25270WX20180115-103014@2x.png

圖 7:ReLU 函式圖

那麼你可能會問,「它是線性函式吧?為何我們說它是非線性函式?」

線上代中,線性函式就是兩個向量空間進行向量加和標量乘的對映。

13536WX20180115-103118@2x.png

給定上面的定義,我們知道 max(0, x) 是一個分段線性函式。之所以說是分段線性,是因為它在 (−∞, 0] 或 [0,+∞) 上符合線性函式的定義。但是在整個定義域上並不滿足線性函式的定義。例如

f(−1) + f(1) ≠f (0)

所以 Relu 就是一個非線性啟用函式且有良好的數學性質,並且比 sigmoid 和 tanh 都運算得快。除此以外,Relu 還因避免了梯度消失問題而聞名。然而,ReLU 有一個致命缺點,叫「ReLU 壞死」。ReLu 壞死是指網路中的神經元由於無法在正向傳播中起作用而永久死亡的現象。

更確切地說,當神經元在向前傳遞中啟用函式輸出為零時,就會出現這個問題,導致它的權值將得到零梯度。因此,當我們進行反向傳播時,神經元的權重將永遠不會被更新,而特定的神經元將永遠不會被啟用。

還有件事值得一提。你可能注意到,不像 sigmoid 和 tanh,Relu 並未限定輸出範圍。這通常會成為一個很大的問題,它可能在另一個深度學習模型如遞迴神經網路(RNN)中成為麻煩。具體而言,由 ReLU 生成的無界值可能使 RNN 內的計算在沒有合理的權重的情況下發生數值爆炸。因此反向傳播期間權重在錯誤方向上的輕微變化都會在正向傳遞過程中顯著放大啟用值,如此一來學習過程可能就非常不穩定。我會嘗試在下一篇部落格文章中詳細介紹這一點。

神經網路如何預測和學習?

1458321.png

圖 8:多層感知機

圖 8 所示架構叫多層感知機(MLP)。從名字我們就可以推知,我們只是簡單地堆積多層感知機而已。上圖是一個三層感知機模型:一個輸入層、一個隱藏層,以及一個輸出層。然而,在深度學習或神經網路領域,人們並不叫它三層神經網路。通常,我們只統計隱藏層或其加上輸出層的層數,因此,上圖的網路也叫兩層神經網路。隱藏層並不單單指輸入層或輸出層。現在,如你所猜,所謂的深度學習,就意味著有更多的隱藏層

那麼神經網路如何進行預測暱?

當所有的輸入通過所有隱藏層到輸出層後,神經網路就會產生一個預測。這一過程叫前饋。如圖 8 所示,網路接受輸入 X,然後計算啟用函式並逐層傳遞,直到輸出。在監督任務中,對於此類分類任務,我們通常在輸出層使用一個 sigmoid 函式,以便將預測值轉化為概率值。在圖 8 中,我們可以看到輸出值為 0.24,由於它小於 0.5,我們可以說預測值 y_hat 為 0 .

跟一般的分類任務一樣,我們有一個代價函式,用於評估我們的模型擬合真實標籤的程度。事實上,訓練神經網路可以簡單地看作儘可能最小化代價函式的過程。我們可以定義如下的代價函式:

5600622.png

均方誤差

所以我們的目的就是找到最佳 w 和 b,使代價函式 J 儘可能的小。為了達到這一目的,我們得靠兩大重要的演算法,梯度下降和反向傳播。

梯度下降

對那些已經接觸過機器學習的人來說,你們已經很熟悉梯度下降法了。訓練神經網路與訓練任何其它使用梯度下降法的機器學習模型沒有多大區別。唯一明顯的區別是網路中的非線性效應使得我們的代價函式非凸。

為了幫助你理解,我們假設有一個如下圖 9 所示的凸代價函式:

0707423.png

圖 9:梯度下降法圖解

上表中,水平座標表示引數空間,權重和偏置,代價函式 J(w, b) 就是水平軸上面的拋物面。圖中的紅色圓圈代表初始權重 w 和 b 對應的代價。為了最小化這一代價,我們需要走到這個拋物面底。那麼問題來了,我們怎麼知道沿哪個方向走暱?是引數變大的方向還是變小的方向?我們可以做一個隨機搜尋,但這顯然耗時且開銷過大。

通過處理可學習的權重和偏置可以找到最佳方向。微積分告訴我們,在給定的點上,梯度方向就會指向函式值改變最快的方向。因此,我們將使用代價函式對權重和偏置的梯度。

現在,我們簡單地看看圖 10 所示的代價-權重變化。

6184824.png

圖 10:梯度的形象化表示

圖 10 描繪了代價函式對應權重的函式值。你可以把圖上的黑色圓看作初始代價。考慮到函式或變數的梯度可負可正可 0。負梯度意味著該線反向傾斜,反之亦然。現在,我們的目的是最小化代價函式,我們就必須沿著梯度的反方向更新權重。這一更新過程可以用以下公式表示:

9048925.png

圖 11:梯度下降法的引數更新

其中α是步長或學習率,我們將它與可學習引數 w 的偏微分相乘。所以α有啥用暱?

梯度告訴我們哪個方向函式值改變的最快,但是它並未告訴我們應該沿這一方向跨多大步。我們需要一個超引數去控制步長的大小,例如,我們沿某一方向該移動多遠,這就是 α 存在的意義。選取正確的學習率十分重要,因為它對兩方面有很大的影響:演算法的學習速度和我們是否收斂到區域性極小值。實際運用中,你可能會運用一個自適應學習率演算法,例如動量演算法、RMSProp、Adam 等等。AYLIEN 的一個大佬寫了一篇關於學習率演算法的很棒的文章(如下參考第一篇)。


參考閱讀:

反向傳播

我們在前一節講述了梯度下降演算法,它是深度學習的學習問題的一個優化演算法。考慮到我們需要計算關於可學習引數 w 和 b 的偏微分才能使用梯度下降法。換句話說,我們需要計算 w 和 b 的偏微分。

但是,如果我們仔細看看代價函式 J,下圖 12 所示,就會發現 J 和 w 、 b 並沒有直接關係。

5386526.png

圖 12:均方誤差

只有從得到 y_hat 的輸出層追溯到輸入層,我們才會發現 J 與 w 、b 的間接關係,如下圖 13 所示:

5850227.png

圖 13:反向傳播圖解

你現在應該明白,為了得到代價函式的引數關於 w 和 b 的梯度,我們需要計算所有引數的偏微分,例如前面層的*a* (啟用函式) 和 *z* (線性運算: wx + b),這就是反向傳播存在的意義。反向傳播其實就是反覆運用微積分的鏈式法則,這可能是神經網路中最有效的計算可學習引數梯度的方法了。(就是實戰1234中用到的梯度下降)

接下來,我手把手帶你算一下代價函式對第二層神經網路權重 w2 的梯度,簡單起見,我們使用圖 8 的結構,一個包含三個神經元的隱藏層。

8898128.png

為了得到 y_hat 對 z2 的變化率,我們需要對 sigmoid 啟用函式的 z 求微分。

1233729.png

一旦我們得到 J 對 W2 的偏導值,就可以使用圖 11 中的公式更新 W2 的值。

我們通常對所有可學習引數重複這一過程,直到得到儘可能小的代價函式值。

可解決異或問題的神經網路

不錯!我想我們已經瞭解瞭如何構建一個神經網路模型甚至深度學習模型的所有知識,這些知識將幫助我們解決異或問題。 

寫這篇部落格時,我順便搭了一個簡單的單隱藏層神經網路模型。圖 14 是我使用的樣例網路。我畫出了一些由我的模型生成的不同數量的神經元的決策邊界。如你後面將看到的,包含更多的神經元會使網路模型變得更加複雜,從而創造一個更復雜的決策邊界。

6300330.png

圖 14:3 隱藏神經元的兩層神經網路

20881WX20180115-104144@2x.png

圖 15:由一個包含多個神經元(2 個、3 個、4 個)的隱藏層生成的決策邊界

但是,到底怎樣才是最佳選擇?包含更多的神經元還是更多的隱藏層?

理論上講,網路深的主要好處就是可以表示更復雜的函式。具體而言,通過使用更深層次的網路結構,我們可以學習許多不同抽象層次的特徵,例如,從邊緣(較底層)到非常複雜的特徵(較深層)。

然而,實際中使用深度網路並非總是有用。我們訓練深度網路時最常遇到的就是梯度消失問題:一個非常深的網路通常會發生某個梯度迅速變為零的狀況,因此使得梯度下降非常緩慢。

再具體點說,使用梯度下降時,因為反向傳播是從輸出層傳播到輸入層,而從輸入到輸出的每一步都是權重矩陣的相乘,因此梯度可能呈指數衰減到 0,某些情況下甚至會發生梯度爆炸。

為了結束這篇冗長的博文,簡要總結的要點如下:

  • 神經網路直觀地引入了可以用來解決一個複雜的非線性可分的資料的非線性模型。 
  • 感知機演算法為之後的很多高階機器學習甚至是深度學習演算法提供了思路。 
  • 深度學習直觀上就是使用很多隱藏層來搭建一個網路,當然有很多版本,例如卷積網路、迴圈網路等。 
  • 啟用函式是神經網路中的重要一環,你必須理解。 
  • 目前反向傳播搭配梯度下降法是訓練神經網路的最佳方案。 
  • 使用更多的隱藏層並不一定能提高我們的模型的表現。事實上,深度網路飽受梯度消失之苦。