日常生活中的很多问题都可以建模为多目标优化问题,如典型的路径规划问题,您既想能够较快地到达目的地,又想能够减少路费成本。多目标优化算法在一次运行的过程中可以产生一组Pareto最优解,是解决这类问题的强大的框架。
本文主要结合论文:A Knee Point Driven Evolutionary Algorithm for Many-Objective Optimization,分析一下PlatEMO中KnEA算法的具体实现。
1. 算法思路
常见的多目标进化算法可以分为以下三类:
- 修改传统的Pareto支配关系,以增强到Pareto前沿的选择压力;
- 将传统的Pareto支配关系和其他收敛性相关的指标相结合,例如NSGA-II;
- 提出基于一些性能指标的新的选择算子,例如IBEA、HypE和SMS-EMOA。
本文要讲的KnEA属于第二类。考虑下图的双目标优化问题,A,C,D均为非支配解集,对于超体积指标来说,选择B作为非支配解集所带来的收益要远远大于选择B’所带来的收益,即能够较好地提高解集的收敛性和多样性。当然,基于超体积指标的多目标算法在选择的时候也是选择收益最大的点,所不同的是,KnEA不仅能够找到收益较大的点,同时,避免了计算的时间复杂度。总所周知,超体积的计算是一个非常耗时的操作,同时,随着目标个数的增加,计算时间呈指数增大。另一方面,KnEA在识别Knee points时采用自适应领域策略,该策略能够识别出knee points的同时保持种群的分布性,而只考虑hypervolume值并不能保证种群最终能有较好的分布性。
knee points(凹点)是Pareto前沿面上的“最凹”一个点,该点对Pareto解集的贡献最大。大部分现有的多目标进化算法在高维多目标优化上性能较差的一个主要原因是由于种群中大部分解,甚至是所有的解均为非支配解而导致选择压力的缺失。
如下图所示,通过找到一条极端直线
,并计算非支配解集中其他个体到这条直线的距离,可以找到当前解集中的拐点。具体的步骤为:
- 计算点 - 到直线的距离,并按照降序排序,明显可以看出,E点到极端直线 的距离最大。因此,第一个点应该为E点;
- 删除点 临近的区域内的点,即图中,以 为中心的虚线框中的点。由于点 在其邻域内,因此, 点被删除;
- 找到距离第二大的点 ,执行同样的操作;
- 若点已删除,则不执行任何操作,直到遍历完所有的非支配解集。
寻找非支配解集中的knee points的伪代码如下所示:
其中,
为自定义参数,控制knee point占非支配解集的比例。
随着迭代次数的增大自适应变化曲线如下所示,
随着迭代次数的增大急剧下降,直至收敛到一个固定的值,
也稳定在
:
2. 代码分析
上面主要是以二维空间举例,需要计算非支配解集中每一个解到极端参考直线的距离。对于多维空间,需要计算非支配解集中的每个解到极端“超平面”的距离。
维空间中的超平面由下面的方程确定:
其中, 为 阶方阵, 均为 维列向量, 是一个实数,代表超平面到原点的距离。
非支配解集中的每个解到极端“超平面”的距离:
function [KneePoints,Distance,r,t] = FindKneePoints(PopObj,FrontNo,MaxFNo,r,t,rate)
% Find all the knee points in each front
[N,M] = size(PopObj);
KneePoints = false(1,N);
Distance = zeros(1,N);
%% 寻找每个参考平面的 knee points
for i = 1 : MaxFNo
Current = find(FrontNo==i);
if length(Current) <= M
KneePoints(Current) = 1;
else
% 寻找极值点
[~,Rank] = sort(PopObj(Current,:),'descend');
Extreme = zeros(1,M);
Extreme(1) = Rank(1,1);
for j = 2 : length(Extreme)
k = 1;
Extreme(j) = Rank(k,j);
while ismember(Extreme(j),Extreme(1:j-1))
k = k+1;
Extreme(j) = Rank(k,j);
end
end
% 计算超平面
Hyperplane = PopObj(Current(Extreme),:)\ones(length(Extreme),1);
% 计算每个解到参考超平面的距离
Distance(Current) = -(PopObj(Current,:)*Hyperplane-1)./sqrt(sum(Hyperplane.^2));
% 更新近邻的距离
Fmax = max(PopObj(Current,:),[],1);
Fmin = min(PopObj(Current,:),[],1);
if t(i) == -1
r(i) = 1;
else
r(i) = r(i)/exp((1-t(i)/rate)/M);
end
R = (Fmax-Fmin).*r(i);
% 选择 knee points
[~,Rank] = sort(Distance(Current),'descend');
Choose = zeros(1,length(Rank));
Remain = ones(1,length(Rank));
for j = Rank
if Remain(j)
for k = 1 : length(Current)
if abs(PopObj(Current(j),:)-PopObj(Current(k),:)) <= R
Remain(k) = 0;
end
end
Choose(j) = 1;
end
end
t(i) = sum(Choose)/length(Current);
Choose(Rank(find(Choose(Rank)==1,1,'last'))) = 0;
KneePoints(Current(Choose==1)) = 1;
end
end
end