無需深度學習框架,如何從零開始用Python構建神經網路
選自TowardsDataScience
作者:
James Loy
機器之心編譯
參與:陳韻竹
、王淑婷
這是一份用於理解深度學習內部運作方式的初學者指南。作者根據自己從零開始學慣用 Python 構建神經網路的經驗,編寫了一份攻略。內容涵蓋神經網路定義、損失函數、前向傳播、反向傳播、梯度下降演算法,對於想要了解深度學習運作原理的各位來說,內容精彩不可錯過。
動機:
為了深入了解深度學習,我決定從零開始構建神經網路,並且不使用類似 Tensorflow 的深度學習庫。我相信,對於任何有理想的數據科學家而言,理解神經網路內部的運作方式都非常重要。
本文涵蓋了我學到的所有東西,希望你也能從中獲益!
什麼是神經網路?
許多有關神經網路的介紹資料會將神經網路與大腦進行類比。但我發現,將神經網路簡單地描述為一個從輸入映射到輸出的數學函數理解起來更容易。
神經網路由以下部分組成:
一個輸入層,x
任意數量的隱藏層
一個輸出層,?
每兩層之間都有一組權重和偏置,W 和 b
每個隱藏層都要選擇一個激活函數 σ。在本文中,我們選用 Sigmoid 激活函數。
下圖展示了 2 層神經網路的結構(請注意,在計算神經網路層數的時候,通常不計入輸入層)。
二層神經網路的結構
利用 Python 建立神經網路非常容易。
class NeuralNetwork
:
def
__init__
(self, x, y)
:self.input = x
self.weights1 = np.random.rand(self.input.shape[
1
],4
)self.weights2 = np.random.rand(
4
,1
)self.y = y
self.output = np.zeros(y.shape)
訓練神經網路
一個簡單 2 層神經網路的輸出 ? 可以表示為:
你可能注意到,在上面的等式當中,權重 W 和偏置 b 是影響輸出 ? 的唯一變數。
自然,權重和偏差的正確值決定了預測的強度。根據輸入數據微調權重和偏置的過程稱為神經網路訓練。
訓練過程的每一次迭代包含以下步驟:
計算預測的輸出 ?,稱為前向傳播
更新權重和偏置,稱為反向傳播
以下流程圖說明了這個過程:
前向傳播
正如我們在上圖中所看到的,前向傳播只是一個簡單的計算。對於一個基本的 2 層神經網路,神經網路的輸出計算如下:
我們可以在 Python 代碼中添加一個前向傳播函數來做到這一點。簡單起見,我們假設偏置為 0。
class NeuralNetwork
def
__init__
(self, x, y)
:self.input = x
self.weights1 = np.random.rand(self.input.shape[
1
],4
)self.weights2 = np.random.rand(
4
,1
)self.y = y
self.output = np.zeros(self.y.shape)
def
feedforward
(self)
:self.layer1 = sigmoid(np.dot(self.input, self.weights1))
self.output = sigmoid(np.dot(self.layer1, self.weights2))
然而,我們仍然需要一種方法來評估我們的預測的「優秀程度」(即,我們的預測與真實值相差多少?)這就需要用到損失函數了。
損失函數
損失函數有很多種,而我們問題的性質會決定我們使用哪種損失函數。在本文中,我們將採用簡單的誤差平方和。
誤差平方和,即每個預測值和真實值之間差值的平均值。這個差值是取了平方項的,所以我們測量的是差值的絕對值。
在訓練過程中,我們的目標是找到一組最佳的權重和偏置,使損失函數最小化。
反向傳播
現在,我們已經找到了預測誤差的方法(損失函數),那麼我們需要一種方法將錯誤「傳播」回去,從而更新權重和偏置。
為了確定權重和偏置調整的適當值,我們需要知道損失函數對權重和偏置的偏導數。
從微積分的角度來看,函數的偏導數也就是函數的斜率。
梯度下降演算法
如果我們知道了偏導數,我們可以通過簡單增加或減少偏導數(如上圖所示)的方式來更新權重和偏置。這就是所謂的梯度下降。
然而,由於損失函數的方程不包含權重和偏置,所以我們不能直接計算損失函數對權重和偏置的偏導數。因此,我們需要鏈式法則來幫助計算。
以上是用於計算損失函數對權重偏導數的鏈式法則。簡單起見,我們只展示了一層神經網路的偏導數。
唷!這看起來不大好看,但這能讓我們獲得所需——損失函數對權重的偏導數(斜率),以便相應調整權重。
既然我們已經有了鏈式法則公式,接下來我們把反向傳播函數添加到 Python 代碼中。
class NeuralNetwork
def
__init__
(self, x, y)
:self.input = x
self.weights1 = np.random.rand(self.input.shape[
1
],4
)self.weights2 = np.random.rand(
4
,1
)self.y = y
self.output = np.zeros(self.y.shape)
def
feedforward
(self)
:self.layer1 = sigmoid(np.dot(self.input, self.weights1))
self.output = sigmoid(np.dot(self.layer1, self.weights2))
def
backprop
(self)
:# application of the chain rule to find derivative of the loss function with respect to weights2 and weights1
d_weights2 = np.dot(self.layer1.T, (
2
*(self.y - self.output) * sigmoid_derivative(self.output)))d_weights1 = np.dot(self.input.T, (np.dot(
2
*(self.y - self.output) * sigmoid_derivative(self.output), self.weights2.T) * sigmoid_derivative(self.layer1)))
# update the weights with the derivative (slope) of the loss function
self.weights1 += d_weights1
self.weights2 += d_weights2
為了更深入地理解微積分和鏈式法則在反向傳播中的應用,我強烈推薦 3Blue1Brown 的視頻教程。
整合
既然我們已經有了做前向傳播和反向傳播的完整 Python 代碼,我們可以將神經網路應用到一個示例中,看看它的效果。
我們的神經網路應該能夠習得理想的權重集合以表示這個函數。請注意,對於我們來說,僅通過檢查來計算權重並非一件小事。
如果我們將神經網路進行 1500 次迭代,看看會發生什麼。下圖展示了每次迭代的損失函數值,我們可以清晰地發現損失函數單調下降到最小值。這與我們前面討論的梯度下降演算法是一致的。
讓我們看看神經網路在進行 1500 次迭代後的最終預測(輸出):
進行 1500 次迭代後的預測值
我們成功了!我們的前向傳播和反向傳播演算法成功訓練了神經網路,且預測值收斂到了真實值。
請注意,預測值和真實值之間還是有一些輕微差異的。這是可取的,因為它防止了過度擬合,並且使得神經網路具有更強的泛化能力。
下一步
幸運的是,我們的探索還沒有結束。關於神經網路和深度學習還有很多需要學習的地方。例如:
除了 Sigmoid 函數之外,我們還可以使用哪些激活函數?
在訓練神經網路時使用學習率
使用卷積進行圖像分類任務
最後一點想法
在撰寫此文的過程中,我已經學到了很多,希望本文也能對你有所幫助。
在沒有完全了解神經網路內部工作原理的情況下,雖然使用諸如 TensorFlow 和 Keras 之類的深度學習庫可以讓我們很容易地建立深度網路,但我認為對於有抱負的數據科學家而言,深入理解神經網路還是大有裨益的。
延伸閱讀:
從零開始:教你如何訓練神經網路
了解神經網路,你需要知道的名詞都在這裡
從感知機到深度神經網路,帶你入坑深度學習
原文鏈接:https://towardsdatascience.com/how-to-build-your-own-neural-network-from-scratch-in-python-68998a08e4f6
本文為機器之心編譯,
轉載請聯繫本公眾號獲得授權
。?------------------------------------------------
加入機器之心(全職記者/實習生):hr@jiqizhixin.com
投稿或尋求報道:
content
@jiqizhixin.com廣告&商務合作:bd@jiqizhixin.com
※資源 | 簡單快捷的數據處理,數據科學需要注意的命令行
※「凡是過往,皆為序章」64歲RODNEY BROOKS談人工智慧起源與發展
TAG:機器之心 |