冰冰点灯,照亮我家门前~
欢迎进入nnetinfo
用户名:
密码:
深圳学习数据分析,数据挖掘,请联系yahushuxue@163.com~
nnetinfo : 本网发布神经网络相关的学习与研讨内容。
当前位置:教学区
[重要]写自己的BP神经网络(traingd)
作者:梁小h   日期:2015-12-17 17:23:06.0

        怎么写出像matlab一样的BP神经网络?

        小编初学神经网络之际,曾经按神经网络原理和梯度下降算法,写了一个神经网络,后来发现,稍为复杂一些的网络,经常收敛不到好的训练效果,而用matlab的工具箱,却可以!!!更让小编郁闷的是,matlab工具箱使用trainlm法,会比matlab工具箱的梯度下降法的效果要好更多,这样小编就凌乱了,小编的梯度下降法

     还好,不久之后,小编把工具箱的代码读了一篇,终于把它的主线整理了出来,现在拿出来供大家学习。

直接先贴代码:

     由于之前有读者问过类似的问题,在这里强调一遍,本文中的代码,跟matlab工具箱中traingd算法的原理是一样的,只不过去掉了工具箱繁杂的框架。

function W = myTraingdNet()
%本代码来自www.nnetinfo.com
%本代码模仿matlab神经网络工具箱的BP神经网络traingd算法,
%代码主旨用于教学,供大家学习理解matlab工具箱BP神经网络的原理(基于traingd算法)
%阅读本代码,建议逐行调试

%固定随机种子,这样保障每位同学运行出来的都是可行的结果
rs = RandStream('mcg16807','Seed',pi);
RandStream.setGlobalStream(rs);

%生成输入输出数据
P  = [1:0.2:10;-5:0.2:4];
T  = sin (P( 1,:)) + P( 2,:);

%本例的网络拥有双隐层:输入层--隐层--隐层--输出层
trainFun={'purelin','tansig','tansig','purelin'};

%读取网络的基本信息
[in,sn] = size(P);  %in:输入变量的个数(inputNum),sn:样本个数(sampleNum)
[on,~]  = size(T);  %on:输出变量个数(outputNum)
hnn = [5,4];        %hn: 隐节点个数(hideNodeNum)
nn  = [in,hnn,on];  %nn: 各层的节点个数(nodeNum)
ln  = size(nn,2);   %ln: 网络层数(layerNum)

%=======================初始化权值 W 和阈值 B========================top
%irMat:输入范围矩阵(inputRangeMatrix),irMat(i,j)代表i层传递给j层的输入范围
irMat = cell(ln,ln);
irMat{1,2} = [min(P,[],2),max(P,[],2)]; %第1层传给第2层的输入数据范围
irMat{2,3} = repmat([-1,1],nn(2),1);    %第2层到第3层范围为[-1,1]
irMat{3,4} = repmat([-1,1],nn(3),1);    %第3层到第4层(输出层)范围为[-1,1]

arMat = [inf inf;-2,2;-2,2;inf inf];    %传递函数的激活范围矩阵(activeRangeMatrix)

W = cell(ln,ln); %初始化权重
B = cell(ln,ln); %初始化阈值

%初始化第i层到i+1层的权值和阈值
for i = 1 : ln-2  
    ir    = irMat{i,i+1}; %读取传范围
    irMax = ir(:,2);      %第i层传给i+1层的最大值
    irMin = ir(:,1);      %第i层传给i+1层的最小值
    
    arMin = arMat(i+1,1);  %i+1层传递函数的最小激活值
    arMax = arMat(i+1,2);  %i+1层传递函数的最大激活值
    
    inn   = nn(i);    %inn:输入层神经元个数(inputNodeNum),即第i层的神经元个数
    tnn = nn(i+1);    %tnn:目标层的神经元个数(targetNodeNum),即第i+1层的神经元个数
    
    wMag = 0.7*tnn^(1/inn); %线性系数
    
    w     = 2*rand(tnn,inn)-1;            %随机初始化权值在[-1,1]之间
    wlen  = sqrt( sum(( w.*w), 2));          %求出每个目标节点权重长度
    normW = w./repmat( wlen,1,size( w,2));  %w除以权重,即对w单位化
    w     = wMag * normW;                 %w乘以线性系数
    
                   
    bIniVal = linspace(-1,1,tnn)';   %线性生成i+1层的阈值
    bSign   = sign(w(:,1));          %b的方向要与w中的某一维保持相同
    b       = wMag*bIniVal.*bSign;   %b乘以线性系数
    
    activeLen    = (arMax - arMin)/2; %i+1层的激活范围的长度(数值)
    activeCenter = (arMax + arMin)/2; %i+1层的激活范围的中点(数值)
    irL    = (irMax - irMin)/2;    %i层传给i+1层的输入范围长度(向量).
    irCenter = (irMax + irMin)/2;  %i层传给i+1层的输入范围中心(向量).
    
    curW = (w ./repmat(irL',tnn,1))*activeLen; %权值除以输入范围 再乘以激活范围.
    %阈值扩大到激活范围,移到激活中心,送去权值的期望输入.
    curB = b * activeLen + activeCenter - curW * irCenter;
    
    W{i,i+1} = curW;%赋值给权值CELL
    B{i,i+1} = curB;%赋值给阈值CELL
end

%第3层到第4层(输出层)的传递函数为purelin,不涉及tansig函数,初始值直接随机生成即可
inn = nn(ln-1); %第3层的神经元个数
tnn = nn(ln);   %第4层的神经元个数
W{ln-1,ln} = 2 * rand(tnn,inn)-1; %随机生成权值范围为[-1,1]
B{ln-1,ln} = 2 * rand(tnn,1)-1;           %随机生成阈值范围为[-1,1]
%=======================初始化权值 W 和阈值 B===================bot

%#!#!!#@!____网络训练的一些参数设置_____
goal = 0;         %目标误差
maxStep   = 1000; %最大步数
lr = 0.01;        %学习率  
lr_inc = 1.05;    %学习率升上率
lr_dec = 0.7;     %学习率下降率
min_grad  = 1.0e-6; %最小梯度
maxE_inc  = 1.04;   %减少学习率的阈值

 

%#!#!!#@!_____开始训练_____
layerVal = calNodeVal(P,W,B,ln,sn);%计算各个节点的值
simT   = layerVal{end}; %提取最后一个神经元的值
[E,E2] = myMse( simT, T); %计算误差

newW = cell(size( W));  %初始化newW
newB = cell(size( B));  %初始化newB
trainStep = 1;      %初始化当前训练步数
while (1)
    normgW  = min_grad * 2;   
    [gW,gB] = getGrad(W,layerVal,ln,E);%计算梯度
    for i = 1:ln-1
        newW{i,i+1} = W{i,i+1} + lr * gW{i,i+1};%更新梯度
        newB{i,i+1} = B{i,i+1} + lr * gB{i,i+1};%更新阈值
    end
    newLayerVal = calNodeVal(P,newW,newB,ln,sn);%计算各个节点的值
    newSimT = newLayerVal{end};     %提取最后一个神经元的值
    [newE,newE2] = myMse(newSimT,T);%计算误差
    if newE2/E2 > maxE_inc   %若果误差上升大于阈值
        lr = lr * lr_dec;    %则降低学习率
    else
        if newE2 < E2        %若果误差减少
            lr = lr * lr_inc;%则增加学习率
        end
        W  = newW;  %更新权值
        B  = newB;  %更新阈值
        E2 = newE2; %更新误差记录
        E  = newE; %更新误差(用于下一轮梯度计算)
        layerVal  = newLayerVal; %更新节点值(用于下一轮梯度计算)
        gradArray = dwCell2array(gW,gB); % 将w 和 b转为向量,用于计算梯度值
        normgW    = sqrt(sum(sum(gradArray.^2)));%计算梯度值
    end
    trainStep = trainStep + 1; %训练次数+1
    if E2 < goal || trainStep > maxStep  || normgW       

        break; %若是,则退出训练
    end
end

%展示网络拟合结果
LayerVal = calNodeVal(P,W,B,ln,sn);%计算各个节点的值
simT     = LayerVal{end};          %提取最后一个神经元的值
[E , E2] = myMse(simT,T);          %计算误差

x = 1:length(T);
plot(x,T,x,simT,'r');    %拟合绘画

end


function [E,E2] = myMse(y,y0)  %计算误差
%用于计算误差,E返回向量,用于计算梯度。E2则是均方误差,用于度量y与y0的误差大小。
e  = y0 - y;
E  = 2*e/length(y);        %误差向量, 用于梯度计算
E2 = sum(e.*e)/length(y);  %均方差
end

function layerVal = calNodeVal(P,W,B,ln,sn)%计算各个神经元的值
layerVal    = cell(1,ln); %初始化神经元值cell,
layerVal{1} = P;  %第1层的值等于输入
for i = 2 : ln-1 %计算第2层和第3层tansig的值
    %W(i-1)是后层个数*前层个数(5*2)的矩阵
    layerVal{i} = tansig(W{i-1,i}*layerVal{i-1}+repmat(B{i-1,i},1,sn));  
end
layerVal{ln} = W{ln-1,ln}*layerVal{ln-1}+B{ln-1,ln}; %计算最后一层的值.
end

function  [gw,gb] = getGrad(W,layerVal,layerNum,E)
%逐层计算梯度
sn = size(layerVal{1},2);     %样本个数
for i = layerNum-1 : -1 : 1   %计算第i层到i+1层的权值梯度,
    curNodeVal = layerVal{i}; %第i层节点值
    if i == layerNum-1        %第3到第4层是purelin,
        gwLast = E.*ones(1,sn);
    else
        gwLast = W{i+1,i+2}'*gwLast.*dtansig(1,layerVal{i+1}); %
    end
    gw{i,i+1} = gwLast*curNodeVal';
    gb{i,i+1} = sum(gwLast,2);
end
end

function gradArray = dwCell2array(dwCell,dbCell) %转为向量
[layerNum,~] = size(dwCell); %共有多少层
gradArray = [];          
for i = 1 : layerNum-1  %将每层的权值和阈值合并到向量中
    curdW = dwCell{i,i+1}; %
    gradArray =  [gradArray;curdW(:);dbCell{i,i+1}];
end
end

     代码本来可以写得更简洁,为了保留支持多层网络的功能,就不作化简了。

      本代码,直接拷到matlab的m文件中运行即可(小编在2012b,2014a上用过,都可以,其它应该也可以吧)。学习本代码的同学,最好使用调试模式,逐行代码调试,看变量,结合本文下面提供的理论文章,相信大家都能写出matlab工具箱一样的BP神经网络。

大家在阅读的时候,对于初始化方法可能会遇到一些困难,请参考下面的文章:

 

小编也终于找出了,自己写的神经网络没有matlab工具效果好的原因:

  1. matlab神经网络工具箱初始化使用了nguyen法。
  2. Matlab神经网络工具箱用了自适应学习率。

 

祝大家学习顺利。