天天看點

最簡單的人工神經網絡

原文作者:Givi Odikadze
最簡單的人工神經網絡

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

最簡單的人工神經網絡

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

内容分為兩部分:

第一部分:最簡單的人工神經網絡

第二部分:最基礎的反向傳播算法

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

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

一、最簡單的人工神經網絡

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

示例代碼:https://github.com/gokadin/ai-simplest-network

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

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

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

假設我們現在的一個網絡有兩個輸入值 , ​,它們對應兩個權重值 和 。

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

在初始化時,因為我們不知曉最優值,往往是對權重随機指派,這裡我們為了簡單,将它們都初始化為 1 。

最簡單的人工神經網絡

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

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

例如,如果我們希望目标值是 ,那麼這裡相內插補點就是

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

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

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

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

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

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

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

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

讓我們用一些數字來模拟一個簡單的例子。假設我們有一個函數是 ,那麼梯度将是

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

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

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

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

還記得我們上面的公式 和 嗎?對于 和 ,我們可以将其帶入并通過微積分中的鍊式求導法則來分别計算其梯度

簡潔起見,後面我們将采用 這個術語來表示 ​ 。

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

然後重複這個過程,直到誤內插補點最小并趨近于零。

附帶的示例采用梯度下降法,将如下資料集訓練成有兩個輸入值和一個輸出值的神經網絡:

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

二、最基礎的反向傳播算法

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

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

示例代碼:https://github.com/gokadin/ai-backpropagation

感覺機是這樣一個處理單元:它接受輸入 , 采用激活函數 對其進行轉換,并輸出結果 。

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

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

最簡單的人工神經網絡

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

針對 ​,典型的激活函數有以下形式:

Sigmoid 函數 : ​

線性整流函數:

tanh 函數:

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

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

​ 分别是 I, J, K 層節點的輸入值。

分别是 I, J, K 層節點的輸出值。

是 K 輸出節點的期望輸出值。

分别是 I 到 J 層和 J 到 K 層的權重值。

代表 T 組關聯中目前的一組關聯。

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

輸入層 -&gt; 恒等函數

隐藏層 -&gt; Sigmoid 函數

輸出層 -&gt; 恒等函數

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

對于隐藏層的每個節點的輸入就是輸入層輸入值的權重總和:

因為隐藏層的激活函數是 sigmoid,那麼輸出将會是:

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

因為我們賦予了恒等函數做為激活函數,是以這一層的輸出将等同于輸入值。

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

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

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

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

是以,對權重的調整即為 ​

對于多個關聯,那麼對權重的調整将為每個關聯的權重調整值之和 ​​

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

那麼,基于所有關聯的權重調整即為每次關聯計算得到的調整值之和

這裡, 我們對 ​ 可以再做進一步的探索。上文中,我們看到 ​​ 。

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

對後半部分的 ,因為我們在這一層采用了 sigmoid 函數,我們知道,sigmoid 函數的導數形式是 ​​​​ ,是以,有

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

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

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

對每次關聯,通過神經網絡向前傳輸,得到輸出值

計算每個輸出節點的誤差 (​)

疊加計算每個輸出權重的梯度 ()

計算隐藏層每個節點的 ()

疊加計算每個隐藏層權重的梯度 (​​)

更新所有權重值,重置疊加梯度 ()

在這個示例中,我們通過真實資料來模拟神經網絡中的每個步驟。輸入值是[1.0, 1.0],輸出期望值為 [0.5]。為了簡化,我們将初始化權重設為 0.5 (雖然在實際操作中,經常會采用随機值)。對于輸入、隐藏和輸出層,我們分别采用恒等函數、 sigmoid 函數 和恒等函數作為激活函數,學習率 則定為 0.01 。

運算一開始,我們将輸入層的節點輸入值設為 。

因為我們對輸入層采用的是恒等函數作為激活函數,是以有 。

最簡單的人工神經網絡

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

最簡單的人工神經網絡
最簡單的人工神經網絡

然後,我們将 J 層節點的值輸入到 sigmoid 函數(​,将 代入,得到 0.731)進行激活。

最簡單的人工神經網絡

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

因為我們輸出層的激活函數也是恒等函數,是以

最簡單的人工神經網絡

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

采用 計算 J 和 K 兩層節點間的權重梯度:

最簡單的人工神經網絡

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

最簡單的人工神經網絡

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

最簡單的人工神經網絡

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

最簡單的人工神經網絡

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

第一遍我們得到的 ,采用新的權重值計算得到 。

由此,,而 。

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

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

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

注意,XOR 運算符是不能通過第一部分中的線性網絡進行模拟的,因為資料集分布是非線性的。也就是你不能通過一條直線将 XOR 的四個輸入值正确劃分到兩類中。如果我們将 sigmoid 函數換為恒等函數,這個網絡也将是不可行的。

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

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

關注 HelloGitHub 公衆号 第一時間收到更新。

還有更多開源項目的介紹和寶藏項目等待你的發現。

- END -

作者:削微寒

繼續閱讀