一、理论基础
1.1 引言
粒子群优化算法(Particle Swarm Optimization, PSO)自1995年由Eberhart和Kennedy提出以来,已经成为解决优化问题的一种有效且广泛应用的方法。作为一种进化计算技术,PSO受到社会行为模式,特别是鸟群和鱼群的觅食行为的启发。本篇博客将从计算机科学与工程专家学者的角度,深入探讨PSO算法的基本原理、理论推导及其在各个领域的应用。
粒子群算法来源于对鸟类群体活动规律性的研究,进而利用群体智能建立的简化模型,它模拟了鸟类的觅食行为,将求解问题的搜索空间比作鸟类的飞行空间,将每只鸟抽象成一个没有质量和体积的粒子,用来表征问题的一个可行解。粒子群算法与其他的进化算法类似,也是基于种群、进化概念,通过个体间的协作与竞争,实现对复杂空间最优解的搜索。同时,它又不像其他的进化算法那样对个体进行交叉、变异、选择等进化算子操作,而将群体中的个体看做在D维空间中没有质量和体积的粒子,每个粒子以一定的速度在解空间中运动,并向自身历史最佳位置P1和群体最佳位置G聚集,实现对候选解的进化。
粒子群算法具有很好的生物社会背景而易于理解,由于参数少而容易实现,对非线性、多峰问题具有较强的全局搜索能力,在科学研究和工程应用中得到可广泛的关注。目前,该算法已广泛应用在函数优化、神经网络训练、模式识别、模糊控制等领域。
1.2 粒子群算法描述
PSO算法中,每个解都被视为搜索空间内的一个“粒子”,每个粒子都有其位置和速度,这些粒子在解空间中飞行以寻找最优解。粒子的飞行是根据个体和社会经验来调整的,具体来说,是根据两个最佳值来调整。第一个是粒子自身找到的最优解(个体最优解,pbest),另一个是整个种群目前找到的最优解(全局最优解,gbest)。
粒子群算法的信息共享机制可以解释为一种共生合作的行为,即每个粒子都在不停地进行搜索,并且其搜索行为在不同程度上受到群体其他个体的影响。同时,这些粒子还具备对所经历历史最佳位置的记忆能力,即其搜索行为在受其他个体影响的同时还受到自身经验的引导。
基于独特的搜索机制,粒子群算法首先生成了初始种群,即在可行解空间和速度空间随机初始化粒子的速度和位置,其中粒子的位置用于表征问题的可行解,然后通过种群间粒子个体的合作和竞争来求解优化问题。
1.3 粒子群算法特点
粒子群算法本质是一种随机搜索算法,它是一种新兴的智能优化技术。该算法能以较大概率收敛于全局最优解。实践证明,它适合在动态、多目标优化环境中寻优,与传统优化算法相比,具有较快的计算速度和更好的全局搜索能力。
(1)粒子群算法是基于群智能理论的优化算法,通过群体中粒子间的合作与竞争产生的群体智能指导优化搜索。与其他算法相比,粒子群算法是一种高效的并行搜索算法。
(2)粒子群算法与遗传算法都是随机初始化种群,使用适应值来评价个体的优劣程度和进行一定的随机搜索。但粒子群算法根据自己的速度来决定搜索,没有遗传算法的交叉与变异。与进化算法相比,粒子群算法保留了基于种群的全局搜索策略,但是其采用的速度-位移模型操作简单,避免了复杂的遗传操作。
(3)由于每个粒子在算法结束时仍保持其个体极值,即粒子群算法除了可以找到问题的最优解外,还会得到若干较好的次优解,因此将粒子群算法用于调度和决策问题可以给出多种有意义的方案。
(4)粒子群算法特有的记忆使其可以动态地跟踪当前搜索情况并调整其搜索策略,另外,粒子群算法对种群的大小不敏感,即使种群数量下降时,性能下降也不是很大。
参考:粒子群优化算法(Particle Swarm Optimization, PSO)的详细解读 - 知乎
二、算法模型
2.1 粒子群算法建模
粒子群算法的思想源于对鸟群觅食行为的研究,鸟群通过集体的信息共享使群体找到最优的目的地。如下图,设想这样一个场景:鸟群在森林中随机搜索食物,它们想要找到食物量最多的位置。但是所有的鸟都不知道食物具体在哪个位置,只能感受到食物大概在哪个方向。每只鸟沿着自己判定的方向进行搜索,并在搜索的过程中记录自己曾经找到过食物且量最多的位置,同时所有的鸟都共享自己每一次发现食物的位置以及食物的量,这样鸟群就知道当前在哪个位置食物的量最多。在搜索的过程中每只鸟都会根据自己记忆中食物量最多的位置和当前鸟群记录的食物量最多的位置调整自己接下来搜索的方向。鸟群经过一段时间的搜索后就可以找到森林中哪个位置的食物量最多(全局最优解)。
将鸟群觅食行为和算法原理对应,如下图:
2.2 基本粒子群算法
在找到这两个最优解时,粒子根据下式更新位置和速度参数:
1. 速度更新
2. 位置更新
2.3 标准粒子群算法
2.4 压缩因子粒子群算法
2.5 离散粒子群算法
三、算法流程
伪代码:
四、关键参数说明
粒子的两个属性:速度和位置(算法的两个核心要素)
速度表示粒子下一步迭代时移动的方向和距离,位置是所求解问题的一个解。
五、算法实现
(6)邻域结构的设定
全局版本的粒子群算法将整个群体作为粒子的邻域,具有收敛速度快的优点,但有时算法会陷入局部最优。局部版本的粒子群算法将位置相近的个体作为粒子的邻域,收敛速度较慢,不易陷入局部最优值。实际应用中,可先采用全局粒子群算法寻找最优解的方向,即得到大致的结果,然后采用局部粒子群算法在最优点附近进行精细搜索。
(7)边界条件处理
当某一维或若干维的位置或速度超过设定值时,采用边界条件处理策略可将粒子的位置限制在可行搜索空间内,这样能避免种群的膨胀与发散,也能避免粒子大范围地盲目搜索,从而提高了搜索效率。具体的方法有很多种,比如通过设当超过最大位置或最大速度时,在取置最大位置限制 xmax 和最大速度限制 vmax,值范围内随机产生一个数值代替,或者将其设置为最大值,即边界吸收。
六、算法仿真
6.1 Matlab仿真实例
%%%%%%%%%%%%%%%%%粒子群算法求函数极值%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%初始化%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear all; %清除所有变量
close all; %清图
clc; %清屏
N=100; %群体粒子个数
D=10; %粒子维数
T=200; %最大迭代次数
c1=1.5; %学习因子1
c2=1.5; %学习因子2
w=0.8; %惯性权重
Xmax=20; %位置最大值
Xmin=-20; %位置最小值
Vmax=10; %速度最大值
Vmin=-10; %速度最小值
%%%%%%%%%%%%%%%%初始化种群个体(限定位置和速度)%%%%%%%%%%%%%%%%
x=rand(N,D) * (Xmax-Xmin)+Xmin;
v=rand(N,D) * (Vmax-Vmin)+Vmin;
%%%%%%%%%%%%%%%%%%初始化个体最优位置和最优值%%%%%%%%%%%%%%%%%%%
p=x;
pbest=ones(N,1);
for i=1:N
pbest(i)=func1(x(i,:));
end
%%%%%%%%%%%%%%%%%%%初始化全局最优位置和最优值%%%%%%%%%%%%%%%%%%
g=ones(1,D);
gbest=inf;
for i=1:N
if(pbest(i)<gbest)
g=p(i,:);
gbest=pbest(i);
end
end
gb=ones(1,T);
%%%%%%%%%%%按照公式依次迭代直到满足精度或者迭代次数%%%%%%%%%%%%%
for i=1:T
for j=1:N
%%%%%%%%%%%%%%更新个体最优位置和最优值%%%%%%%%%%%%%%%%%
if (func1(x(j,:))<pbest(j))
p(j,:)=x(j,:);
pbest(j)=func1(x(j,:));
end
%%%%%%%%%%%%%%%%更新全局最优位置和最优值%%%%%%%%%%%%%%%
if(pbest(j)<gbest)
g=p(j,:);
gbest=pbest(j);
end
%%%%%%%%%%%%%%%%%跟新位置和速度值%%%%%%%%%%%%%%%%%%%%%
v(j,:)=w*v(j,:)+c1*rand*(p(j,:)-x(j,:))...
+c2*rand*(g-x(j,:));
x(j,:)=x(j,:)+v(j,:);
%%%%%%%%%%%%%%%%%%%%边界条件处理%%%%%%%%%%%%%%%%%%%%%%
for ii=1:D
if (v(j,ii)>Vmax) | (v(j,ii)< Vmin)
v(j,ii)=rand * (Vmax-Vmin)+Vmin;
end
if (x(j,ii)>Xmax) | (x(j,ii)< Xmin)
x(j,ii)=rand * (Xmax-Xmin)+Xmin;
end
end
end
%%%%%%%%%%%%%%%%%%%%记录历代全局最优值%%%%%%%%%%%%%%%%%%%%%
gb(i)=gbest;
end
g; %最优个体
gb(end); %最优值
figure
plot(gb)
xlabel('迭代次数');
ylabel('适应度值');
title('适应度进化曲线')
6.2 Python
python中的粒子群算法库、包:pyPSO、scikit-opt、deap
- 启发式算法库scikit-opt:包括遗传算法(Genetic Algorithm, GA)、粒子群优化(Particle Swarm Optimization, PSO)、模拟退火算法(Simulated Annealing, SA)、蚁群算法(Ant Colony Algorithm, ACA)、免疫算法(Immune Algorithm, IA)、人工鱼群算法(Artificial Fish Swarm Algorithm, AFSA),旅行商问题(Traveling Salesman Problem, TSP )。
- 优化算法库deap:包括遗传算法、粒子群优化等。
# coding: utf-8
import numpy as np
import random
import matplotlib.pyplot as plt
# ----------------------PSO参数设置---------------------------------
class PSO():
def __init__(self, pN, dim, max_iter):
self.w = 0.8
self.c1 = 2
self.c2 = 2
self.r1 = 0.6
self.r2 = 0.3
self.pN = pN # 粒子数量
self.dim = dim # 搜索维度
self.max_iter = max_iter # 迭代次数
self.X = np.zeros((self.pN, self.dim)) # 所有粒子的位置和速度
self.V = np.zeros((self.pN, self.dim))
self.pbest = np.zeros((self.pN, self.dim)) # 个体经历的最佳位置和全局最佳位置
self.gbest = np.zeros((1, self.dim))
self.p_fit = np.zeros(self.pN) # 每个个体的历史最佳适应值
self.fit = 1e10 # 全局最佳适应值
# ---------------------目标函数-----------------------------
def function(self, X):
return X**2-4*X+3
# ---------------------初始化种群----------------------------------
def init_Population(self):
for i in range(self.pN):
for j in range(self.dim):
self.X[i][j] = random.uniform(0, 1)
self.V[i][j] = random.uniform(0, 1)
self.pbest[i] = self.X[i]
tmp = self.function(self.X[i])
self.p_fit[i] = tmp
if tmp < self.fit:
self.fit = tmp
self.gbest = self.X[i]
# ----------------------更新粒子位置----------------------------------
def iterator(self):
fitness = []
for t in range(self.max_iter):
for i in range(self.pN): # 更新gbest\pbest
temp = self.function(self.X[i])
if temp < self.p_fit[i]: # 更新个体最优
self.p_fit[i] = temp
self.pbest[i] = self.X[i]
if self.p_fit[i] < self.fit: # 更新全局最优
self.gbest = self.X[i]
self.fit = self.p_fit[i]
for i in range(self.pN):
self.V[i] = self.w * self.V[i] + self.c1 * self.r1 * (self.pbest[i] - self.X[i]) + \
self.c2 * self.r2 * (self.gbest - self.X[i])
self.X[i] = self.X[i] + self.V[i]
fitness.append(self.fit)
print(self.X[0], end=" ")
print(self.fit) # 输出最优值
return fitness
# ----------------------程序执行-----------------------
my_pso = PSO(pN=30, dim=1, max_iter=100)
my_pso.init_Population()
fitness = my_pso.iterator()
# -------------------画图--------------------
plt.figure(1)
plt.title("Figure1")
plt.xlabel("iterators", size=14)
plt.ylabel("fitness", size=14)
t = np.array([t for t in range(0, 100)])
fitness = np.array(fitness)
plt.plot(t, fitness, color='b', linewidth=3)
plt.show()