下面是函數實作的代碼部分:
clc
clear all
close all
%% 加載神經網絡的訓練樣本 測試樣本每列一個樣本 輸入P 輸出T,T是标簽
%樣本資料就是前面問題描述中列出的資料
%epochs是計算時根據輸出誤差傳回調整神經元權值和閥值的次數
load data
% 初始隐層神經元個數
hiddennum=31;
% 輸入向量的最大值和最小值
threshold=[0 1;0 1;0 1;0 1;0 1;0 1;0 1;0 1;0 1;0 1;0 1;0 1;0 1;0 1;0 1];
inputnum=size(P,1); % 輸入層神經元個數
outputnum=size(T,1); % 輸出層神經元個數
w1num=inputnum*hiddennum; % 輸入層到隐層的權值個數
w2num=outputnum*hiddennum;% 隐層到輸出層的權值個數
N=w1num+hiddennum+w2num+outputnum; %待優化的變量的個數
%% 定義遺傳算法參數
NIND=40; %個體數目
MAXGEN=50; %最大遺傳代數
PRECI=10; %變量的二進制位數
GGAP=0.95; %代溝
px=0.7; %交叉機率
pm=0.01; %變異機率
trace=zeros(N+1,MAXGEN); %尋優結果的初始值
FieldD=[repmat(PRECI,1,N);repmat([-0.5;0.5],1,N);repmat([1;0;1;1],1,N)]; %區域描述器
Chrom=crtbp(NIND,PRECI*N); %初始種群
%% 優化
gen=0; %代計數器
X=bs2rv(Chrom,FieldD); %計算初始種群的十進制轉換
ObjV=Objfun(X,P,T,hiddennum,P_test,T_test); %計算目标函數值
while gen<MAXGEN
fprintf('%d\n',gen)
FitnV=ranking(ObjV); %配置設定适應度值
SelCh=select('sus',Chrom,FitnV,GGAP); %選擇,随機周遊抽樣
SelCh=recombin('xovsp',SelCh,px); %重組,單點交叉
SelCh=mut(SelCh,pm); %變異
X=bs2rv(SelCh,FieldD); %子代個體的十進制轉換
ObjVSel=Objfun(X,P,T,hiddennum,P_test,T_test); %計算子代的目标函數值
[Chrom,ObjV]=reins(Chrom,SelCh,1,1,ObjV,ObjVSel); %重插入子代到父代,得到新種群,注意插入後新種群與老種群的規模是一樣的
%代溝隻是說選擇子種群的時候是選擇95%的個體作為待插入的子種群
%1,父代chrome和子代selch中的子種群個數都是1,1,基于适應度的選擇,子代代替父代中适應度最小的個體
X=bs2rv(Chrom,FieldD);%插入完成後,重新計算個體的十進制值
gen=gen+1; %代計數器增加
%擷取每代的最優解及其序号,Y為最優解,I為個體的序号
[Y,I]=min(ObjV);%Objv是目标函數值,也就是預測誤差的範數
trace(1:N,gen)=X(I,:); %記下每代個體的最優值,即各個權重值
trace(end,gen)=Y; %記下每代目标函數的最優值,即預測誤差的範數
end
%% 畫進化圖
figure(1);
plot(1:MAXGEN,trace(end,:));
grid on
xlabel('遺傳代數')
ylabel('誤差的變化')
title('進化過程')
bestX=trace(1:end-1,end);%注意這裡僅是記錄下了最優的初始權重,訓練得到的最終的網絡的權值并未記錄下來
bestErr=trace(end,end);
fprintf(['最優初始權值和門檻值:\nX=',num2str(bestX'),'\n最小誤差err=',num2str(bestErr),'\n'])
%% 比較優化前後的訓練&測試
callbackfun
子函數:
function Obj=Objfun(X,P,T,hiddennum,P_test,T_test)
%% 用來分别求解種群中各個個體的目标值
%% 輸入
% X:所有個體的初始權值和門檻值
% P:訓練樣本輸入
% T:訓練樣本輸出
% hiddennum:隐含層神經元數
% P_test:測試樣本輸入
% T_test:測試樣本期望輸出
%% 輸出
% Obj:所有個體的預測樣本的預測誤差的範數
%這個函數的目的就是用種群中所有個體所代表的神經網絡的初始權重值去進行網絡的訓練,訓練次數是1000次,然
%後得出所有個體作為初始權重訓練網絡1000次所得出的預測誤差,也就是這裡的obj,傳回到原函數中,疊代maxgen=50次
%記錄下每一代的最優權重值和最優目标值(最小誤內插補點)
[M,N]=size(X);
Obj=zeros(M,1);
for i=1:M%M是40,即有40個個體,每個個體就是一次初始權重,在BPfun中用每個個體作為初始值去進行了1000次的訓練
Obj(i)=BPfun(X(i,:),P,T,hiddennum,P_test,T_test);%Obj是一個40*1的向量,每個值對應的是一個個體作為初始權重值去進行訓練
%網絡1000次得出來的誤差
end
function err=BPfun(x,P,T,hiddennum,P_test,T_test)
%% 訓練&測試BP網絡
%% 輸入
% x:一個個體的初始權值和門檻值
% P:訓練樣本輸入
% T:訓練樣本輸出
% hiddennum:隐含層神經元數
% P_test:測試樣本輸入
% T_test:測試樣本期望輸出
%% 輸出
% err:預測樣本的預測誤差的範數
%用每一個個體的初始權值去訓練1000次
inputnum=size(P,1); % 輸入層神經元個數
outputnum=size(T,1); % 輸出層神經元個數
%% 建立BP網絡
%神經網絡的隐含層神經元的傳遞函數采用S型正切函數tansing(),輸出層神經元的函數采用S型對數函數logsig()
net=newff(minmax(P),[hiddennum,outputnum],{'tansig','logsig'},'trainlm');
%% 設定網絡參數:訓練次數為1000,訓練目标為0.01,學習速率為0.1
net.trainParam.epochs=1000;%允許最大訓練次數,實際這個網絡訓練到疊代次數是3時就已經到達要求結束了
net.trainParam.goal=0.01;%訓練目标最小誤差,應該是mean square error, 均方誤差,就是網絡輸出和目标值的差的平方再求平均值
LP.lr=0.1;%學習速率學習率的作用是不斷調整權值門檻值。w(n+1)=w(n)+LP.lr*(d(n)-y(n))*x(n),d(n)是期望的相應,y(n)是
%量化的實際響應,x(n)是輸入向量,如果d(n)與y(n)相等的話,則w(n+1)=w(n),這裡是指輸入到隐含層的調整方式
%隐含層到輸出層的調整 Iout(j)=1/(1+exp(-I(j)));
%dw2=e*Iout;db2=e';w2=w2_1+xite*dw2';e是錯誤值
%b2=b2_1+xite*db2';xite是學習率
%對于traingdm等函數建立的BP網絡,學習速率一般取0.01-0.1之間。
net.trainParam.show=NaN;
% net.trainParam.showwindow=false; %高版MATLAB
%% BP神經網絡初始權值和門檻值
w1num=inputnum*hiddennum; % 輸入層到隐層的權值個數
w2num=outputnum*hiddennum;% 隐層到輸出層的權值個數
w1=x(1:w1num); %初始輸入層到隐層的權值
B1=x(w1num+1:w1num+hiddennum); %初始隐層門檻值
w2=x(w1num+hiddennum+1:w1num+hiddennum+w2num); %初始隐層到輸出層的門檻值
B2=x(w1num+hiddennum+w2num+1:w1num+hiddennum+w2num+outputnum); %輸出層門檻值
net.iw{1,1}=reshape(w1,hiddennum,inputnum);%輸入到隐藏層的權重
net.lw{2,1}=reshape(w2,outputnum,hiddennum);%隐藏到輸出層的權重
net.b{1}=reshape(B1,hiddennum,1);
net.b{2}=reshape(B2,outputnum,1);
%% 訓練網絡以
net=train(net,P,T);
%% 測試網絡
Y=sim(net,P_test);%測試樣本的仿真結果
err=norm(Y-T_test);%測試樣本的仿真誤差
callbackfun函數,比較實用遺傳算法和不使用遺傳算法優化的結果對比
clc
%% 不使用遺傳算法
%% 使用随機權值和門檻值
% P:訓練樣本輸入
% T:訓練樣本标簽
% P_test:測試樣本輸入
% T_test:測試樣本期望輸出
inputnum=size(P,1); % 輸入層神經元個數
outputnum=size(T,1); % 輸出層神經元個數
%% 建立BP網絡
net=newff(minmax(P),[hiddennum,outputnum],{'tansig','logsig'},'trainlm');
%% 設定網絡參數:訓練次數為1000,訓練目标為0.01,學習速率為0.1
net.trainParam.epochs=1000;
net.trainParam.goal=0.01;
LP.lr=0.1;
%% 訓練網絡以
net=train(net,P,T);
%% 測試網絡
disp(['1、使用随機權值和門檻值 '])
disp('測試樣本預測結果:')
Y1=sim(net,P_test)%測試樣本的網絡仿真輸出
err1=norm(Y1-T_test); %測試樣本的仿真誤差
err11=norm(sim(net,P)-T); %訓練樣本的仿真誤差
disp(['測試樣本的仿真誤差:',num2str(err1)])
disp(['訓練樣本的仿真誤差:',num2str(err11)])
%% 使用遺傳算法
%% 使用優化後的權值和門檻值,利用遺傳算法得出來的最優的初始權重和門檻值去進行網絡的初始化
inputnum=size(P,1); % 輸入層神經元個數
outputnum=size(T,1); % 輸出層神經元個數
%% 建立BP網絡
net=newff(minmax(P),[hiddennum,outputnum],{'tansig','logsig'},'trainlm');
%% 設定網絡參數:訓練次數為1000,訓練目标為0.01,學習速率為0.1
net.trainParam.epochs=1000;
net.trainParam.goal=0.01;
LP.lr=0.1;
%% BP神經網絡初始權值和門檻值
w1num=inputnum*hiddennum; % 輸入層到隐層的權值個數
w2num=outputnum*hiddennum;% 隐層到輸出層的權值個數
w1=bestX(1:w1num); %初始輸入層到隐層的權值
B1=bestX(w1num+1:w1num+hiddennum); %初始隐層門檻值
w2=bestX(w1num+hiddennum+1:w1num+hiddennum+w2num); %初始隐層到輸出層的門檻值
B2=bestX(w1num+hiddennum+w2num+1:w1num+hiddennum+w2num+outputnum); %輸出層門檻值
net.iw{1,1}=reshape(w1,hiddennum,inputnum);
net.lw{2,1}=reshape(w2,outputnum,hiddennum);
net.b{1}=reshape(B1,hiddennum,1);
net.b{2}=reshape(B2,outputnum,1);
%% 訓練網絡以
net=train(net,P,T);
%% 測試網絡
disp(['2、使用優化後的權值和門檻值'])
disp('測試樣本預測結果:')
Y2=sim(net,P_test)%測試樣本的仿真輸出
err2=norm(Y2-T_test);%測試樣本的仿真誤差
err21=norm(sim(net,P)-T);%訓練樣本的仿真誤差
disp(['測試樣本的仿真誤差:',num2str(err2)])
disp(['訓練樣本的仿真誤差:',num2str(err21)])
運作的結果:
1、使用随機權值和門檻值
測試樣本預測結果:
Y1 =
0.8823 0.0030 0.0490
0.0057 0.9545 0.0103
0.0000 0.0000 0.9551
測試樣本的仿真誤差:0.12883
訓練樣本的仿真誤差:0.22123
2、使用優化後的權值和門檻值
測試樣本預測結果:
Y2 =
0.9805 0.0180 0.0234
0.0319 0.9813 0.0154
0.0121 0.0299 0.9718
測試樣本的仿真誤差:0.048476
訓練樣本的仿真誤差:0.1262
這裡之是以訓練樣本的誤差反而更大的原因是訓練樣本是多于測試樣本的,這裡訓練樣本的個數是9個,而測試樣本的個數是3個,是以積累的誤差比較多。
這裡程式運作所使用的資料及程式我在資源裡有上傳,可以下載下傳使用學習。
後記:::BP神經網絡——與validation check相關
在使用神經網絡模組化過程中,預設把樣本随機分為3類:訓練樣本,驗證樣本和測試樣本。驗證樣本的檢查值預設是6,是指在網絡利用訓練樣本進行訓練的過程中,驗證樣本的誤差連續6次疊代不再下降。則,訓練終止(這隻是訓練終止條件之一,其他的如訓練步數,目标誤差等,滿足任一條件,訓練過程都将終止)。我們可以這樣了解,如果随着網絡的訓練,驗證樣本的誤差已經基本不再減小,甚至增大,那麼就沒有必要再去訓練網絡了。因為即使繼續訓練下去,當我們利用測試樣本進行網絡測試時,測試樣本的誤差同樣也不會有所改善,甚至會過度拟合。validation checks已經達到設定的值了,是以網絡停止訓練,即如果網絡在連續max_fail epochs後不能提高網絡性能,就停止訓練。
通常,有三種方法解決這個問題:
1.提高validation checks的數值,比如設定net.trainParam.max_fail = 200,其實,這就是自己糊弄自己,非常不嚴謹,嚴重不推薦。訓練時候,出現停止這種情況,就是因為被訓練的網絡出現了問題,已經過拟合,應該停下來。但6,的确,可能,有點小,建議改為10到20之間的數吧?這個需要細細思量一下,一般情況預設就好吧?
2.修改被訓練的網絡,比如說再加一個隐藏層試試
3.如果是資料太相近的問題,試試選擇用輸入訓練資料的亂序排法,以及分類
-----------------------------------------------------------------------------------------
divideblock,divideind,divideint和dividerand分别是block方法抽取、按數組标号自定義抽取、交錯索引抽取和随機抽.
[trainV,valV,testV,trainInd,valInd,testInd] =divideblock(allV,trainRatio,valRatio,testRatio)[訓練資料,變量資料,測試資料,訓練資料矩陣的标号,變量資料标号,測試資料标号] =divideblock(所有資料,訓練資料百分比,變量資料百分比,測試資料百分比)通過設定網絡的divideFcn函數來實作,比如,net.divideFcn='divideblock',但不是說不可以在代碼中像dividevec直接調用
----------------------------------------------------------------
我們要明白它為什麼要停止。連續6次誤差不斷增大,說明網絡性能越訓練越差。這可能是兩方面原因:
1.過拟合。網絡學習得太好了,反而泛化能力下降。
2.網絡規模不夠大,資訊存儲能力不夠強,原先學習的知識又被新樣本抹去了,導緻網絡性能無法提升。
要解決這個問題:
1.如果要改變validation的驗證次數,可以用這個語句net.trainParam.max_fail = 20;
2.或者是增多隐節點或隐層數。
另外,提前停止的網絡雖然陷入局優,但不一定就不能用吧,看一下實際效果;
一般來說,不下降就是增大,不可能誤差不變。資料少就降低隐層節點數。