粒子群算法的调参技巧及改进方法
C++源码实现
1 基本粒子群算法简单介绍
1.1 粒子群算法( Particle Swarm Optimization, PSO)是一种典型的群体智能算法。最早是由美国心理学家Eberhart和电气工程师Kennedy于1995年提出,是一种模拟鸟类群体觅食行为的仿生智能计算方法。鸟群在整个搜寻的过程中,通过相互传递各自的信息,让其他的鸟知道自己的位置,同时也将最优解的信息传递给整个鸟群,最终,整个鸟群都能聚集在食物源周围,即我们所说的找到了最优解,即问题收敛。每只鸟会实时记录下自己的历史最优位置、鸟群的全局最优位置,作为下一次更新位置和速度的依据。
1.2 优点:简单容易实现、参数少、易于改进、对初始位置的依耐性弱、时间复杂度底
1.3 标准PSO算法的流程:(本博客,附源码实现)
步骤1: 随机初始化粒子群的位置和速度(种群规模N);
步骤2: 计算每个粒子的适应度;
步骤3: 对每个粒子,将其适应值与其经过的最好位置pbest作比较,如果当前值更好,更新pbest(历史最优);
步骤4: 对每个粒子,将其适应值与其经过的最好位置gbest作比较,如果当前值更好,更新gbest(全局最优);
步骤5: 调整粒子速度和位置;
步骤6: 满足结束条件,结束;否则,转到第2)步。
2 调参与改进综述
看了不少论文,目前学术界提高粒子群算法的性能主要有如下三种方法:
2.1 合理的设置基本参数: (本博客,附源码实现)
(1)种群规模amout的设置:根据模型复杂度定义
(2)惯性权重w,加速系数c1、c2的设置:w范围在0.7-0.9,c1=c2=(1-w)/2
(3)最大速度系数V_scale:范围在0.05-0.15之间
(4)退火思想(学习率渐渐减小):种群规模N、Vmax、w前期设置较大,后期渐渐变小,前期偏重全局搜索避免遗漏最优解,后期偏重局部搜索以提高收敛速度。
2.2 使用拓扑结构(本博客,附源码实现)
全局拓扑:全连接(标准粒子群算法)
局部拓扑:环形拓扑、冯诺依曼、四簇结构
优缺点:
全局拓扑结构速度快,但易早熟,陷入局部最优;
局部拓扑结构收敛速度慢一点,全局搜索的能力强;
通常情况下,冯诺依曼、四簇结构的综合性能比较好。
2.3 与其他智能优化算法联用(源码实现,见后续博客)
1 粒子群算法与禁忌搜索联用:可以过早避免陷入局部 (仿真速度和优化结构与四簇结构相似);
2 粒子群算法与PCA主成分分析联用:可以大大提高收敛速度(参数之间有较强相关性时使用有较好效果);
3 粒子群与梯度下降联用:可以提高收敛速度;
4 将基因算法的变异因子引入粒子群:在所有粒子陷入局部并停滞后,可以通过变异因子跳出局部继续仿真
等等
3. C++源码文件
3.1 Pso_single类:包含单个粒子的基本信息和方法函数,粒子的位置、适应值、各权重和系数、速度位置的更新方法、适应值计算方法等。所在文.3件:SinglePso.cpp、SinglePso.h;
3.2 Pso类:包含多个Pso_single对象,组成粒子群。包括系数退火方法、gbest计算方法、粒子群初始化、更新等。所在文件:Pso.cpp、Pso.h;
3.3 Pso_Top类:继承自Pso类。实现了3种局部top结构(环形拓扑、冯诺依曼、四簇结构)。所在文件:top.cpp、top.h;
3.4 工具函数:适应值函数、随机数组生成函数、粒子群优劣度比较函数等。所在文件:funs.cpp、funs.h;
3.5 主函数:main.cpp
4. 源码
4.1 SinglePso.h
#ifndef SINGLEPSO_H_INCLUDED #define SINGLEPSO_H_INCLUDED /* Pso_single:封装了单个粒子的类; 该类的数据成员,包括粒子的: 坐标维度:in_dims,坐标,适应值,位置上下限,速度上下限,惯性系数, 全局速度因子,局部速度因子,速度上下限,速度范围因子,局部最优解,全局最优解, 方法: 随机初始化、计算适应值、更新全局最优、更新局部最优 */ class Pso_single { private: int in_dims; //坐标维度 float *in_max, *in_min; //位置上下限 float *v_max,*v_min; //速度上下限 float w,c1,c2; //惯性系数,局部速度因子、全局速度因子 public: float *v_; //速度 float fitness_; //粒子的适应值 float *in_; //粒子的坐标 float fitness_pbest; //粒子的历史最优适应值 float *in_pbest; //粒子的历史最优坐标 float fitness_gbest; //粒子的全局最优适应值 float *in_gbest; //粒子的全局最优坐标 //Pso_single:构造函数 Pso_single(int in_dims_,float *inmax, float *inmin, float *vmax, float *vmin,float w_,float c1_,float c2_); //init_:随机初始化粒子的速度和位置 void init_(); //get_fitness:计算当前坐标的适应值 void get_fitness(); //init_pbest:初始化历史最优 void init_pbest(); //updata_pbest:更新历史最优 void updata_pbest(); //get_gbest: 更新全局最优 void get_gbest(float *in_gbest_,float fitness_gbest_); //updata_v::更新速度 void updata_v(); //updata_in:更新坐标 void updata_in(); }; #endif // SINGLEPSO_H_INCLUDED
4.2 SinglePso.cpp
#include <iostream> #include<algorithm> #include"SinglePso.h" #include"funs.hpp" using namespace std; Pso_single::Pso_single(int in_dims_,float *inmax, float *inmin, float *vmax, float *vmin,float w_,float c1_,float c2_) { in_dims = in_dims_; //坐标维度 fitness_ = 0.0; //粒子的适应值 in_ = new float [in_dims]; //粒子的坐标 v_ = new float [in_dims]; //速度 fitness_pbest = 0.0 ; //粒子的历史最优适应值 in_pbest = new float [in_dims]; //粒子的历史最优坐标 fitness_gbest = 0.0; //粒子的全局最优适应值 in_gbest = new float [in_dims]; //粒子的全局最优坐标 in_max = inmax; //位置上限 in_min = inmin; //位置下限 v_max = vmax; //速度上限 v_min = vmin; //速度下限 w = w_; //惯性系数, c1 = c1_; //历史速度因子 c2 = c2_; //全局速度因子 } void Pso_single::init_() { // 随机初始化坐标 in_ = random_array(in_dims,in_max,in_min); // 随机初始化速度 v_ = random_array(in_dims,v_max,v_min); } void Pso_single::get_fitness() { //计算适应值 fitness_ = fitness_fun(in_); } void Pso_single::init_pbest() { //将坐标值和适应值,直接拷贝 copy_(in_,in_pbest,in_dims); fitness_pbest = fitness_; } void Pso_single::updata_pbest() { //如果当前粒子的适应值大于历史最优值pbest,重新拷贝 if (fitness_<=fitness_pbest) copy_(in_,in_pbest,in_dims); fitness_pbest = fitness_; } void Pso_single::get_gbest(float *in_gbest_,float fitness_gbest_) { //将传入的全局最优,赋值给当前粒子的全局最优 //全局最优需要比较所有粒子的适应值, //所以先在外部对所有粒子进行比较。然后再传入。 copy_(in_gbest_,in_gbest,in_dims); fitness_gbest = fitness_gbest_; } void Pso_single::updata_v(){ //根据如下公式更新速度 //v=v*w + (pbestin-in)*c1 + (gbestin-in)*c2 //速度不得超过上限、下限 for(int i = 0;i<in_dims;i++){ v_[i] = v_[i]*w + (in_pbest[i] - in_[i])*c1 + (in_gbest[i] - in_[i])*c2; if (v_[i]>v_max[i]) {v_[i] = v_max[i];} if (v_[i]<v_min[i]) {v_[i] = v_min[i];} } } void Pso_single::updata_in(){ //更新位置。位置不得超过上限、下限 for(int i = 0;i<in_dims;i++){ in_[i] = in_[i] + v_[i]; if (in_[i]>in_max[i]) {in_[i] = in_max[i];} if (in_[i]<in_min[i]) {in_[i] = in_min[i];} } }
4.3 Pso.h
#ifndef PSO_H_INCLUDED #define PSO_H_INCLUDED #include "SinglePso.h" #include <vector> using namespace std; /* Pso类:标准粒子群算法 成员:多个Single_pso,速度因子、惯性因子等 */ class Pso { protected: int swarm_amount; //粒子群数量 int in_dim; //粒子坐标的维度 float w,c1,c2; // float* max_in; // float* min_in; // float* max_v; // float* min_v; // float* fitness_list; // float v_scale = 0.1; //速度范围因子。建议0.05<v_scale<0.2 float w_col = 1.0; // 惯性衰减因子,建议0.96<w_col<1.0 float v_col = 1.0; //速度衰减因子,建议0.96<v_col<1.0 vector<Pso_single> vector_pso; //存储所有粒子的容器 public: float* in_gbest_final; //存放最终的最优坐标 float fitness_gbest_final; //最优坐标对应的最优适应值 //Pso:构造函数 Pso(int swarm_amount_,int in_dim_,float w_,float* max_,float* min_,float v_scale_); //set_w_col:设置惯性衰减因子 void set_w_col(float w_col); //set_v_col:设置速度衰减因子 void set_v_col(float v_col); //gbest:计算全局最优gbest void gbest(int flag); //initialize:初始化每个粒子的坐标值、速度、适应值、局部最优、全局最优、 void initialize(); //update:更新坐标值、速度、适应值、局部最优、全局最优、 void update(); //gone:循环迭代。circle_表示迭代次数 void gone(int circle_); }; #endif // PSO_H_INCLUDED
4.4 Pso.cpp
#include <iostream> #include <stdlib.h> #include <vector> #include <time.h> #include<algorithm> #include "Pso.h" #include"funs.hpp" using namespace std; //构造函数:初始化参数 Pso::Pso(int swarm_amount_,int in_dim_,float w_,float* max_,float* min_,float v_scale_) { in_gbest_final = new float[in_dim]; swarm_amount = swarm_amount_; fitness_list = new float[swarm_amount]; in_dim = in_dim_; w = w_; c1 = (1-w)/2; c2 = (1-w)/2; min_in = new float[in_dim]; max_in = new float[in_dim]; min_v = new float[in_dim]; max_v = new float[in_dim]; max_in = max_; min_in = min_; v_scale = v_scale_; for(int i = 0; i<in_dim; i++) { max_v[i] = (max_[i]-min_[i])*v_scale; min_v[i] = -(max_[i]-min_[i])*v_scale; } } //设置惯性衰减系数 void Pso::set_w_col(float w_col_) { w_col = w_col_; } //设置速度范围的衰减系数 void Pso::set_v_col(float v_col_) { v_col = v_col_; } //更新粒子群的全局最优解gbest void Pso::gbest(int init_flag) { float* in_gbest_temp = new float[in_dim]; //存放每轮的最优粒子坐标 float fitness_gbest_temp; //每一轮最优适应值 get_min(swarm_amount,vector_pso,in_gbest_temp,&fitness_gbest_temp,in_dim); for(int i=0; i<swarm_amount; i++) { //如果是处于第一轮迭代,或者fitness_gbest_temp优于粒子的gbest //将粒子的gbest替换为fitness_gbest_temp和in_gbest_temp if (init_flag == 0 ||fitness_gbest_temp < vector_pso[i].fitness_gbest) { vector_pso[i].get_gbest(in_gbest_temp,fitness_gbest_temp); } } } //初始化粒子群的坐标、速度、历史最优、全局最优 void Pso::initialize() { for(int i=0; i<swarm_amount; i++) { Pso_single single(in_dim,max_in,min_in,max_v,min_v,w,c1,c2); single.init_(); single.get_fitness(); single.init_pbest(); vector_pso.push_back(single); } gbest(0); } //更新粒子群的速度、坐标、历史最优、全局最优 void Pso::update() { if (w_col != 1.0) { w = w*w_col; //衰减w c1 = (1-w)/2; //增加c1 c2 = (1-w)/2; //增加c2 } if (v_col != 1.0) { v_scale = v_scale * v_col; //衰减v_scale } for(int i=0; i<swarm_amount; i++) { vector_pso[i].updata_v(); vector_pso[i].updata_in(); vector_pso[i].get_fitness(); vector_pso[i].updata_pbest(); } gbest(1); } //初始化,并且反复进行circle_轮的迭代 void Pso::gone(int circle_) { this->initialize(); for (int i = 0; i<circle_; i++) { if (i%5 == 0) { cout<<"第"<<i<<"次迭代"<<endl; } this->update(); } //get_final_gbest:计算最终的最优粒子 get_final_gbest(swarm_amount,vector_pso,in_gbest_final,&fitness_gbest_final,in_dim); }
4.5 top.h
#ifndef TOP_H_INCLUDED #define TOP_H_INCLUDED #include <iostream> #include <vector> #include "Pso.h" using namespace std; /* Pso_Top : 为Pso的子类,实现局部top结构的功能。 */ class Pso_Top : public Pso { private: int top_flag = 0; //拓扑值标签 public: //Pso_Top: 构造函数 Pso_Top(int swarm_amount_,int in_dim_,float w_,float* max_,float* min_,float v_scale_):Pso(swarm_amount_,in_dim_,w_,max_,min_,v_scale_) { } //set_topFlag: 设置top类型 void set_topFlag(int topFlag); //gbest:生成全局最优 void gbest(int init_flag); }; #endif // TOP_H_INCLUDED
4.6 top.cpp
#include <iostream> //#include <stdlib.h> #include <vector> #include <cmath> #include <time.h> //#include<algorithm> #include "top.h" #include"funs.hpp" using namespace std; void Pso_Top::set_topFlag(int topFlag) { top_flag = topFlag; } /*top_index_0:环形top结构中,每个粒子共享其前后粒子的信息 top_index_1:冯诺依曼top结构中,每个粒子共享其前后粒子的信息 top_index_2:四簇结构,将所有粒子等分为4簇,簇内粒子共享信息;部分粒子与其他簇共享信息*/ //环形top结构的索引获取 void top_index_0(int i_ ,int size_v,int* index_v) { index_v[0] = (i_+size_v-1)%size_v; index_v[1] = i_; index_v[2] = (i_+1)%size_v; } //get_row_col:top_index_1的内置函数。 //功能:合理的分配行和列,行和列的值尽量接近些 int get_row_col(int num_) { int row = 0; for(int i=1; i<=sqrt(num_); i++) { if (num_%i==0) { row = i; } } return row; } //get_index:top_index_1的内置函数。 //功能:获取目标粒子前、后、左、右的索引。 //(i,j) = (-1,0) 前 //(i,j) = (1,0) 后 //(i,j) = (0,-1) 左 //(i,j) = (0,1) 右 int get_index(int row,int col,int row_curr,int col_curr,int i,int j) { return ((row_curr+i+row)%row)*col + ((col_curr+j+col)%col); } //冯诺依曼结构的索引获取 void top_index_1(int i_ ,int size_v,int* index_v) { int row,col,row_curr,col_curr; row = get_row_col(size_v); col = size_v/row; row_curr = i_/col; col_curr = i_%col; index_v[0] = get_index(row,col,row_curr,col_curr,-1,0);//上 index_v[1] = get_index(row,col,row_curr,col_curr,1,0);//上 index_v[2] = i_;//中间 index_v[3] = get_index(row,col,row_curr,col_curr,0,-1);//左 index_v[4] = get_index(row,col,row_curr,col_curr,0,11);;//右 } //四簇结构:将所有粒子等分为4簇,簇内粒子共享信息;部分粒子与其他簇共享信息 int top_index_2(int group_id ,int swarm_id) { /* 注:下文以group-i-k表示第i簇的第k号粒子;例如group-1-0,表示第1簇的第0号粒子; group_index_0数组表示:group-0中的第0、1、2个粒子分别与group-1-0,group-2-1, group-3-2相连接;group_index_1,group_index_2,group_index_3以此类推。 */ int group_index_0[3][2] = {
{1,0},{2,1},{3,2}}; int group_index_1[3][2] = {
{0,0},{3,1},{2,0}}; int group_index_2[3][2] = {
{1,2},{0,1},{3,0}}; int group_index_3[3][2] = {
{2,2},{1,1},{0,2}}; switch(group_id) { case 0: return group_index_0[swarm_id][0]*4 + group_index_0[swarm_id][1]; break; case 1: return group_index_1[swarm_id][0]*4 + group_index_0[swarm_id][1]; break; case 2: return group_index_2[swarm_id][0]*4 + group_index_0[swarm_id][1]; break; case 3: return group_index_3[swarm_id][0]*4 + group_index_0[swarm_id][1]; break; }; } void Pso_Top::gbest(int init_flag) { float* in_gbest_temp = new float[in_dim]; float fitness_gbest_temp; int* index_; if (top_flag < 2) { // top_flag = 0:环形结构 // top_flag = 1:冯诺依曼结构 for(int i=0; i<swarm_amount; i++) { //for循环:用于计算每个粒子的在当前top模型下的全局最优解gbest switch(top_flag) { case 0: index_ = new int[3]; //每个粒子以及与其共享信息的粒子的索引 top_index_0(i ,swarm_amount,index_); //索引值复杂 break; case 1: index_ = new int[5];//每个粒子以及与其共享信息的粒子的索引 top_index_1(i ,swarm_amount,index_);//索引值复杂 break; } vector<Pso_single> vector_local; //定义容器,用于存储与当前粒子共享信息的粒子 for(int j = 0; j<sizeof(index_); j++) { vector_local[j] = vector_pso[index_[j]];//逐个存储共享信息的粒子 } //获取最优适应值的粒子 get_min(vector_local.size(),vector_local,in_gbest_temp,&fitness_gbest_temp,in_dim); //更新粒子的gbest // init_flag == 0,代表是第一轮迭代 if (init_flag == 0||fitness_gbest_temp<vector_pso[i].fitness_gbest) { vector_pso[i].get_gbest(in_gbest_temp,fitness_gbest_temp); } } } else { //top_flag = 2:四簇结构 //下面的for循环:循环4次,分别计算每一个簇中的粒子的gbest for(int i=0; i<4; i++) { int sub_amout = int(swarm_amount/4);//sub_amout:每个子簇的粒子数量 vector<Pso_single> vector_local; //定义容器,用于存储每个簇粒子群 for(int j = 0; j<sub_amout; j++) { vector_local[j] = vector_pso[index_[i*sub_amout+j]];//存储当前簇内的所有粒子 } //get_min:计算当前子簇内的gbest get_min(vector_local.size(),vector_local,in_gbest_temp,&fitness_gbest_temp,in_dim); for(int j=0; j<sub_amout; j++) { //给当前簇的所有粒子的gbest赋值 if (init_flag == 0||fitness_gbest_temp<vector_pso[i].fitness_gbest) { vector_pso[i*sub_amout+j].fitness_gbest = fitness_gbest_temp; vector_pso[i*sub_amout+j].in_gbest = in_gbest_temp; } if(j<3) { //如果j<3:前三个粒子,还要再比较一下其和外簇相连粒子的gbest int index_ = top_index_2(i ,j); if (fitness_gbest_temp<vector_pso[i].fitness_gbest) { vector_pso[i*sub_amout+j].fitness_gbest = vector_pso[index_].fitness_; vector_pso[i*sub_amout+j].in_gbest = vector_pso[index_].in_; } } } } } }
4.7 funs.h
#ifndef FUNS_HPP_INCLUDED #define FUNS_HPP_INCLUDED #include <vector> #include "SinglePso.h" using namespace std; //random_single:生成0-1的浮点数 float random_single(); //random_array:随机生成dims维度的数组 float* random_array(int dims,float *max_, float *min_); //fitness_fun:生成适应值的函数 float fitness_fun(float* in_); //copy_:数组 深拷贝 void copy_(float src[],float dst[],int dims); //get_max:通过计算,得到适应值最小的粒子 void get_min(int mount,vector<Pso_single> temp_pso,float* max_in,float* max_fit,int in_dims_); //get_max:通过计算,得到适应值最小的粒子 void get_final_gbest(int mount,vector<Pso_single> temp_pso,float* final_in,float* final_fit,int in_dims_); #endif // FUNS_HPP_INCLUDED
4.8 funs.cpp
#include <iostream> #include <stdlib.h> #include <time.h> #include <vector> #include <math.h> #include "SinglePso.h" #include "funs.hpp" #define PI 3. using namespace std; //生成0——1之间的随机数 float random_single() { return ((float)(rand()%1000))/1000.0; } //生成维度为dims的随机数组 float* random_array(int dims,float *max_,float *min_) { float *temp = new float[dims]; for(int i=0; i<dims; i++) { float random_ = random_single(); temp[i] = (max_[i] - min_[i])*random_ + min_[i]; } return temp; } //fitness_fun:适应度函数使用 “Rastrigin's 函数” float fitness_fun(float* inin) { float x1 = inin[0]; float x2 = inin[1]; float rastrigin_ = 20 + pow(x1,2) + pow(x2,2) - 10*(cos(2*PI*x1) + cos(2*PI*x2)); return rastrigin_; } //数组复制 void copy_(float src[],float dst[],int dims) { for(int i = 0; i<dims; i++) { dst[i] = src[i]; } } //通过计算,得到vector中的最小适应值和对应的坐标 void get_min(int mount,vector<Pso_single> temp_pso,float* min_in,float* min_fit,int in_dims_) { for(int i=0; i<mount; i++) { if(i == 0 || temp_pso[i].fitness_ < *min_fit) { *min_fit = temp_pso[i].fitness_; copy_(temp_pso[i].in_,min_in,in_dims_); } } } //通过计算,得到最终的最优解 void get_final_gbest(int mount,vector<Pso_single> temp_pso,float* final_in,float* final_fit,int in_dims_) { for(int i=0; i<mount; i++) { if(i == 0 || temp_pso[i].fitness_ < *final_fit) { *final_fit = temp_pso[i].fitness_gbest; copy_(temp_pso[i].in_,final_in,in_dims_); } } }
4.9 main.cpp
#include <iostream> #include <stdlib.h> #include <vector> #include <time.h> #include <math.h> #include "SinglePso.h" #include "Pso.h" #include "funs.hpp" #include "top.h" using namespace std; int main() { srand(time(0)); //随机种子 int amout_ = 400; //粒子数 float w_ = 0.8; // 惯性权重 // 通常情况下,全局加速因子 c1、历史加速因子c2 ,等于 (1-w)/2 float v_scale_ = 0.1; //速度比例因子。最大速度、最小速度的范围 int circle_ = 80 ;//迭代次数 //本适应度函数是两维。网友可以根据自身需要更改适应度函数、 int in_dims_ = 2; //输入坐标的维度为2 float max_in[] = {5.0,5.0}; //输入参数的上限 float min_in[] = {-5.0,-5.0}; //输入参数的下限 Pso_Top pso_(amout_,2,0.8,max_in,min_in,v_scale_); //初始化粒子群对象,局部top结构的粒子群对象 //使用冯诺依曼top结构,粒子数应为偶数且大于12 //使用四簇拓扑结构,粒子数应为4的倍数且大于12 pso_.set_topFlag(2); //设置top类型{0:环形拓扑结构;1:冯诺依曼拓扑结构; 2:四簇拓扑结构} /* 上述代码为 局部top结构的粒子群算法的初始方式。 标准粒子群算法的初始化代码如下: Pso pso_(amout_,2,0.8,max_in,min_in,v_scale_); */ pso_.set_w_col(0.99); //设置惯性衰减系数。惯性系数每次迭代乘以0.99。c1和c2相应增加 pso_.set_v_col(0.99); //设置速度衰减因子。速度的上限和下限。随着迭代衰减 pso_.gone(circle_); //执行 cout<<"*迭代完成"<<endl; cout<<"*final_gbest fitness:"<<pso_.fitness_gbest_final<<endl; cout<<"*final_gbest in_:("<<pso_.in_gbest_final[0]<<","<<pso_.in_gbest_final[1]<<")"<<endl; cout<<"*迭代完成"<<endl; }
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/151995.html