当前位置:   article > 正文

MATLAB神经网络43个案例——遗传算法优化BP神经网络_bp算法matlab实例

bp算法matlab实例

        在本书第二章案例中,用BP神经网络拟合非线性函数,在此基础上用遗传算法对神经网络进行进一步优化。主要优化对象为神经网络的权值阈值,以此达到更为精准的效果。

        本文主要对代码进行解析,书中的实例代码有一些差错,运行不了。本文进行了一些修正,供大家参考。

遗传算法主函数

算法大致流程: 

一、构建神经网络

  1. %% 网络结构建立
  2. %读取数据
  3. load data input output
  4. %节点个数
  5. inputnum=2;
  6. hiddennum=5;
  7. outputnum=1;
  8. %训练数据和预测数据
  9. input_train=input(1:1900,:)';
  10. input_test=input(1901:2000,:)';
  11. output_train=output(1:1900)';
  12. output_test=output(1901:2000)';
  13. %选连样本输入输出数据归一化
  14. [inputn,inputps]=mapminmax(input_train);
  15. [outputn,outputps]=mapminmax(output_train);
  16. %构建网络
  17. net=newff(inputn,outputn,hiddennum);

二、遗传算法参数初始化

  1. %% 遗传算法参数初始化
  2. maxgen=20; %进化代数,即迭代次数
  3. sizepop=10; %种群规模
  4. pcross=[0.2]; %交叉概率选择,0和1之间
  5. pmutation=[0.1]; %变异概率选择,0和1之间
  6. %节点总数 w1+b1+w2+b2
  7. numsum=inputnum*hiddennum+hiddennum+hiddennum*outputnum+outputnum;%2*5+5+5*1+1=21
  8. %w1=输入层与隐藏层之间的权值
  9. %b1=隐藏层阈值
  10. %w2=隐藏层与输出层之间的权值
  11. %b2=输出层阈值
  12. %用来进行染色体编码的参数
  13. lenchrom=ones(1,numsum); %染色体长度 1*21行向量(全1
  14. bound=[-3*ones(numsum,1) 3*ones(numsum,1)]; %数据范围[-3 3] 维度21*2
  15. %种群信息初始化
  16. individuals=struct('fitness',zeros(1,sizepop), 'chrom',[]); %将种群信息定义为一个结构体
  17. avgfitness=[]; %每一代种群的平均适应度
  18. bestfitness=[]; %每一代种群的最佳适应度
  19. bestchrom=[]; %适应度最好的染色体

三、计算个体适应度

        注意理解x中的数据的含义,x(21位)实际上携带w1(1~10位)B1(11~15位)w2(16~20位)B2(第21位)的信息。

        对染色体进行寻优的实际意义——找到最优的权值阈值组合

  1. for i=1:sizepop % 1:10
  2. %个体初始化
  3. individuals.chrom(i,:)=Code(lenchrom,bound); %编码
  4. x=individuals.chrom(i,:);%x为编码后的染色体(携带权值阈值信息)
  5. %计算适应度
  6. individuals.fitness(i)=fun(x,inputnum,hiddennum,outputnum,net,inputn,outputn); %染色体的适应度
  7. end
  8. FitRecord=[];%拟合记录
  9. %找最好的染色体(适应度最小)和它的下标
  10. [bestfitness bestindex]=min(individuals.fitness);%最小适应度 及其下标
  11. bestchrom=individuals.chrom(bestindex,:); %最好的染色体 即适应度最小的
  12. avgfitness=sum(individuals.fitness)/sizepop; %染色体的平均适应度
  13. trace=[avgfitness bestfitness]; % 记录初代平均适应度和最优适应度

四、迭代寻优

        整个算法的核心,也是最难理解的地方。本章先对主函数进行分析,其中的选择、交叉、变异函数接下来逐步详细讲解

  1. %% 迭代求解最佳初始阀值和权值
  2. % 进化开始
  3. for i=1:maxgen %迭代20代
  4. %选择
  5. individuals=Select(individuals,sizepop);
  6. %交叉
  7. individuals.chrom=Cross(pcross,lenchrom,individuals.chrom,sizepop,bound);
  8. %变异
  9. individuals.chrom=Mutation(pmutation,lenchrom,individuals.chrom,sizepop,i,maxgen,bound);
  10. % 计算适应度
  11. for j=1:sizepop
  12. x=individuals.chrom(j,:);
  13. individuals.fitness(j)=fun(x,inputnum,hiddennum,outputnum,net,inputn,outputn);
  14. end
  15. %找到这一代最优和最差适应度的染色体及它们在种群中的位置
  16. [newbestfitness,newbestindex]=min(individuals.fitness);
  17. [worestfitness,worestindex]=max(individuals.fitness);
  18. %代替上一次进化中最好的染色体
  19. if bestfitness>newbestfitness %优胜
  20. bestfitness=newbestfitness;
  21. bestchrom=individuals.chrom(newbestindex,:);
  22. end
  23. %劣汰 淘汰适应度最差的染色体 用最优的取代
  24. individuals.chrom(worestindex,:)=bestchrom;
  25. individuals.fitness(worestindex)=bestfitness;
  26. avgfitness=sum(individuals.fitness)/sizepop;
  27. trace=[trace;avgfitness bestfitness]; %记录每一代进化的平均适应度和最优适应度 21代(加初代
  28. FitRecord=[FitRecord;individuals.fitness];%记录每一代拟合后的适应度 共20代
  29. end

        遗传算法结果展示 :

  1. figure(1)
  2. hold on
  3. [r c]=size(trace);
  4. plot([1:r],trace(:,1),'r-');%平均适应度
  5. plot([1:r],trace(:,2),'b--');%trace(:,2) 第二列为最优适应度
  6. title(['适应度曲线' '(终止代数=' num2str(maxgen)],'fontsize',12);
  7. xlabel('进化代数','fontsize',12);ylabel('适应度','fontsize',12);
  8. legend('平均适应度','最佳适应度');

五、遗传算法优化BP神经网络

  1. %% 把最优初始阀值权值赋予网络预测
  2. x=bestchrom;%迭代后的最优染色体赋给x
  3. w1=x(1:inputnum*hiddennum);%x(1:10)
  4. B1=x(inputnum*hiddennum+1:inputnum*hiddennum+hiddennum);%x(11:15)
  5. w2=x(inputnum*hiddennum+hiddennum+1:inputnum*hiddennum+hiddennum+hiddennum*outputnum);%x(16:20)
  6. B2=x(inputnum*hiddennum+hiddennum+hiddennum*outputnum+1:inputnum*hiddennum+hiddennum+hiddennum*outputnum+outputnum);%x(21)
  7. %% BP网络参数设置
  8. net.iw{1,1}=reshape(w1,hiddennum,inputnum);
  9. net.lw{2,1}=reshape(w2,outputnum,hiddennum);
  10. net.b{1}=reshape(B1,hiddennum,1);
  11. net.b{2}=B2;
  12. net.trainParam.epochs=100;
  13. net.trainParam.lr=0.1;
  14. net.trainParam.goal=0.00001;
  15. %% BP网络训练
  16. [net,per2]=train(net,inputn,outputn);
  17. %% BP网络预测
  18. inputn_test=mapminmax('apply',input_test,inputps);%数据归一化
  19. an=sim(net,inputn_test);%预测输出
  20. test_simu=mapminmax('reverse',an,outputps);%反归一化后的预测输出
  21. error=test_simu-output_test;%误差=预测输出-期望输出

        优化后的BP神经网络预测结果展示:

  1. figure(2)
  2. plot(test_simu,':og')
  3. hold on
  4. plot(output_test,'-*')
  5. legend('预测输出','期望输出')
  6. title('GA优化后的BP网络预测输出','fontsize',12)
  7. ylabel('函数输出','fontsize',12);xlabel('样本','fontsize',12)
  8. figure(3)
  9. plot(error,'-*')
  10. title('GABP网络预测误差','fontsize',12)
  11. ylabel('误差','fontsize',12);xlabel('样本','fontsize',12)

        

初始化函数

一、编码函数

        编码函数对神经网络的权值和阈值信息进行编码,代码如下:

  1. function ret=Code(lenchrom,bound)
  2. %本函数将变量编码成染色体,用于随机初始化一个种群
  3. % lenchrom input : 染色体长度
  4. % bound input : 变量的取值范围
  5. % ret output: 染色体的编码值
  6. flag=0;
  7. while flag==0
  8. pick=rand(1,length(lenchrom));%随机数 1*21
  9. ret=bound(:,1)'+(bound(:,2)-bound(:,1))'.*pick; %线性插值,编码结果以实数向量存入ret中
  10. flag=test(lenchrom,bound,ret); %检验染色体的可行性 flag=1可行、结束循环 flag=0再循环
  11. end

二、测试函数

        测试编码后的染色体是否可行,flag为1时可行,flag为0时不可行:

  1. function flag=test(lenchrom,bound,code)
  2. % lenchrom input : 染色体长度
  3. % bound input : 变量的取值范围
  4. % code output: 染色体的编码值
  5. x=code;
  6. flag=1;
  7. if (x(1)<0)&&(x(2)<0)&&(x(3)<0)&&(x(1)>bound(1,2))&&(x(2)>bound(2,2))&&(x(3)>bound(3,2))
  8. flag=0;
  9. end

适应度函数

        在遗传算法中,适应度(fitness)的具体含义取决于问题的性质。一般而言,适应度越大越好是常见的情况,因为遗传算法的目标通常是优化问题,即找到问题的最优解或接近最优解的解

例如,如果遗传算法用于解决最大化问题(如最大化某个函数的值),那么个体的适应度值越大,说明该个体的性能越好。相反,如果遗传算法用于解决最小化问题(如最小化成本或误差),那么个体的适应度值越小,说明该个体的性能越好。

总体而言,适应度的选择应该与问题的优化目标相一致,以便遗传算法能够有效地搜索解空间并找到问题的良好解。下面对适应度函数代码进行分析:

  1. function error = fun(x,inputnum,hiddennum,outputnum,net,inputn,outputn)
  2. %该函数用来计算适应度值
  3. %x input 个体(染色体:包含权值阈值信息)
  4. %inputnum input 输入层节点数
  5. %outputnum input 隐含层节点数
  6. %net input 网络
  7. %inputn input 训练输入数据
  8. %outputn input 训练输出数据
  9. %error output 个体适应度值
  10. %提取
  11. w1=x(1:inputnum*hiddennum);%输入层与隐含层的权值 x(1:10)
  12. B1=x(inputnum*hiddennum+1:inputnum*hiddennum+hiddennum);%隐含层的阈值 x(11:15)
  13. w2=x(inputnum*hiddennum+hiddennum+1:inputnum*hiddennum+hiddennum+hiddennum*outputnum);%隐含层与输出层的权值 x(16:20)
  14. B2=x(inputnum*hiddennum+hiddennum+hiddennum*outputnum+1:inputnum*hiddennum+hiddennum+hiddennum*outputnum+outputnum);
  15. %输出层阈值 x(21:21)
  16. %网络进化参数
  17. net.trainParam.epochs=20;%迭代次数
  18. net.trainParam.lr=0.1;%学习率
  19. net.trainParam.goal=0.00001;%目标函数 最小误差函数
  20. net.trainParam.show=100;%显示训练迭代过程
  21. net.trainParam.showWindow=0;%显示训练窗口
  22. %网络权值/阈值赋值
  23. net.iw{1,1}=reshape(w1,hiddennum,inputnum);%iw{1,1} i-h间的权值 5*2
  24. net.lw{2,1}=reshape(w2,outputnum,hiddennum);%iw{2,1} h-o间的权值 1*5
  25. net.b{1}=reshape(B1,hiddennum,1);%隐含层阈值 B1 5*1
  26. net.b{2}=B2;%输出层阈值
  27. %网络训练
  28. net=train(net,inputn,outputn);
  29. an=sim(net,inputn);%an为预测输出
  30. error=sum(abs(an-outputn));%适应度(越小越好)

        由最后一行代码可见,遗传算法中的适应度为:神经网络的预测输出与期望输出差值的绝对值之和。

选择函数

        选择函数涉及到概率论中的轮盘赌问题。轮盘赌是什么呢?听起来很复杂,其实很简单。相信大家都参加过超市的优惠活动,让你转转盘的指针,转盘上有各种奖项。一等奖的扇形区域很小,其余奖项的扇形区域面积逐渐增大。所以当你转动指针,大概率会停在低等奖的区域(因为它所占面积相较于其他奖更大)。

        而遗传算法的选择函数,要选择适应度好的个体进入下一轮操作(交叉、变异)。因此采用这种转轮盘的方式随机选择染色体,适应度越好,那么该染色体被选中的概率也就越大

  1. function ret=select(individuals,sizepop)
  2. % 本函数对每一代种群中的染色体进行选择,以进行后面的交叉和变异
  3. % individuals input : 种群信息
  4. % sizepop input : 种群规模
  5. % ret output : 经过选择后的种群
  6. %根据个体适应度值进行排序
  7. fitness1=10./individuals.fitness;%由于适应度越小越好 对每个适应度求倒数 k为系数=10
  8. sumfitness=sum(fitness1);%求和
  9. p=fitness1./sumfitness;%每个个体的选择概率(概率之和为1)
  10. %轮盘赌法选择新个体
  11. index=[]; %j的值 选中的个体下标
  12. for i=1:sizepop %转sizepop(10次轮盘
  13. pick=rand;%返回一个在区间 (0,1) 内均匀分布的随机数
  14. for j=1:sizepop
  15. pick=pick-p(j); %这一步就是转轮盘
  16. if pick<=0 %对应指针停下
  17. index=[index j]; %寻找指针停下的区间,此次转轮盘选中了第j个染色体
  18. break;
  19. %注意:在转sizepop次轮盘的过程中,有可能会重复选择某些染色体
  20. end
  21. end
  22. end
  23. %更新种群信息(选中的染色体及其适应度)
  24. individuals.chrom=individuals.chrom(index,:);
  25. individuals.fitness=individuals.fitness(index);
  26. ret=individuals;

        算法中,pick=pick-p(j) 这句的理解很关键。画个图大家就明白为啥这一步操作就是转轮盘了。把圆盘区域拉成一条线段,区域面积映射在线段上。pick是随机数,该随机数作累减操作,当pick=0时,它就落在了线段中某个区域里(相当于指针停下),这就完成了随机选择染色体。从图直观可见概率为0.4的染色体被选中的概率最大。

        该算法中实际p的可视化图:

交叉函数

  1. function ret=Cross(pcross,lenchrom,chrom,sizepop,bound)
  2. %本函数完成交叉操作
  3. % pcorss input : 交叉概率
  4. % lenchrom input : 染色体的长度
  5. % chrom input : 染色体群
  6. % sizepop input : 种群规模
  7. % ret output : 交叉后的染色体
  8. for i=1:sizepop % 1:10
  9. %每一轮for循环中,可能会进行一次交叉操作,染色体是随机选择的,交叉位置也是随机选择的,
  10. %但该轮for循环中是否进行交叉操作则由交叉概率决定(continue控制)
  11. % 随机选择两个染色体进行交叉
  12. pick=rand(1,2);
  13. index=ceil(pick.*sizepop);%随机选取2个染色体来进行交叉
  14. % 交叉概率决定是否进行交叉
  15. pick=rand;
  16. if pick>pcross
  17. continue;
  18. end
  19. flag=0;
  20. while flag==0
  21. % 随机选择交叉位
  22. pick=rand;
  23. % while pick==0
  24. % pick=rand;
  25. % end
  26. %随机选择进行交叉的位置,即选择第几个变量进行交叉,注意:两个染色体交叉的位置相同
  27. pos=ceil(pick.*sum(lenchrom));%要进行交叉的染色体元(21位)的位置
  28. % temp=chrom(index(1),pos); %两元素调换位置
  29. % chrom(index(1),pos)=chrom(index(2),pos);
  30. % chrom(index(2),pos)=temp;
  31. pick=rand; %交叉开始
  32. v1=chrom(index(1),pos);%(随机选取的第一个染色体,对应元的位置)
  33. v2=chrom(index(2),pos);%(随机选取的第二个染色体,对应元的位置)
  34. %实数交叉法
  35. chrom(index(1),pos)=pick*v2+(1-pick)*v1;
  36. chrom(index(2),pos)=pick*v1+(1-pick)*v2; %交叉结束
  37. flag1=test(lenchrom,bound,chrom(index(1),:)); %检验染色体1的可行性
  38. flag2=test(lenchrom,bound,chrom(index(2),:)); %检验染色体2的可行性
  39. if flag1*flag2==0
  40. flag=0;
  41. else flag=1;
  42. end %如果两个染色体不是都可行,则重新交叉
  43. end
  44. end
  45. ret=chrom;

 

变异函数

        与交叉函数对应,基本思路是一致的。

  1. function ret=Mutation(pmutation,lenchrom,chrom,sizepop,num,maxgen,bound)
  2. % 本函数完成变异操作
  3. % pmutation input : 变异概率
  4. % lenchrom input : 染色体长度
  5. % chrom input : 染色体群
  6. % sizepop input : 种群规模
  7. % num input : 当前迭代次数
  8. % maxgen input :最大迭代次数
  9. % bound input : 每个个体的上界和下界
  10. % ret output : 变异后的染色体
  11. for i=1:sizepop %每一轮for循环中,可能会进行一次变异操作,染色体是随机选择的,变异位置也是随机选择的,
  12. %但该轮for循环中是否进行变异操作则由变异概率决定(continue控制)
  13. % 随机选择一个染色体进行变异
  14. pick=rand;
  15. index=ceil(pick*sizepop);%随机选择1个染色体进行变异
  16. % 变异概率决定该轮循环是否进行变异
  17. pick=rand;
  18. if pick>pmutation
  19. continue;
  20. end
  21. flag=0;
  22. while flag==0
  23. pick=rand;
  24. pos=ceil(pick*sum(lenchrom)); %随机选择了染色体变异的位置,即选择了第pos个变量进行变异
  25. %变异开始
  26. pick=rand;
  27. fg=(rand*(1-num/maxgen))^2; %num为遗传算法当前迭代次数 maxgen为总迭代次数
  28. if pick>0.5
  29. chrom(index,pos)=chrom(index,pos)+(chrom(index,pos)-bound(pos,2))*fg;
  30. else
  31. chrom(index,pos)=chrom(index,pos)+(bound(pos,1)-chrom(index,pos))*fg;
  32. end
  33. %变异结束
  34. flag=test(lenchrom,bound,chrom(index,:)); %检验变异的染色体的可行性
  35. end
  36. end
  37. ret=chrom;

ENDING

        以主函数算法流程为基础,仔细分析每一个子函数作用,通过调试代码来加深理解,才能对整个算法流程有全面的认知。代码的实验数据来自《MATLAB神经网络43个案例分析》。本文是个人学习记录,以便回顾复习。若有错误之处,请大家指正。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/554866
推荐阅读
相关标签
  

闽ICP备14008679号