反向傳播算法(Back Propagation)分二步進行,即正向傳播和反向傳播。這兩個過程簡述如下:
1.正向傳播
輸入的樣本從輸入層經過隱單元一層一層進行處理,傳向輸出層;在逐層處理的過程中。在輸出層把當前輸出和期望輸出進行比較,如果現行輸出不等於期望輸出,則進入反向傳播過程。
2.反向傳播
反向傳播時,把誤差信號按原來正向傳播的通路反向傳回,逐層修改連接權值,以望代價函數趨向最小。
下面以單隱層的神經網絡為例,進行權值調整的公式推導,其結構示意圖如下:
輸入層輸入向量(n維):X=(x1,x2,…,xi,…,xn)T
隱層輸出向量(隱層有m個結點):Y=(y1,y2,…,yj,…,ym)T
輸出層輸出向量(l維):O=(o1,o2,…,ok,…,ol)T
期望輸出向量:d=(d1, d2,…,dk,…,dl)T
輸入層到隱層之間的權值矩陣:V=(V1,V2,…,Vj,…,Vm)
隱層到輸出層之間的權值矩陣用:W=(W1,W2,…,Wk,…,Wl)
對輸出層第k個結點和隱含層的第j個結點有如下關系:
激活函數f(x)常用sigmoid函數(一個在生物學中常見的S型的函數,也稱為S形生長曲線)或者tanh(雙曲正切)函數。各種S型曲線函數如下圖所示:
下面以sigmoid函數進行推導。sigmoid函數定義為:
其導函數為:
定義對單個樣本輸出層所有神經元的誤差總能量總和為:
將以上誤差定義式展開至隱層:
權值調整思路為:
上面式子中負號表示梯度下降,常數η∈(0,1)表示權值調整步長(學習速度)。推導過程中,對輸出層有j=0,1,2,…,m; k=1,2,…,l;對隱層有 i=0,1,2,…,n; j=1,2,…,m
對輸出層和隱層,上面式子可寫為:
對輸出層和隱層,定義δ:
將以上結果代入δ的表達式,並根據sigmoid函數與其導函數的關系:f'(x)=f(x)*[1-f(x)],可以計算出:
可以看出要計算隱層的δ,需要先從輸出層開始計算,顯然它是反向遞推計算的公式。以此類推,對於多層神經網絡,要先計算出最后一層(輸出層)的δ,然后再遞推計算前一層,直到輸入層。根據上述結果,三層前饋網的BP學習算法權值調整計算公式為:
對所有輸入樣本(P為訓練樣本的個數),以總的平均誤差能量作為經驗損失函數(經驗風險函數,代價函數)為:
最終目標是需要調整連接權使得經驗損失函數最小。用BP算法訓練網絡時有兩種訓練方式:一種是順序方式(Stochastic Learning),即每輸入一個訓練么樣本修改一次權值;以上給出的權值修正步驟就是按順序方式訓練網絡的;另一種是批處理方式(Batch Learning),即待組成一個訓練周期的全部樣本都一次輸入網絡后,以Ep為目標函數修正權值.
順序方式所需的臨時存儲空間較批處理方式小,而且隨機輸入樣本有利於權值空間的搜索具有隨機性,在一定程度上可以避免學習陷入局部最小值。但是順序方式的誤差收斂條件難以建立,而批處理方式能精確地計算出梯度向量,誤差收斂條件簡單。
Stochastic learning is generally the preferred method for basic backpropagation for the following three reasons:
Despite the advantages of stochastic learning, there are still reasons why one might consider using batch learning:
下面采用激活函數為tanh(x)的三層網絡來解決異或問題(當激活函數為奇函數時,BP 算法的學習速度要快一些,最常用的奇函數是雙曲正切函數)
1 # -*- coding: utf-8 -*- 2 import numpy as np 3 4 5 # 雙曲正切函數,該函數為奇函數 6 def tanh(x): 7 return np.tanh(x) 8 9 # tanh導函數性質:f'(t) = 1 - f(x)^2 10 def tanh_prime(x): 11 return 1.0 - tanh(x)**2 12 13 14 class NeuralNetwork: 15 def __init__(self, layers, activation = 'tanh'): 16 """ 17 :參數layers: 神經網絡的結構(輸入層-隱含層-輸出層包含的結點數列表) 18 :參數activation: 激活函數類型 19 """ 20 if activation == 'tanh': # 也可以用其它的激活函數 21 self.activation = tanh 22 self.activation_prime = tanh_prime 23 else: 24 pass 25 26 # 存儲權值矩陣 27 self.weights = [] 28 29 # range of weight values (-1,1) 30 # 初始化輸入層和隱含層之間的權值 31 for i in range(1, len(layers) - 1): 32 r = 2*np.random.random((layers[i-1] + 1, layers[i] + 1)) -1 # add 1 for bias node 33 self.weights.append(r) 34 35 # 初始化輸出層權值 36 r = 2*np.random.random( (layers[i] + 1, layers[i+1])) - 1 37 self.weights.append(r) 38 39 40 def fit(self, X, Y, learning_rate=0.2, epochs=10000): 41 # Add column of ones to X 42 # This is to add the bias unit to the input layer 43 X = np.hstack([np.ones((X.shape[0],1)),X]) 44 45 46 for k in range(epochs): # 訓練固定次數 47 if k % 1000 == 0: print 'epochs:', k 48 49 # Return random integers from the discrete uniform distribution in the interval [0, low). 50 i = np.random.randint(X.shape[0],high=None) 51 a = [X[i]] # 從m個輸入樣本中隨機選一組 52 53 for l in range(len(self.weights)): 54 dot_value = np.dot(a[l], self.weights[l]) # 權值矩陣中每一列代表該層中的一個結點與上一層所有結點之間的權值 55 activation = self.activation(dot_value) 56 a.append(activation) 57 58 # 反向遞推計算delta:從輸出層開始,先算出該層的delta,再向前計算 59 error = Y[i] - a[-1] # 計算輸出層delta 60 deltas = [error * self.activation_prime(a[-1])] 61 62 # 從倒數第2層開始反向計算delta 63 for l in range(len(a) - 2, 0, -1): 64 deltas.append(deltas[-1].dot(self.weights[l].T)*self.activation_prime(a[l])) 65 66 67 # [level3(output)->level2(hidden)] => [level2(hidden)->level3(output)] 68 deltas.reverse() # 逆轉列表中的元素 69 70 71 # backpropagation 72 # 1. Multiply its output delta and input activation to get the gradient of the weight. 73 # 2. Subtract a ratio (percentage) of the gradient from the weight. 74 for i in range(len(self.weights)): # 逐層調整權值 75 layer = np.atleast_2d(a[i]) # View inputs as arrays with at least two dimensions 76 delta = np.atleast_2d(deltas[i]) 77 self.weights[i] += learning_rate * np.dot(layer.T, delta) # 每輸入一次樣本,就更新一次權值 78 79 def predict(self, x): 80 a = np.concatenate((np.ones(1), np.array(x))) # a為輸入向量(行向量) 81 for l in range(0, len(self.weights)): # 逐層計算輸出 82 a = self.activation(np.dot(a, self.weights[l])) 83 return a 84 85 86 87 if __name__ == '__main__': 88 nn = NeuralNetwork([2,2,1]) # 網絡結構: 2輸入1輸出,1個隱含層(包含2個結點) 89 90 X = np.array([[0, 0], # 輸入矩陣(每行代表一個樣本,每列代表一個特征) 91 [0, 1], 92 [1, 0], 93 [1, 1]]) 94 Y = np.array([0, 1, 1, 0]) # 期望輸出 95 96 nn.fit(X, Y) # 訓練網絡 97 98 print 'w:', nn.weights # 調整后的權值列表 99 100 for s in X: 101 print(s, nn.predict(s)) # 測試
輸出為:
參考:
https://triangleinequality.wordpress.com/2014/03/31/neural-networks-part-2/
https://databoys.github.io/Feedforward/
http://www.cnblogs.com/wentingtu/archive/2012/06/05/2536425.html
《Neural Networks Tricks of the Trade》Second Edition