通过阅读本文,你将了解粒子群算法(Sciopt-kit PSO)的具体实现。
一、什么是粒子群算法
1.1 粒子群算法简介
鸟群在森林中随机搜索食物,它们想要找到食物量最多的位置。但是所有的鸟都不知道食物具体在哪个位置,只能感受到食物大概在哪个方向。每只鸟沿着自己判定的方向进行搜索,并在搜索的过程中记录自己曾经找到过食物且量最多的位置,同时所有的鸟都共享自己每一次发现食物的位置以及食物的量,这样鸟群就知道当前在哪个位置食物的量最多。在搜索的过程中每只鸟都会根据自己记忆中食物量最多的位置和当前鸟群记录的食物量最多的位置调整自己接下来搜索的方向。鸟群经过一段时间的搜索后就可以找到森林中哪个位置的食物量最多(全局最优解)。
鸟群寻找食物,目标是找到食物最多的地方。每只鸟沿着某个方向搜索,并会记录路径上食物最多的地方。完成一次搜索后,鸟群进行信息共享。于是下一次搜索,每只鸟便根据自己和鸟群找到的食物最多的这两个地方来调整方向。如此迭代,鸟群就可以找到食物最多的地方。[1]
1.2 粒子群算法的基本原理
1.2.1 鸟群觅食行为和粒子群算法求解过程的对应关系
鸟群觅食 | 粒子群算法 |
---|---|
鸟 | 粒子 |
森林 | 求解空间 |
食物的量 | 目标函数值(适应度) |
每只鸟的位置 | 空间中的一个解(粒子位置) |
食物量最多的位置 | 全局最优解 |
1.2.2 粒子群算法的流程图
见参考资料 [1]。
1.3 粒子群算法的两个关键公式
1.3.1 粒子位置迭代式
$x_{i}(t+1) = x_{i}(t) + v_{i}(t+1)$
1.3.2 速度更新迭代式
$v_{ij}(t + 1) = w * v_{ij}(t) + c_{p}r_{1j}(t)[y_{ij}(t) − x_{ij}(t)] + c_{g}r_{2j}(t)[\hat{y}_{j}(t) − x_{ij}(t)]$
该迭代式右边三项视作三部分理解:
- 惯性部分:由惯性权重和粒子自身速度构成,表示粒子对先前自身运动状态的信任。$w$指惯性权重;$v_{ij}$指粒子自身速度。
- 认知部分:表示粒子本身的思考,即粒子自己经验的部分,可理解为粒子当前位置与自身历史最优位置之间的距离和方向。$c_{p}$指个体学习因子;$r_{1j}(t)$为某个 0 到 1 之间随机数,作用是增加搜索的随机性;$y_{ij}$指第 t 次迭代粒子自身找到的最优位置。
- 社会部分:表示粒子之间的信息共享与合作,即来源于群体中其他优秀粒子的经验,可理解为粒子当前位置与群体历史最优位置之间的距离和方向。$c_{g}$指社会学习因子;$r_{2j}(t)$同$r_{1j}(t)$;$\hat{y}_{j}(t)$指第 t 次迭代所有粒子找到的最优位置。
1.3.3 惯性参数取值对粒子群算法的影响
惯性权重$w$表示粒子对自身运动速度的信任程度。较大的$w$有利于全局搜索,跳出局部极值;而较小的$w$有利于局部搜索,让算法快速收敛到最优解。
当$w=1$时,退化成基本粒子群算法,当$w=0$时,失去对粒子本身经验的思考。推荐取值范围:[0.4,2],典型取值:0.9、1.2、1.5、1.8
1.3.4 学习因子取值对粒子群算法的影响
分析方法类似,见参考资料 [1]。
参考资料列表
二、Sciopt-kit PSO 源码关键对象阅读
下面将总结 Sciopt-kit PSO 源码中的关键对象,以便日后复习。
2.1 SkoBase 类
SkoBase 类提供 register 方法,具体应用再理解:
方法名 | 作用 |
---|---|
register(self, operator_name, operator, args, *kwargs) | 注册 udf 到 class |
2.2 PSO(SkoBase) 类
PSO(SkoBase) 类是粒子群算法(PSO,Particle swarm optimization)的主要实现。
2.2.1 __init__(...) #L83
PSO 类的实例化属性 | 说明 |
---|---|
func | 由 func_transformer(func) 转换,见 GA 源码解读 |
w | 惯性权重,w = 0.8 |
cp | 个体学习因子,c1 = 0.5 |
cg | 社会学习因子,c2 = 0.5 |
pop | 粒子数量,pop = 40 |
n_dim | 自变量维度,对应 ndarray.n_dim |
max_iter | 最大迭代次数,max_iter = 150 |
verbose | 控制是否打印每步结果 |
lb | 自变量的下界 |
ub | 自变量的上界 |
has_constraint | 表示是否存在等式约束条件 |
constraint_ueq | 不等式约束 |
is_feasible | np.array([True] * pop) |
X | 粒子位置,np.random.uniform(low=self.lb, high=self.ub, size=(self.pop, self.n_dim)) |
V | 粒子速度,self.V = np.random.uniform(low=-v_high, high=v_high, size=(self.pop, self.n_dim)) |
Y | cal_y() |
pbest_x | self.X.copy() |
pbest_y | np.array([[np.inf]] * pop) |
gbest_x | self.pbest_x.mean(axis=0).reshape(1, -1) |
gbest_y | 最优的 Y 值,np.inf |
gbest_y_hist | 用于存储历史最优值 |
record_mode | False |
record_value | {'X': [], 'V': [], 'Y': []} |
2.2.2 Sciopt-kit PSO 类方法阅读
方法名 | 作用 |
---|---|
check_constraint | 判断粒子是否满足约束条件 |
update_V | 由速度更新迭代式更新速度 |
update_X | 由位置更新公式更新位置,这里使用 np.clip 函数确保粒子不越界,粒子吸附于边界 |
cal_y | 计算粒子的函数值,并转换成二维 ndarray,func(X).reshape(-1, 1) |
update_pbest | 更新个体最优解,更新的时机是如果粒子仍在约束条件内,且粒子的函数值小于历史个体最优解 |
update_gbest | 更新群体最优解,更新的时机是出现比历史全体最优解更小的个体最优解 |
recorder | 在 record_value 中记录当前粒子的信息 |
run | 执行运算 |
2.2.3 Sciopt-kit 类 run 方法解读
每一次迭代需要进行的操作:
- 更新粒子速度
- 记录当前粒子的信息
- 更新粒子位置
- 计算粒子的函数值
- 更新个体最优解
- 更新群体最优解
- 记录个体最优解
终止迭代的依据:连续 N 次迭代,粒子聚集在某点;或者到达到最大迭代次数。
返回结果:最终个体最优解(gbest_x, gbest_y)