- Python神經網絡項目實戰
- (美)詹姆斯·洛伊
- 3534字
- 2022-12-20 18:56:28
1.3 神經網絡
神經網絡是一類機器學習算法,該算法受到了人類大腦中神經元的啟發。不過,我們沒必要將其完全類比于人類大腦,我發現把神經網絡用數學方程描述為將給定輸入映射到期望輸出,理解起來會更簡單。為了理解上述問題,讓我們看看單層神經網絡是什么樣的(單層神經網絡也被稱為感知器)。
感知器(perceptron)如圖1-4所示。

圖1-4
它的核心就是一個數學函數,接收一組輸入,然后進行某種數學運算,然后將計算結果輸出。

指的是感知器的權重。我們會在后面的章節中介紹神經網絡中的權重。目前我們只需要知道神經網絡就是一些簡單的數學函數,它們將給定的輸入映射為期望的輸出。
1.3.1 為什么要使用神經網絡
在開始編寫神經網絡之前,有必要了解一下神經網絡算法能夠在機器學習和人工智能領域具有舉足輕重的地位的原因。
第一個原因,神經網絡是一種通用函數逼近器(universal function approximator)。這句話的意思是,給定任意我們希望建模的函數,不論該函數多么復雜,神經網絡總是能夠表示該函數。這一特性對神經網絡和人工智能具有深遠的影響。假設現實中的任何問題都可以被數學函數所表示(不論其多么復雜),那么我們都可以用神經網絡來表示該函數,從而有效地對現實世界的問題進行建模。需要補充一點的是,盡管科學家們已經證明了神經網絡的通用性,但是一個超大且復雜的神經網絡可能永遠都無法完成訓練及泛化。
第二個原因,神經網絡結構的可擴展性非常好而且很靈活。在后續章節中會看到,我們可以將神經網絡堆疊起來,以此來增加神經網絡的復雜性。更有趣的可能是,神經網絡的能力僅僅局限于我們的想象力。通過富有創造性的神經網絡結構設計,機器學習工程師已經學會了如何利用神經網絡預測時間序列數據(這個模型被稱為RNN),它被應用于語音識別等領域。最近幾年,科學家還展示了通過讓兩個神經網絡在競賽中互相對抗[稱為生成對抗網絡(generative adversarial network,GAN)],來生成人眼無法辨別的寫實圖像。
1.3.2 神經網絡基礎結構
在本節中,我們會研究神經網絡的基礎結構,所有復雜的神經網絡都是基于此構建的。同時,我們也會使用Python開始構建最基礎的神經網絡(不使用任何機器學習函數庫)。這一練習會幫助我們理解神經網絡的內部工作原理。
神經網絡包含如下組成部分:
● 一個輸入層x;
● 一定數量的隱藏層;
● 一個輸出層?;
● 每一層之間包含權重W和偏差b;
● 為每個隱藏層所選擇的激活函數σ。
圖1-5所示的為一個兩層神經網絡的結構(注意,在統計神經網絡層數的時候,輸入層通常不被計算在內)。

圖1-5
1.3.3 使用Python從頭開始訓練一個神經網絡
現在我們已經了解了神經網絡的基本結構,讓我們使用Python從頭創建一個神經網絡吧!
首先,在Python中創建一個神經網絡的類:
import numpy as np 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)
注意前述代碼,權重(self.weights1和self.weights2)被初始化為一個包含隨機數的NumPy數組。NumPy數組被用來表示Python中的多維數組。上述代碼中權重的維度是通過np.random.rand函數的參數來設定的。基于輸入的維度,使用變量(self.input.shape[1])創建了對應維度的數組。
一個簡單的兩層神經網絡的輸出:?,表述為如下形式:

你也許注意到了,在上述公式中,權重W以及偏差b是影響輸出?的唯一變量。
因此,正確的權重和偏差決定了預測的強度。對權重和偏差進行調優的過程被稱為神經網絡的訓練。
迭代訓練神經網絡的每一個循環都包括以下步驟:
1.計算預測輸出?,被稱為前饋(feedforward);
2.更新權重和偏差,被稱為反向傳播(backpropagation)。
圖1-6對該步驟做出了解釋。

圖1-6
1.前饋
在圖1-6中我們可以看到,前饋就是簡單的計算。而對于一個基礎的兩層神經網絡來說,網絡的輸出可以用下列公式表示:

下面,在Python代碼中增加一個feedforward函數來完成上述功能。注意,為了降低難度,我們假設偏差為0:
import numpy as np def sigmoid(x): return 1.0/(1 + np.exp(-x)) 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))
然而,我們還需要找到一種方法來評估預測的準確率(預測偏差有多大)。損失函數(loss function)可以幫助我們完成這個工作。
2.損失函數
損失函數有很多種,它的選擇需要根據待解決問題的本質來決定。就目前來講,我們選擇一個平方和誤差(Sum-of-Squares Error)作為損失函數:

平方和誤差就是對實際值和預測值之間的差值求和,不過我們對其進行了平方運算,因此計算結果是其絕對差值。
我們的目標是訓練神經網絡并找到能使得損失函數最小化的最優權重和偏差。
3.反向傳播
現在已經計算出了預測結果的誤差(損失),我們需要找到一種方法將誤差在網絡中反向傳導以便更新權重和偏差。
為了找到合適的權重及偏差矯正量,我們需要知道損失函數關于權重及偏差的導數。
回憶一下微積分知識,一個函數的導數就是該函數的斜率,如圖1-7所示。

圖1-7
如果得到了導數,我們就可以根據導數,通過增加導數值或減少導數值的方式來調節權重和偏差(如圖1-7所示)。這種方法稱為梯度下降法(gradient descent)。
然而,我們不能直接求損失函數關于權重和偏差的導數,因為損失函數中并不包含它們。我們需要利用鏈式法則(chain rule)進行計算。就目前階段來講,我們不會深究鏈式法則,因為其背后的數學原理相當復雜。而且,像Keras等機器學習庫會幫我們完成梯度下降計算而不需要從頭編寫鏈式法則。
我們需要理解的關鍵點是,一旦我們得到了損失函數關于權重的導數(斜率),我們便可以依此相應地調整權重。
現在,向代碼添加backprop函數 :
import numpy as np def sigmoid(x): return 1.0/(1 + np.exp(-x)) def sigmoid_derivative(x): return x * (1.0 - x) 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): # 使用鏈式法則來找到損失函數關于weights2和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))) self.weights1 += d_weights1 self.weights2 += d_weights2 if __name__ == "__main__": X = np.array([[0,0,1], [0,1,1], [1,0,1], [1,1,1]]) y = np.array([[0],[1],[1],[0]]) nn = NeuralNetwork(X,y) for i in range(1500): nn.feedforward() nn.backprop() print(nn.output)
注意上述代碼,我們在feedforward函數中使用了一個sigmoid函數。sigmoid函數是一種激活函數,它將函數值壓縮到0~1。這一特性很重要,因為對于二元預測問題,我們需要預測結果位于0~1。我們將在第2章中詳細探討sigmoid激活函數。
1.3.4 綜合應用
現在我們已經完成了具有前饋和反向傳播功能的Python代碼,讓我們在以下案例中應用該神經網絡,看看它效果如何。
表1-1包括了4個數據點,每個點包括3個輸入變量(x1, x2和x3)和一個目標變量(Y)。
表1-1

我們的神經網絡需要學習到能夠表示該函數的最理想權重。注意,如果我們想要通過觀察的方式來確定這一組權重,可不是什么容易的事。
迭代訓練神經網絡1500次,看看發生了什么。如圖1-8所示,從損失-迭代次數圖可以清晰地看出,損失是單調遞減到最小值的。這和我們之前討論的梯度下降算法的描述是一致的。
讓我們看一下神經網絡經過1500次迭代訓練后最終的預測(輸出)結果,如表1-2所示。
表1-2


圖1-8
成功了!我們的前饋和反向傳播算法成功地訓練了神經網絡且預測值向真實值收斂。
注意預測值和真實值之間存在的微小差異。這是我們期望發生的,它可以防止模型的過擬合(overfitting)并使其能夠更好地泛化以便處理新的數據。
現在已經知道了神經網絡的內部原理,接下來會介紹Python的機器學習函數庫,這些函數庫在后續的章節中都會用到。如果你感到從頭創建一個神經網絡非常困難,請不必擔心。在本書的后續部分,我們會使用機器學習庫來極大地簡化神經網絡的構建和訓練過程。
1.3.5 深度學習和神經網絡
深度學習是什么?它和神經網絡有什么區別?簡單來講,深度學習是一種機器學習算法,它使用多層神經網絡進行學習(也被稱為深網)。如果我們將一個單層感知器看作最簡單的神經網絡,那么深度神經網絡則走向了復雜性的一個極端。
在深度神經網絡(DNN)中,每一層學習到的信息的復雜度是不斷增加的。例如,當訓練一個用于進行面部識別的深度神經網絡時,第一層用于檢測臉部的輪廓,然后是識別輪廓(例如眼睛)的層,直到最后完成全部的臉部特征識別。
盡管感知器在20世紀50年代就產生了,但是深度學習一直到近幾年才開始蓬勃發展。深度學習在過去一個世紀發展相對比較緩慢,很大程度上是由于缺少數據以及相應的計算能力。然而,在過去的幾年中,深度學習成為了驅動機器學習的關鍵技術。今天,深度學習已經成為圖像識別、自動駕駛、語音識別和游戲領域的首選算法。那么,過去幾年究竟發生了什么呢?
近些年來,用于存儲深度學習所需的海量數據的計算機存儲設備變得經濟實惠。如果你將數據存放在云端,存儲數據的費用還可以變得更便宜,而且可以被世界各地的計算機集群訪問。除了擁有能夠消費得起的數據存儲服務之外,數據也變得更加平民化。例如像ImageNet這樣的網站,它們向機器學習研究人員提供了1400萬張圖像。數據已經不再是少數人才能擁有的商品了。
深度學習所需的計算能力同樣變得更便宜也更強大。大多數的深度學習項目受益于圖形處理單元(GPU),它非常擅長滿足深度神經網絡的計算需求。繼續剛才關于平民化的話題,現在很多網站給深度學習愛好者提供免費的GPU處理資源。舉例來說,Google Colab提供免費的Tesla K80 GPU云服務用于深度學習,每個人都可以使用。
基于這些近期的技術發展,深度學習已經成為了人人都能使用的技術。在后面的章節中,我們會介紹一些你將會用到的Python深度學習函數庫。
- C及C++程序設計(第4版)
- Learning Microsoft Windows Server 2012 Dynamic Access Control
- Learning Python Web Penetration Testing
- Hands-On RESTful Web Services with Go
- Visual FoxPro程序設計
- 從零開始學Linux編程
- Windows Embedded CE 6.0程序設計實戰
- Web性能實戰
- JavaScript+jQuery網頁特效設計任務驅動教程
- 人人都能開發RPA機器人:UiPath從入門到實戰
- 深度學習入門:基于Python的理論與實現
- HTML5與CSS3權威指南
- Using Yocto Project with BeagleBone Black
- jQuery Mobile Web Development Essentials(Second Edition)
- 軟件測試(慕課版)