本人毕业设计是
基于遗传算法优化的城市公交系统车站客流量预测
在学习过程中
不断积累经验并优化改进模型
学习完后
认为遗传神经网络预测模型
可以在大多专业领域应用实现预测效果
通俗地总结分享一下
自己最满意的流程和方法供小白参考
有误请指正和讨论
目录
导入特征数据和目标数据
使用mapminmax函数对数据归一化处理
提取数据特征数量
设置遗传算法参数
设置两层隐藏层的神经元个数
计算整个网络层的节点数之和
设置种群长度和初始参数范围
流程框架
流程代码
建模方法特点
适应度计算函数
选择操作函数
交叉操作函数
变异操作函数
神经网络算法模块的设置
使用mapminmax函数对得到的最优数据反归一化
绘制实验误差曲线图
实际值与预测值曲线图
设置实验结果提示窗口
运行结果图
自行分析整理出
影响目标数据的因素作为特征数据
每一个因素为一个特征种类
每一个特征需要多个样本数据来反映
☆☆☆☆☆
以下为5组特征数据
1组目标数据
1组预测特征数据
clear;
clc;
%导入数据
data1=[1.87,2.07,2.25,2.4,2.5,2.64,2.7,2.9,3.1,3.25,3.4,3.72,3.95];%2009年~2021年的机动车数量/(亿辆)
data2=[41.19,42.05,45.33,47.49,50.96,52.88,56.18,60.86,65.12,67.34,69.33,70.44,70.94];%2009年~2021年的公共汽电车数量/(万辆)
data3=[25460,32237,35884,38243,41738,45052,48905,52789,56786,60590,65730,70643,75770];%2009年~2021年的公共汽电车线路数/(条)
data4=[28.92,63.37,67.29,71.46,74.89,81.78,89.43,98.12,106.9,119.9,133.6,148.21,159.38];%2009年~2021年的公共汽电车线路网里程/(万公里)
data5=[186.63,317.86,331.73,346.82,348.96,346.69,352.33,358.32,355.2,346.1,354.13,302.79,335.27];%2009年~2021年的公共汽电车运营里程/(亿公里)
data6=[742.91,670.12,715.79,749.8,771.17,781.88,765.4,745.23,722.87,697,691.76,442.36,489.16];%2009年~2021年的公共汽电车客流量/(亿人次)
newdata=[4.17;67.45;79955;171.31;330];%每个值分别为2022年的:机动车数量、公共汽电车数量、线路数、线路网里程、运营里程
P=[data1;data2;data3;data4;data5]; %特征数据
T=data6; %目标数据
具体用法及原理
学习MATLAB官方帮助文档mapminmax
后面还要对预测特征数据归一化处理
此时,使用mapminmax的'apply'命令
调用特征数据归一化得到的结构体PS1
完成归一化处理
[P_regular,PS1]=mapminmax(P,-1,1);
[T_regular,PS2]=mapminmax(T,-1,1);
input_num=size(P,1); %输入特征个数
output_num=size(T,1); %输出特征个数
iter_num=300; %进化迭代次数
group_num=100; %种群规模
cross_pro=0.625; %交叉概率
mutation_pro=0.05; %变异概率,相对来说比较小
hidden_num1=8; %第一层隐藏层神经元个数
hidden_num2=6; %第二层隐藏层神经元个数(比第一层小)
总结点数=输入特征数×第一层隐藏层节点数+第一层隐藏层节点数+第一层隐藏层节点数×第二层隐藏层节点数+第二层隐藏层节点数+...+第n层隐藏层节点数+第n层隐藏层节点数×第n+1层隐藏层节点数+第n+1层隐藏层节点数+第n+1层隐藏层节点数×输出特征数+输出特征数。
☆☆☆☆☆
以下是2层隐藏层的网络层节点数之和
num_all=input_num*hidden_num1+hidden_num1+hidden_num1*hidden_num2+...
+hidden_num2+hidden_num2*output_num+output_num; %节点总数
lenchrom=ones(1,num_all); %种群总长度
limit=[-1*ones(num_all,1) 1*ones(num_all,1)]; %初始参数给定范围
遗传算法部分
主要思想就是
优化神经网络的权重和阈值(偏置)初始选择
其初始选择
对于神经网络的预测效果好坏
是有较大影响的
%% 实验开始
TIME=[];
num_iter_all=5; %实验次数
input_data=P_regular;
output_data=T_regular;
test=[];
EMS_all=[];
titlestr=['实验运行中,请稍等......'];
mm=waitbar(0,titlestr);%实验循环进度条
set(mm,'Position',[245 376.8750 270 56.2500])
for NN=1:num_iter_all
t1=clock;%每次实验开始的时间
str=['实验运行第',num2str(NN),'次中,请稍等......'];
waitbar(NN/num_iter_all,mm,str); %实验进度条
%% 基因编译
for i=1:group_num
initial=rand(1,length(lenchrom)); %产生0-1的随机数
initial_chrom(i,:)=limit(:,1)'+(limit(:,2)-limit(:,1))'.*initial; %变成染色体的形式,一行为一条染色体
fitness_value=fitness(initial_chrom(i,:),input_num,hidden_num1,hidden_num2,output_num,input_data,output_data);%计算初始适应度
fitness_group(i)=fitness_value;
end
[bestfitness,bestindex]=min(fitness_group);
bestchrom=initial_chrom(bestindex,:); %最好的染色体
avgfitness=sum(fitness_group)/group_num; %染色体的平均适应度
trace=[avgfitness bestfitness]; % 记录每一代进化中最好的适应度和平均适应度
%% 迭代过程
input_chrom=initial_chrom;
titlestr=['GA迭代中,请稍等......'];
kk=waitbar(0,titlestr);%遗传迭代循环进度条
set(kk,'Position',[925 376.8750 270 56.2500])
for num=1:iter_num
str=['GA迭代第',num2str(num),'次中,请稍等......',num2str(num/iter_num*100),'%'];
waitbar(num/iter_num,kk,str); %GA迭代进度条
% 选择
[new_chrom,new_fitness]=select(input_chrom,fitness_group,group_num); %把表现好的挑出来,还是和种群数量一样
% 交叉
new_chrom=Cross(cross_pro,lenchrom,new_chrom,group_num,limit);
% 变异
new_chrom=Mutation(mutation_pro,lenchrom,new_chrom,group_num,num,iter_num,limit);
% 计算每次迭代后的适应度
for j=1:group_num
sgroup=new_chrom(j,:); %个体
new_fitness(j)=fitness(sgroup,input_num,hidden_num1,hidden_num2,output_num,input_data,output_data);
end
%找到最小和最大适应度的染色体及它们在种群中的位置
[newbestfitness,newbestindex]=min(new_fitness);
[worestfitness,worestindex]=max(new_fitness);
% 代替上一次进化中最好的染色体
if newbestfitness
关闭了遗传算法的适应度迭代图
和神经网络训练误差图
但保存在了MATLAB打开的文件夹里
☆☆☆☆☆
模型中对整个实验循环
(神经网络每次预测的值是不唯一的,选取实验误差最小的模型数据作为最终预测值)
遗传算法迭代循环、神经网络训练循环
设置了进度条显示
(循环次数设置的比较大,可以查看实验进程)
☆☆☆☆☆
在遗传算法优化模块中:
function fitness_value=fitness(input_chrom,input_num,hidden_num1,hidden_num2,output_num,input_data,output_data)
%该函数用来计算适应度值
%input_chrom 输入种群
%input_num 输入层的节点数,即数据特征数量
%output_num 隐含层节点数,隐藏层神经元的个数
%input_data 训练输入数据
%output_data 训练输出数据
%fitness_value 个体适应度值
w1=input_chrom(1:input_num*hidden_num1); %输入层和第一层隐藏层之间的权重参数
B1=input_chrom(input_num*hidden_num1+1:input_num*hidden_num1+hidden_num1); %第一层隐藏层神经元的偏置
w2=input_chrom(input_num*hidden_num1+hidden_num1+1:input_num*hidden_num1+hidden_num1+hidden_num1*hidden_num2); %第一层隐藏层和第二层隐藏层之间的权重参数
B2=input_chrom(input_num*hidden_num1+hidden_num1+hidden_num1*hidden_num2+1:input_num*hidden_num1+hidden_num1+hidden_num1*hidden_num2+hidden_num2); %第二层隐藏层神经元的偏置
w3=input_chrom(input_num*hidden_num1+hidden_num1+hidden_num1*hidden_num2+hidden_num2+1:input_num*hidden_num1+hidden_num1+hidden_num1*hidden_num2+hidden_num2+hidden_num2*output_num);%第二层隐藏层和输出层之间的权重参数
B3=input_chrom(input_num*hidden_num1+hidden_num1+hidden_num1*hidden_num2+hidden_num2+hidden_num2*output_num+1:end);%输出层神经元的偏置
%网络权值赋值
W1=reshape(w1,hidden_num1,input_num);
W2=reshape(w2,hidden_num2,hidden_num1);
W3=reshape(w3,output_num,hidden_num2);
B1=reshape(B1,hidden_num1,1);
B2=reshape(B2,hidden_num2,1);
B3=reshape(B3,output_num,1);
[~,n]=size(input_data);
A1=tansig(W1*input_data+repmat(B1,1,n)); %需与主函数中激活函数相同
A2=tansig(W2*A1+repmat(B2,1,n)); %需与主函数中激活函数相同
A3=purelin(W3*A2+repmat(B3,1,n)); %需与主函数中激活函数相同
error=sumsqr(output_data-A3);
fitness_value=error; %误差即为适应度
end
利用神经网络的权重和阈值
计算输入数据的预测值
从而计算适应度值=预测值与输出值差的平方和
其中:
输入层与第一层隐藏层之间的权重值w1=索引输入染色体值initial_chrom的范围(1:输入染色体的第1个到输入特征数目×第一层隐藏层节点数);
第一层隐藏层神经元的偏置B1=索引输入染色体值initial_chrom的范围(w1索引范围的末值+1:w1索引范围的末值+第一层隐藏层节点数);
第一层隐藏层和第二层隐藏层之间的权重值w2=索引输入染色体值initial_chrom的范围(B1索引范围的末值+1:B1索引范围的末值+第一层隐藏层节点数×第二层隐藏层节点数);
第二层隐藏层神经元的偏置B2=索引输入染色体值initial_chrom的范围(w2索引范围的末值+1:w2索引范围的末值+第二层隐藏层节点数);
第二层隐藏层和输出层之间的权重值w3=索引输入染色体值initial_chrom的范围(B2索引范围的末值+1:B2索引范围的末值+第二层隐藏层节点数);
输出层神经元的偏置B3=索引输入染色体值initial_chrom的范围(w3索引范围的末值+1:end);
☆☆☆☆☆
以上权重和偏置的各索引
范围必须设置正确
否则后面的网络权值reshape重组时
与网络结构不匹配会报错
(后面神经网络中初始权重和偏置的索引范围同理)
function [new_chrom,new_fitness]=select(input_chrom,fitness_group,group_num)
% 用轮盘赌在原来的函数里选择
% fitness_group 种群信息
% group_num 种群规模
% newgroup 选择后的新种群
%求适应度值倒数
fitness1=10./fitness_group; %individuals.fitness为个体适应度值
%个体选择概率
sumfitness=sum(fitness1);
sumf=fitness1./sumfitness;
%采用轮盘赌法选择新个体
index=[];
for i=1:1000 %group_num为种群数
pick=rand;
while pick==0
pick=rand;
end
for j=1:group_num
pick=pick-sumf(j);
if pick<0
index=[index j];
break;
end
end
if length(index) == group_num
break;
end
end
%新种群
new_chrom=input_chrom(index,:);
new_fitness=fitness_group(index);
end
function new_chrom=Cross(cross_pro,lenchrom,input_chrom,group_num,limit)
%随机选择两个染色体位置交叉
% cross_pro 交叉概率
% lenchrom 染色体的长度,即所有参数的数量
% input_chrom 染色体群,经过选择遗传下来的表现比较好的
% group_num 种群规模
% new_chrom 交叉后的染色体
for i=1:group_num %每一轮for循环中,可能会进行一次交叉操作,染色体是随机选择的,交叉位置也是随机选择的,
%但该轮for循环中是否进行交叉操作则由交叉概率决定(continue控制)
pick=rand(1,2); % 随机选择两个染色体进行交叉
while prod(pick)==0 %连乘
pick=rand(1,2);
end
index=ceil(pick.*group_num); % 交叉概率决定是否进行交叉
pick=rand;
while pick==0
pick=rand;
end
if pick>cross_pro
continue;
end
% 随机选择交叉位
pick=rand;
while pick==0
pick=rand;
end
flag=0;
while flag==0
pos=ceil(pick*length(lenchrom)); %随机选择进行交叉的位置,即选择第几个变量进行交叉,注意:两个染色体交叉的位置相同
pick=rand; %交叉开始
v1=input_chrom(index(1),pos);
v2=input_chrom(index(2),pos);
input_chrom(index(1),pos)=pick*v2+(1-pick)*v1;
input_chrom(index(2),pos)=pick*v1+(1-pick)*v2; %交叉结束
%判断交叉后的两条染色体可不可行
limit1=mean(limit);
f11=isempty(find(input_chrom(index(1),:)>limit1(2), 1));
f12=isempty(find(input_chrom(index(1),:)limit1(2), 1));
f22=isempty(find(input_chrom(index(2),:)
function new_chrom=Mutation(mutation_pro,lenchrom,input_chrom,group_num,num,iter_num,limit)
% 本函数完成变异操作
% mutation_pro 变异概率
% lenchrom 染色体长度
% input_chrom 输入交叉过后的染色体
% group_num 种群规模
% iter_num 最大迭代次数
% limit 每个个体的上限和下限
% num 当前迭代次数
% new_chrom 变异后的染色体
for i=1:group_num %每一轮for循环中,可能会进行一次变异操作,染色体是随机选择的,变异位置也是随机选择的,
%但该轮for循环中是否进行变异操作则由变异概率决定(continue控制)
% 随机选择一个染色体进行变异
pick=rand;
while pick==0
pick=rand;
end
index=ceil(pick*group_num);
% 变异概率决定该轮循环是否进行变异
pick=rand;
if pick>mutation_pro
continue;
end
flag=0;
while flag==0
% 变异位置
pick=rand;
while pick==0
pick=rand;
end
pos=ceil(pick*sum(lenchrom)); %随机选择了染色体变异的位置,即选择了第pos个变量进行变异
pick=rand; %变异开始
fg=(pick*(1-num/iter_num))^2;
if pick>0.5
input_chrom(index,pos)=input_chrom(index,pos)+(limit(pos,2)-input_chrom(index,pos))*fg;
else
input_chrom(index,pos)=input_chrom(index,pos)-(input_chrom(index,pos)-limit(pos,1))*fg;
end %变异结束
limit1=mean(limit);
f1=isempty(find(input_chrom(index,:)>limit1(2)));
f2=isempty(find(input_chrom(index,:)
经过多次测试
第一层和第二层隐藏层的激活函数用‘tansig’
输出层的激活函数用‘purelin’
训练算法函数用‘trainlm’
预测效果是最好的
☆☆☆☆☆
网络的权重和阈值索引的是
遗传算法优化模块迭代进化后的最优值bestchrom
各权重和偏置索引最优值bestchrom的范围
同遗传算法中的适应度计算中的设置相同
☆☆☆☆☆
由于,神经网络的训练易陷入最小值
训练次数设置很大也不容易找到更优预测
所以,隐藏层数设置为2层、减少训练次数
同样能够完成优化
且隐藏层增加1层并不会对所耗时间有太大影响
整体模型预测
效果更好,消耗时间更短
☆☆☆☆☆
整个实验中
对遗传算法的每一代
适应度值、神经网络训练值、预测值、误差值
都进行了存储
并选择了最优值
使用mapminmax函数的返回命令'reverse'
调用归一化时得到的结构体PS2
完成最优数据反归一化
⭐⭐⭐⭐⭐
%% 提取2022年的预测数据
Ab=a(1,14);
disp('2022年的预测客流量为:')
disp(Ab)
%% 绘制曲线图
%绘制GA-BP误差曲线
figure('visible','on');
plot(EMS_all,'LineWidth',2)
xlabel('实验优化次数')
ylabel('误差')
titlestr=('GA-BP实验误差曲线');
title(titlestr)
%绘制目标数据与预测数据曲线图
figure
x1=2009:1:2021;%实际值年份
x2=2009:1:2022;%预测值年份
axy3=plot(x1,T,'b-','linewidth',1);
hold on
axy4=plot(x2,test(n,:),'r--.','linewidth',1);
xlabel('年份')
ylabel('客流量/亿人次')
titlestr=['GABP遗传神经网络算法预测——我国城市公交系统车站客流量',' 误差:',num2str(m),'%',' 2022年预测客流量为:',num2str(Ab),'亿人次'];
title(titlestr)
set(gca,'xtick',2009:1:2022);
legend([axy3(1),axy4(1)],'真实数据','预测数据')
axis([2009 2022 400 800])
grid on
%% 绘制结果提示窗口
titlestr1=['实验',num2str(num_iter_all),'次','所耗时间:',num2str(time),' 秒',' 最小误差为: 第 ',num2str(n(1)),' 次的 ',num2str(m(1)),'%'];
titlestr2=['2022年预测客流量为 : ',num2str(Ab),' 亿人次'];
disp(titlestr1)
disp(titlestr2)
h=msgbox({titlestr1;titlestr2},'GA-BP模型预测结果');
% 文本居中
th = findall(0, 'Tag','MessageBox' );
boxPosition = get(h,'position');
textPosition = get(th, 'position');
set(th, 'position', [boxPosition(3).*0.5 textPosition(2) textPosition(3)]);
set(th, 'HorizontalAlignment', 'center');
(本文为原创,禁止抄袭,转发请注明来源)