python拓展包之pymoo使用方法:多目标优化
一、pymoo的安装
pip安装
pip install -U pymoo
二、多目标优化的一般模式
一般来说,多目标优化具有几个受不等式和等式约束的目标函数。其目标是找到一组满足所有约束条件并尽可能好地处理其所有目标值的解决方案。问题的一般形式的定义为:
目标函数f(x)
不等式约束g(x)
等式约束和h(x)
变量x的上下约束
在下面,我们说明了一个具有两个约束条件的双目标优化问题。
它的可视化图像如下:
可以看到,它的最优解在(图中橙色区域)
下面我们在python中使用pymoo来实现上述问题公式。
三、pymoo处理多目标优化问题的格式
一、pymoo支能够处理目标函数最小化的优化问题
然而,在不失去一般性的情况下,一个应该最大化的目标可以乘以−1并最小化。
在上述优化问题中,我们需要将目标函数
max f2(x)
转化为
min -f2(x)
此外,所有的约束函数都需要表述为一个小于等于的约束。
我们需要将约束条件
g2(x)>=0
转化为
-g2(x)<=0
我们建议将约束标准化,使它们都同等重视。将约束条件的每一个因式的常数部分相乘得到一个约束常量,
对于g1(x),约束常量为 2 ⋅ (−0.1) ⋅(−0.9) = 0.18
对于g2(x),约束常量为 20 ⋅ (−0.4) ⋅ (−0.6) = 4.8
最后,将使用pymoo进行优化的优化问题定义为:
四、python中pymoo的使用
接下来,用Python实现该优化问题。pymoo中的每个优化问题都必须从Problem类中继承。
首先,通过调用super()函数,可以初始化问题属性。
from pymoo.model.problem import Problem
class MyProblem(Problem):
def __init__(self,cost_matrix: list, ):
self.cost_matrix = cost_matrix
super().__init__(n_var=2, # 变量数
n_obj=2, # 目标数
n_constr=2, # 约束数
xl=np.array([-2, -2]), # 变量下界
xu=np.array([2, 2]), # 变量上界
)
def _evaluate(self, x, out, *args, **kwargs):
# 定义目标函数
f1 = x[:, 0]**2 + x[:, 1]**2 # x1放在x的第0列,x2放在x的第一列
f2 = (x[:, 0] - 1)**2 + x[:, 1]**2
# 定义约束条件
g1 = 2*(x[:, 0] - 0.1) * (x[:, 0] - 0.9) / 0.18
g2 = -20*(x[:, 0] - 0.4) * (x[:, 0] - 0.6) / 4.8
# todo
out["F"] = np.column_stack([f1, f2])
out["G"] = np.column_stack([g1, g2])
super初始化中
变量数(n_var)
目标(n_obj)
约束(n_constr)
此外,下(xl)和上变量边界(xu)作为NumPy数组提供。
_evaluate函数中
定义目标函数以键“F”添加到字典中
定义约束条件以键“G”添加到字典中
五、选择优化算法
在pymoo中,需要创建一个算法对象来进行优化。对于每种算法,都有API文档,通过提供不同的参数,可以以即插即用的方式定制算法。一般来说,选择一个合适的优化问题的算法本身就是一个挑战。当事先知道问题特征时,我们建议通过定制操作符使用这些特征。然而,在我们的情况下,优化问题相当简单,但应该考虑有两个目标和两个约束的方面。因此,我们决定使用NSGA-II[12]及其默认配置,并进行少量修改。我们选择了40人的人口规模,但我们没有产生相同数量的后代,而是每一代只创造10个后代。这是NSGA-II的一个稳态变体,对于比较简单的优化问题,可以提高收敛性,存在局部帕雷托前沿。此外,我们启用了一个重复的检查,以确保交配产生的后代,以及与现有种群关于其可变向量的不同。
使用所提供的参数调用NSGA2的构造函数并返回一个初始化的算法对象。
from pymoo.algorithms.nsga2 import NSGA2
from pymoo.factory import get_sampling, get_crossover, get_mutation
from pymoo.optimize import minimize
from example import MyProblem
# 定义遗传算法
algorithm = NSGA2(
pop_size=40,
n_offsprings=10,
sampling=get_sampling("real_random"),
crossover=get_crossover("real_sbx", prob=0.9, eta=15),
mutation=get_mutation("real_pm", eta=20),
eliminate_duplicates=True
)
接下来,我们使用初始化的算法对象来优化所定义的问题。因此,将具有实例问题和算法的最小化函数作为参数。此外,我们提供了运行40代算法的终止标准,这将导致40个+40×10=440函数评估。此外,我们定义了一个随机种子,以确保重现性,并使冗长标志能够看到每一代的打印输出。该方法返回一个结果对象,其中包含该算法找到的非支配解集。
res = minimize(MyProblem(),
algorithm,
('n_gen', 40),
seed=1,
verbose=True
)
打印输出
D:\Soft\Anaconda3\envs\datadeal\python.exe D:/WorkSpace/pymooto/alg_example.py
=====================================================================================
n_gen | n_eval | cv (min) | cv (avg) | n_nds | eps | indicator
=====================================================================================
1 | 40 | 0.00000E+00 | 2.36399E+01 | 1 | - | -
2 | 50 | 0.00000E+00 | 1.15486E+01 | 1 | 0.00000E+00 | f
3 | 60 | 0.00000E+00 | 5.277918607 | 1 | 0.00000E+00 | f
4 | 70 | 0.00000E+00 | 2.406068542 | 2 | 1.000000000 | ideal
5 | 80 | 0.00000E+00 | 0.908316880 | 3 | 0.869706146 | ideal
6 | 90 | 0.00000E+00 | 0.264746300 | 3 | 0.00000E+00 | f
7 | 100 | 0.00000E+00 | 0.054063822 | 4 | 0.023775686 | ideal
8 | 110 | 0.00000E+00 | 0.003060876 | 5 | 0.127815454 | ideal
9 | 120 | 0.00000E+00 | 0.00000E+00 | 6 | 0.004633441 | ideal
10 | 130 | 0.00000E+00 | 0.00000E+00 | 6 | 0.062649485 | nadir
11 | 140 | 0.00000E+00 | 0.00000E+00 | 7 | 0.026769546 | f
12 | 150 | 0.00000E+00 | 0.00000E+00 | 7 | 0.047566009 | f
13 | 160 | 0.00000E+00 | 0.00000E+00 | 7 | 0.000678729 | f
14 | 170 | 0.00000E+00 | 0.00000E+00 | 9 | 0.043888006 | ideal
15 | 180 | 0.00000E+00 | 0.00000E+00 | 10 | 0.013506283 | f
16 | 190 | 0.00000E+00 | 0.00000E+00 | 11 | 0.052719639 | ideal
17 | 200 | 0.00000E+00 | 0.00000E+00 | 14 | 0.008531768 | f
18 | 210 | 0.00000E+00 | 0.00000E+00 | 16 | 0.011165361 | f
19 | 220 | 0.00000E+00 | 0.00000E+00 | 18 | 0.004040787 | f
20 | 230 | 0.00000E+00 | 0.00000E+00 | 20 | 0.001296614 | f
21 | 240 | 0.00000E+00 | 0.00000E+00 | 22 | 0.000911500 | f
22 | 250 | 0.00000E+00 | 0.00000E+00 | 24 | 0.003461153 | nadir
23 | 260 | 0.00000E+00 | 0.00000E+00 | 27 | 0.004261571 | ideal
24 | 270 | 0.00000E+00 | 0.00000E+00 | 28 | 0.029927769 | nadir
25 | 280 | 0.00000E+00 | 0.00000E+00 | 28 | 0.001150567 | f
26 | 290 | 0.00000E+00 | 0.00000E+00 | 30 | 0.002181257 | f
27 | 300 | 0.00000E+00 | 0.00000E+00 | 33 | 0.006887739 | nadir
28 | 310 | 0.00000E+00 | 0.00000E+00 | 36 | 0.001237981 | f
29 | 320 | 0.00000E+00 | 0.00000E+00 | 37 | 0.000102543 | f
30 | 330 | 0.00000E+00 | 0.00000E+00 | 38 | 0.000747546 | f
31 | 340 | 0.00000E+00 | 0.00000E+00 | 40 | 0.005157126 | nadir
32 | 350 | 0.00000E+00 | 0.00000E+00 | 40 | 0.00000E+00 | f
33 | 360 | 0.00000E+00 | 0.00000E+00 | 40 | 0.002321697 | f
34 | 370 | 0.00000E+00 | 0.00000E+00 | 40 | 0.016674348 | nadir
35 | 380 | 0.00000E+00 | 0.00000E+00 | 40 | 0.000760584 | f
36 | 390 | 0.00000E+00 | 0.00000E+00 | 40 | 0.016011922 | nadir
37 | 400 | 0.00000E+00 | 0.00000E+00 | 40 | 0.000333129 | f
38 | 410 | 0.00000E+00 | 0.00000E+00 | 40 | 0.000980877 | f
39 | 420 | 0.00000E+00 | 0.00000E+00 | 40 | 0.001264690 | f
40 | 430 | 0.00000E+00 | 0.00000E+00 | 40 | 0.001571863 | f
关于pymoo的中文使用教程很少,本文内容大都翻译自pymoo的官方使用说明。