多目标优化简单介绍:

我们经常会使用机器学习、深度学习等训练出一个模型,来对目标进行预测。有时,我们还希望通过更改输入来优化我们的目标。比如通过“早上几点起床、坐什么公交车、吃什么早饭等等”来预测“到达学校所花费的时间”,其中“到达学校所花费的时间”是一个单一的目标,通常我们希望这个数字越小越好。这是单目标优化问题,我们只需要让这个数字达到最小值即可。
但生活中的问题往往不只一个目标。比如,当我们买电脑时目标至少有两个:
1.价格低
2.性能高
这两个目标可以说是冲突的,不可能同时达到最优,也就是说我们不可能花最少的钱购买到性能最高的电脑。但价格高且性能低的电脑我们一定会舍去。我们会希望在同等价格中找到性能最好的电脑,在同等性能中找到价格最低的电脑,比如在5000元价位找到性能最好的电脑A、6000元价位找到性能最好的电脑B、…,这些方案没有谁比谁好,他们都是最优解,即为帕累托最优解(非支配解)。这些方案组成了一个解集,名为帕累托前沿。我们的多目标优化就是要找到这个解集。


Pymoo

Pymoo是一个开源的多目标优化库,其中内置了大量的问题和算法。我们可以自定义目标函数、约束条件等。我们的目标函数往往是一个复杂的模型,下面我会使用pytorch定义一个简单的模型用于演示。
首先直接pip下载pymoo

pip install pymoo

引入必需的库

from pymoo.core.problem import ElementwiseProblem
import numpy as np
import torch.nn as nn
from torch.nn import Sequential
from torch.nn import Conv1d,Linear
import torch
from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.operators.sampling.rnd import FloatRandomSampling
from pymoo.operators.crossover.sbx import SBX
from pymoo.operators.mutation.pm import PM
from pymoo.termination import get_termination
from pymoo.optimize import minimize
from pymoo.visualization.scatter import Scatter

定义一个测试模型

class test_model(nn.Module):
    def __init__(self):
        super().__init__()
    def forward(self,x):
        output = [0]*2
        output[0] = 100 * (x[0]**2 + x[1]**2) #这里也就是我们的一个目标函数
        output[1] = (x[0]-1)**2 + x[1]**2
        return output

模型保存

model = test_model()
torch.save(model,f'test.pth')

模型加载

model1 = torch.load('test.pth')

这里可以看一下输出结果,验证一下模型是否可用

x = torch.tensor((1,2,3),dtype=torch.float16)
model1(x)[0].item()

输出结果

500.0

然后开始多目标优化的工作,定义输入输出、约束、目标函数和问题等。

class My_question(ElementwiseProblem):
    def __init__(self):
        # 参数初始化
        super().__init__(
            n_var = 3,  #输入变量个数
            n_obj = 2,  #目标函数个数
            n_ieq_constr=2, #约束条件个数
            xl = np.array([-2,-3,-5]), #输入变量上界
            xu = np.array([3,5,2])     #输入变量下界
        )
    def _evaluate(self,x,out,*args, **kwargs):
        # 定义目标函数和问题
        f1 = model1(x)[0]
        f2 = model1(x)[1]
        # 定义约束条件
        g1 = 2*(x[0]-0.1) * (x[0]-0.9) / 0.18
        g2 = -20*(x[0]-0.4) * (x[0]-0.6) / 4.8
        定义目标函数以键“F”添加到字典中,定义约束条件以键“G”添加到字典中
        out["F"] = [f1,f2]
        out["G"] = [g1,g2]

实例化这个问题

problem = My_question()

定义算法,这里使用pymoo自带的NSGA2算法。

algorithm = NSGA2(
    pop_size=40,
    n_offsprings=10,
    sampling=FloatRandomSampling(),
    crossover=SBX(prob=0.9, eta=15),
    mutation=PM(eta=20),
    eliminate_duplicates=True
)

定义终止条件(运行到何时停止),这里定义为运行40次停止

termination = get_termination("n_gen", 40)

然后就是最后一步,优化计算

# 优化计算
res = minimize(
		problem,
		algorithm,
		termination,
        seed=1,
		save_hastory=True,
		verbose=True
)
X = res.X
F = res.F

输出结果

==========================================================================================
n_gen  |  n_eval  | n_nds  |     cv_min    |     cv_avg    |      eps      |   indicator  
==========================================================================================
     1 |       40 |      2 |  0.000000E+00 |  2.126352E+01 |             - |             -
     2 |       50 |      2 |  0.000000E+00 |  1.112156E+01 |  0.000000E+00 |             f
     3 |       60 |      2 |  0.000000E+00 |  4.2405589724 |  0.000000E+00 |             f
     4 |       70 |      2 |  0.000000E+00 |  1.5966515139 |  0.000000E+00 |             f
     5 |       80 |      2 |  0.000000E+00 |  0.3928333408 |  0.000000E+00 |             f
     6 |       90 |      2 |  0.000000E+00 |  0.0711190036 |  0.1893979501 |         ideal
     7 |      100 |      3 |  0.000000E+00 |  0.0289906074 |  0.5239945605 |         ideal
     8 |      110 |      4 |  0.000000E+00 |  0.0039635944 |  0.0064057292 |             f
     9 |      120 |      6 |  0.000000E+00 |  0.000000E+00 |  0.1316947158 |         ideal
    10 |      130 |      7 |  0.000000E+00 |  0.000000E+00 |  0.0200091530 |             f
    11 |      140 |      8 |  0.000000E+00 |  0.000000E+00 |  0.0122675207 |             f
    12 |      150 |     11 |  0.000000E+00 |  0.000000E+00 |  0.0246851126 |         ideal
    13 |      160 |     14 |  0.000000E+00 |  0.000000E+00 |  0.0228325772 |         ideal
    14 |      170 |     12 |  0.000000E+00 |  0.000000E+00 |  0.0100238709 |             f
    15 |      180 |     13 |  0.000000E+00 |  0.000000E+00 |  0.0209608342 |             f
    16 |      190 |     12 |  0.000000E+00 |  0.000000E+00 |  0.0887728172 |         ideal
    17 |      200 |     10 |  0.000000E+00 |  0.000000E+00 |  0.0152945638 |             f
    18 |      210 |     10 |  0.000000E+00 |  0.000000E+00 |  0.000000E+00 |             f
    19 |      220 |     11 |  0.000000E+00 |  0.000000E+00 |  0.0127617944 |         ideal
    20 |      230 |     12 |  0.000000E+00 |  0.000000E+00 |  0.0028883428 |         ideal
    21 |      240 |     13 |  0.000000E+00 |  0.000000E+00 |  0.0047200924 |             f
    22 |      250 |     14 |  0.000000E+00 |  0.000000E+00 |  0.0009336431 |             f
    23 |      260 |     18 |  0.000000E+00 |  0.000000E+00 |  0.0133784203 |         nadir
    24 |      270 |     20 |  0.000000E+00 |  0.000000E+00 |  0.0003146637 |             f
    25 |      280 |     23 |  0.000000E+00 |  0.000000E+00 |  0.0070939034 |             f
    26 |      290 |     27 |  0.000000E+00 |  0.000000E+00 |  0.0145158246 |         nadir
    27 |      300 |     29 |  0.000000E+00 |  0.000000E+00 |  0.0014772868 |             f
    28 |      310 |     28 |  0.000000E+00 |  0.000000E+00 |  0.0042634083 |         ideal
    29 |      320 |     30 |  0.000000E+00 |  0.000000E+00 |  0.0052194968 |         nadir
    30 |      330 |     32 |  0.000000E+00 |  0.000000E+00 |  0.0038116530 |             f
    31 |      340 |     38 |  0.000000E+00 |  0.000000E+00 |  0.0075633821 |         ideal
    32 |      350 |     39 |  0.000000E+00 |  0.000000E+00 |  0.0013810785 |             f
    33 |      360 |     40 |  0.000000E+00 |  0.000000E+00 |  0.0111958927 |         ideal
    34 |      370 |     40 |  0.000000E+00 |  0.000000E+00 |  0.0006297348 |             f
    35 |      380 |     40 |  0.000000E+00 |  0.000000E+00 |  0.0028259690 |             f
    36 |      390 |     40 |  0.000000E+00 |  0.000000E+00 |  0.0006139626 |             f
    37 |      400 |     40 |  0.000000E+00 |  0.000000E+00 |  0.0008831560 |             f
    38 |      410 |     40 |  0.000000E+00 |  0.000000E+00 |  0.0018102019 |             f
    39 |      420 |     40 |  0.000000E+00 |  0.000000E+00 |  0.0027017664 |             f
    40 |      430 |     40 |  0.000000E+00 |  0.000000E+00 |  0.0006390855 |             f

至此我们已经完成了一个完整的多目标优化计算。
下面对结果进行可视化

plot = Scatter()
plot.add(problem.pareto_front(), plot_type="line", color="black", alpha=0.7)
plot.add(res.F, color="red")
plot.show()

多目标元启发算法 python_约束条件

初次学习,如有理解错误或不到位的地方,欢迎指正!