前言

自从上三篇博客详细讲解了Python遗传和进化算法工具箱及其在带约束的单目标函数值优化中的应用、利用遗传算法求解有向图的最短路径、利用进化算法优化SVM参数之后,这篇不再局限于单一的进化算法工具箱的讲解,相反,这次来个横向对比,比较目前最流行的几个python进化算法工具箱/框架在求解多目标问题上的表现。

正文

1 多目标优化概念

首先大致讲一下多目标优化:

在生活中的优化问题,往往不只有一个优化目标,并且往往无法同时满足所有的目标都最优。例如工人的工资与企业的利润。那么多目标优化里面什么解才算是优秀的?我们一般采用“帕累托最优”来衡量解是否优秀,其定义我这里摘录百度百科的一段话:

帕累托最优(Pareto Optimality),是指资源分配的一种理想状态。假定固有的一群人和可分配的资源,从一种分配状态到另一种状态的变化中,在没有使任何人境况变坏的前提下,使得至少一个人变得更好。帕累托最优状态就是不可能再有更多的帕累托改进的余地;换句话说,帕累托改进是达到帕累托最优的路径和方法。 帕累托最优是公平与效率的“理想王国”。是由帕累托提出的。

这段话好像让人看着依旧有点懵逼,下面直接摘录一段学术性的定义:

【摘自:http://www.geatpy.com】

因此,只要找到一组解,其对应的待优化目标函数值的点均落在上面的黑色加粗线上,那么就是我们想要的“帕累托最优解”了。此外,假如帕累托最优解个数不可数,那么我们只需找到上面黑色加粗线上的若干个点即可,并且这些点越分散、分布得越均匀,说明算法的效果越好。

2 多目标进化优化算法

多目标进化优化算法即利用进化算法结合多目标优化策略来求解多目标优化问题。经典而久经不衰的多目标优化算法有:NSGA2、NSGA3、MOEA/D等。其中NSGA2和NSGA3是基于支配的MOEA(Multi-objective evolutionary algorithm),而MOEA/D是基于分解的MOEA。

前两者(NSGA2、NSGA3)通过非支配排序(后面马上讲到)来筛选出一堆解中的“非支配解”,并且通过种群的不断进化,使得种群个体对应的解对应的目标函数值的点不断逼近上图的黑色加粗线。具体算法就不作详细阐述了,可详见以下参考文献,或看下方的代码实战部分:

Deb K , Pratap A , Agarwal S , et al. A fast and elitist multiobjective genetic algorithm: NSGA-II[J]. IEEE Transactions on Evolutionary Computation, 2002, 6(2):0-197.

Deb K , Jain H . An Evolutionary Many-Objective Optimization Algorithm Using Reference-Point-Based Nondominated Sorting Approach, Part I: Solving Problems With Box Constraints[J]. IEEE Transactions on Evolutionary Computation, 2014, 18(4):577-601.

后者(MOEA/D)通过线性或非线性的方式将原多目标问题中各个目标进行聚合,即将多个目标聚合成一个目标,然后利用种群进化不断逼近全局帕累托最优解。这里可能有人会有疑问:“为什么MOEA/D是基于分解的MOEA,但过程中需要对各个目标进行聚合?那不就不叫分解了吗?”答案很简单:分解是指将多目标优化问题分解为一组单目标子问题或多个多目标子问题,利用子问题之间的邻域关系,通过协作来同时优化所有的子问题,从而不断逼近全局帕累托最优解;而聚合是指将多个目标聚合成一个目标,因此MOEA/D里面有“分解”和“聚合”两个步骤,分解是确定邻域关系,聚合是用来方便比较解的优劣,两者并不是矛盾的。具体算法就不作详细阐述了,可详见以下参考文献,或看下方的代码实战部分:

Qingfu Zhang, Hui Li. MOEA/D: A Multiobjective Evolutionary Algorithm Based on Decomposition[M]. IEEE Press, 2007.

那么存在只利用聚合而没有分解这一步来进化优化的算法吗?答案是存在的,比如多目标优化自适应权重法(awGA),代码详见链接:

https://github.com/geatpy-dev/geatpy/blob/master/geatpy/templates/moeas/awGA/moea_awGA_templet.py

这个算法就是通过自适应地生成一个权重向量,来将所有的优化目标聚合成单一的优化目标,然后进行进化优化,当然这样效果自然比不上MOEA/D。

有些单目标优化学得比较溜的读者可能会疑问:”我找一组固定的权重,把各个优化目标加权聚合成一个目标,再用单目标优化的方法进行优化不就完事了吗?“答案非常简单:如果有理论能证明所找的这组权重是最合理、最适合当前的优化模型的,那么用单目标优化的方法来解决多目标优化问题当然是好事;相反,假如没有依据地随便设置一组权重,那么肯定不能用这种方法。

3 代码实战

下面以一个双目标优化测试函数ZDT1和一个三目标优化测试函数DTLZ1为例,横向对比deap、pymoo和geatpy三款进化算法代码包的NSGA2、NSGA3和MOEA/D算法的表现,版本分别为1.3、0.4.0、2.5.0,测试代码均为三款代码包官网给出的案例(在代码组织结构上稍作修改以方便本文显示)。

【注】:由于在计算ZDT1和DTLZ1时deap默认采用的是循环来计算种群中每个个体的目标函数值,而pymoo和geatpy均为利用numpy来矩阵化计算的,为了统一个体评价时间,避免前者带来的性能下降,这里将deap也改用与后两者相同的方法。

实验设备:cpu i5 9600k,16g ddr4内存,windows10,Python 3.7 x64。

相关库的安装:

pip install deap
pip install pymoo
pip install geatpy

ZDT1的模型为:

其中M为待优化的目标个数,y为决策变量。

3.1 用NSGA2 优化 ZDT1

下面用NSGA2算法来优化上面的ZDT1,实验参数为:【种群个体数40,进化代数200,其他相关参数均设为一样】,代码1、代码2、代码3分别为用deap、pymoo和geatpy的nsga2来优化ZDT1:

代码 1. deap_nsga2.py

import array
import random
from deap import base
from deap import creator
from deap import tools
import numpy as np
import matplotlib.pyplot as plt
import timedef ZDT1(Vars, NDIM): # 目标函数ObjV1 = Vars[:, 0]gx = 1 + 9 * np.sum(Vars[:, 1:], 1) / (NDIM - 1)hx = 1 - np.sqrt(np.abs(ObjV1) / gx) # 取绝对值是为了避免浮点数精度异常带来的影响ObjV2 = gx * hxreturn np.array([ObjV1, ObjV2]).Tcreator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0))
creator.create("Individual", array.array, typecode='d', fitness=creator.FitnessMin)
toolbox = base.Toolbox()
# Problem definition
BOUND_LOW, BOUND_UP = 0.0, 1.0
NDIM = 30
def uniform(low, up, size=None):try:return [random.uniform(a, b) for a, b in zip(low, up)]except TypeError:return [random.uniform(a, b) for a, b in zip([low] * size, [up] * size)]toolbox.register("attr_float", uniform, BOUND_LOW, BOUND_UP, NDIM)
toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.attr_float)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("mate", tools.cxSimulatedBinaryBounded, low=BOUND_LOW, up=BOUND_UP, eta=20.0)
toolbox.register("mutate", tools.mutPolynomialBounded, low=BOUND_LOW, up=BOUND_UP, eta=20.0, indpb=1.0/NDIM)
toolbox.register("select", tools.selNSGA2)def main(seed=None):random.seed(seed)NGEN = 300MU = 40CXPB = 1.0pop = toolbox.population(n=MU)# Evaluate the individuals with an invalid fitnessinvalid_ind = [ind for ind in pop if not ind.fitness.valid]ObjV = ZDT1(np.array(invalid_ind), NDIM)for ind, i in zip(invalid_ind, range(MU)):ind.fitness.values = ObjV[i, :]# This is just to assign the crowding distance to the individuals# no actual selection is donepop = toolbox.select(pop, len(pop))# Begin the generational processfor gen in range(1, NGEN):# Vary the populationoffspring = tools.selTournamentDCD(pop, len(pop))offspring = [toolbox.clone(ind) for ind in offspring]      for ind1, ind2 in zip(offspring[::2], offspring[1::2]):if random.random() <= CXPB:toolbox.mate(ind1, ind2)toolbox.mutate(ind1)toolbox.mutate(ind2)del ind1.fitness.values, ind2.fitness.values# Evaluate the individuals with an invalid fitnessinvalid_ind = [ind for ind in offspring if not ind.fitness.valid]ObjV = ZDT1(np.array([ind for ind in offspring if not ind.fitness.valid]), NDIM)for ind, i in zip(invalid_ind, range(MU)):ind.fitness.values = ObjV[i, :]# Select the next generation populationpop = toolbox.select(pop + offspring, MU)return pop
if __name__ == "__main__":start = time.time()pop = main()end = time.time()p = np.array([ind.fitness.values for ind in pop])plt.scatter(p[:, 0], p[:, 1], marker="o", s=10)plt.grid(True)plt.show()print('耗时:', end - start, '秒')

上述代码1利用deap提供的进化算法框架来实现NSGA2算法。deap的主要特点是轻量化和支持扩展。整个deap内部的代码量很少,可以通过”函数注册“来扩展模块,但由此带来的结果便是需要自己写大量的代码。比如要在deap上写一个基因表达式编程(GEP),以geppy这个GEP框架为例,它除了使用到了deap中的”函数注册“功能(方便模块调用)外,几乎等于重写了一遍deap。

代码2:pymoo_nsga2.py

from pymoo.algorithms.nsga2 import NSGA2
from pymoo.factory import get_problem
from pymoo.optimize import minimize
import matplotlib.pyplot as plt
import time
problem = get_problem("ZDT1")
algorithm = NSGA2(pop_size=40, elimate_duplicates=False)
start = time.time()
res = minimize(problem,algorithm,('n_gen', 300),verbose=False)
end = time.time()
plt.scatter(res.F[:, 0], res.F[:, 1], marker="o", s=10)
plt.grid(True)
plt.show()
print('耗时:', end - start, '秒')

上述代码2调用了pymoo内置的NSGA2算法模块,不过该模块与pymoo框架深度耦合,无法直观地看到NSGA2的执行过程,非pymoo开发者想要在上面扩展自己设计的算法会遇到较大的困难。

代码3:geatpy_nsga2.py

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import numpy as np
import geatpy as eaclass ZDT1(ea.Problem): # 继承Problem父类def __init__(self):name = 'ZDT1' # 初始化name(函数名称,可以随意设置)M = 2 # 初始化M(目标维数)maxormins = [1] * M # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)Dim = 30 # 初始化Dim(决策变量维数)varTypes = [0] * Dim # 初始化varTypes(决策变量的类型,0:实数;1:整数)lb = [0] * Dim # 决策变量下界ub = [1] * Dim # 决策变量上界lbin = [1] * Dim # 决策变量下边界(0表示不包含该变量的下边界,1表示包含)ubin = [1] * Dim # 决策变量上边界(0表示不包含该变量的上边界,1表示包含)# 调用父类构造方法完成实例化ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)def aimFunc(self, pop): # 目标函数Vars = pop.Phen # 得到决策变量矩阵ObjV1 = Vars[:, 0]gx = 1 + 9 * np.sum(Vars[:, 1:], 1) / (self.Dim - 1)hx = 1 - np.sqrt(np.abs(ObjV1) / gx) # 取绝对值是为了避免浮点数精度异常带来的影响ObjV2 = gx * hxpop.ObjV = np.array([ObjV1, ObjV2]).T # 把结果赋值给ObjVif __name__ == "__main__":"""================================实例化问题对象============================="""problem = ZDT1()          # 生成问题对象"""==================================种群设置================================"""Encoding = 'RI'           # 编码方式NIND = 40                 # 种群规模Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders) # 创建区域描述器population = ea.Population(Encoding, Field, NIND) # 实例化种群对象(此时种群还没被初始化,仅仅是完成种群对象的实例化)"""================================算法参数设置==============================="""myAlgorithm = ea.moea_NSGA2_templet(problem, population) # 实例化一个算法模板对象myAlgorithm.MAXGEN = 300  # 最大进化代数myAlgorithm.logTras = 0  # 设置每多少代记录日志,若设置成0则表示不记录日志myAlgorithm.verbose = True  # 设置是否打印输出日志信息myAlgorithm.drawing = 1  # 设置绘图方式(0:不绘图;1:绘制结果图;2:绘制目标空间过程动画;3:绘制决策空间过程动画)"""==========================调用算法模板进行种群进化=========================调用run执行算法模板,得到帕累托最优解集NDSet以及最后一代种群。NDSet是一个种群类Population的对象。NDSet.ObjV为最优解个体的目标函数值;NDSet.Phen为对应的决策变量值。详见Population.py中关于种群类的定义。"""[NDSet, population] = myAlgorithm.run()  # 执行算法模板,得到非支配种群以及最后一代种群NDSet.save()  # 把非支配种群的信息保存到文件中"""==================================输出结果=============================="""print('用时:%f 秒' % myAlgorithm.passTime)

上面代码3调用了Geatpy内置的NSGA2算法模板,见链接:

https://github.com/geatpy-dev/geatpy/blob/master/geatpy/templates/moeas/nsga2/moea_NSGA2_templet.py

该算法模板代码与Geatpy提供的进化算法框架的耦合度很小,可以清晰地看到NSGA2算法的执行过程。只需遵循Geatpy中设计的种群数据结构(如染色体用什么表示、目标函数值用什么表示、约束用什么表示),就能很容易在上面扩展自己设计的新进化算法。

此外由代码3可以看到使用Geatpy时可以更加专注于待优化模型的建立,即在做进化算法的应用时,不用管进化算法的细节,而专注于把待优化模型写成一个自定义问题类。

代码1、2、3的运行结果如下图:

3.2 用NSGA3 优化 DTLZ1

下面用NSGA3算法来优化上面的DTLZ1,【种群个体数91,进化代数500,其他相关参数均设为一样】,代码4、代码5、代码6分别为用deap、pymoo和geatpy的nsga3来优化DTLZ1:

代码4:deap_nsga3.py

from math import factorial
import random
import matplotlib.pyplot as plt
import numpy as np
from deap import algorithms
from deap import base
from deap import creator
from deap import tools
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as Axes3d
import time# Problem definition
NOBJ = 3
K = 5
NDIM = NOBJ + K - 1
P = 12
H = factorial(NOBJ + P - 1) / (factorial(P) * factorial(NOBJ - 1))
BOUND_LOW, BOUND_UP = 0.0, 1.0def DTLZ1(Vars, NDIM): # 目标函数XM = Vars[:,(NOBJ-1):]g = 100 * (NDIM - NOBJ + 1 + np.sum(((XM - 0.5)**2 - np.cos(20 * np.pi * (XM - 0.5))), 1, keepdims = True))ones_metrix = np.ones((Vars.shape[0], 1))f = 0.5 * np.hstack([np.fliplr(np.cumprod(Vars[:,:NOBJ-1], 1)), ones_metrix]) * np.hstack([ones_metrix, 1 - Vars[:, range(NOBJ - 2, -1, -1)]]) * (1 + g)return f##
# Algorithm parameters
MU = int(H)
NGEN = 500
CXPB = 1.0
MUTPB = 1.0/NDIM
##
# Create uniform reference point
ref_points = tools.uniform_reference_points(NOBJ, P)
# Create classes
creator.create("FitnessMin", base.Fitness, weights=(-1.0,) * NOBJ)
creator.create("Individual", list, fitness=creator.FitnessMin)
# Toolbox initialization
def uniform(low, up, size=None):try:return [random.uniform(a, b) for a, b in zip(low, up)]except TypeError:return [random.uniform(a, b) for a, b in zip([low] * size, [up] * size)]
toolbox = base.Toolbox()
toolbox.register("attr_float", uniform, BOUND_LOW, BOUND_UP, NDIM)
toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.attr_float)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("mate", tools.cxSimulatedBinaryBounded, low=BOUND_LOW, up=BOUND_UP, eta=30.0)
toolbox.register("mutate", tools.mutPolynomialBounded, low=BOUND_LOW, up=BOUND_UP, eta=20.0, indpb=1.0/NDIM)
toolbox.register("select", tools.selNSGA3, ref_points=ref_points)
##
def main(seed=None):random.seed(seed)# Initialize statistics objectpop = toolbox.population(n=MU)# Evaluate the individuals with an invalid fitnessinvalid_ind = [ind for ind in pop if not ind.fitness.valid]ObjV = DTLZ1(np.array(invalid_ind), NDIM)for ind, i in zip(invalid_ind, range(MU)):ind.fitness.values = ObjV[i, :]# Begin the generational processfor gen in range(1, NGEN):offspring = algorithms.varAnd(pop, toolbox, CXPB, MUTPB)# Evaluate the individuals with an invalid fitnessinvalid_ind = [ind for ind in offspring if not ind.fitness.valid]ObjV = DTLZ1(np.array([ind for ind in offspring if not ind.fitness.valid]), NDIM)for ind, i in zip(invalid_ind, range(MU)):ind.fitness.values = ObjV[i, :]# Select the next generation population from parents and offspringpop = toolbox.select(pop + offspring, MU)return popif __name__ == "__main__":start = time.time()pop = main()end = time.time()fig = plt.figure()ax = fig.add_subplot(111, projection="3d")p = np.array([ind.fitness.values for ind in pop])ax.scatter(p[:, 0], p[:, 1], p[:, 2], marker="o", s=10)ax.view_init(elev=30, azim=45)plt.grid(True)plt.show()print('耗时:', end - start, '秒')

代码5:pymoo_nsga3.py

from pymoo.algorithms.nsga3 import NSGA3
from pymoo.factory import get_problem, get_reference_directions
from pymoo.optimize import minimize
from pymoo.visualization.scatter import Scatter
import time# create the reference directions to be used for the optimization
M = 3
ref_dirs = get_reference_directions("das-dennis", M, n_partitions=12)
N = ref_dirs.shape[0]
# create the algorithm object
algorithm = NSGA3(pop_size=N,ref_dirs=ref_dirs)
start = time.time()
# execute the optimization
res = minimize(get_problem("dtlz1", n_obj = M),algorithm,termination=('n_gen', 500))
end = time.time()
Scatter().add(res.F).show()
print('耗时:', end - start, '秒')

代码6:geatpy_nsga3.py

# -*- coding: utf-8 -*-
import numpy as np
import geatpy as eaclass DTLZ1(ea.Problem): # 继承Problem父类def __init__(self, M):name = 'DTLZ1' # 初始化name(函数名称,可以随意设置)maxormins = [1] * M # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)Dim = M + 4 # 初始化Dim(决策变量维数)varTypes = np.array([0] * Dim) # 初始化varTypes(决策变量的类型,0:实数;1:整数)lb = [0] * Dim # 决策变量下界ub = [1] * Dim # 决策变量上界lbin = [1] * Dim # 决策变量下边界(0表示不包含该变量的下边界,1表示包含)ubin = [1] * Dim # 决策变量上边界(0表示不包含该变量的上边界,1表示包含)# 调用父类构造方法完成实例化ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)def aimFunc(self, pop): # 目标函数Vars = pop.Phen # 得到决策变量矩阵XM = Vars[:,(self.M-1):]g = 100 * (self.Dim - self.M + 1 + np.sum(((XM - 0.5)**2 - np.cos(20 * np.pi * (XM - 0.5))), 1, keepdims = True))ones_metrix = np.ones((Vars.shape[0], 1))f = 0.5 * np.hstack([np.fliplr(np.cumprod(Vars[:,:self.M-1], 1)), ones_metrix]) * np.hstack([ones_metrix, 1 - Vars[:, range(self.M - 2, -1, -1)]]) * (1 + g)pop.ObjV = f # 把求得的目标函数值赋值给种群pop的ObjVif __name__ == "__main__":"""================================实例化问题对象============================="""problem = DTLZ1(3)        # 生成问题对象"""==================================种群设置================================"""Encoding = 'RI'           # 编码方式NIND = 91                # 种群规模Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders) # 创建区域描述器population = ea.Population(Encoding, Field, NIND) # 实例化种群对象(此时种群还没被初始化,仅仅是完成种群对象的实例化)"""================================算法参数设置==============================="""myAlgorithm = ea.moea_NSGA3_templet(problem, population) # 实例化一个算法模板对象`myAlgorithm.MAXGEN = 500  # 最大进化代数myAlgorithm.logTras = 0  # 设置每多少代记录日志,若设置成0则表示不记录日志myAlgorithm.verbose = True  # 设置是否打印输出日志信息myAlgorithm.drawing = 1  # 设置绘图方式(0:不绘图;1:绘制结果图;2:绘制目标空间过程动画;3:绘制决策空间过程动画)"""==========================调用算法模板进行种群进化=========================调用run执行算法模板,得到帕累托最优解集NDSet以及最后一代种群。NDSet是一个种群类Population的对象。NDSet.ObjV为最优解个体的目标函数值;NDSet.Phen为对应的决策变量值。详见Population.py中关于种群类的定义。"""[NDSet, population] = myAlgorithm.run()  # 执行算法模板,得到非支配种群以及最后一代种群NDSet.save()  # 把非支配种群的信息保存到文件中"""==================================输出结果=============================="""print('用时:%f 秒' % myAlgorithm.passTime)

上面代码6调用了Geatpy内置的NSGA3算法模板,见链接:

https://github.com/geatpy-dev/geatpy/blob/master/geatpy/templates/moeas/nsga3/moea_NSGA3_templet.py

代码4、5、6的运行结果如下图:

3.3 用MOEA/D 优化 ZDT1

下面用MOEA/D算法来优化上面的ZDT1,【种群个体数40,进化代数300,其他相关参数均设为一样】,由于deap并无提供MOEA/D的代码,因此这里只比较pymoo和geatpy。

值得注意的是:理论上MOEA/D会比NSGA2快,但由于python语言的限制,MOEA/D的算法设计在python上的执行效率会大打折扣。这种情况同样存在于Matlab中。如果是纯C/C++或者Java等,MOEA/D的高效率才能够真正展现出来。当然,假如在C/C++或Java中采用细粒度的并行来执行进化,那么MOEA/D的执行效率是远远比不上NSGA2的,这是因为MOEA/D的算法设计天然地不支持细粒度的并行。

下面的代码7、代码8分别为用pymoo和geatpy的MOEA/D来优化ZDT1的代码:

代码7:pymoo_moead.py

from pymoo.algorithms.moead import MOEAD
from pymoo.factory import get_problem, get_reference_directions
from pymoo.optimize import minimize
import matplotlib.pyplot as plt
import time
problem = get_problem("ZDT1")
algorithm = MOEAD(get_reference_directions("das-dennis", 2, n_partitions=40),n_neighbors=40,decomposition="tchebi",prob_neighbor_mating=1
)
start = time.time()
res = minimize(problem, algorithm, termination=('n_gen', 300))
end = time.time()
plt.scatter(res.F[:, 0], res.F[:, 1], marker="o", s=10)
plt.grid(True)
plt.show()
print('耗时:', end - start, '秒')

代码8:geatpy_moead.py

# -*- coding: utf-8 -*-
import numpy as np
import geatpy as eaclass ZDT1(ea.Problem): # 继承Problem父类def __init__(self):name = 'ZDT1' # 初始化name(函数名称,可以随意设置)M = 2 # 初始化M(目标维数)maxormins = [1] * M # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)Dim = 30 # 初始化Dim(决策变量维数)varTypes = [0] * Dim # 初始化varTypes(决策变量的类型,0:实数;1:整数)lb = [0] * Dim # 决策变量下界ub = [1] * Dim # 决策变量上界lbin = [1] * Dim # 决策变量下边界(0表示不包含该变量的下边界,1表示包含)ubin = [1] * Dim # 决策变量上边界(0表示不包含该变量的上边界,1表示包含)# 调用父类构造方法完成实例化ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)def aimFunc(self, pop): # 目标函数Vars = pop.Phen # 得到决策变量矩阵ObjV1 = Vars[:, 0]gx = 1 + 9 * np.sum(Vars[:, 1:], 1) / (self.Dim - 1)hx = 1 - np.sqrt(np.abs(ObjV1) / gx) # 取绝对值是为了避免浮点数精度异常带来的影响ObjV2 = gx * hxpop.ObjV = np.array([ObjV1, ObjV2]).T # 把结果赋值给ObjVif __name__ == "__main__":"""================================实例化问题对象============================="""problem = ZDT1()          # 生成问题对象"""==================================种群设置================================"""Encoding = 'RI'           # 编码方式NIND = 40                 # 种群规模Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders) # 创建区域描述器population = ea.Population(Encoding, Field, NIND) # 实例化种群对象(此时种群还没被初始化,仅仅是完成种群对象的实例化)"""================================算法参数设置==============================="""myAlgorithm = ea.moea_MOEAD_templet(problem, population) # 实例化一个算法模板对象`myAlgorithm.Ps = 1.0 # (Probability of Selection)表示进化时有多大的概率只从邻域中选择个体参与进化myAlgorithm.MAXGEN = 300  # 最大进化代数myAlgorithm.logTras = 0  # 设置每多少代记录日志,若设置成0则表示不记录日志myAlgorithm.verbose = True  # 设置是否打印输出日志信息myAlgorithm.drawing = 1  # 设置绘图方式(0:不绘图;1:绘制结果图;2:绘制目标空间过程动画;3:绘制决策空间过程动画)"""==========================调用算法模板进行种群进化=========================调用run执行算法模板,得到帕累托最优解集NDSet以及最后一代种群。NDSet是一个种群类Population的对象。NDSet.ObjV为最优解个体的目标函数值;NDSet.Phen为对应的决策变量值。详见Population.py中关于种群类的定义。"""[NDSet, population] = myAlgorithm.run()  # 执行算法模板,得到非支配种群以及最后一代种群NDSet.save()  # 把非支配种群的信息保存到文件中"""==================================输出结果=============================="""print('用时:%f 秒' % myAlgorithm.passTime)

上面代码8调用了Geatpy内置的MOEA/D算法模板,见链接:

https://github.com/geatpy-dev/geatpy/blob/master/geatpy/templates/moeas/moead/moea_MOEAD_templet.py

代码7、8的运行结果如下:

这里比较奇怪的是pymoo的基于切比雪夫法的MOEA/D经常会出现一个很差的解,如上面左图的最左边的点,应该是该版本(0.4.0)有BUG。

3.3 更多的比较

下面加大计算规模,比较deap、pymoo和geatpy在大规模种群下的表现。

先将代码1、2、3中种群规模调整为500,进化代数依旧是300,运行结果如下:

再将代码2、3、4中种群规模调整为496,进化代数依旧是500,运行结果如下:

更多比较结果见下表,这里增添4个经典的EA框架/平台:Pygmo、Pagmo、JMetal和Platemo来共同参与对比,各实验参数均保持一致。其中Pygmo的底层是Pagmo,顶层用Python代码驱动;Pagmo是纯C++的EA框架;JMetal是Java的EA框架;Platemo是Matlab的MOEA实验与测试平台。

此外需要注明的是本次实验中除了Pymoo和Platemo在大规模计算下会自动使用CPU并行计算外,其余框架的测试均不会自动使用CPU进行并行计算。其中Pagmo可以利用C++的Eigen矩阵库来加速进化计算,但众所周知Eigen速度不及Matlab的MKL矩阵库,且鉴于实验环境配置复杂,这里就没有使用Pagmo的矩阵计算了。

在上面的实验中没有计算多目标优化的各项评价指标,但从图便可大致推断效果的好坏。其中性能大幅领先于其他的是国产的Geatpy和Platemo,这是因为Geatpy的种群进化过程采用的是Geatpy团队自主研发的超高性能矩阵库进行计算(注:由于Geatpy尚未提供一个统一的标签来设置是否采用内核并行,上面的实验为了图方便均没使用Geatpy的内核并行);而Platemo利用Matlab自带的高性能矩阵库MKL进行计算(在大规模下会智能地采用CPU并行计算)。

总结

本文比较了几种经典的进化算法框架/工具箱/平台在多目标优化上的表现。由于都具有权威性和各自的特色,这里我不作好坏对比,即不评价孰优孰劣,也不强行说推荐一定要用哪个,一切以自己习惯的编程语言为主。

不过如果读者有将进化计算与机器学习、神经网络、深度学习结合的需求,我在此建议使用Python的进化算法框架。用Python的话编程效率高,并且Python作为一种胶水语言,可以很容易嵌套进各种实际应用项目中,同时由上面的实验也可看出某些Python进化算法框架/工具箱也能达到乃至超越Java、C/C++的进化算法框架的执行速度(如Geatpy)。

最后有句话不得不说,尤其是中兴事件和华为事件之后,美国技术变得越来越不可靠了。我个人建议大家在以后的学习、工作、研究中逐步减小对matlab的依赖。因为matlab是闭源的,我们能否使用全靠美国总统一张嘴。日后哪天上台一个更加激进的美国总统,下令全面封禁我们使用matlab,那就麻烦大了。而Python、C/C++等是开源的,不惧怕被封禁。因此更加推荐使用它们来开发更多国产平台、框架。

算法代码_Python进化算法之多目标优化与代码实战相关推荐

  1. 智能优化算法之 差分进化算法

    差分进化算法原理 差分进化算法是一种随机的启发式搜索算法,简单易用,有较强的鲁棒性和全局搜索能力. 差分进化算法是一种自组织最小化方法,利用种群中随机选择的不同向量来干扰一个现有向量,种群中的每个向量 ...

  2. python常用代码_Python常用算法学习(3)(原理+代码)——最全总结

    1,什么是算法的时间和空间复杂度 算法(Algorithm)是指用来操作数据,解决程序问题的一组方法,对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但是在过程中消耗的资源和时间却会有很大 ...

  3. 基于多目标粒子群算法(MOPSO)的含风管柴储的微电网多目标优化——附代码

    目录 摘要: 1.微电网模型 2.微电网多目标优化调度的目标函数 2.1运行成本最小: 2.2风光消纳率最高: 3.微电网多目标优化调度的约束条件 3.1最大最小功率约束: 3.2最大最小功率约束: ...

  4. 量子进化算法c语言代码,量子进化算法.pdf

    第 卷 第 期 工 程 数 学 学 报 ] 年 月 文章编号 一 一 一 量子进化算法 杨淑媛 , 焦李成 , 刘 芳 西 安 电子科技大学智能信 息处理研 究所 , 西 安 摘 要 进化算法 是解 ...

  5. 差分进化算法python_差分进化算法Python实现

    本文you清华大学硕士大神金天撰写,欢迎大家转载,不过请保留这段版权信息,对本文内容有疑问欢迎联系作者微信:jintianiloveu探讨,多谢合作~ 导语 差分进化算法是一种寻优算法,提出时间比遗传 ...

  6. 差分进化算法_差分进化算法

    差分进化算法(Differential Evolution Algorithm,DE)是一种高效的全局优化算法.是一种模拟生物进化的随机模型,通过反复迭代,使得那些适应环境的个体被保存了下来.它的进化 ...

  7. 粒子群算法matlab多元,进化算法之粒子群算法和Matlab实现(多维)

    前面一篇文章介绍了遗传算法,这里再介绍一种进化算法,称为粒子群算法.同遗传算法类似,粒子群算法也是仿照了自然界的生物现象得到的.这种现象就是鸟群在某个未知空间内寻找食物这一思想. 鸟群通过自身经验和种 ...

  8. python数据分析算法调用_python数据分析算法(决策树2)CART算法

    CART(Classification And Regression Tree),分类回归树,,决策树可以分为ID3算法,C4.5算法,和CART算法.ID3算法,C4.5算法可以生成二叉树或者多叉树 ...

  9. 面向削峰填谷的电动汽车多目标优化调度策略 代码主要实现了考虑电动汽车参与削峰填谷的场景下,电动汽车充放电策略的优化,是一个多目标优化

    MATLAB代码:面向削峰填谷的电动汽车多目标优化调度策略 关键词:电动汽车 削峰填谷 多目标 充放电优化 仿真平台:MATLAB YALMIP+CPLEX 主要内容:代码主要实现了考虑电动汽车参与削 ...

最新文章

  1. 基础矩阵,本质矩阵,单应性矩阵讲解
  2. 第31届NIPS正式开幕,3240篇提交论文创历史新高,公布3篇最佳论文
  3. JS 总结之原型继承的几种方式
  4. 快速了解 MySQL 的性能优化
  5. 通过例子学习 Keystone - 每天5分钟玩转 OpenStack(19)
  6. mysql applier_MySQL推出Applier,可实时复制数据到Hadoop-阿里云开发者社区
  7. 使用PaddlePaddle.org工具构建PaddlePaddle文档
  8. 50个常用sql语句 网上流行的学生选课表的例子
  9. qtvs添加qchart_如何使用Qt Designer在窗体中插入QChartView?
  10. 基于matlab 的电力系统潮流仿真
  11. 地理数据处理之矢量数据
  12. E20-591考试必备资料分享
  13. 用光盘怎样重装电脑系统
  14. 双开乃至多开电脑微信的简单方法
  15. ubuntu中使用宋体和雅黑字体
  16. linux 挂载3t硬盘分区,Ubuntu挂载3T硬盘或大于2T磁盘的方法
  17. Conda各平台安装配置和使用Python环境(保姆级教程)
  18. python 节点关系图_在Python中如何分析和识别有向图关系(节点间)
  19. 阿里云主机遭受DDOS攻击IP不能使用如何更换弹性公网IP
  20. OpenCV各模块函数使用实例(5)--特征检测(Feature Detection)

热门文章

  1. 4 C++对C的加强之namespace命名空间
  2. unity 解决乱码_Unity3d中IOS应用出现乱码怎么办?
  3. OCR识别缺点_福利:OCR大全
  4. MySQL里面json_MySQL中的JSON
  5. 07_NoSQL数据库之Redis数据库:Redis的高级应用之事务处理、持久化操作、pub_sub、虚拟内存
  6. 13-jdbc分页+事务
  7. python 深度学习模型训练 多GPU下如何调用
  8. oracle触发器的测试,ORACLE触发器的测试
  9. dr.oracle素颜霜好用吗,treechada素颜霜好用吗_treechada素颜霜评测
  10. BM2 链表内指定区间反转