前言
本文目的在于快速上手PSO的基本设置和应用,关于PSO具体原理请见参考文献。PSO算法参数设置简单,使用方便。
本文所用PSO算法程序参考github:GitHub - kkentzo/pso: Particle Swarm Optimization (PSO) in C
1.参数设置
在设置参数前,我们先选择系统中的待优化参数的初值和范围,以及建立相应的评价函数,然后利用结构体pso_settings_t设置参数。结构体定义如下:
// PSO SETTINGS
typedef struct {
int dim; // problem dimensionality
double *range_lo; // lower range limit (array of length DIM)
double *range_hi; // higher range limit (array of length DIM)
double goal; // optimization goal (error threshold)
int size; // swarm size (number of particles)
int print_every; // ... N steps (set to 0 for no output)
int steps; // maximum number of iterations
int step; // current PSO step
double c1; // cognitive coefficient
double c2; // social coefficient
double w_max; // max inertia weight value
double w_min; // min inertia weight value
int clamp_pos; // whether to keep particle position within defined bounds (TRUE)
// or apply periodic boundary conditions (FALSE)
int nhood_strategy; // neighborhood strategy (see PSO_NHOOD_*)
int nhood_size; // neighborhood size
int w_strategy; // inertia weight strategy (see PSO_W_*)
} pso_settings_t;
接下来,对结构体中各参数设置进行说明。
以下参数的设置需要结合实际问题确定,需要额外注意:
dim:维数,根据待优化参量的个数确定。比如优化一个三维向量,则设置维数为3.
range_lo&range_hi:粒子位置的数组边界;这些是数组 ( double *
),其长度由维数确定。根据待优化参量的物理意义定义各参量的边界范围。
goal:优化的误差阈值,满足误差要求即可。如果目标函数返回的值低于此目标,则搜索将停止。
nhood_strategy:领域吸引子的策略选择。
pso 提供了三种不同的策略来确定每个粒子的邻域吸引子:
全局拓扑 (
PSO_NHOOD_GLOBAL
),其中每个粒子都由群体中的每个其他粒子通知环形拓扑 (
PSO_NHOOD_RING
) 其中存在一个固定的环形拓扑,每个粒子由其相邻粒子通知随机拓扑 (
PSO_NHOOD_RANDOM
),其中使用随机拓扑,当两次连续迭代 [3,4] 中的误差未得到改善时更新。用于调整每个粒子nhood_size
的pso_settings_t
平均告密者数量。
以下参数的设置可根据经验取值确定,根据优化需求适当调整:
size:粒子群规模,可根据维数计算。推荐取值范围:[20,1000],简单问题一般取20~40,较难或特定类别的问题可以取100~200。
// calulate swarm size based on dimensionality
int pso_calc_swarm_size(int dim) {
int size = 10. + 2. * sqrt(dim);
return (size > PSO_MAX_SIZE ? PSO_MAX_SIZE : size);
}
print_every:步骤数。如果大于零,则此值指定在屏幕上打印有关搜索状态的信息之前应经过多少步
steps:算法的最大迭代次数。
c1,c2:学习因子,调节学习最大步长,即粒子经验来源的权重。根据经验值取值。默认值为 c1 = c2 = 1.496
clamp_pos:取TRUE则将粒子位置保持在定义的边界内。取FALSE则应用周期边界条件。一般取TRUE(1)。
nhood_size:邻域大小。
w_strategy:惯性权重策略。惯性权重 (w) 的值决定了全局搜索和局部搜索之间的平衡。实施了两种不同的策略:
使用 w=0.7298 [5] 的常数值 (PSO_W_CONST)
线性递减惯性权重 (
PSO_W_LIN_DEC
) 。使用w_max
和w_min
inpso_settings_t
分别控制起点和终点。
w_max&w_min:惯性因子的范围,可调节对解空间的搜索范围。根据经验取值。
以下为参考github的pso设置函数示例。
// create pso settings
pso_settings_t *pso_settings_new(int dim, double range_lo, double range_hi) {
pso_settings_t *settings = (pso_settings_t *)malloc(sizeof(pso_settings_t));
if (settings == NULL) { return NULL; }
// set some default values
settings->dim = dim;
settings->goal = 1e-5;
// set up the range arrays
settings->range_lo = (double *)malloc(settings->dim * sizeof(double));
if (settings->range_lo == NULL) { free(settings); return NULL; }
settings->range_hi = (double *)malloc(settings->dim * sizeof(double));
if (settings->range_hi == NULL) { free(settings); free(settings->range_lo); return NULL; }
for (int i=0; i<settings->dim; i++) {
settings->range_lo[i] = range_lo;
settings->range_hi[i] = range_hi;
}
settings->size = pso_calc_swarm_size(settings->dim);
settings->print_every = 1000;
settings->steps = 100000;
settings->c1 = 1.496;
settings->c2 = 1.496;
settings->w_max = PSO_INERTIA;
settings->w_min = 0.3;
settings->clamp_pos = 1;
settings->nhood_strategy = PSO_NHOOD_RING;
settings->nhood_size = 5;
settings->w_strategy = PSO_W_LIN_DEC;
return settings;
}
函数使用示例:
pso_settings_t *settings = NULL;
settings = pso_settings_new(30, -2.048, 2.048);
printf("Optimizing function: rosenbrock (dim=%d, swarm size=%d)\n",
settings->dim, settings->size);
2.算法设置
2.1目标函数设置
我们需要将已知目标函数(误差评价函数)导入算法,这里提供了一个自定义函数来接收目标函数,用于更新适应值。三个形参的意义分别为:粒子的顺序数,维数,目标函数的参数
// OBJECTIVE FUNCTION TYPE
typedef double (*pso_obj_fun_t)(double *, int, void *);
我们编写好目标函数后,用pso_obj_fun_t接收即可。github示例如下:
//目标函数
double pso_rosenbrock(double *vec, int dim, void *params) {
double sum = 0;
int i;
for (i=0; i<dim-1; i++)
sum += 100 * pow((vec[i+1] - pow(vec[i], 2)), 2) + \
pow((1 - vec[i]), 2);
return sum;
}
//main函数中应用
pso_obj_fun_t obj_fun = NULL;
obj_fun = pso_rosenbrock;
2.2gbest初始化
gbest为所有粒子搜寻到的整体最佳解,在迭代过程中不断更新。当算法运行结束时,gbest为目标解,因此需要初始化并分配内存,以保存最优解和最小误差。
首先定义一个结构体用来接收数据。
// PSO SOLUTION -- Initialized by the user
typedef struct {
double error;
double *gbest; // should contain DIM elements!!
} pso_result_t;
然后根据维数为数据分配内存。
// initialize GBEST solution
pso_result_t solution;
// allocate memory for the best position buffer
solution.gbest = (double *)malloc(settings->dim * sizeof(double));
2.3优化算法
关于PSO算法的核心程序在github中封装为函数pso_solve(),设置好参数后运行即可。
void pso_solve(pso_obj_fun_t obj_fun, void *obj_fun_params,
pso_result_t *solution, pso_settings_t *settings)
以下为函数使用示例,运行完毕后注意释放内存。
// run optimization algorithm
pso_solve(obj_fun, NULL, &solution, settings);
// free the gbest buffer
free(solution.gbest);
// free the settings
pso_settings_free(settings);
3.总结
关于github源程序的使用请见作者的自述文件,PSO核心的设置流程为:设置一般参数,设置目标函数,分配内存,运行算法,释放内存。