BP神經網絡學習筆記

神經網絡簡介

神經網絡的結構模仿生物神經網絡,生物神經網絡中的每個神經元與其他神經元相連,當它“興奮”時,向下一級相連的神經元發送化學物質,改變這些神經元的電位;如果某神經元的電位超過一個閾值,則被激活,否則不被激活。

神經網絡中最基本的單元是神經元模型(neuron)。在生物神經網絡的原始機制中,每個神經元通常都有多個樹突(dendrite),一個軸突(axon)和一個細胞體(cell body),樹突短而多分支,軸突長而只有一個;在功能上,樹突用於傳入其它神經元傳遞的神經衝動,而軸突用於將神經衝動傳出到其它神經元,當樹突或細胞體傳入的神經衝動使得神經元興奮時,該神經元就會通過軸突向其它神經元傳遞興奮。神經元的生物學結構如下圖所示:

一直沿用至今的“M-P神經元模型”正是對這一結構進行了抽象,也稱“閾值邏輯單元”,其中樹突對應於輸入部分,每個神經元收到n個其他神經元傳遞過來的輸入信號,這些信號通過帶權重的連接傳遞給細胞體,這些權重又稱為連接權(connection weight)。細胞體分為兩部分,前一部分計算總輸入值(即輸入信號的加權和,或者說累積電平),後一部分先計算總輸入值與該神經元閾值的差值,然後通過激活函數(activation function)的處理,產生輸出從軸突傳送給其它神經元。M-P神經元模型如下圖所示:

BP神經網絡學習筆記

一直沿用至今的“M-P神經元模型”正是對這一結構進行了抽象,也稱“閾值邏輯單元”,其中樹突對應於輸入部分,每個神經元收到n個其他神經元傳遞過來的輸入信號,這些信號通過帶權重的連接傳遞給細胞體,這些權重又稱為連接權(connection weight)。細胞體分為兩部分,前一部分計算總輸入值(即輸入信號的加權和,或者說累積電平),後一部分先計算總輸入值與該神經元閾值的差值,然後通過激活函數(activation function)的處理,產生輸出從軸突傳送給其它神經元。M-P神經元模型如下圖所示:

BP神經網絡學習筆記

與線性分類十分相似,神經元模型最理想的激活函數也是階躍函數,即將神經元輸入值與閾值的差值映射為輸出值1或0,若差值大於零輸出1,對應興奮;若差值小於零則輸出0,對應抑制。但階躍函數不連續,不光滑(定義域內不完全可導),故在M-P神經元模型中,採用Sigmoid函數來近似, Sigmoid函數將較大範圍內變化的輸入值擠壓到 (0,1) 輸出值範圍內,所以也稱為擠壓函數(squashing function)。

BP神經網絡學習筆記

將多個神經元按一定的層次結構連接起來,就得到了神經網絡。它是一種包含多個參數的模型,比方說10個神經元兩兩連接,則有100個參數需要學習(每個神經元有9個連接權以及1個閾值),若將每個神經元都看作一個函數,則整個神經網絡就是由這些函數相互嵌套而成。

多次前饋神經網絡有三部分組成,分別是輸入層(input layer),隱藏層(hide layer),輸出層(output layer)。隱藏層可以有,也可以沒有,輸入層和輸出層必須要有。沒有隱藏層的神經網絡是線性的,只能處理線性可分的問題(線性可分問題從二維的角度就是分界線是一條直線,多維就是存在線性超平面將其分類)。一個沒有隱藏層且輸出層只有一個單元的神經網絡就相當於線性的Logistic模型。

BP神經網絡學習筆記

感知機與多層網絡

感知機(Perceptron)是由兩層神經元組成的一個簡單模型,但只有輸出層是M-P神經元,即只有輸出層神經元進行激活函數處理,也稱為功能神經元(functionalneuron);輸入層只是接受外界信號(樣本屬性)並傳遞給輸出層(輸入層的神經元個數等於樣本的屬性數目),而沒有激活函數。這樣一來,感知機與之前線性迴歸的思想基本是一樣的,都是通過對屬性加權與另一個常數求和,再使用sigmoid函數將這個輸出值壓縮到0-1之間,從而解決分類問題。不同的是感知機的輸出層應該可以有多個神經元,從而可以實現多分類問題,同時兩個模型所用的參數估計方法十分不同。

給定訓練集,則感知機的n+1個參數(n個權重+1個閾值)都可以通過學習得到。閾值可以看作一個輸入值固定為-1的啞結點的權重,即假設有一個固定輸入

BP神經網絡學習筆記

的輸入層神經元,其對應的權重為,這樣就把權重和閾值統一為權重的學習了。簡單感知機的結構如下圖所示:

BP神經網絡學習筆記

感知機權重的學習規則如下:對於訓練樣本(x,y),當該樣本進入感知機學習後,會產生一個輸出值,若該輸出值與樣本的真實標記不一致,則感知機會對權重進行調整,若激活函數為階躍函數,則調整的方法與Logistic迴歸類似(基於梯度下降法)。

感知機是通過逐個樣本輸入來更新權重,首先設定好初始權重(一般為隨機),逐個地輸入樣本數據,若輸出值與真實標記相同則繼續輸入下一個樣本,若不一致則更新權重,然後再重新逐個檢驗,直到每個樣本數據的輸出值都與真實標記相同。容易看出:感知機模型總是能將訓練數據的每一個樣本都預測正確,和決策樹模型總是能將所有訓練數據都分開一樣,感知機模型很容易產生過擬合問題。

BP神經網絡算法

由上面可以得知:神經網絡的學習主要蘊含在權重和閾值中,多層網絡使用上面簡單感知機的權重調整規則顯然不夠用了,BP神經網絡算法即誤差逆傳播算法正是為學習多層前饋神經網絡而設計,BP神經網絡算法是迄今為止最成功的的神經網絡學習算法。BP神經網絡中的BP為Back Propagation的簡寫,BP(Back Propagation)神經網絡分為兩個過程:

  • 工作信號正向傳遞子過程
  • 誤差信號反向傳遞子過程

在BP神經網絡中,單個樣本有個輸入,有個輸出,在輸入層和輸出層之間通常還有若干個隱含層。實際上,1989年Robert Hecht-Nielsen證明了對於任何閉區間內的一個連續函數都可以用一個隱含層的BP網絡來逼近,這就是萬能逼近定理。所以一個三層的BP網絡就可以完成任意的維到維的映射。即這三層分別是輸入層(I),隱含層(H),輸出層(O)。一般說到BP網絡算法時,默認指用BP算法訓練的多層前饋神經網絡。

BP神經網絡學習筆記

正向傳遞子過程

BP神經網絡學習筆記

設節點i和節點j之間的權值為,節點j的閥值為,每個節點的輸出值為,而每個節點的輸出值是根據上層所有節點的輸出值、當前節點與上一層所有節點的權值和當前節點的閥值還有激活函數來實現的。具體計算方法如下:

BP神經網絡學習筆記

BP神經網絡學習筆記

其中f為激活函數,一般選取S型(Sigmoid函數)函數或者線性函數。正向傳遞的過程比較簡單,按照上述公式計算即可。在BP神經網絡中,輸入層節點沒有閥值。

反向傳遞子過程

在BP神經網絡中,誤差信號反向傳遞子過程比較複雜。假設輸出層的所有結果為,誤差函數如下:

BP神經網絡學習筆記

而BP神經網絡的主要目的是反覆修正權值和閥值,使得誤差函數值達到最小。Widrow-Hoff學習規則是通過沿著相對誤差平方和的最速下降方向,連續調整網絡的權值和閥值,根據梯度下降法,權值矢量的修正正比於當前位置上

BP神經網絡學習筆記

的梯度,對於第個j輸出節點有:

BP神經網絡學習筆記

假設選擇激活函數為:

BP神經網絡學習筆記

對激活函數求導,得到:

BP神經網絡學習筆記

那麼接下來針對有:

BP神經網絡學習筆記

其中有:

BP神經網絡學習筆記

同樣對於有:

BP神經網絡學習筆記

這就是著名的學習規則,通過改變神經元之間的連接權值來減少系統實際輸出和期望輸出的誤差,這個規則又叫做Widrow-Hoff學習規則或者糾錯學習規則。

上面是對隱含層和輸出層之間的權值和輸出層的閥值計算調整量,而針對輸入層和隱含層和隱含層的閥值調整量的計算更為複雜。假設是輸入層第k個節點和隱含層第i個節點之間的權值,那麼有:

BP神經網絡學習筆記

其中有:

BP神經網絡學習筆記

有了上述公式,根據梯度下降法,那麼對於隱含層和輸出層之間的權值和閥值調整如下:

BP神經網絡學習筆記

BP神經網絡學習筆記

而對於輸入層和隱含層之間的權值和閥值調整同樣有:

BP神經網絡學習筆記

BP神經網絡學習筆記

容易看出,BP學習算法中,各層權值調整公式形式上都是一樣的,均由3個因素決定,即:

  • 學習率
  • 本層輸出的誤差信號
  • 本層輸入信號Y或X

隱含層的選取

在BP神經網絡中,輸入層和輸出層的節點個數都是確定的,而隱含層節點個數不確定,那麼應該設置為多少才合適呢?實際上,隱含層節點個數的多少對神經網絡的性能是有影響的,有一個經驗公式可以確定隱含層節點數目,如下:

BP神經網絡學習筆記

其中h為隱含層節點數目,m為輸入層節點數目,n為輸出層節點數目,a為1~10之間的調節常數。

BP神經網絡的注意點

BP神經網絡一般用於分類或者逼近問題。如果用於分類,則激活函數一般選用Sigmoid函數或者硬極限函數,如果用於函數逼近,則輸出層節點用線性函數。

BP神經網絡在訓練數據時可以採用增量學習或者批量學習。增量學習要求輸入模式要有足夠的隨機性,對輸入模式的噪聲比較敏感,即對於劇烈變化的輸入模式,訓練效果比較差,適合在線處理。批量學習不存在輸入模式次序問題,穩定性好,但是隻適合離線處理。

標準BP神經網絡的缺陷:

  • 容易形成局部極小值而得不到全局最優值。BP神經網絡中極小值比較多,所以很容易陷入局部極小值,這就要求對初始權值和閥值有要求,要使得初始權值和閥值隨機性足夠好,可以多次隨機來實現。
  • 訓練次數多使得學習效率低,收斂速度慢。
  • 隱含層的選取缺乏理論的指導。
  • 訓練時學習新樣本有遺忘舊樣本的趨勢。

BP算法的改進:

  • 增加動量項。引入動量項是為了加速算法收斂,即如下公式。動量因子一般選取1~0.8。
  • 自適應調節學習率,如隨著迭代次的代數t動態變化的學習率:
  • 引入陡度因子

通常BP神經網絡在訓練之前會對數據歸一化處理,即將數據映射到更小的區間內,比如[0,1]或[-1,1]。

BP神經網絡的Python實現

<code># coding:UTF-8import numpy as npfrom math import sqrtdef load_data(file_name):'''導入數據input:file_name(string):文件的存儲位置output: feature_data(mat):特徵label_data(mat):標籤n_class(int):類別的個數'''# 1、獲取特徵f = open(file_name)# 打開文件feature_data = []label_tmp = []for line in f.readlines():feature_tmp = []lines = line.strip().split("\\t")for i in range(len(lines) - 1):feature_tmp.append(float(lines[i]))label_tmp.append(int(lines[-1]))feature_data.append(feature_tmp)f.close()# 關閉文件# 2、獲取標籤m = len(label_tmp)n_class = len(set(label_tmp))# 得到類別的個數label_data = np.mat(np.zeros((m, n_class)))for i in range(m):label_data[i, label_tmp[i]] = 1return np.mat(feature_data), label_data, n_classdef sig(x):'''Sigmoid函數input:x(mat/float):自變量,可以是矩陣或者是任意實數output: Sigmoid值(mat/float):Sigmoid函數的值'''return 1.0 / (1 + np.exp(-x))def partial_sig(x):'''Sigmoid導函數的值input:x(mat/float):自變量,可以是矩陣或者是任意實數output: out(mat/float):Sigmoid導函數的值'''m, n = np.shape(x)out = np.mat(np.zeros((m, n)))for i in range(m):for j in range(n):out[i, j] = sig(x[i, j]) * (1 - sig(x[i, j]))return outdef hidden_in(feature, w0, b0):'''計算隱含層的輸入input:feature(mat):特徵w0(mat):輸入層到隱含層之間的權重b0(mat):輸入層到隱含層之間的偏置output: hidden_in(mat):隱含層的輸入'''m = np.shape(feature)[0]hidden_in = feature * w0for i in range(m):hidden_in[i,] += b0return hidden_indef hidden_out(hidden_in):'''隱含層的輸出input:hidden_in(mat):隱含層的輸入output: hidden_output(mat):隱含層的輸出'''hidden_output = sig(hidden_in)return hidden_outputdef predict_in(hidden_out, w1, b1):'''計算輸出層的輸入input:hidden_out(mat):隱含層的輸出w1(mat):隱含層到輸出層之間的權重b1(mat):隱含層到輸出層之間的偏置output: predict_in(mat):輸出層的輸入'''m = np.shape(hidden_out)[0]predict_in = hidden_out * w1for i in range(m):predict_in[i,] += b1return predict_indef predict_out(predict_in):'''輸出層的輸出input:predict_in(mat):輸出層的輸入output: result(mat):輸出層的輸出'''result = sig(predict_in)return resultdef bp_train(feature, label, n_hidden, maxCycle, alpha, n_output):'''計算隱含層的輸入input:feature(mat):特徵label(mat):標籤n_hidden(int):隱含層的節點個數maxCycle(int):最大的迭代次數alpha(float):學習率n_output(int):輸出層的節點個數output: w0(mat):輸入層到隱含層之間的權重b0(mat):輸入層到隱含層之間的偏置w1(mat):隱含層到輸出層之間的權重b1(mat):隱含層到輸出層之間的偏置'''m, n = np.shape(feature)# 1、初始化w0 = np.mat(np.random.rand(n, n_hidden))w0 = w0 * (8.0 * sqrt(6) / sqrt(n + n_hidden)) - \\ np.mat(np.ones((n, n_hidden))) * \\ (4.0 * sqrt(6) / sqrt(n + n_hidden))b0 = np.mat(np.random.rand(1, n_hidden))b0 = b0 * (8.0 * sqrt(6) / sqrt(n + n_hidden)) - \\ np.mat(np.ones((1, n_hidden))) * \\ (4.0 * sqrt(6) / sqrt(n + n_hidden))w1 = np.mat(np.random.rand(n_hidden, n_output))w1 = w1 * (8.0 * sqrt(6) / sqrt(n_hidden + n_output)) - \\ np.mat(np.ones((n_hidden, n_output))) * \\ (4.0 * sqrt(6) / sqrt(n_hidden + n_output))b1 = np.mat(np.random.rand(1, n_output))b1 = b1 * (8.0 * sqrt(6) / sqrt(n_hidden + n_output)) - \\ np.mat(np.ones((1, n_output))) * \\ (4.0 * sqrt(6) / sqrt(n_hidden + n_output))# 2、訓練i = 0while i <= maxCycle:# 2.1、信號正向傳播# 2.1.1、計算隱含層的輸入hidden_input = hidden_in(feature, w0, b0)# mXn_hidden# 2.1.2、計算隱含層的輸出hidden_output = hidden_out(hidden_input)# 2.1.3、計算輸出層的輸入output_in = predict_in(hidden_output, w1, b1)# mXn_output# 2.1.4、計算輸出層的輸出output_out = predict_out(output_in)# 2.2、誤差的反向傳播# 2.2.1、隱含層到輸出層之間的殘差delta_output = -np.multiply((label - output_out), partial_sig(output_in))# 2.2.2、輸入層到隱含層之間的殘差delta_hidden = np.multiply((delta_output * w1.T), partial_sig(hidden_input))# 2.3、 修正權重和偏置w1 = w1 - alpha * (hidden_output.T * delta_output)b1 = b1 - alpha * np.sum(delta_output, axis=0) * (1.0 / m)w0 = w0 - alpha * (feature.T * delta_hidden)b0 = b0 - alpha * np.sum(delta_hidden, axis=0) * (1.0 / m)if i % 100 == 0:print("\\t-------- iter: ", i, " ,cost: ",(1.0 / 2) * get_cost(get_predict(feature, w0, w1, b0, b1) - label))i += 1return w0, w1, b0, b1def get_cost(cost):'''計算當前損失函數的值input:cost(mat):預測值與標籤之間的差output: cost_sum / m (double):損失函數的值'''m, n = np.shape(cost)cost_sum = 0.0for i in range(m):for j in range(n):cost_sum += cost[i, j] * cost[i, j]return cost_sum / mdef get_predict(feature, w0, w1, b0, b1):'''計算最終的預測input:feature(mat):特徵w0(mat):輸入層到隱含層之間的權重b0(mat):輸入層到隱含層之間的偏置w1(mat):隱含層到輸出層之間的權重b1(mat):隱含層到輸出層之間的偏置output: 預測值'''return predict_out(predict_in(hidden_out(hidden_in(feature, w0, b0)), w1, b1))def save_model(w0, w1, b0, b1):'''保存最終的模型input:w0(mat):輸入層到隱含層之間的權重b0(mat):輸入層到隱含層之間的偏置w1(mat):隱含層到輸出層之間的權重b1(mat):隱含層到輸出層之間的偏置output:'''def write_file(file_name, source):f = open(file_name, "w")m, n = np.shape(source)for i in range(m):tmp = []for j in range(n):tmp.append(str(source[i, j]))f.write("\\t".join(tmp) + "\\n")f.close()write_file("weight_w0", w0)write_file("weight_w1", w1)write_file("weight_b0", b0)write_file("weight_b1", b1)def err_rate(label, pre):'''計算訓練樣本上的錯誤率input:label(mat):訓練樣本的標籤pre(mat):訓練樣本的預測值output: rate[0,0](float):錯誤率'''m = np.shape(label)[0]err = 0.0for i in range(m):if label[i, 0] != pre[i, 0]:err += 1rate = err / mreturn rateif __name__ == "__main__":# 1、導入數據print("--------- 1.load data ------------")feature, label, n_class = load_data("data.txt")# 2、訓練網絡模型print("--------- 2.training ------------")w0, w1, b0, b1 = bp_train(feature, label, 20, 1000, 0.1, n_class)# 3、保存最終的模型print("--------- 3.save model ------------")save_model(w0, w1, b0, b1)# 4、得到最終的預測結果print("--------- 4.get prediction ------------")result = get_predict(feature, w0, w1, b0, b1)print("訓練準確性為:", (1 - err_rate(np.argmax(label, axis=1), np.argmax(result, axis=1))))/<code> 


分享到:


相關文章: