BP神經網絡以及python實現

一、BP神經網絡結構模型

BP算法的基本思想是,學習過程由信號的正向傳播和誤差的反向傳播倆個過程組成,輸入從輸入層輸入,經隱層處理以後,傳向輸出層。如果輸出層的實際輸出和期望輸出不符合,就進入誤差的反向傳播階段。誤差反向傳播是將輸出誤差以某種形式通過隱層向輸入層反向傳播,並將誤差分攤給各層的所有單元,從而獲得各層單元的誤差信號,這個誤差信號就作為修正個單元權值的依據。知道輸出的誤差滿足一定條件或者迭代次數達到一定次數。

層與層之間為全連接,同一層之間沒有連接。結構模型如下圖所示。

BP神經網絡以及python實現

使用的傳遞函數sigmoid可微的特性使他可以使用梯度下降法。所以,在隱層函數中使用sigmoid函數作為傳遞函數,在輸出層採用線性函數作為傳遞函數。

輸入向量、隱層輸出向量、最終輸出向量、期望輸出向量:

X=(x1,x2,x3……xn),其中圖中x0是為隱層神經元引入閾值設置的;

Y=(y1,y2,y3……ym),其中圖中y0是為輸出神經元引入閾值設置的;

O=(o1,o2,o3……ol)

D=(d1,d2,d3……dl)

輸出層的輸入是隱層的輸出,隱層的輸入是輸入層的輸出,計算方法和單層感知器的計算方法一樣。

單極性Sigmoid函數:

BP神經網絡以及python實現

雙極性sigmoid函數:

BP神經網絡以及python實現

二、BP神經網絡的學習算法

標準BP神經網絡沿著誤差性能函數梯度的反方向修改權值,原理與LMS算法比較類似,屬於最速下降法。此外還有以下改進算法,如動量最速下降法,擬牛頓法等。

最速下降法又稱為梯度下降法。LMS算法就是最小均方誤差算法。LMS算法體現了糾錯原則,與梯度下降法本質上沒有區別,梯度下降法可以求目標函數的極小值,如果將目標函數取為均方誤差,就得到了LMS算法。

梯度下降法原理:對於實值函數F(x),如果函數在某點x0處有定義且可微,則函數在該點處沿著梯度相反的方向下降最快,因此,使用梯度下降法時,應首先計算函數在某點處的梯度,再沿著梯度的反方向以一定的步長調整自變量的值。其中實值函數指的是傳遞函數,自變量x指的是上一層權值和輸入值的點積作為的輸出值。

網絡誤差定義:

BP神經網絡以及python實現

BP神經網絡以及python實現

三層BP網絡算法推導:

1、變量定義

網絡的實際輸出:

BP神經網絡以及python實現

BP神經網絡以及python實現

  1. 信號正向傳播
BP神經網絡以及python實現

BP神經網絡以及python實現

  1. 誤差信號反向傳播

首先誤差反向傳播首先經過輸出層,所以首先調整隱含層和輸出層之間的權值。

BP神經網絡以及python實現

BP神經網絡以及python實現

然後對輸入神經元和隱層神經元的誤差進行調整。

BP神經網絡以及python實現

權值矩陣的調整可以總結為:

權值調整量det(w)=學習率*局部梯度*上一層輸出信號。

BP神經網絡的複雜之處在於隱層輸入層、隱層和隱層之間的權值調整時,局部梯度的計算需要用到上一步計算的結果,前一層的局部梯度是後一層局部梯度的加權和。

訓練方式:

  1. 串行方式:網絡每獲得一個新樣本,就計算一次誤差並更新權值,直到樣本輸入完畢。
  2. 批量方式:網絡獲得所有的訓練樣本,計算所有樣本均方誤差的和作為總誤差;
BP神經網絡以及python實現

在串行運行方式中,每個樣本依次輸入,需要的存儲空間更少,訓練樣本的選擇是隨機的,可以降低網絡陷入局部最優的可能性。

批量學習方式比串行方式更容易實現並行化。由於所有樣本同時參加運算,因此批量方式的學習速度往往遠優於串行方式。

BP神經網絡的優點:

  1. 非線性映射能力
  2. 泛化能力
  3. 容錯能力 允許輸入樣本中帶有較大誤差甚至個別錯誤。反應正確規律的知識來自全體樣本,個別樣本中的誤差不能左右對權矩陣的調整。

BP神經網絡的侷限性:

梯度下降法的缺陷:

  1. 目標函數必須可微;
  2. 如果一片區域比較平坦會花費較多時間進行訓練;
  3. 可能會陷入局部極小值,而沒有到達全局最小值;(求全局極小值的目的是為了實現誤差的最小值)

BP神經網絡的缺陷:

  1. 需要的參數過多,而且參數的選擇沒有有效的方法。確定一個BP神經網絡需要知道:網絡的層數、每一層神經元的個數和權值。權值可以通過學習得到,如果,隱層神經元數量太多會引起過學習,如果隱層神經元個數太少會引起欠學習。此外學習率的選擇也是需要考慮。目前來說,對於參數的確定缺少一個簡單有效的方法,所以導致算法很不穩定;
  2. 屬於監督學習,對於樣本有較大依賴性,網絡學習的逼近和推廣能力與樣本有很大關係,如果樣本集合代表性差,樣本矛盾多,存在冗餘樣本,網絡就很難達到預期的性能;
  3. 由於權值是隨機給定的,所以BP神經網絡具有不可重現性;

梯度下降法(最速下降法的改進):

針對算法的不足出現了幾種BP算法的改進。

  1. 動量法

動量法是在標準BP算法的權值更新階段引入動量因子α(0

BP神經網絡以及python實現

,可以看出,在原有的權值調整公式中,加入了動量因子以及上一次的權值改變量。加入的動量項表示本次權值的更新方向和幅度,不但與本次計算所得的梯度有關,還與上一次更新的方向和幅度有關。動量項反映了以前積累的調整經驗,對於t時刻的調整起到了阻尼作用。當誤差曲面出現驟然起伏時,可減小震盪趨勢,提高訓練速度。

  1. 調節學習率法

在平緩區域希望學習率大一點減小迭代次數,在坑凹處希望學習率小一點,較小震盪。所以,為了加速收斂過程,希望自適應改變學習率,在該大的時候大,在該小的時候小。

學習率可變的BP算法是通過觀察誤差的增減來判斷的,當誤差以減小的方式區域目標時,說明修正方向是正確的,可以增加學習率;當誤差增加超過一定範圍時,說明前一步修正進行的不正確,應減小步長,並撤銷前一步修正過程。學習率的增減通過乘以一個增量/減量因子實現:

BP神經網絡以及python實現

  1. 引入陡度因子。
BP神經網絡以及python實現

BP神經網絡的設計:

BP神經網絡採用有監督學習。解決具體問題時,首先需要一個訓練集。然後神經網絡的設計主要包括網絡層數、輸入層節點數、隱層節點數、輸出層節點數、以及傳輸函數、訓練方法、訓練參數。

(一)輸入輸出數據的預處理:尺度變換。尺度變化也稱為歸一化或者標準化,是指變換處理將網絡的輸入、輸出數據限制在[0,1]或者[-1,1]區間內。進行變換的原因是,(1)網絡的各個輸入數據常常具有不同的物理意義和不同的量綱。尺度變換使所有分量都在一個區間內變化,從而使網絡訓練一開始就給各輸入分量以同等重要的地位;(2)BP神經網絡神經元均採用sigmoid函數,變換後可防止因淨輸入的絕對值過大而使神經元輸出飽和,繼而使權值調整進入誤差曲面的平坦區;(3)sigmoid函數輸出在區間[0,1]或者[-1,1]內,如果不對期望輸出數據進行變換處理,勢必使數值大的分量絕對誤差大,數值小的分量絕對誤差小。

BP神經網絡以及python實現

(二)神經網絡結構設計

1)網絡層數 BP神經網絡最多隻需要倆個隱層,在設計的時候一般先只考慮設一個隱層,當一個隱層的節點數很多但是依然不能改善網絡情況時,才考慮增加一個隱層。經驗表明,如果在第一個隱層較多的節點數,第二個隱層較少的節點數,可以改善網絡性能。

2)輸入層節點數 輸入層節點數取決於輸入向量的維數。應用神經網絡解決實際問題時,首先應從問題中提煉出一個抽象模型。如果輸入的是64*64的圖像,則輸入向量應為圖像中左右的像素形成的4096維向量。如果待解決的問題是二院函數擬合,則輸入向量應為二維向量。

3)隱層節點數設計 隱含節點數對BP神經網絡的性能有很大影響,一般較多的隱含層節點數可以帶來更好的性能,但是導致訓練時間過長。通常是採用經驗公式給出估計值:

BP神經網絡以及python實現

4)輸出層神經元個數

BP神經網絡以及python實現

5)傳遞函數的選擇 一般隱層選擇sigmoid函數,輸出層選擇線性函數。如果也使用sigmoid函數,則輸出值將會被限制在(0,1)或者(-1,1)之間。

6)訓練方法的選擇 一般來說,對於包含數百個權值的函數逼近網絡,使用LM算法收斂速度最快,均方誤差也小,但是LM算法對於模式識別相關問題的處理能力較弱,且需要較大的存儲空間。對於模式識別問題,使用RPROP算法能收到較好的效果。SCG算法對於模式識別和函數逼近都有較好的性能表現。串行方式需要更小的存儲空間,且輸入樣本具有一定隨機性,可以避免陷入局部最優。批量方式的誤差收斂條件非常簡單,訓練速度快。

三、python實現加動量法改進

BP神經網絡以及python實現

BP神經網絡以及python實現

# -*- coding: utf-8 -*-
"""
Created on Mon Oct 1 22:15:54 2018

@author: Heisenberg
"""
import numpy as np
import math
import random
import string
import matplotlib as mpl
import matplotlib.pyplot as plt

#random.seed(0) #當我們設置相同的seed,每次生成的隨機數相同。如果不設置seed,則每次會生成不同的隨機數
#參考https://blog.csdn.net/jiangjiang_jian/article/details/79031788

#生成區間[a,b]內的隨機數
def random_number(a,b):
return (b-a)*random.random()+a

#生成一個矩陣,大小為m*n,並且設置默認零矩陣
def makematrix(m, n, fill=0.0):
a = []
for i in range(m):
a.append([fill]*n)
return a

#函數sigmoid(),這裡採用tanh,因為看起來要比標準的sigmoid函數好看
def sigmoid(x):
return math.tanh(x)

#函數sigmoid的派生函數
def derived_sigmoid(x):
return 1.0 - x**2

#構造三層BP網絡架構
class BPNN:
def __init__(self, num_in, num_hidden, num_out):

#輸入層,隱藏層,輸出層的節點數
self.num_in = num_in + 1 #增加一個偏置結點
self.num_hidden = num_hidden + 1 #增加一個偏置結點
self.num_out = num_out

#激活神經網絡的所有節點(向量)
self.active_in = [1.0]*self.num_in
self.active_hidden = [1.0]*self.num_hidden
self.active_out = [1.0]*self.num_out

#創建權重矩陣
self.wight_in = makematrix(self.num_in, self.num_hidden)
self.wight_out = makematrix(self.num_hidden, self.num_out)

#對權值矩陣賦初值
for i in range(self.num_in):
for j in range(self.num_hidden):
self.wight_in[i][j] = random_number(-0.2, 0.2)
for i in range(self.num_hidden):
for j in range(self.num_out):
self.wight_out[i][j] = random_number(-0.2, 0.2)

#最後建立動量因子(矩陣)
self.ci = makematrix(self.num_in, self.num_hidden)
self.co = makematrix(self.num_hidden, self.num_out)

#信號正向傳播
def update(self, inputs):
if len(inputs) != self.num_in-1:
raise ValueError('與輸入層節點數不符')

#數據輸入輸入層
for i in range(self.num_in - 1):
#self.active_in[i] = sigmoid(inputs[i]) #或者先在輸入層進行數據處理
self.active_in[i] = inputs[i] #active_in[]是輸入數據的矩陣

#數據在隱藏層的處理
for i in range(self.num_hidden - 1):

sum = 0.0
for j in range(self.num_in):
sum = sum + self.active_in[i] * self.wight_in[j][i]
self.active_hidden[i] = sigmoid(sum) #active_hidden[]是處理完輸入數據之後存儲,作為輸出層的輸入數據

#數據在輸出層的處理
for i in range(self.num_out):
sum = 0.0
for j in range(self.num_hidden):
sum = sum + self.active_hidden[j]*self.wight_out[j][i]
self.active_out[i] = sigmoid(sum) #與上同理

return self.active_out[:]

#誤差反向傳播
def errorbackpropagate(self, targets, lr, m): #lr是學習率, m是動量因子
if len(targets) != self.num_out:
raise ValueError('與輸出層節點數不符!')

#首先計算輸出層的誤差
out_deltas = [0.0]*self.num_out
for i in range(self.num_out):
error = targets[i] - self.active_out[i]
out_deltas[i] = derived_sigmoid(self.active_out[i])*error

#然後計算隱藏層誤差
hidden_deltas = [0.0]*self.num_hidden
for i in range(self.num_hidden):
error = 0.0
for j in range(self.num_out):
error = error + out_deltas[j]* self.wight_out[i][j]
hidden_deltas[i] = derived_sigmoid(self.active_hidden[i])*error

#首先更新輸出層權值
for i in range(self.num_hidden):
for j in range(self.num_out):
change = out_deltas[j]*self.active_hidden[i]
self.wight_out[i][j] = self.wight_out[i][j] + lr*change + m*self.co[i][j]
self.co[i][j] = change

#然後更新輸入層權值

for i in range(self.num_in):
for i in range(self.num_hidden):
change = hidden_deltas[j]*self.active_in[i]
self.wight_in[i][j] = self.wight_in[i][j] + lr*change + m* self.ci[i][j]
self.ci[i][j] = change

#計算總誤差
error = 0.0
for i in range(len(targets)):
error = error + 0.5*(targets[i] - self.active_out[i])**2
return error

#測試
def test(self, patterns):
for i in patterns:
print(i[0], '->', self.update(i[0]))
#權重
def weights(self):
print("輸入層權重")
for i in range(self.num_in):
print(self.wight_in[i])
print("輸出層權重")
for i in range(self.num_hidden):
print(self.wight_out[i])

def train(self, pattern, itera=100000, lr = 0.1, m=0.1):
for i in range(itera):
error = 0.0
for j in pattern:
inputs = j[0]
targets = j[1]
self.update(inputs)
error = error + self.errorbackpropagate(targets, lr, m)
if i % 100 == 0:
print('誤差 %-.5f' % error)

#實例
def demo():
patt = [
[[1,2,5],[0]],
[[1,3,4],[1]],
[[1,6,2],[1]],
[[1,5,1],[0]],
[[1,8,4],[1]]
]
#創建神經網絡,3個輸入節點,3個隱藏層節點,1個輸出層節點

n = BPNN(3, 3, 1)
#訓練神經網絡
n.train(patt)
#測試神經網絡
n.test(patt)
#查閱權重值
n.weights()


if __name__ == '__main__':
demo()

計算結果:

BP神經網絡以及python實現


分享到:


相關文章: