本文介绍了一种用于优化连续非线性函数的方法。该方法是通过对一个简化的社会模型的模拟发现的;因此讨论了社会隐喻,尽管该算法没有隐喻的支持。本文从粒子群优化概念的先驱方面来描述它,简要回顾了它从社会模拟到优化器的发展阶段。接下来讨论的是实现这一概念的几个范式。最后,更详细地讨论了一个范式的实施,然后是应用和测试中获得的结果,该范式已被证明是成功的。粒子群优化植根于两个主要的组成方法。也许更明显的是它与一般的人工生命(A-life),特别是与鸟群、鱼群和蜂群理论的关系。然而,它也与进化计算有关,并且与遗传算法和进化编程都有联系。本文对这些关系进行了简要的回顾。作者开发的粒子群优化包括一个非常简单的概念,并且可以在几行计算机代码中实现范式。它只需要原始的数学运算符,在内存需求和速度方面的计算都很昂贵。早期的测试发现该实现对几种问题都很有效。本文讨论了该算法在人工神经网络权重训练中的应用,粒子群优化也被证明在遗传算法测试函数中表现良好。
一些科学家对鸟群或鱼群中的生物体运动的各种解释进行了计算机模拟。值得注意的是,Reynolds[8]和Heppner和Grenander[4]提出了对鸟群的模拟。雷诺兹对鸟群编排的美学很感兴趣,而作为动物学家的赫普纳则对发现使大量鸟类同步成群的基本规则感兴趣,这些鸟类经常突然改变方向,分散并重新组合,等等。这两位科学家都有这样的洞察力:局部过程,如细胞自动机的模型,可能是鸟类社会行为的不可预测的群体动力学的基础。这两个模型都在很大程度上依赖于对个体间距离的操纵;也就是说,鸟群行为的同步性被认为是鸟类努力保持它们与邻居间最佳距离的一个函数。
假设一些相同的规则是动物社会行为的基础,包括畜群、学校和羊群,以及人类的社会行为,这似乎不是一个太大的逻辑飞跃。正如社会生物学家在谈到鱼群时写道:“至少在理论上,在寻找食物的过程中,鱼群中的个别成员可以从鱼群中所有其他成员的发现和先前的经验中获益。当资源不可预测地成片分布时,这种优势可能成为决定性的,超过了竞争食物的劣势”。这句话表明,联营体之间的社会信息共享提供了一种进化优势:这一假设是粒子群优化发展的基础。
开发模拟的一个动机是为了模拟人类的社会行为,当然这与鱼群或鸟群不尽相同。一个重要的区别是它的抽象性。鸟类和鱼类调整它们的物理运动,以避免捕食者,寻找食物和伴侣,优化环境参数,如温度等。人类;不仅调整身体运动,而且还调整认知或经验变量。我们通常不会步调一致地行走和鸣叫(尽管一些关于人类一致性的迷人研究表明我们有能力做到这一点);相反,我们倾向于调整我们的信念和态度,以符合我们社会同伴的信念和态度。
就设计计算机模拟而言,这是一个重大的区别,至少有一个明显的原因:碰撞。两个人可以持有相同的态度和信仰而不碰撞,但两只鸟不能在空间中占据相同的位置而不发生碰撞。在讨论人类社会行为时,将变化的概念映射到鸟类/鱼类的运动类比中似乎是合理的。这与亚里士多德的经典观点一致,即质和量的变化是运动的类型。因此,除了在三维物理空间中移动,并避免碰撞,人类在抽象的多维空间中变化,没有碰撞。物理空间当然会影响信息输入,但它可以说是心理经验的一个微不足道的组成部分。人类在很小的时候就学会了避免物理碰撞,而在n维社会心理空间的导航则需要几十年的练习–我们中的许多人似乎永远无法获得我们所需要的所有技能!
粒子群优化器可能最好通过解释其概念发展来介绍。如上所述,该算法开始是对一个简化的社会环境的模拟。代理人被认为是防碰撞的鸟类,最初的意图是图形化地模拟鸟群的优雅但不可预测的编排。
一个令人满意的模拟很快就写好了,它依赖于两个道具:最近邻的速度匹配和 “疯狂”。一群鸟被随机初始化,每个鸟在环形像素网格上有一个位置,并有X和Y速度。在每次迭代中,程序中的一个循环确定,对于每个代理(一个比鸟更合适的术语),哪个其他代理是其最近的邻居,然后将该代理的X和Y速度分配给焦点代理。从本质上讲,这个简单的规则创造了一个运动的同步性。
不幸的是,鸟群很快就确定了一个一致的、不变的方向。因此,我们引入了一个叫做 "疯狂 "的随机变量。在每次迭代中,随机选择的X和Y速度都会增加一些变化。这给系统引入了足够的变化,使模拟具有有趣和 "栩栩如生 "的外观,当然,这种变化完全是人为的。
Heppner的鸟类模拟有一个特点,就是在模拟中引入了一种动态的力量。他的鸟儿围绕着 "栖息地 "成群结队,这是像素屏幕上吸引它们的一个位置,直到它们最终落到那里。这消除了对疯狂等变量的需要,因为模拟有了自己的生命。虽然栖息地的想法很吸引人,但它导致了另一个似乎更刺激的问题。海普纳的鸟儿知道它们的栖息地在哪里,但在现实生活中,鸟儿会降落在任何能满足它们迫切需要的树或电话线上。更重要的是,鸟群会在有食物的地方降落。它们是如何找到食物的呢?任何曾经摆放过喂鸟器的人都知道,在几个小时内,大量的鸟类可能会找到它,即使它们之前对它的位置、外观等一无所知。似乎有可能的是,鸟群的某些动态使鸟群成员能够利用彼此的知识,就像上面威尔逊的那句话。
模拟的第二个变化定义了一个 “田矢量”,即像素平面上XY坐标的二维矢量。每个代理人都被编程以评估其目前的位置,即方程式:
每个代理 "记住 "了最佳值和产生该值的XY位置。该值被称为pbest[],位置为spbestx[]和pbestyl](括号内表示这些是数组,元素数=代理的数量)。当每个代理人在评估位置的像素空间中移动时,其X和Y速度以一种简单的方式被调整。如果它在pbestx的右边,那么它的X速度(称为vx)被负向调整,由系统的一个参数加权的随机量:vx[]=vx[]-rand()*p-increment。如果它在pbestx的左边,rand()*p-increment被加到vx[]上。同样地,Y方向的速度vy[]也被向上和向下调整,这取决于代理是在pbesty之上还是之下。
其次,每个代理人都 "知道 "鸟群中某个成员找到的全球最佳位置及其价值。这是通过简单地将具有最佳值的代理人的数组索引分配给一个叫做gbest的变量来实现的,因此,pbestx[gbest]是该组的最佳X位置,pbesty[gbest]是其最佳Y位置,而且这一信息对所有羊群成员都可用。同样,每个成员的vx[]和vy[]被调整如下,其中g-increment是一个系统参数。
在模拟中,一个圆圈标志着像素场上的(100,100)位置,代理被表示为彩色的点。因此,观察者可以看到成群结队的代理人在周围盘旋,直到他们找到模拟的玉米田。结果是令人惊讶的。在p-increment和g-increment设置相对较高的情况下,羊群似乎被猛烈地吸进了玉米田。在极少数的迭代中,整个羊群,通常是15到30个个体,被看到聚集在目标周围的小圈内。当p-增量和g-增量设置较低时,鸟群围绕目标旋转,真实地接近目标,有节奏地摆动,子群同步,最后 "落在 "目标上。
一旦明确了该范式可以模拟简单的二维线性函数,就必须确定该范式中对该任务有必要的部分。例如,作者很快就发现,如果没有疯狂,该算法也能很好地工作,而且看起来也很现实,所以它被删除了。接下来显示,当近邻速度匹配被移除时,优化实际上发生得稍快,尽管视觉效果有所改变。现在的鸟群是游动的,但它能够很好地找到玉米地。
变量best和gbest以及它们的增量都是必要的。从概念上讲,最佳状态类似于自传体记忆,因为每个人都记得自己的经历(尽管只是关于它的一个事实),与最佳状态相关的速度调整被称为 “简单的怀旧”,即个人倾向于回到过去最让他满意的地方。另一方面,gbest在概念上类似于公开的知识,或一个群体规范或标准,个人寻求达到这个标准。在模拟中,相对于g-increment而言,p-increment的高值会导致孤立的个体在问题空间中过度徘徊,而反之(相对高的g-increment)则会导致羊群过早地冲向局部最小值。两种增量的数值大致相等,似乎可以对问题域进行最有效的搜索。
虽然该算法似乎令人印象深刻地模拟了一个寻找玉米田的羊群,但最有趣的优化问题既不是线性的,也不是二维的。由于作者的目标之一是为社会行为建模,而社会行为是多维和无碰撞的,因此将presentx和presenty(当然还有vx[]和vy[])从一维数组改为D×N矩阵,其中D是任意维数,N是代理人的数量,这似乎是一个简单的步骤。
进行了多维实验,使用了一个非线性的多维问题:调整权重来训练一个前馈多层感知器神经网络(NN)。作者的第一个实验涉及训练一个三层NN的权重,以解决排他性问题(XOR)。这个问题需要两个输入和一个输出处理元件(PEs),加上一些隐藏的PEs。除了来自上一层的连接外,隐藏和输出PE层都有一个与之相关的偏置PE。因此,一个2,3,1的NN需要优化13个参数。这个问题是通过代理人在13维空间飞行来解决的,直到达到每个PE的平均总误差标准。该算法在这个问题上表现非常好。三维XOR网络在20个代理的平均30.7次迭代中被训练到e<0.05的标准。
虽然该算法运行良好,但在美学上有一些令人不悦和难以理解的地方。速度调整是基于一个粗略的不等式测试:如果presentx>bestx,使其变小;如果presentx
(注意参数vx和presentx有两组括号,因为它们现在是按维度划分的代理矩阵;increment和bestx也可以在其开头有一个g而不是p) 。
人们很快意识到,没有好的方法来猜测p-或g-增量是否应该更大。因此,这些条款也被从算法中删除了。随机因素被乘以2,使其平均值为1,这样代理人将 "飞越 "目标大约一半的时间。这个版本的性能优于以前的版本。进一步的研究将表明,目前设置为2的常数是否有一个最佳值,该值是否应该针对每个问题进行演化,或者该值是否可以从某个特定问题的某些知识中确定。目前的简化粒子群优化器现在通过以下公式调整速度:
我们尝试了该算法的其他变化,但似乎都没有改进目前的简化版本。例如,很明显,代理人被推向了问题空间中两个 "最佳 "点的加权平均。该算法的一个版本将两个条件减少到一个,即每个维度上处于pbest和gbest位置中间的点。然而,这个版本有一个不幸的趋势,即无论它是否是一个最佳点,都会收敛在这个点上。显然,这两个随机的 "踢 "是这个过程中的一个必要部分。
另一个版本考虑使用两类代理人,设想为 "探险者 "和 “定居者”。探索者使用不等式检验,这往往会导致他们超过目标一大段距离,而定居者则使用差异项。假设探索者会在问题域的 "已知 "区域之外进行推断,而定居者则会爬山或对已发现的良好区域进行微探索。同样,这种方法与目前的简化版本相比没有任何改进。
另一个被测试的版本删除了vx[][]的动力。新的调整是:
这个版本虽然简化了,但在寻找全局最优值方面被证明是相当无效的。
正如第2.3节所述,在简化范式的过程中,很明显,现在的代理群体的行为更像一个蜂群,而不是一个羊群。蜂群一词在文献中是有依据的。特别是,作者根据Millonas的一篇论文使用了这个术语,他为人工生命的应用开发了他的模型,并阐明了蜂群智能的五个基本原则。首先是接近原则:群体应该能够进行简单的空间和时间计算。第二是质量原则:群体应该能够对环境中的质量因素做出反应。第三是多样化反应原则:人口不应该沿着过于狭窄的渠道投入其活动。第四是稳定性原则:当环境发生变化时,人口不应改变其行为模式。第五是适应性原则:人口必须能够在值得计算的时候改变行为模式。请注意,原则四和原则五是同一枚硬币的相反面。
本文提出的粒子群优化概念和范式似乎坚持了所有五个原则。该范式的基础是在一系列的时间步骤中进行的n维空间计算。人口对质量因素pbest和gbest作出反应。pbest和gbest之间的反应分配确保了反应的多样性。只有当gbest发生变化时,种群才会改变其状态(行为模式),从而遵守稳定的原则。该种群具有适应性,因为当gbest发生变化时,它确实发生了变化。
选择粒子这个术语是一种折中的办法。虽然可以说群体成员是无质量和无体积的,因此可以称为 “点”,但我们认为速度和加速度更适合应用于粒子,即使每个粒子被定义为具有任意小的质量和体积。此外,里夫斯讨论了由原始粒子云组成的粒子系统,作为云、火和烟等扩散物体的模型。因此,作者选择的标签是代表优化概念的粒子群。
main.m % 主函数
ObjectiveFunction.m % 目标函数
PSO.m % 粒子群优化算法
clear
close all
clc
% Problem preparation
problem.nVar = 2;
problem.ub = 50 * ones(1, 2);
problem.lb = -50 * ones(1, 2);
problem.fobj = @ObjectiveFunction;
% PSO parameters
noP = 4;
maxIter = 500;
visFlag = 1; % set this to 0 if you do not want visualization
RunNo = 30;
BestSolutions_PSO = zeros(1 , RunNo);
[ GBEST , cgcurve ] = PSO( noP , maxIter, problem , visFlag ) ;
disp('Best solution found')
GBEST.X
disp('Best objective value')
GBEST.O
function [ o ] = ObjectiveFunction (x)
o = sum ( abs(x) ) + prod( abs(x) );
end
function [ GBEST , cgCurve ] = PSO ( noP, maxIter, problem, dataVis )
% Define the details of the objective function
nVar = problem.nVar;
ub = problem.ub;
lb = problem.lb;
fobj = problem.fobj;
% Extra variables for data visualization
average_objective = zeros(1, maxIter);
cgCurve = zeros(1, maxIter);
FirstP_D1 = zeros(1 , maxIter);
position_history = zeros(noP , maxIter , nVar );
% Define the PSO's paramters
wMax = 0.9;
wMin = 0.2;
c1 = 2;
c2 = 2;
vMax = (ub - lb) .* 0.2;
vMin = -vMax;
% The PSO algorithm
% Initialize the particles
for k = 1 : noP
Swarm.Particles(k).X = (ub-lb) .* rand(1,nVar) + lb;
Swarm.Particles(k).V = zeros(1, nVar);
Swarm.Particles(k).PBEST.X = zeros(1,nVar);
Swarm.Particles(k).PBEST.O = inf;
Swarm.GBEST.X = zeros(1,nVar);
Swarm.GBEST.O = inf;
end
% Main loop
for t = 1 : maxIter
% Calcualte the objective value
for k = 1 : noP
currentX = Swarm.Particles(k).X;
position_history(k , t , : ) = currentX;
Swarm.Particles(k).O = fobj(currentX);
average_objective(t) = average_objective(t) + Swarm.Particles(k).O;
% Update the PBEST
if Swarm.Particles(k).O < Swarm.Particles(k).PBEST.O
Swarm.Particles(k).PBEST.X = currentX;
Swarm.Particles(k).PBEST.O = Swarm.Particles(k).O;
end
% Update the GBEST
if Swarm.Particles(k).O < Swarm.GBEST.O
Swarm.GBEST.X = currentX;
Swarm.GBEST.O = Swarm.Particles(k).O;
end
end
% Update the X and V vectors
w = wMax - t .* ((wMax - wMin) / maxIter);
FirstP_D1(t) = Swarm.Particles(1).X(1);
for k = 1 : noP
Swarm.Particles(k).V = w .* Swarm.Particles(k).V + c1 .* rand(1,nVar) .* (Swarm.Particles(k).PBEST.X - Swarm.Particles(k).X) ...
+ c2 .* rand(1,nVar) .* (Swarm.GBEST.X - Swarm.Particles(k).X);
% Check velocities
index1 = find(Swarm.Particles(k).V > vMax);
index2 = find(Swarm.Particles(k).V < vMin);
Swarm.Particles(k).V(index1) = vMax(index1);
Swarm.Particles(k).V(index2) = vMin(index2);
Swarm.Particles(k).X = Swarm.Particles(k).X + Swarm.Particles(k).V;
% Check positions
index1 = find(Swarm.Particles(k).X > ub);
index2 = find(Swarm.Particles(k).X < lb);
Swarm.Particles(k).X(index1) = ub(index1);
Swarm.Particles(k).X(index2) = lb(index2);
end
if dataVis == 1
outmsg = ['Iteration# ', num2str(t) , ' Swarm.GBEST.O = ' , num2str(Swarm.GBEST.O)];
disp(outmsg);
end
cgCurve(t) = Swarm.GBEST.O;
average_objective(t) = average_objective(t) / noP;
fileName = ['Resluts after iteration # ' , num2str(t)];
save( fileName)
end
GBEST = Swarm.GBEST;
if dataVis == 1
iterations = 1: maxIter;
%% Draw the landscape
figure
x = -50 : 1 : 50;
y = -50 : 1 : 50;
[x_new , y_new] = meshgrid(x,y);
for k1 = 1: size(x_new, 1)
for k2 = 1 : size(x_new , 2)
X = [ x_new(k1,k2) , y_new(k1, k2) ];
z(k1,k2) = ObjectiveFunction( X );
end
end
subplot(1,5,1)
surfc(x_new , y_new , z);
title('Search landscape')
xlabel('x_1')
ylabel('x_2')
zlabel('Objective value')
shading interp
camproj perspective
box on
set(gca,'FontName','Times')
%% Visualize the cgcurve
subplot(1,5,2);
semilogy(iterations , cgCurve, 'r');
title('Convergence curve')
xlabel('Iteration#')
ylabel('Weight')
%% Visualize the average objectives
subplot(1,5,3)
semilogy(iterations , average_objective , 'g')
title('Average objecitves of all particles')
xlabel('Iteration#')
ylabel('Average objective')
%% Visualize the fluctuations
subplot(1,5,4)
plot(iterations , FirstP_D1, 'k');
title('First dimention in first Particle')
xlabel('Iteration#')
ylabel('Value of the first dimension')
%% Visualize the search history
subplot(1,5,5)
hold on
for p = 1 : noP
for t = 1 : maxIter
x = position_history(p, t , 1);
y = position_history(p, t , 2);
myColor = [0+t/maxIter 0 1-t/maxIter ];
plot(x , y , '.' , 'color' , myColor );
end
end
contour(x_new , y_new , z);
plot(Swarm.GBEST.X(1) , Swarm.GBEST.X(2) , 'og');
xlim([lb(1) , ub(1)])
ylim([lb(2) , ub(2) ])
title('search history')
xlabel('x')
ylabel('y')
box on
set(gcf , 'position' , [128 372 1634 259])
end
J. Kennedy and R. Eberhart, “Particle swarm optimization,” Proceedings of ICNN’95 - International Conference on Neural Networks, Perth, WA, Australia, 1995, pp. 1942-1948 vol.4, doi: 10.1109/ICNN.1995.488968.