用 Python 手動進行 ANN 的搭建(簡單的BP型網絡),可加深對其深入的理解,同時熟練coding,具有xxx、yyy之功效。
網上手動建NN的帖子有不少,不過多數沒有實現過程,這里把這一部分補上,方便大家參考使用。
前半步主要是參考如下的帖子,建立 ANN:
-
https://towardsdatascience.com/how-to-build-your-own-neural-network-from-scratch-in-python-68998a08e4f6
但這個帖子有個問題,就是建立了ANN 卻並沒有實現過程,同時其中前后傳播方法的‘feedforward’和 ‘backprop’ 中的sigmoid函數及其導數也沒有具體的函數定義。
-
因此,進一步參考如下的網頁:
-
http://python3.codes/neural-network-python-part-1-sigmoid-function-gradient-descent-backpropagation/
-
http://python.jobbole.com/82758/
另外下面的這個也不錯,只是有點太細碎了,大線條看着有點亂:
- http://www.bogotobogo.com/python/python_Neural_Networks_Backpropagation_for_XOR_using_one_hidden_layer.php
以下具體步驟:
1. 建立 ANN:
有些說明在注釋中寫了。
1 class NeuralNetwork: 2 3 # 通過初始化的方式實現 輸入‘x’及輸出‘y’、以及網絡權重(定義了二層4節點的網絡結構) 的基本結構: 4 def __init__(self, x, y): 5 self.input = x 6 self.weights1 = np.random.rand(self.input.shape[1],4) 7 # 這樣的結構,意味着weights1的行數是輸入變量x的列數,那么相乘時可能是與常規的 W·X 的方式相反,即 X·W 8 # 因此,X的每一行,代表着一次x輸入,對應着每一個輸出y 9 10 # 同上的,這樣的結構形式定義,代表的表達式或應是:[(X·W1+b1)·W2]+b2 11 ## 而在經過上述W1與W2的相乘后,每一行的x對應着一個y_i的值 12 self.weights2 = np.random.rand(4,1) 13 14 # 對應着y作為輸出,定義self.output 的結構 15 self.y = y 16 self.output = np.zeros(y.shape) 17 18 19 def feedforward(self): #定義前傳方法: 20 # 注意忽略了每層的偏差 b_i 21 # 且轉換函數使用 sigmoid() 22 # 因沒有相應的函數定義,將其中的 sigmoid() 函數進行展開 23 # sigmoid(m)=1/(1 + np.exp(-m)) 24 25 npDotW1_Inpt = np.dot(self.input, self.weights1) 26 self.layer1 = 1/(1+np.exp(-1*npDotW1_Inpt)) 27 28 npDotL1_W2 = np.dot(self.layer1, self.weights2) 29 self.output = 1/(1+np.exp(-1*npDotL1_W2)) 31 32 def backprop(self): # 定義后傳方法: 33 # application of the chain rule to find derivative of the loss function with respect to weights2 and weights1 34 # 以上是原文注解,即在向后傳播 backprop 時,使用鏈式法則獲得相應權重值的損失函數 35 # 因沒有相應的函數定義,將原貼中的 sigmoid_derivative() 函數進行展開 36 # sigmoid_derivative(m)=m * (1 - m) 37 38 ## 此處采用的是 s_deriv(x) = x*(1-x) 的方式,如采用 exp(-x)/(1+exp(-x))^2) 的解析式也可以,本質相同: 39 d_weights2 = np.dot(self.layer1.T, (2*(self.y - self.output) * (self.output*(1-self.output)))) 40 d_weights1 = np.dot(self.input.T, (np.dot(2*(self.y - self.output) * (self.output*(1-self.output)), self.weights2.T) * (self.layer1*(1-self.layer1)))) 41 42 # update the weights with the derivative (slope) of the loss function 43 # 通過上面的求導斜率,對W1和W2進行更新: 44 self.weights1 += d_weights1 45 self.weights2 += d_weights2
2. 實現過程:
此步原文沒有寫,估計作者覺得太簡單了,貂尾續根草吧。
但這里的輸入和輸出是按原文給的,便於對比實現結果是否一致。
1 # 建立輸入和輸出變量: 2 x_input = np.array([[0,0,1],[0,1,1],[1,0,1],[1,1,1]]) 3 y_obj = np.array([[0,1,1,0]]).T 4 5 # 實例化 ANN: 6 nn = NeuralNetwork(x_input, y_obj) 7 8 # 進行計算,迭代次數為1500次: 9 m = 1500 10 loss = np.zeros(m) 11 12 for i in range(m): 13 nn.feedforward() # 前傳計算結果 14 nn.backprop() # 后傳更新權重 15 16 loss[i] = np.sum((nn.output-y_obj)**2) # 記錄每次的結果偏差 17 18 # 繪制結果圖形: 19 plt.plot(loss) 20 plt.xlabel('Iteration') 21 plt.ylabel('LossValue') 22 plt.grid(True)
結果如圖:
Enjoy it :)