快速入门:NSGA-II 求解 DTLZ2#

本例在 12 维决策空间内最小化 DTLZ2 的 3 个目标,决策变量范围为 [0, 1]。 程序自动选择可用的 CUDA 设备或 CPU,固定随机种子,运行初始化和 50 次迭代, 最后从当前种群中提取非支配解并计算 IGD。

完整代码#

"""Optimize DTLZ2 with NSGA-II and report the current population's IGD."""

import torch

from evomo.algorithms import NSGA2
from evomo.metrics import igd
from evomo.operators.selection import non_dominate_rank
from evomo.problems.numerical import DTLZ2
from evomo.workflows import UnifiedWorkflow


def main():
    device = "cuda" if torch.cuda.is_available() else "cpu"
    torch.set_default_device(device)
    torch.manual_seed(42)

    problem = DTLZ2(d=12, m=3)
    algorithm = NSGA2(
        pop_size=100,
        n_objs=problem.m,
        lb=torch.zeros(problem.d, device=device),
        ub=torch.ones(problem.d, device=device),
        device=device,
    )
    workflow = UnifiedWorkflow(algorithm, problem, device=device)
    workflow.init_step()
    for _ in range(50):
        workflow.step()

    fitness = workflow.algorithm.fit
    finite = torch.isfinite(fitness).all(dim=1)
    population = workflow.algorithm.pop[finite]
    fitness = fitness[finite]
    if fitness.shape[0] == 0:
        raise RuntimeError("No finite objective values; check the problem evaluation")
    front = non_dominate_rank(fitness) == 0
    pareto_solutions = population[front]
    pareto_fitness = fitness[front]
    reference_front = problem.pf().to(device=fitness.device, dtype=fitness.dtype)

    print(f"Device: {device}")
    print(f"Population: {tuple(workflow.algorithm.pop.shape)}")
    print(f"Non-dominated solutions: {pareto_solutions.shape[0]}")
    print(f"IGD: {igd(pareto_fitness, reference_front).item():.6f}")


if __name__ == "__main__":
    main()

源码安装后,从仓库根目录运行:

python docs/examples/quickstart.py

理解结果#

workflow.algorithm.pop 保存决策变量,workflow.algorithm.fit 保存对应目标值。 每个目标值行对应同一行的候选解。算法内部以最小化为约定;本例没有改变优化方向, 所以这些值也是 DTLZ2 的原始目标值。

non_dominate_rank(fitness) == 0 提取当前解集的第一非支配前沿。筛选在迭代结束之后执行, 不放进编译的 step 中。problem.pf() 提供问题的参考前沿采样;IGD 越小表示参考点 平均更接近求得的解集。不同硬件、版本和随机过程可能得到不同的数值,本文不规定固定分数。

本例的 50 次迭代用于展示接口,不构成收敛保证或算法性能结论。

下一步#