基本概念
粒子群优化算法(PSO:Particle swarm optimization) 是一种进化计算技术(evolutionary computation)。源于对鸟群捕食的行为研究。粒子群优化算法的基本思想:是通过群体中个体之间的协作和信息共享来寻找最优解。粒子群算法通过设计一种无质量的粒子来模拟鸟群中的鸟,粒子仅具有两个属性:速度和位置,速度代表移动的快慢,位置代表移动的方向。每个粒子在搜索空间中单独的搜寻最优解,并将其记为当前个体极值,并将个体极值与整个粒子群里的其他粒子共享,找到最优的那个个体极值作为整个粒子群的当前全局最优解,粒子群中的所有粒子根据自己找到的当前个体极值和整个粒子群共享的当前全局最优解来调整自己的速度和位置。下面的动图很形象地展示了PSO算法的过程:
PSO的优势:在于简单容易实现并且没有许多参数的调节。目前已被广泛应用于函数优化、神经网络训练、模糊系统控制以及其他遗传算法的应用领域。
算法
粒子信息
假设目标函数有n个输入参数:f(x1, x2, ..., xn)。
对于每个粒子,它包含的元素:
(1) 当前时刻位置:(x1, x2, ..., xn)
(2) 当前时刻的目标函数值(也称为适应度):f(x1, x2, ..., xn)
(3) 该粒子的历史最优位置:(x1_pbest, x2_pbest, ..., xn_pbest)
(4) 该粒子的历史最优目标函数值:f_pbest = f(x1_pbest, x2_pbest, ..., xn_pbest)
对于全部粒子,它们共有的元素:
(1) 粒子总数:num
(2) 迭代总次数:cnt。粒子群算法也是一个迭代的过程,需要多次迭代才能获取到理想的最优解。
(3) 全局最优位置:(x1_gbest, x2_gbest, ..., xn_gbest)
(4) 全局最优目标函数值:f_gbest = f(x1_gbest, x2_gbest, ..., xn_gbest)
(5) 位置随机化的上下限:xmin,xmax。迭代开始的时候以及迭代的过程中,均需要对粒子的位置进行随机分布,需要设置随机分布的上下限,不然随机分布偏离得太远,会严重影响优化结果。
(6) 速度的上下限:Vmin,Vmax。迭代过程中,速度也具有一定的随机性,需要限制速度的大小在一定范围内,不然如果速度值太大也会严重影响优化结果。
(7) 速度计算参数:c1、c2。通常取1.0~1.8的值。
速度位置更新
对于每一个粒子的每一个位置参数xi,其位置更新公式如下,其中randf为0~1的随机数,c1与c2通常取1.0~1.8之间的固定值。
为了加快收敛速度,根据速度具有惯性的原理,后来人们提出了在速度计算中增加惯性,也即加上上一轮迭代时该位置参数的速度。如下式,其中w为0~1的参数,通常随着迭代次数的增加,逐渐减小w,因为越到后面可能解就越接近真实解,迭代收敛就越慢,所以需要减小w来放慢速度,否则容易错过最优解。
算法步骤、流程
- 步骤1:初始化粒子群(速度和位置)、惯性因子、加速常数、最大迭代次数、算法终止的最小允许误差。
- 步骤2:评价每个粒子的初始适应值。
- 步骤3:将初始适应值作为当前每个粒子的局部最优值,并将各适应值对应的位置作为每个粒子的局部最优值所在的位置。
- 步骤4:将最佳初始适应值作为当前全局最优值,并将最佳适应值对应的位置作为全局最优值所在的位置。
- 步骤5:更新每个粒子的速度。
- 步骤6:对每个粒子的飞翔速度进行限幅处理,使之不能超过设定的最大飞翔速度。
- 步骤7:更新每个粒子当前所在的位置。
- 步骤8:比较当前每个粒子的适应值是否比历史局部最优值好,如果好,则将当前粒子适应值作为粒子的局部最优值,其对应的位置作为每个粒子的局部最优值所在的位置。
- 步骤9:在当前群中找出全局最优值,并将当前全局最优值对应的位置作为粒子群的全局最优值所在的位置。
- 步骤10:重复步骤(5)~(9),直到满足设定的最小误差或最大迭代次数
- 步骤11:输出粒子群的全局最优值和其对应的位置以及每个粒子的局部最优值和其对应的位置。
实战
python
案例方程为:
第一步:基本算法
import numpy as np
import random
class ParticleSwarmOptimization:
"""粒子群优化算法"""
def __init__(self, n=20, c1=2, c2=2, loss=0.001, w=0.6):
"""
:param n: 粒子个数 一般为[10, 40]个粒子。根据实际情况而定可以取更多
:param c1: 社会学习因子 通常在[0, 4]区间范围,一般取2
:param c2: 个体学习因子 通常在[0, 4]区间范围,一般取2
:param loss: 判断是否收敛条件
:param w:惯性因子 较大时全局搜索能力强,较小时局部搜索能力强(本文为定值) 参考:https://blog.csdn.net/qq_38883271/article/details/101538704
"""
self.n = n
self.c1 = c1
self.c2 = c2
self.loss = loss
self.w = w
def fit(self, region):
"""
初始化变量
:param region: 每个维度的可行域,该变量的格式为字典
例如:region = {0:[20, 50], 1:[30, 100],....}
其中0为第一个维度,其可行域为[20, 50]
其中1为第二个维度,其可行域为[30, 100]
"""
self.partical = {} # 存储每个粒子初始化的位置结果
# 为每个粒子进行初始化位置
for num in range(self.n): # 初始化self.n个粒子
init = [] # 用于存储每个粒子的初始化参数
for ndim in range(len(region)): # 每个粒子初始化len(region)个维度
init.append(random.uniform(region[ndim][0], region[ndim][1]))
self.partical[num] = init
# 初始化每个粒子的初始速度V为0。
self.V = {}
for temp in range(self.n):
self.V[temp] = 0
# 设每个粒子的个体最优解Pb为当前初始化的位置(也可以设为随机位置)
self.Pb = self.partical.copy()
def predict(self, callback):
"""
计算最终最优结果
:param callback: 回调函数,用于优化的目标函数
:return:
"""
# 存储上一步的位置信息
last_position = self.partical.copy()
last_value = [] # 存储上一步目标函数值,value的上一步值
# 存储位置变化绝对值
loss = 100
# 循环更新每个粒子的位置,直到位置变化小于某一值
loop = 0 # 循环次数
while True:
# 计算每一个粒子当前位置下目标函数的值,并将最小值所对应的粒子记录下来index
min = callback(self.partical[0]) # 设最小值为min
index = 0 # 最小值所对应的粒子索引
value = [] # 存储每个粒子所处位置对应目标函数的值
for temp in range(self.n): # 对每一个粒子进行遍历
value.append(callback(self.partical[temp])) # 计算每一个粒子当前时刻位置所对应目标函数的值,存储到value中
if min > value[temp]:
min = value[temp]
index = temp
if loop == 0:
last_value = value.copy()
else:
# 更新个体最优解Pb
for temp in range(self.n):
if last_value[temp] > value[temp]: # 如果当上一步值比当前值大,那么就将self.Pb的坐标值改变,也就是每一个粒子self.Pb的值等于该粒子当前时刻历史数据中所处位置对应目标函数最小值的位置
last_value[temp] = value[temp]
self.Pb[temp] = self.partical[temp]
# 将全局最优解设为index粒子对应的坐标
Pz = self.partical[index]
# 更新每个粒子的坐标
for temp in range(self.n):
# 更新速度
if temp != index:
self.V[temp] = self.w*np.array(self.V[temp]) + self.c1*random.random()*(np.array(Pz) - np.array(self.partical[temp]))\
+ self.c2*random.random()*(np.array(self.Pb[temp]) - np.array(self.partical[temp])) # 这个地方谁减谁不能搞反, 不然出错
# 更新位置
self.partical[temp] = np.array(self.partical[temp]) + np.array(self.V[temp])
# 计算更新前后位置变化
if loss > np.sqrt(np.mean(np.square(np.array(self.partical[temp]) - np.array(last_position[temp])))):
loss = np.sqrt(np.mean(np.square(np.array(self.partical[temp]) - np.array(last_position[temp]))))
# 判断位置变化,是否跳出循环,是否收敛
print(f"loop:{loop} loss:{loss}")
if loss < self.loss:
break
last_position = self.partical.copy()
loop += 1
return Pz
第二步:设置方程、寻优
def callback(x):
"""回调函数"""
sum = 0
for i in range(len(x)):
sum += (x[i]-i)*(x[i]-i)
sum /= 100
return sum
def run():
# 实例化粒子群优化算法
pso = ParticleSwarmOptimization(n=10, c1=2, c2=2, loss=0.001, w=0.6)
# 定义变量取值范围
region = {0:[-50, 50], 1:[-50, 50], 2:[-40, 100]}
pso.fit(region)
result = pso.predict(callback)
print(result)
if __name__ == "__main__":
run()
这是我对于粒子群算法的学习笔记,参考如下:
参考:粒子群优化算法Python实现_husky66的博客-CSDN博客