本文的目的是學習和掌握BP神經網絡的原理及其學習算法。在MATLAB平臺上編程構造一個3-3-1型的singmoid人工神經網絡,并使用隨機反向傳播算法和成批反向傳播算法來訓練這個網絡,這里設置不同的初始權值,研究算法的學習曲線和訓練誤差。有了以上的理論基礎,最后將構造并訓練一個3-3-4型的神經網絡來分類4個等概率的三維數據集合。
##一、技術論述
**1.神經網絡簡述**
神經網絡是一種可以適應復雜模型的非常靈活的啟發式的統計模式識別技術。而反向傳播算法是多層神經網絡有監督訓練中最簡單也最一般的方法之一,它是線性LMS算法的自然延伸。
網絡基本的學習方法是從一個未訓練網絡開始,向輸入層提供一個訓練模式,再通過網絡傳遞信號,并決定輸出層的輸出值。此處的這些輸出都與目標值進行比較;任一差值對應一誤差。該誤差或準則函數是權值的某種標量函數,它在網絡輸出與期望輸出匹配時達到最小。權值向著減小誤差值的方向調整。一個BP神經網絡的基本結構如下圖所示,圖中Wji,Wkj是需要學習的權值矩陣:

**2.三層BP神經網絡**
一個三層神經網絡是由一個輸入層、一個隱含層和一個輸出層組成,他們由可修正的權值互連。在這基礎上構建的3-3-1神經網絡,是由三個輸入層、三個隱含層和一個輸出層組成。隱含層單元對它的各個輸入進行加權求和運算而形成標量的“凈激活”。也就是說,凈激活是輸入信號與隱含層權值的內積。通常可把凈激活寫成:

其中x為增廣輸入特征向量(附加一個特征值x0=1),w為權向量(附加一個值W0)。由上面的圖可知,這里的下標i是輸入層單元的索引值,j是隱含層單元的索引。Wji表示輸入層單元i到隱含層單元j的權值。為了跟神經生物學作類比,這種權或連接被稱為“突觸”,連接的值叫“突觸權”。每一個隱含層單元激發出一個輸出分量,這個分量是凈激活net的非線性函數f(net),即:

**這里需要重點認識激活函數的作用。激活函數的選擇是構建神經網絡過程中的重要環節,下面簡要介紹常用的激活函數:**
(a)線性函數 ( Liner Function )

(b) 閾值函數 ( Threshold Function )

以上激活函數都屬于線性函數,下面是兩個常用的非線性激活函數:
(c)S形函數 ( Sigmoid Function )

(d) 雙極S形函數

S形函數與雙極S形函數的圖像如下:

由于S形函數與雙極S形函數都是可導的,因此適合用在BP神經網絡中。(BP算法要求激活函數可導)
介紹完激活函數,類似的,每個輸出單元在隱含層單元信號的基礎上,使用類似的方法就可以算出它的凈激活如下:

同理,這里的下標k是輸出層單元的索引值,nH表示隱含層單元的數目,這里把偏置單元等價于一個輸入恒為y0=1的隱含層單元。將輸出單元記為zk,這樣輸出單元對net的非線性函數寫為:

綜合以上公式,顯然輸出zk可以看成是輸入特征向量x的函數。當有c個輸出單元時,可以這樣來考慮此網絡:計算c個判別函數,并通過使判別函數最大來將輸入信號分類。在只有兩種類別的情況下,一般只采用單個輸出單元,而用輸出值z的符號來標識一個輸入模式。
**3.網絡學習**
反向傳播算法(BP算法)由兩部分組成:信息的正向傳遞與誤差的反向傳播。在正向傳播過程中,輸入信息從輸入經隱含層逐層計算傳向輸出層,第一層神經元的狀態只影響下一層神經元的狀態。如果輸出層沒有得到期望的輸出,則計算輸出層的誤差變化值,然后轉向反向傳播,通過網絡將誤差信號沿原來的連接通路反傳回來修改各層神經元的權值直至達到期望目標。
神經網絡的學習方法正是依賴以上兩個步驟,對于單個模式的學習規則,考慮一個模式的訓練誤差,先定義為輸出端的期望輸出值tk(由教師信號給出)和實際輸出值zk的差的平方和:

定義目標函數:

其中t和z是長度為c的目標向量和網絡輸出向量;w表示神經網絡里的所有權值。
反向傳播算法學習規則是基于梯度下降算法的。權值首先被初始化為隨機值,然后向誤差減小的方向調整:

其中η是學習率,表示權值的相對變化尺度。反向傳播算法在第m次迭代時的權向量更新公式可寫為:

其中m是特定模式的索引。由于誤差并不是明顯決定于Wjk,這里需要使用鏈式微分法則:

其中單元k的敏感度定義為:

又有:

綜上所述,隱含層到輸出層的權值更新為:

輸入層到隱含層的權值學習規則更微妙。再運用鏈式法則計算:

其中:

可以用上式來定義隱單元的敏感度:

因此,輸入層到隱含層的權值的學習規則就是:

**4.訓練協議**
反向傳播的隨機協議和成批協議如下步驟所示:

在成批訓練中,所有的訓練模式都先提供一次,然后將對應的權值更新相加,只有這時網絡里的實際權值才開始更新。這個過程將一直迭代知道某停止準則滿足。

##二、自編函數實現BP網絡
以下簡單編寫了一個3-3-1三層BP神經網絡的構建與兩種訓練方法(寫得比較雜亂),以下是基本步驟:
1.構造 3-3-1 型的sigmoid 網絡,用以下表格中的w1和w2類的數據進行訓練,并對新模式進行分類。利用隨機反向傳播(算法1),學習率η=0.1,以及sigmoid函數,其中a=1.716,b =2/3,作為其隱單元和輸出單元的激活函數。

2.構造三層神經網絡,參數包括:輸入層、中間層、輸出層神經元向量,以及輸入層到中間層的權值矩陣,中間層到輸出層的權值矩陣,中間層神經元的偏置向量,輸出層神經元的偏置向量等。其實質是定義上述變量的一維和二維數組。
3.編寫函數[net_j,net_k,y,z] = BackPropagation(x, Wxy, Wyz, Wyb, Wzb),實現BP網絡的前饋輸出。為了便于后續的學習算法的實現,該函數的輸出變量包含:第j個隱單元對各輸入的凈激活net_j;第k個輸出單元對各輸入的凈激活net_k;網絡中隱藏層的輸出y和輸出單元的輸出向量z。
4.編寫函數,實現BP網絡的權值修正。其中,神經元函數,及其導數可參考:
神經元函數:

神經元函數導數:

5.編寫函數[NewWxy, NewWyz, NewWyb, NewWzb, J] = train(x, t, Wxy, Wyz, Wyb, Wzb),實現算法1和2所述的BP網絡的訓練算法,輸出各權向量的變化量和當前目標函數值J。
~~~
% 函數:計算人工神經網絡的各級輸出
% 輸入參數:
% x:輸入層神經元向量(3維)
% Wxy:隨機生成的從輸入層到隱含層的權值矩陣
% Wyz:隨機生成的從隱含層到輸出層的權值矩陣
% Wyb:權值偏置向量
% Wzb:權值偏置向量
% 內部變量與公式:
% f(net)=a*tanh(b*net):Sigmoid激活函數
% a:Sigmoid激活函數的參數
% b:Sigmoid激活函數的參數
% 輸出參數:
% net_j:第j個隱單元對各輸入的凈激活
% net_k:第k個輸出單元對各輸入的凈激活
% y:隱含層的輸出向量
% z:輸出單元的輸出向量
function [net_j,net_k,y,z] = BackPropagation(x, Wxy, Wyz, Wyb, Wzb)
% Sigmoid激活函數的參數
a = 1.716;
b = 2/3;
net_j = Wxy * x' + Wyb;
y = 3.432 ./ (1 + exp(-1.333 * net_j)) - 1.716;% a * tanh(b * net_j); % 隱含層的輸出
net_k = Wyz' * y + Wzb;
z = 3.432 ./ (1 + exp(-1.333 * net_k)) - 1.716;%a * tanh(b * net_k); % 輸出層結果
~~~
~~~
% 人工神經網絡訓練函數
% 輸入參數:
% x:輸入層神經元向量(3維)
% t:教師向量
% Wxy:隨機生成的從輸入層到隱含層的權值矩陣
% Wyz:隨機生成的從隱含層到輸出層的權值矩陣
% Wyb:權值偏置向量
% Wzb:權值偏置向量
% 內部變量與公式:
% Error_xy:隱藏層反傳回輸入層的誤差
% Error_yz:輸出層反傳回隱藏層的誤差
% 輸出參數:
% NewWxy:更新后的從輸入層到隱含層的權值矩陣
% NewWyz:更新后的從隱含層到輸出層的權值矩陣
% NewWyb:更新后的權值偏置向量
% NewWzb:更新后的權值偏置向量
function [NewWxy, NewWyz, NewWyb, NewWzb, J] = train(x, t, Wxy, Wyz, Wyb, Wzb)
% 基本參數設定
Eta = 0.01; % 學習因子
% Sigmoid激活函數的參數
a = 1.716;
b = 2/3;
% 計算訓練樣本經過神經網絡后的輸出
[net_j,net_k,y,z] = BackPropagation(x, Wxy, Wyz, Wyb, Wzb);
% 計算當前目標函數值
J = power(norm((t - z),2), 2) / 2;
% 計算輸出層反傳回隱藏層的敏感度
Error_yz = (t - z) .* (a * b - b / a * z .* z); % zz
% 計算隱藏層反傳回輸入層的敏感度
Error_xy = Wyz * Error_yz .* (a * b - b / a * y .* y);
% 計算輸出層到隱藏層的權值更新量
delta_Wyz = Eta * y * Error_yz';
% 計算隱藏層到輸入層的權值更新量
delta_Wxy = Eta * Error_xy * x;
% 更新權值
NewWxy = Wxy + delta_Wxy;
NewWyz = Wyz + delta_Wyz;
NewWyb = Wyb + Eta * Error_xy;
NewWzb = Wzb + Eta * Error_yz;
~~~
~~~
function [delta_Wxy, delta_Wyz, Error_xy, Error_yz, J] = BatchTrain(x, t, Wxy, Wyz, Wyb, Wzb)
% 基本參數設定
Eta = 0.01; % 學習因子
% Sigmoid激活函數的參數
a = 1.716;
b = 2/3;
% 計算訓練樣本經過神經網絡后的輸出
[net_j,net_k,y,z] = BackPropagation(x, Wxy, Wyz, Wyb, Wzb);
% 計算當前目標函數值
J = 1 / 2 * power(norm(t - z), 2);
% 計算輸出層反傳回隱藏層的誤差
Error_yz = (t - z) .* (a * b - b / a * z .* z);
% 計算隱藏層反傳回輸入層的誤差
Error_xy = Wyz * Error_yz .* (a * b - b / a * y .* y);
% 計算輸出層到隱藏層的權值更新量
delta_Wyz = Eta * Error_yz * y';
% 計算隱藏層到輸入層的權值更新量
delta_Wxy = Eta * Error_xy * x;
~~~
~~~
% clear all;
% close all;
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% % BP神經網絡實驗
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear;
close all;
% 訓練樣本(3類)
w1 = [ 1.58 2.32 -5.8 ;...
0.67 1.58 -4.78;...
1.04 1.01 -3.63;...
-1.49 2.18 -0.39;...
-0.41 1.21 -4.73;...
1.39 3.16 2.87;...
1.20 1.40 -1.89;...
-0.92 1.44 -3.22;...
0.45 1.33 -4.38;...
-0.76 0.84 -1.96];
w2 = [ 0.21 0.03 -2.21;...
0.37 0.28 -1.8 ;...
0.18 1.22 0.16;...
-0.24 0.93 -1.01;...
-1.18 0.39 -0.39;...
0.74 0.96 -1.16;...
-0.38 1.94 -0.48;...
0.02 0.72 -0.17;...
0.44 1.31 -0.14;...
0.46 1.49 0.68];
w3 = [-1.54 1.17 0.64;...
5.41 3.45 -1.33;...
1.55 0.99 2.69;...
1.86 3.19 1.51;...
1.68 1.79 -0.87;...
3.51 -0.22 -1.39;...
1.40 -0.44 0.92;...
0.44 0.83 1.97;...
0.25 0.68 -0.99;...
-0.66 -0.45 0.08];
% 初始化權值(隨機初始化)
Wxy = rand(3,3) * 2 - 1;
Wyz = rand(3,1) * 2 - 1;
Wyb = rand(3,1) * 2 - 1;
Wzb = rand() * 2 - 1;
w = [w1; w2];
T = [1 1 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]; % 教師向量
% 以下是隨機反向傳播算法
delta_J = 0.001;
time = 1;
J(time) = 2 * delta_J; % 只是讓迭代開始,沒什么作用
detJ = 2 * delta_J;
while time < 10000%detJ > delta_J
num = ceil(rand(1) * 20); % 選擇產生一個1到20之間的隨機數
x = w(num, :); % 任取w中一個模式
t = T(num);
time = time + 1; % 計算迭代次數
[NewWxy, NewWyz, NewWyb, NewWzb, J(time)] = train(x, t, Wxy, Wyz, Wyb, Wzb);
% 更新權值
Wxy = NewWxy;
Wyz = NewWyz;
Wyb = NewWyb;
Wzb = NewWzb;
detJ = abs(J(time) - J(time - 1)); % 前后兩次目標函數的差值
end
Jt = J(2:time); % J(1)和J(2)存儲了相同的值,從J(2)算起,迭代了(time - 1)次
figure,plot(Jt);grid on;% subplot(1,2,1)
xlabel(['權值為隨機時,迭代次數為:',num2str(time - 1),'次']);
for i = 1:20
[net_j,net_k,y,z] = BackPropagation(w(i,:), Wxy, Wyz, Wyb, Wzb);
a(i) = z;
end
% 初始化權值(0.5和-0.5)
Wxy = 0.5 * ones(3,3);
Wyz = -0.5 * ones(3,1);
Wyb = 0.5 * ones(3,1);
Wzb = -0.5;
w = [w1; w2];
T = [1 1 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]; % 教師向量
%
% 以下是隨機反向傳播算法
delta_J = 0.001;
time = 1;
% [net_j,net_k,y,z] = BackPropagation(x, Wxy, Wyz, Wyb, Wzb);
% J(time) = 1 / 2 * power(norm(t - z), 2);
detJ = 2 * delta_J;
while time < 10000%detJ > delta_J
num = ceil(rand(1) * 20); % 選擇產生一個1到20之間的隨機數
x = w(num, :); % 任取w中一個模式
t = T(num);
time = time + 1; % 計算迭代次數
[NewWxy, NewWyz, NewWyb, NewWzb, J(time)] = train(x, t, Wxy, Wyz, Wyb, Wzb);
% 更新權值
Wxy = NewWxy;
Wyz = NewWyz;
Wyb = NewWyb;
Wzb = NewWzb;
% detJ = abs(J(time) - J(time - 1)); % 前后兩次目標函數的差值
end
Jt = J(2:time); % J(1)和J(2)存儲了相同的值,從J(2)算起,迭代了(time - 1)次
figure,plot(Jt);grid on;% subplot(1,2,2),
xlabel(['權值為固定時,迭代次數為:',num2str(time - 1),'次']);
for i = 1:20
[net_j,net_k,y,z] = BackPropagation(w(i,:), Wxy, Wyz, Wyb, Wzb);
b(i) = z;
end
% 以下是成批反向傳播算法
Eta = 0.01; % 學習因子
% 初始化權值(隨機初始化)
Wxy = rands(3,3);
Wyz = rand(3,1);
Wyb = rand(3,1);
Wzb = rand();
SumDelta_Wxy = zeros(3,3);
SumDelta_Wyz = zeros(3,1);
SumError_xy = zeros(3,1);
SumError_yz = 0;
w = [w1; w2];
T = [1 1 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]; % 教師向量
delta_J = 0.001;
time = 1;
J(time) = 2 * delta_J;
detJ = 2 * delta_J;
% [net_j,net_k,y,z] = BackPropagation(x, Wxy, Wyz, Wyb, Wzb);
% J(time) = delta_J * 2; % 1 / 2 * power(norm(t - z), 2);
while time < 1000 %detJ > delta_J
time = time + 1; % 計算迭代次數
J(time) = 0;
for num = 1:20
x = w(num, :); % 任取w1中一個模式
t = T(num);
[delta_Wxy, delta_Wyz, Error_xy, Error_yz, Jt] = BatchTrain(x, t, Wxy, Wyz, Wyb, Wzb);
SumDelta_Wxy = SumDelta_Wxy + delta_Wxy;
SumDelta_Wyz = SumDelta_Wyz + delta_Wyz';
SumError_xy = SumError_xy + Error_xy;
SumError_yz = SumError_yz + Error_yz;
J(time) = J(time) + Jt;
end
% 更新權值
Wxy = Wxy + SumDelta_Wxy;
Wyz = Wyz + SumDelta_Wyz;
Wyb = Wyb + Eta * SumError_xy;
Wzb = Wzb + Eta * SumError_yz;
detJ = abs(J(time) - J(time - 1)); % 前后兩次目標函數的差值
end
Jt = J(1, 2:time); % J(1)和J(2)存儲了相同的值,從J(2)算起,迭代了(time - 1)次
figure,plot(Jt);grid on;
xlabel(['權值為隨機時,迭代次數為:',num2str(time - 1),'次']);
for i = 1:20
[net_j,net_k,y,z] = BackPropagation(w(i,:), Wxy, Wyz, Wyb, Wzb);
c(i) = z;
end
c
% 以下是成批反向傳播算法
Eta = 0.001; % 學習因子
% 初始化權值(0.5和-0.5)
Wxy = 0.5 * ones(3,3);
Wyz = -0.5 * ones(3,1);
Wyb = 0.5 * ones(3,1);
Wzb = -0.5;
SumDelta_Wxy = zeros(3,3);
SumDelta_Wyz = zeros(3,1);
SumError_xy = zeros(3,1);
SumError_yz = 0;
w = [w1; w2];
T = [1 1 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]; % 教師向量
delta_J = 0.001;
time = 1;
while time < 1000 % detJ > delta_J
time = time + 1; % 計算迭代次數
J(time) = 0;
for num = 1:20
x = w(num, :); % 任取w1中一個模式
t = T(num);
[delta_Wxy, delta_Wyz, Error_xy, Error_yz, Jt] = BatchTrain(x, t, Wxy, Wyz, Wyb, Wzb);
SumDelta_Wxy = SumDelta_Wxy + delta_Wxy;
SumDelta_Wyz = SumDelta_Wyz + delta_Wyz';
SumError_xy = SumError_xy + Error_xy;
SumError_yz = SumError_yz + Error_yz;
J(time) = J(time) + Jt;
end
% 更新權值
Wxy = Wxy + SumDelta_Wxy;
Wyz = Wyz + SumDelta_Wyz;
Wyb = Wyb + Eta * SumError_xy;
Wzb = Wzb + Eta * SumError_yz;
detJ = abs(J(time) - J(time - 1)); % 前后兩次目標函數的差值
end
Jt = J(1, 2:time); % J(1)和J(2)存儲了相同的值,從J(2)算起,迭代了(time - 1)次
figure,plot(Jt);grid on;
xlabel(['權值為固定時,迭代次數為:',num2str(time - 1),'次']);
for i = 1:20
[net_j,net_k,y,z] = BackPropagation(w(i,:), Wxy, Wyz, Wyb, Wzb);
d(i) = z;
end
d
~~~
一個目標函數經多次訓練后的變化曲線:

##三、調用MATLAB實現BP網絡
其實MATLAB中可直接調用函數來構造神經網絡,但自己編寫程序實現有助于了解整個神經網絡的各個細節。 使用Matlab建立前饋神經網絡時可以使用到下面3個函數:
~~~
newff :前饋網絡創建函數
train:訓練一個神經網絡
sim:使用網絡進行仿真
~~~
關于這3個函數的用法如下:
**1.newff函數**
**(1)newff函數的調用方法**
newff函數參數列表有很多的可選參數,具體可以參考Matlab的幫助文檔,這里介紹newff函數的一種簡單的形式。
~~~
net = newff(A,... % 一個n*2的矩陣,第i行元素為輸入信號xi的最小值和最大值
B,... % 一個k維行向量,其元素為網絡中各層節點數
{C},... % 一個k維字符串行向量,每一分量為對應層神經元的激活函數
'trainFun'); % 選用的訓練算法
~~~
**(2)常用的激活函數**
常用的激活函數有:
a) 線性函數 (Linear transfer function):f(x) = x。該函數所對應的字符串為’purelin’。
b) 對數S形轉移函數( Logarithmic sigmoid transfer function )。該函數所對應的字符串為’logsig’。
c) 雙曲正切S形函數 (Hyperbolic tangent sigmoid transfer function ),也就是上面所提到的Sigmoid函數。該函數所對應的字符串為’tansig’。
**注:Matlab的安裝目錄下的toolbox\nnet\nnet\nntransfer子目錄中有所有激活函數的定義說明。**
**(3)常見的訓練函數**
traingd:梯度下降BP訓練函數(Gradient descent backpropagation)
traingdx:梯度下降自適應學習率訓練函數
**(4)一些重要的網絡配置參數設置**
~~~
net.trainparam.goal:神經網絡訓練的目標誤差
net.trainparam.show: 顯示中間結果的周期
net.trainparam.epochs:最大迭代次數
net.trainParam.lr: 學習率
~~~
**2.神經網絡訓練函數train**
~~~
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 神經網絡訓練函數train
% 各參數的含義如下:
% X:網絡實際輸入
% Y:網絡應有輸出
% tr:訓練跟蹤信息
% Y1:網絡實際輸出
% E:誤差矩陣
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[ net, tr, Y1, E ] = train(net, X, Y )
~~~
**3.sim函數**
~~~
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 各參數的含義如下:
% net:網絡
% X:輸入給網絡的K*N矩陣,其中K為網絡輸入個數,N為數據樣本數
% Y:輸出矩陣Q*N,其中Q為神經網絡輸出單元的個數
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Y = sim(net, X)
~~~
**基于MATLAB自帶函數的BP神經網絡設計實例**
一個實例參考:[http://blog.csdn.net/gongxq0124/article/details/7681000](http://blog.csdn.net/gongxq0124/article/details/7681000)
參考鏈接:[http://www.cnblogs.com/heaad/archive/2011/03/07/1976443.html](http://www.cnblogs.com/heaad/archive/2011/03/07/1976443.html)