前言:最近太忙,这个系列已经很久没有更新了,本次就更新一个Deb大神的NSGA2的“升级版”算法NSGA3。因为multi-objective optimization已经被做烂了,现在学者们都在做many-objective optimization,也就是5个以上的目标函数(悄悄说一句,我觉得这个也要被做烂了)。此次我是用python复现的,这篇文章也主要以python代码讲解为主。在编写代码过程中,一些小技巧借鉴了platEMO,这个平台在我之前的博客里已经介绍过了,里面包含了近乎所有知名的多目标优化算法,想学习的小伙伴可以看我之前的博客介绍:https://blog.csdn.net/qq_40434430/article/details/88366639
为了哪些只想使用NSGA3的同学,我将NSGA3的matlab代码从平台中扣了出来,想要的在我的主页里下载吧,链接如下:(matlab我使用的是2017版的)
https://download.csdn.net/download/qq_40434430/11079440
python代码使用的python3.6,其它版本的也可以,毕竟没有用到什么复杂的库,就只是用到了numpy等常用的库。完整的python代码大家也可以去我CSDN主页下载哈,连接如下:
https://download.csdn.net/download/qq_40434430/11079551
摘要:此次博客主要记录了Kalyanmoy Deb和Himanshu Jain《An Evolutionary Many-Objective Optimization Algorithm Using Reference-point Based Non-dominated Sorting Approach, Part I: Solving Problems with Box Constraints》的论文学习心得[1],此篇文章提出了处理多个优化目标的进化优化算法NSGAIII。其主要思路是在NSGAII的基础上,引入参考点机制,对于那些非支配并且接近参考点的种群个体进行保留。此次复现处理的优化问题是具有3到15个目标的DTLZ系列[2],仿真结果反应了NSGAIII良好的搜索帕累托最优解集的能力。
关键字:Many-objective optimization,高维问题,NSGAIII,非支配排序,参考点
I.问题简介
在很多真实的应用中,优化目标往往不只有一两个,而是有4个以上。针对这样的高维目标空间,现有的EMO显得有些力不从心,其主要问题为以下六点:
- 随着优化目标数量的增加,非支配解在种群中的比例也在增加,因而会导致搜索过程缓慢;
- 对于高维目标空间,维持多样性的指标计算复杂度过高,解的邻近元素寻找困难;
- 对于高维目标空间,重组算子的搜索能力过于低效了;
- 因为目标函数较多,pareto前沿难以表示,决策者无法选择自己需要的解;
- 性能指标的计算代价过大,算法结果不易评价;
- 针对高维的目标空间,如何可视化结果也是一个难题。
对于前三个问题,我们可以改造EMO缓解,但是后三个问题目前还没有解决办法。除了以上问题,实际问题中由于目标解集集中在pareto front的一个小区域,因此如何寻找也是算法应用到实际中的一个阻碍。但有研究发现目标函数往往会退化成低维的优化,一般会低2~3维,因此冗余目标的识别也是一个难点。以下提出两种解决1,2,3问题的思路:
- 使用特殊的支配关系:可以引入自适应离散化pareto front从而解决问题1、2,使用具有较大指数分布的SBX解决问题3。
- 使用预定义的目标搜索:这种方式又可以分为两种:第一种是预定义一组跨越整个pareto front的搜索方向,这种方法的代表是MOEA/D;另一种是预定义多个参考点,比如算法NSGAIII。
II.NSGAIII算法要点和总结
总体上来说,NSGAIII和NSGAII具有类似的框架,二者区别主要在于选择机制的改变,NSGAII主要靠拥挤度进行排序,其在高维目标空间显然作用不太明显,而NSGAIII对拥挤度排序进行了大刀阔斧的改编,通过引入广泛分布参考点来维持种群的多样性。
以下总结NSGAIII的第t代的步骤。 P t P_t Pt是第t代的父代,其大小为N,其生成的子代为 Q t Q_t Qt,其大小也为N。第一步将子代和父代结合 R t = P t ∪ Q t R_t=P_t\cup Q_t Rt=Pt∪Qt(大小为2N)并从中选出N个个体。为了实现这个选择过程,首先将 R t R_t Rt通过非支配排序分为多个非支配层( F 1 , F 2 , . . . F_1,F_2,... F1,F2,...)。然后从 F 1 F_1 F1开始构造一个新的种群 S t S_t St,直到其大小为N或者第一次超过N。称最后一层为第 l l l层。第 l + 1 l+1 l+1层以上的解将被淘汰出局,在大多数情况下,最后一层被接受层( l l l层)仅有部分被接受。在这种情况下,就要用多样性衡量 l l l层里的解进而进行选择。NSGAII里的这部分使用了拥挤度排序,NSGAIII中我们用以下5步替代。下面先给出这个NSGAIII的第t代的算法步骤如下:
这里需要注意:算法输入里的H是结构化参考点的数目,我在后文中定义为P。
主程序的python代码如下:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # 空间三维画图
from utils import uniformpoint,funfun,cal,GO,envselect,IGD
import copy
import random
#参数设置
N_GENERATIONS = 400 # 迭代次数
POP_SIZE = 100 # 种群大小
name = 'DTLZ1' # 测试函数选择,目前可供选择DTLZ1,DTLZ2,DTLZ3
M = 3 # 目标个数
t1 = 20 # 交叉参数t1
t2 = 20 # 变异参数t2
pc = 1 # 交叉概率
pm = 1 # 变异概率
#画图部分
if(M<=3):
fig = plt.figure()
ax = Axes3D(fig)
###################################################################################################################################################################
#产生一致性的参考点和随机初始化种群
Z,N = uniformpoint(POP_SIZE,M)#生成一致性的参考解
pop,popfun,PF,D = funfun(M,N,name)#生成初始种群及其适应度值,真实的PF,自变量个数
popfun = cal(pop,name,M,D)#计算适应度函数值
Zmin = np.array(np.min(popfun,0)).reshape(1,M)#求理想点
#ax.scatter(Z[:,0],Z[:,1],Z[:,2],c='r')
#ax.scatter(PF[:,0],PF[:,1],PF[:,2],c='b')
#迭代过程
for i in range(N_GENERATIONS):
print("第{name}次迭代".format(name=i))
matingpool=random.sample(range(N),N)
off = GO(pop[matingpool,:],t1,t2,pc,pm)#遗传算子,模拟二进制交叉和多项式变异
offfun = cal(off,name,M,D)#计算适应度函数
mixpop = copy.deepcopy(np.vstack((pop, off)))
Zmin = np.array(np.min(np.vstack((Zmin,offfun)),0)).reshape(1,M)#更新理想点
pop = envselect(mixpop,N,Z,Zmin,name,M,D)
popfun = cal(pop,name,M,D)
if(M<=3):
ax.cla()
type1 = ax.scatter(popfun[:,0],popfun[:,1],popfun[:,2],c='g')
plt.pause(0.00001)
# 绘制PF
if(M<=3):
type2 = ax.scatter(PF[:,0],PF[:,1],PF[:,2],c='r',marker = 'x',s=200)
plt.legend((type1, type2), (u'Non-dominated solution', u'PF'))
else:
fig1 = plt.figure()
plt.xlim([0,M])
for i in range(pop.shape[0]):
plt.plot(np.array(pop[i,:]))
plt.show()
#IGD
score = IGD(popfun,PF)
1.将种群按照非支配层进行划分
将非支配层等级1到 l l l的种群成员依次放入 S t S_t St中,如果 ∣ S t ∣ = N |S_t|=N ∣St∣=N,则无需进行下面的操作,直接 P t + 1 = S t P_{t+1}=S_t Pt+1=St。如果 ∣ S t ∣ > N |S_t|>N ∣St∣>N,那么下一代的一部分解为 P t + 1 = ∪ i = 1 l − 1 F i P_{t+1}=\cup _{i=1}^{l-1}F_i Pt+1=∪i=1l−1Fi,剩余部分( K = N − ∣ P t + 1 ∣ K=N-|P_{t+1}| K=N−∣Pt+1∣)从 F l F_l Fl中选择。这个选择过程见步骤2到5。
非支配排序的python代码如下:
from scipy.special import comb
from itertools import combinations
import numpy as np
import copy
import math
def NDsort(mixpop,N,M):
nsort = N#排序个数
N,M = mixpop.shape[0],mixpop.shape[1]
Loc1=np.lexsort(mixpop[:,::-1].T)#loc1为新矩阵元素在旧矩阵中的位置,从第一列依次进行排序
mixpop2=mixpop[Loc1]
Loc2=Loc1.argsort()#loc2为旧矩阵元素在新矩阵中的位置
frontno=np.ones(N)*(np.inf)#初始化所有等级为np.inf
#frontno[0]=1#第一个元素一定是非支配的
maxfno=0#最高等级初始化为0
while (np.sum(frontno < np.inf) < min(nsort,N)):#被赋予等级的个体数目不超过要排序的个体数目
maxfno=maxfno+1
for i in range(N):
if (frontno[i] == np.inf):
dominated = 0
for j in range(i):
if (frontno[j] == maxfno):
m=0
flag=0
while (m<M and mixpop2[i,m]>=mixpop2[j,m]):
if(mixpop2[i,m]==mixpop2[j,m]):#相同的个体不构成支配关系
flag=flag+1
m=m+1
if (m>=M and flag < M):
dominated = 1
break
if dominated == 0:
frontno[i] = maxfno
frontno=frontno[Loc2]
return frontno,maxfno
2.超平面上参考点的确定
NSGAIII使用一组预定义的参考点以确保解的多样性,这一组参考点可以结构化的方式定义,也可以用户根据自己的参考点。以下介绍一种产生结构化参考点的方法叫Das and Dennis’s method,此方法来源于田野老师对产生参考点方法的综述论文[3]。其参考点在一个(M-1)维的超平面上,M是目标空间的维度,即优化目标的个数。如果我们将每个目标划分为 H H H份,那么其参考点的数量为
P = ( m + H − 1 H ) P=(\begin{matrix} m+H-1\\ H \end{matrix}) P=(m+H−1H)
例如对于一个 H = 4 H=4 H=4的三目标问题,其参考点构成了一个三角形,根据公式(1)可知其产生15个参考点,见图1所示。
那么它们的坐标该如何计算呢?以下通过田野老师的论文讲解一下如何产生这些参考点。假设由Das and Dennis’s method产生的参考点为 s = ( s 1 , s 2 , . . . , s M ) s=(s_1,s_2,...,s_M) s=(s1,s2,...,sM),这里
s j ∈ { 0 / H , 1 / H , . . . , H / H } , ∑ j = 1 M s j = 1 s_j\in \left\{ 0/H,1/H,...,H/H \right\},\sum_{j=1}^{M}s_j=1 sj∈{
0/H,1/H,...,H/H},j=1∑Msj=