快速入门: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 次迭代用于展示接口,不构成收敛保证或算法性能结论。