簡單解釋神經網絡是如何工作的,以及如何在Python中從頭實現一個。
這句話可能會讓你大吃一驚:神經網絡并沒有那么復雜!“神經網絡”這個詞經常被用作流行語,但實際上它們往往比人們想象的要簡單得多。
這篇文章是完全針對初學者準備的,我們假設你沒有任何機器學習的知識。我們將了解神經網絡如何工作,同時在Python中從零開始實現一個。
讓我們開始吧!
1. 構建塊:神經元
首先,我們必需討論神經元,神經網絡的基本單位。一個經元接受輸入,對它們進行一些數學運算,然后產生一個輸出。這里是一個2輸入神經元的樣子:

這里發生了三件事。首先,每個輸入被乘以一個權重:

接下來,將所有加權后的輸入與一個偏差b相加:

最后,總和被傳入一個激活函數:

激活函數用于將一個無界輸入轉換為一個具有良好的、可預測形式的輸出。一個常用的激活函數是sigmoid函數:

sigmoid函數只輸出(0,1)范圍內的數字。你可以認為它是把(−∞,+∞) 壓縮到(0,1)——大負數變成~0,大正數變成~1。
一個簡單的例子
假設我們有一個使用sigmoid激活函數的2輸入神經元,并帶有以下參數:

w =[0,1]就是向量形式的w1 =0,w2 =1。現在,我們給這個神經元提供一個x=[2,3]的輸入 。我們用點積來寫得更簡潔一些:

給定輸入x=[2,3] ,神經元會輸出0.999。就是這樣!這個將輸入向前傳遞以獲得輸出的過程稱為前饋。
編寫一個神經元
是時候實現一個神經元了!我們將使用NumPy,一個流行而強大的Python計算庫,來幫助我們處理數學運算:

認出這些數了嗎?這就是我們剛才舉的例子!我們得到了相同的答案0.999。
2. 將神經元組合成一個神經網絡
一個神經網絡只不過是一群連接在一起的神經元。下面是一個簡單的神經網絡的樣子:

這個網絡有2個輸入,一個帶有2個神經元(h1和h2)的隱藏層,一個帶有1個神經元(σ1)的輸出層。注意σ1的輸入是來自h1和h2的輸出——這就是一個網絡的組成。
隱藏層是在輸入(第一個)層和輸出(最后一個)層之間的任何層。可以有多個隱藏層!
一個例子:前饋
我們來使用上圖所示的網絡,假設所有的神經元都具有相同的權重w=[0,1] ,相同的偏差b= 0,以及相同的sigmoid激活函數。讓h1、h2、σ1表示它們所表示的神經元的輸出。
如果我們傳入輸入x=[2,3] 會發生什么?

該神經網絡對輸入x=[2,3] 的輸出為0.7216。很簡單,對吧?
一個神經網絡可以有任意數量的層,每個層可以包含任意數量的神經元。其基本思想是相同的: 通過網絡中的神經元向前反饋輸入以便最終得到輸出。為了簡單起見,我們將在本文的其余部分繼續使用上面所示的網絡。
編寫一個神經網絡: 前饋
我們來實現我們神經網絡的前饋。這里是該網絡的圖片,再次供參考:


我們又得到了0.7216 !看起來它可以工作。
3.訓練神經網絡,第1部分
假設我們有以下測量值:

我們來訓練我們的網絡,并根據一個人的體重和身高來預測他的性別:

我們將用0表示男性,用1表示女性,我們還將對數據進行移位,使其更容易使用:

我隨意選擇了移位量(135和66)來使數字看起來漂亮一些。一般情況下,你應該使用平均值來移位。
損失
在訓練我們的網絡之前,我們首先需要一種方法來量化它做得有多“好”,這樣它就可以嘗試做得“更好”。這種方法就是損失。
我們將使用均方誤差(MSE)損失:

讓我們來分解一下:
- n是樣本的個數,這里是4 (Alice、 Bob、 Charlie、 Diana)。
- y表示要預測的變量,即性別。
- ytrue是變量(“正確答案”)的真值。例如,對于Alice來說,ytrue是1(女性)。
- ypred是變量的預測值。它是我們網絡的輸出。
(ytrue - ypred)2稱為平方誤差。我們損失函數只是取所有平方誤差的平均值(因此得名均方誤差)。我們的預測越好,我們的損失就會越低!
更好的預測=更低的損失。
訓練一個網絡=盡量減少它的損失。
一個損失計算例子
假設我們的網絡總是輸出0,換句話說,它確信所有的人類都是男性。那我們的損失是多少?

代碼: 均方誤差損失
下面是一些計算損失的代碼:

如果你不理解這段代碼的工作原理,請閱讀NumPy數組操作快速入門。
好了。開始!
4.訓練神經網絡,第2部分
我們現在有了一個明確的目標: 最小化神經網絡的損失。我們知道我們可以改變網絡的權重和偏差來影響它的預測,但我們如何才能以一種減少損失的方式做到這一點呢?
本節使用了一些多變量微積分。如果你對微積分不熟悉,你可以跳過數學部分。
為了簡單起見,假設我們的數據集中只有Alice:

那么均方誤差損失就是Alice的平方誤差:

另一種考慮損失的方法是將損失看作是一個權重和偏差的函數。我們來標記我們的網絡中的每個權重和偏差:

然后,我們可以將損失寫成一個多元函數:

假設我們想調整w1。如果我們改變w1,損失L將如何變化? 偏導數∂L/∂w1可以回答這個問題。我們如何計算它?
這就是數學開始變得更加復雜的地方。不要氣餒!我建議你帶一支筆和一張紙來跟著計算,它們可以幫助你理解。
首先,我們用∂ypred/∂w1來重寫偏導數代替:

由于我們上邊計算出了L=(1-ypred)2,因此,我們可以計算∂L/∂ypred:

現在,讓我們算出∂ypred/∂w1的值。就像之前一樣,假設h1、 h2 、σ1是它們所代表的神經元的輸出。那么

由于w1只影響h1(不影響h2),因此,我們可以寫

我們對∂h1/∂w1做相同的事情:

這里的x1 是體重 , x2是身高。現在已經是我們第二次看到f'(x)(sigmoid函數的推導式)了!我們對它進行推導:

我們稍后將使用f'(x)這個漂亮的形式。
我們完成了!我們已經將∂L/∂w1分解成了幾個我們可以計算的部分:

這種通過逆向運行計算偏導數的系統稱為反向傳播,或者“backprop”。
哦。這里有很多符號——如果你仍然有點困惑,也沒關系。讓我們舉一個例子來實際看一下!
例子: 計算偏導數
我們將繼續假設我們的數據集中只有Alice:

我們初始化所有的權重為1,所有的偏差為0。如果我們執行一個前饋通過網絡,我們得到:

該網絡輸出 ypred =0.524,這并沒有特別偏向男性(0)或女性(1)。我們來計算∂L/∂w1:

提醒: 我們在前面為我們的 sigmoid函數推導出了f'(x) = f(x) * (1 - f(x))。
我們做到了!這告訴我們,如果我們增加w1,L就會相應地增加一點點。
訓練:隨機梯度下降法
我們現在有了訓練神經網絡所需的所有工具! 我們將使用一種稱為隨機梯度下降(SGD)的優化算法,它告訴我們如何改變權重和偏差以最小化損失。它基本上就是這個更新方程:

η是一個稱為學習率的常數,它控制我們訓練的速度。我們所做的就是從w1中減去η∂L/∂w1:
- 如果∂L/∂w1是正數,w1會減小,從而使得L也減小。
- 如果∂L/∂w1 是負數,w1會增加,從而使得L也減小。
如果我們對網絡中的每一個權重和偏差都這樣做,損失就會慢慢減小,我們的網絡就會改善。
我們的訓練過程如下:
- 從數據集中選擇一個樣本。這就是稱其為隨機梯度下降的原因——我們一次只對一個樣本進行操作。
- 計算所有關于權重或偏差的損失的偏導數(例如∂L/∂w1、∂L/∂w2等等)。
- 使用更新方程來更新每個權重和偏差。
- 回到步驟1。
讓我們實際看一下!
代碼: 一個完整的神經網絡
終于是時候實現一個完整的神經網絡了:





你可以自己運行/測試這段代碼(https://repl.it/@vzhou842/An-Introduction-to-Neural-Networks )。你也可以在Github上找到它。(https://github.com/vzhou842/neural-network-from-scratch )
隨著網絡的學習,我們的損失逐漸減小:

我們現在可以用這個網絡來預測性別:

現在怎么辦?
你成功了!現在來快速回顧一下我們所做的:
- 介紹了神經元——神經網絡的構建塊。
- 在我們的神經元中使用了sigmoid激活函數。
- 看到了神經網絡只是連接在一起的神經元。
- 創建了一個帶有體重和身高的數據集,并將其作為輸入(或特征),性別作為輸出(或標簽)。
- 學習了損失函數和均方誤差(MSE)損失。
- 意識到訓練一個網絡只是最小化它的損失。
- 使用反向傳播計算偏導數。
- 利用隨機梯度下降法(SGD)對網絡進行訓練。
還有很多事情要做:
- 使用合適的機器學習庫(如Tensorflow、Keras和PyTorch)來測試更大/更好的神經網絡。
- 使用Keras構建你的第一個神經網絡。
- 在你的瀏覽器中修補一個神經網絡。
- 發現除了sigmoid之外的其它激活函數,如Softmax。
- 發現除了SGD之外的其它優化器。
- 閱讀我的《卷積神經網絡(CNNs)介紹》。CNNs給計算機視覺領域帶來了革命性的變化,其功能非常強大。
- 閱讀我的《遞歸神經網絡(RNNs)介紹》。RNNs經常用于自然語言處理(NLP)。
我可能會在將來寫這些主題或類似的主題,所以如果你想獲得關于新文章的通知,請訂閱我吧。
感謝閱讀!
英文原文:https://victorzhou.com/blog/intro-to-neural-networks/ 譯者:Nothing