最簡單的人工神經網絡

我不會機器學習,但上個月我在 GitHub 上發現了一個極簡、入門級的神經網絡教程。它簡潔易懂能用一行公式說明白的道理,不多寫一句廢話,我看後大呼過癮。

這麼好的東西得讓更多人看到,但原文是英文的無法直接分享,所以得先聯繫作者拿到翻譯的授權,然後由小熊熊翻譯了這個項目,最後纔有您看到的這篇文章。過程艱辛耗時一個月實屬不易,如果您看完覺得還不錯,歡迎點贊、分享給更多人。

內容分爲兩部分:

人工神經網絡是人工智能的基礎,只有夯實基礎,才能玩轉 AI 魔法!

溫馨提示:公式雖多但只是看起來唬人,實際耐下心讀並不難懂。下面正文開始!

通過理論和代碼解釋和演示的最簡單的人工神經網絡。

受人腦工作機制的啓發,人工神經網絡有着相互連接的模擬神經元,用於存儲模式和相互溝通。一個模擬神經元最簡單的形式是有一個或多個輸入值xi和一個輸出值y ,其中每個xi 有一個權重wi 。

拿最簡單的來說,輸出值就是輸入值乘以權重之後的總和。

y=i=1∑nwixi

網絡的作用在於通過多個參數w 模擬一個複雜的函數,從而可以在給定一系列輸入值x 的時候得到一個特定的輸出值y ,而這些參數w 通常是我們自身難以擬定的。

假設我們現在的一個網絡有兩個輸入值x1=0.2 ,x2=0.4,它們對應兩個權重值w1 和w2 。

現在我們需要調整權重值,從而使得它們可以產生我們預設的輸出值。

在初始化時,因爲我們不知曉最優值,往往是對權重隨機賦值,這裡我們爲了簡單,將它們都初始化爲 1 。

這種情況下,我們得到的就是

y=x1w1+x2w2=0.2∗1+0.4∗1=0.6

如果輸出值y 與我們期望的輸出值不一致,那就有了誤差。

例如,如果我們希望目標值是t=0.5 ,那麼這裡相差值就是

y−t=0.6−0.5=0.1

通常我們會採用方差(也就是代價函數)來衡量誤差:

E=21(y−t)2

如果有多套輸入輸出值,那麼誤差就是每組方差的平均值。

E=2n1(yi−ti)2

我們用方差來衡量得到的輸出值與我們期望的目標值之間的差距。通過平方的形式就可以去除負偏離值的影響,更加凸顯那些偏離較大的偏差值(不管正負)。

爲了糾正誤差,我們需要調整權重值,以使得結果趨近於我們的目標值。在我們這個例子中,將w1 從 1.0 降到 0.5 就可以達到目標,因爲

y=t=0.2∗0.5+0.4∗1.0=0.5

然而,神經網絡往往涉及到許多不同的輸入和輸出值,這種情況下我們就需要一個學習算法來幫我們自動完成這一步。

現在是要藉助誤差來幫我們找到應該被調整的權重值,從而使得誤差最小化。但在這之前,讓我們瞭解一下梯度的概念。

梯度本質上是指向一個函數最大斜率的矢量。我們採用 ▽ 來表示梯度,簡單說來,它就是函數變量偏導數的矢量形式。

對於一個雙變量函數,它採用如下形式表示:

▽f(x,y)=[fx,fy]=[ðxðf(x,y),ðyðf(x,y)]

讓我們用一些數字來模擬一個簡單的例子。假設我們有一個函數是f(x,y)=2x2+3y3,那麼梯度將是

▽f(x,y)=[4x,9y2]

下降則可以簡單理解爲通過梯度來找到我們函數最大斜率的方向,然後通過反方向小步幅的多次嘗試,從而找到使函數全局(有時是局部)誤差值最小的權重。

我們採用一個稱爲學習率的常量來表示這個反方向的小步幅,在公式中我們用ε 來進行表徵。

如果ε 取值太大,那有可能直接錯過最小值,但如果取值太小,那我們的網絡就會花費更久的時間來學習,而且也有可能陷入一個淺局部最小值。

對於我們例子中的兩個權重值w1 和w2 ,我們需要找到這兩個權重值相較於誤差函數的梯度

▽wE=<�ðw1ðE,ðw2ðE>

還記得我們上面的公式E=21(y−t)2 和y=x1w1+x2w2 嗎?對於w1 和w2 ,我們可以將其代入並通過微積分中的鏈式求導法則來分別計算其梯度

ðwiðE=ðyðEðwiðy=ðyð(21(y−t)2)ðwið(xiwi)=(y−t)xi

簡潔起見,後面我們將採用δ 這個術語來表示 ðyðE=y−t 。

一旦我們有了梯度,將我們擬定的學習率ε 帶入,可以通過如下方式來更新權重值:

w1=w1−ε▽w1E=w1−εδx1

w2=w2−ε▽w2E=w2−εδx2

然後重複這個過程,直到誤差值最小並趨近於零。

附帶的示例採用梯度下降法,將如下數據集訓練成有兩個輸入值和一個輸出值的神經網絡:

x=[1.01.01.00.0]y′=[0.01.0]

一旦訓練成功,這個網絡將會在輸入兩個 1 時輸出 ~0,在輸入 1 和 0 時,輸出 ~1 。

反向傳播(英語:Backpropagation,縮寫爲 BP)是“誤差反向傳播”的簡稱,是一種與最優化方法(如梯度下降法)結合使用的,用來訓練人工神經網絡的常見方法。

反向傳播技術可以用來訓練至少有一個隱藏層的神經網絡。下面就來從理論出發結合代碼拿下反向傳播算法。

感知機是這樣一個處理單元:它接受輸入x, 採用激活函數f 對其進行轉換,並輸出結果y 。

在一個神經網絡,輸入值是前一層節點輸出值的權重加成總和,再加上前一層的誤差:

xj=i=1∑Ixiwij+bi

如果我們把誤差當作層中另外的一個常量爲 -1 的節點,那麼我們可以簡化這個公式爲

xj=i=1∑I+1xiwij

爲什麼我們需要激活函數呢?如果沒有,我們每個節點的輸出都會是線性的,從而讓整個神經網絡都會是基於輸入值的一個線性運算後的輸出。因爲線性函數組合仍然是線性的,所以必須要引入非線性函數,才能讓神經網絡有區別於線性迴歸模型。

針對x=∑xw,典型的激活函數有以下形式:

Sigmoid 函數 :y=1+e−x1

線性整流函數:y=max(0,x)

tanh 函數:y=tanh(x)

反向傳播算法可以用來訓練人工神經網絡,特別是針對具有多於兩層的網絡。

原理是採用 forward pass 來計算網絡輸出值和誤差,再根據誤差梯度反向更新輸入層的權重值。

在下面的示例中,我們將對不同層節點採用以下激活函數:

在 forward pass 中,我們在輸入層進行輸入,在輸出層得到結果。

對於隱藏層的每個節點的輸入就是輸入層輸入值的加權總和:

xj=i=1∑Iwijyi

因爲隱藏層的激活函數是 sigmoid,那麼輸出將會是:

yj=fj(xj)=1+e−xj1

同樣,輸出層的輸入值則是

xk=j=1∑Jwjkyj

因爲我們賦予了恆等函數作爲激活函數,所以這一層的輸出將等同於輸入值。

yk=fk(xk)=xk

一旦輸入值通過網絡進行傳播,我們就可以計算出誤差值。如果有多套關聯,還記得我們第一部分學習的方差嗎?這裡,我們就可以採用平均方差來計算誤差。

E=i=1∑IEt=2T1t=1∑T(ykt−ykt′)2

現在我們已經得到了誤差,就可以通過反向傳輸,來用誤差來修正網絡的權重值。

通過第一部分的學習,我們知道對權重的調整可以基於誤差對權重的偏導數乘以學習率,即如下形式

Δwjk=−ε∂wjk∂E

我們通過鏈式法則計算出誤差梯度,如下:

∂wjk∂E=∂xk∂E∂wjk∂xk

∂xk∂E=∂yk∂E∂xk∂yk=∂yk∂(21(yk−yk′)2)∂xk∂(xk)=yk−yk′=δk

∂wjk∂xk=∂wjk∂(yjwjk)=yj

因此,對權重的調整即爲 Δwjk=−εδkyj

對於多個關聯,那麼對權重的調整將爲每個關聯的權重調整值之和 Δwjk=−ε∑t=1Tδkyjt

類似地,對於隱藏層之間的權重調整,繼續以上面的例子爲例,輸入層和第一個隱藏層之間的權重調整值爲

Δwij=−ε∂wij∂E

∂wij∂E=∂xj∂E∂wij∂xj=δjyi

那麼,基於所有關聯的權重調整即爲每次關聯計算得到的調整值之和 Δwij=−ε∑t=1Tδjtyit

這裡, 我們對δj 可以再做進一步的探索。上文中,我們看到 ∂wij∂E=∂xj∂E∂wij∂xj 。

對前半部分的 ∂xj∂E ,我們可以有

∂xj∂E=∂yj∂Edxjdyj

∂yj∂E=k=1∑K∂xk∂E∂yj∂xk=k=1∑Kδkwjk

對後半部分的dxjdyj ,因爲我們在這一層採用了 sigmoid 函數,我們知道,sigmoid 函數的導數形式是f(x)(1−f(x)) ,因此,有

dxjdyj=f′(xj)=f(xj)(1−f(xj))=yj(1−yj)

綜上,便可以得到δj 的計算公式如下

δj=yj(1−yj)k=1∑Kδkwjk

首先,對網絡權重值賦予一個小的隨機值。

重複以下步驟,直到誤差爲0 :

在這個示例中,我們通過真實數據來模擬神經網絡中的每個步驟。輸入值是[1.0, 1.0],輸出期望值爲 [0.5]。爲了簡化,我們將初始化權重設爲 0.5 (雖然在實際操作中,經常會採用隨機值)。對於輸入、隱藏和輸出層,我們分別採用恆等函數、 sigmoid 函數 和恆等函數作爲激活函數,學習率ε 則定爲 0.01 。

運算一開始,我們將輸入層的節點輸入值設爲x1=1.0,x2=1.0 。

因爲我們對輸入層採用的是恆等函數作爲激活函數,因此有yi=xi=1.0 。

接下來,我們通過對前一層的加權總和將網絡向前傳遞到 J 層,如下

xj=i=1∑Iyiwij=1.0∗0.5+1.0∗0.5=1.0

然後,我們將 J 層節點的值輸入到 sigmoid 函數(f(x)=1+e−x1,將x=1 代入,得到 0.731)進行激活。

最後,我們將這個結果傳遞到最後的輸出層。

xk=0.731∗0.5+0.731∗0.5=0.731

因爲我們輸出層的激活函數也是恆等函數,因此yk=xk=0.731

反向傳播的第一步,是計算輸出節點的δ ,

δk=yk−yk′=0.731−0.5=0.231

採用δ 計算 J 和 K 兩層節點間的權重梯度:▽wjkE=δkyj=0.231∗0.731=0.168861

接下來,以同樣的方法計算每個隱藏層的δ 值(在本示例中,僅有一個隱藏層):

δj=yj(1−yj)k=1∑Kδkwjk=0.731∗(1−0.731)∗(0.5∗0.231)≈0.0227

針對 I 和 J 層節點權重計算其梯度爲:

▽wijE=δjyi=0.0227∗1=0.0227

最後一步是用計算出的梯度更新所有的權重值。注意這裡如果我們有多於一個的關聯,那麼便可以針對每組關聯的梯度進行累計,然後更新權重值。

wij=wij−ε▽wijE=0.5−0.01∗0.0227=0.499773

wjk=wjk−ε▽wjkE=0.5−0.01∗0.168861=0.49831139

可以看到這個權重值變化很小,但如果我們用這個權重再跑一遍 forward pass,一般來說將會得到一個比之前更小的誤差。讓我們現在來看下……

第一遍我們得到的y1=0.731,採用新的權重值計算得到y2≈0.7285。

由此,y1−y1′=0.231,而y2−y2′=0.2285。

可見,誤差得到了減小!儘管減少值很小,但對於一個真實場景也是很有代表性的。按照該算法重複運行,一般就可以將誤差最終減小到0,那麼便完成了對神經網絡的訓練。

本示例中,將一個 2X2X1 的網絡訓練出 XOR 運算符的效果。

x=[1.01.01.00.00.01.00.00.0]

y′=[0.01.01.00.0]

這裡,f 是針對隱藏層的 sigmoid 激活函數。

注意,XOR 運算符是不能通過第一部分中的線性網絡進行模擬的,因爲數據集分佈是非線性的。也就是你不能通過一條直線將 XOR 的四個輸入值正確劃分到兩類中。如果我們將 sigmoid 函數換爲恆等函數,這個網絡也將是不可行的。

講完這麼多,輪到你自己來動手操作啦!試試採用不同的激活函數、學習率和網絡拓撲,看看效果如何?

恭喜看完本文!你學會了嗎?