✨ 注意:本文使用Pymoo版本为0.6.0。
本文实现了一个具有两个约束的双目标优化问题,其数学定义如下所示:
m i n i m i z e f 1 ( x ) = 100 ( x 1 2 + x 2 2 ) m a x i m i z e f 2 ( x ) = − ( x 1 − 1 ) 2 − x 2 2 s u b j e c t t o g 1 ( x ) = 2 ( x 1 − 0.1 ) ( x 1 − 0.9 ) ≤ 0 g 2 ( x ) = 20 ( x 1 − 0.4 ) ( x 1 − 0.6 ) ≥ 0 − 2 ≤ x 1 ≤ 2 − 2 ≤ x 2 ≤ 2 x ∈ R \begin{equation} \begin{aligned} {\rm minimize} \enspace &f_1(x) = 100(x_1^2 + x_2^2) \\ {\rm maximize} \enspace &f_2(x) = -(x_1 - 1)^2 - x_2^2 \\ {\rm subject \, to} \enspace &g_1(x) = 2(x_1 - 0.1)(x_1 - 0.9) \leq 0 \\ &g_2(x) = 20(x_1 - 0.4)(x_1 - 0.6) \geq 0 \\ &-2 \leq x_1 \leq 2 \\ &-2 \leq x_2 \leq 2 \\ &x \in \R \end{aligned} \end{equation} minimizemaximizesubjecttof1(x)=100(x12+x22)f2(x)=−(x1−1)2−x22g1(x)=2(x1−0.1)(x1−0.9)≤0g2(x)=20(x1−0.4)(x1−0.6)≥0−2≤x1≤2−2≤x2≤2x∈R
上面的问题并不是最优化问题的规范模型,为了使用Pymoo进行问题的求解,我们需要首先进行规范化处理。
最优化问题定义:
对于大部分的最优化框架,我们需要将问题的目标函数规范化为统一的最小化或最大化问题,同时约束也需要规范化为 ≤ \leq ≤或 ≥ \geq ≥的约束。在Pymoo中,每一个目标函数是规范化为最小化问题,约束需要规范化为 ≤ 0 \leq 0 ≤0的约束。
对公式(1)进行规范化处理得到如下的规范化最优化数学表达式:
m i n i m i z e f 1 ( x ) = 100 ( x 1 2 + x 2 2 ) m i n i m i z e f 2 ( x ) = ( x 1 − 1 ) 2 + x 2 2 s u b j e c t t o g 1 ( x ) = 2 ( x 1 − 0.1 ) ( x 1 − 0.9 ) ≤ 0 g 2 ( x ) = − 20 ( x 1 − 0.4 ) ( x 1 − 0.6 ) ≤ 0 − 2 ≤ x 1 ≤ 2 − 2 ≤ x 2 ≤ 2 x ∈ R \begin{equation} \begin{aligned} {\rm minimize} \enspace &f_1(x) = 100(x_1^2 + x_2^2) \\ {\rm minimize} \enspace &f_2(x) = (x_1 - 1)^2 + x_2^2 \\ {\rm subject \, to} \enspace &g_1(x) = 2(x_1 - 0.1)(x_1 - 0.9) \leq 0 \\ &g_2(x) = - 20(x_1 - 0.4)(x_1 - 0.6) \leq 0 \\ &-2 \leq x_1 \leq 2 \\ &-2 \leq x_2 \leq 2 \\ &x \in \R \end{aligned} \end{equation} minimizeminimizesubjecttof1(x)=100(x12+x22)f2(x)=(x1−1)2+x22g1(x)=2(x1−0.1)(x1−0.9)≤0g2(x)=−20(x1−0.4)(x1−0.6)≤0−2≤x1≤2−2≤x2≤2x∈R
使用Pymoo求解最优化问题通常包括如下几个步骤:
这里使用element-wise
问题定义方式来定义最优化问题(2)。在继承了Pymoo中ElementwiseProblem
类的基础上,自定义最优化问题时我们需要设置一些相应的类属性如下所示:
n_var
:自变量个数,本文设置为2
;n_obj
:目标个数,本文设置为2
;n_ieq_constr
:不等式约束函数个数,本文设置为2
;xl
/xu
:自变量的上/下限。优化算法的计算过程是由_evaluate
函数实现,该函数包括两个接口x
与out
,对于element-wise
实现过程,它们功能如下所示:
x
:长度为n_var
的一维Numpy数组,它表示要求解问题的一个解;out
:字典格式数据,长度为n_obj
的Numpy数组列表的目标值写入到out["F"]
中,长度为n_ieq_constr
的约束函数写入到out["G"]
中。在Pymoo定义最优化问题(1)的代码如下所示:import numpy as np
from pymoo.core.problem import ElementwiseProblem
class MyProblem(ElementwiseProblem):
def __init__(self):
super().__init__(
n_var=2,
n_obj=2,
n_ieq_constr=2,
xl=np.array([-2, -2]),
xu=np.array([2, 2])
)
def _evaluate(self, x, out, *args, **kwargs):
f1 = 100 * (x[0]**2 + x[1]**2)
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
out["F"] = [f1, f2]
out["G"] = [g1, g2]
problem = MyProblem()
✨ 注意,对于
ElementwiseProblem
问题定义方法的说明:
ElementwiseProblem
问题定义方法意味着为每一个解x
调用一次_evaluate
函数。
Pymoo中封装了很多现成的优化问题求解算法,这些算法都是基于面向对象的方法/因此,我们使用它们的时候必须实例化一个算法对象,这里以NSGA2遗传算法为例。
Pymoo中对于NSGA2方法提供了很多可配置参数,本文用到的遗传算法参数及其配置如下所示:
pop_size
:种群规模,对于简单的问题设置为40
;n_offsprings
:每一代的后代个数,这里设置为10
;sampling
:抽样算子,本文设置为随机抽样方法;crossover
:交叉算子,本文选用模拟二元交叉(SBX, Simulate Binary Crossover);mutation
:突变算子,本文选用多项式突变方式eliminate_duplicates
:种群个体重复检测,这里设置为True
。Pymoo中初始化NSGA2的代码如下所示:
from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.operators.crossover.sbx import SBX
from pymoo.operators.mutation.pm import PM
from pymoo.operators.sampling.rnd import FloatRandomSampling
algorithm = NSGA2(
pop_size=40,
n_offsprings=10,
sampling=FloatRandomSampling(),
crossover=SBX(prob=0.9, eta=15),
mutation=PM(eta=20),
eliminate_duplicates=True
)
✨ 注意,对于优化算法参数设置的说明:
Pymoo中有很多优化计算方法,它们都有自己的默认超参数。通常我们可以使用默认的超参数,但是当算法能取得良好的收敛性的时候,我们可以很方便的配置其超参数。但是,我们需要深入理解每个算法的实现原理,并能够熟练应用Pymoo的各个模块。
常见的终止准则是限制函数的迭代次数。此外,一些算法还实现了自己的终止算法,比如,单纯形(simplex)退化时的Nelder Mead算法,或使用CMA-ES算法。本文是一个简单的问题,所以这里设置终止条件为40次算法迭代,代码如下所示:
from pymoo.termination import get_termination
termination = get_termination("n_gen", 40)
✨ **注意,算法收敛性分析(Convergence Analysis)的说明:**收敛性分析能够显示算法在某个时刻算法取得的多大的优化程度。
此时就可以使用上面定义的算法与终止条件对问题进行优化了。默认情况下minimize方法对算法和终止对象进行深度复制,以确保它们在函数调用的过程中不会被修改。当算法终止时,minimize函数会返回一个Result对象,代码如下所示:
from pymoo.optimize import minimize
res = minimize(
problem,
algorithm,
save_hastory=True,
verbose=True
)
X = res.X # 最优解集
F = res.F # 最优解
import matplotlib.pyplot as plt
# 获得自变量而区间
xl, xu = problem.bounds()
# 绘制结果
plt.figure(figsize=(7, 5))
# 绘制最优解集的散点图
plt.scatter(X[:, 0], X[:, 1], s=30, facecolors="none", edgecolors="orange")
# 设置x、y周现实区间
plt.xlim(xl[0], xu[0])
plt.ylim(xl[1], xu[1])
plt.title("Design Space")
plt.show()
代码执行结果如下图所示:
plt.figure(figsize=(7, 5))
plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue')
plt.title("Objective Space")
plt.show()
代码执行结果如下图所示: