ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

NSGA3多目标优化:Matlab实现参考点生成与小生境选择

NSGA3多目标优化:Matlab实现参考点生成与小生境选择 简介多目标优化NSGA3代码是一套基于Matlab的遗传算法实现面向需同时权衡多个冲突目标的研究者与工程人员可应用于工程设计、资源分配、投资组合优化等实际决策场景。代码共10个文件全部为M脚本压缩包仅11KB涵盖种群初始化、快速非支配排序、拥挤距离计算、环境选择、交叉变异以及IGD指标评估等核心模块NSGAIII_main.m可直接运行演示完整寻优流程各模块接口清晰适合教学演示与科研复现。已有1165人学习下载。通过这套精简代码读者既能系统理解NSGA-III基于参考点的分层选择、精英保留与多样性维护机制也可以借助Matlab的绘图能力观察Pareto前沿的收敛与分布在此基础上针对具体问题修改适应度函数或遗传算子开展二次开发与算法对比研究。1. NSGA3多目标优化到底解决什么问题先弄清它和NSGA2的分水岭做多目标优化的都知道NSGA2 在三目标以内表现很好一旦目标数到了五个、八个结果就会出问题非支配解占比迅速上升拥挤距离在某些方向失效最终选出来的解挤在一个犄角旮旯Pareto前沿中部大片空白。NSGA3 的出发点就是把“多样性维持”从距离度量换成参考点度量。先用参考点均匀铺开搜索方向再用小生境计数保证每个方向都有解入选这比拥挤距离更适应高维目标空间也是现在研究多目标优化绕不开的基线算法。这篇文章按“机制 - 实现 - 调参 - 验证”的顺序讲清楚 NSGA3 的代码逻辑重点放在参考点生成、归一化、关联这三个关键算子的 Matlab 实现以及跑 DTLZ2 测试问题时你知道自己有没有写对的判断手段。适合正在用 Matlab 写实验、复现论文算法、或者在 PlatEMO 里折腾参数的研究生和工程师阅读也适合从 NSGA2 迁移过来、想搞清楚两代算法代码差异的读者。2. 把NSGA3拆开参考点生成、归一化与小生境选择2.1 拥挤距离失效后NSGA3用参考点重新定义多样性NSGA2 的选择压力来自两部分非支配排序负责收敛拥挤距离负责多样性。三目标以下拥挤距离计算出来的“邻居密度”还比较可信目标数超过四个解在目标空间变得稀疏每个解周围的邻居都很少距离差不多的解大量并列拥挤距离比较失去区分度。更麻烦的是拥挤距离天然偏向目标空间的中间区域边界解容易被挤掉而高维问题的 Pareto 前沿往往很大一部分在边界附近。NSGA3 的设计思路是我先在目标空间放一批均匀分布的参考点这批参考点就代表了所有值得搜索的方向每个个体在归一化后找到自己最近的那根参考线最后做环境选择时优先从关联个体最少的参考点里挑人。这样多样性不再依赖解与解之间的距离而是依赖解在“方向上”的覆盖程度。说直白点NSGA2 是看周围有没有人NSGA3 是看你这个方向有没有人。2.2 参考点怎么生成Das-Dennis 方法与 Matlab 实现NSGA3 参考点最常见的生成方法是 Das-Dennis 构造。它在三维目标空间里相当于在一个单纯形上打网格每个参考点坐标是 M 维、每个维度取值 0 到 p 的整数且所有维度之和等于 p然后统一除以 p 做归一化使每个参考点落在 x1x2...xM1 这个超平面上。下面这个递归实现可以直接存成GenerateReferencePoints.m使用function Z GenerateReferencePoints(M, p) % GenerateReferencePoints: Das-Dennis 方法生成参考点Matlab 教学版 % 输入 M: 目标维度 % p: 每个维度方向的划分份数 % 输出 Z: 参考点矩阵大小为 C(Mp-1, p) x M Z EnumCoef(M, p); Z Z ./ p; end function C EnumCoef(M, p) if M 1 C p; return; end C []; for i 0:p sub EnumCoef(M - 1, p - i); n size(sub, 1); C [C; i * ones(n, 1), sub]; end end这段代码的主干是递归枚举“M 个非负整数和为 p”的全部组合。i是当前维度取的份数p-i是剩下 M-1 个维度需要分配的份数i * ones(n,1)把当前维度的取值扩展成 n 行再和子问题的组合横向拼接。调用GenerateReferencePoints(3, 12)会得到 91 个参考点每一行三个数相加等于 1比如[0, 0.5, 0.5]和[1/12, 5/12, 6/12]都是合法参考点。参考点数量随 p 增长极快这是第一个要记住的数量级。M3、p12 时尚且只有 91 个点M5 时如果 p 还取 12参考点数量会到 C(16,12)1820关联计算的开销会明显上升小生境计数也会变得非常破碎。下表给出常见配置下的参考点数量方便估算种群规模该设多大目标维度 M划分份数 p参考点数量 C(Mp-1, p)典型用途3628做机制验证的小规模实验31291NSGA3 论文和 DTLZ 测试最常用配置320231追求更密的前沿覆盖58495五目标实验常用上限84165高维目标时的保守选择p 增大一档参考点数量可能翻几倍这是 NSGA3 在高维目标空间里代价最高的部分。实际实验里如果 91 个参考点不够覆盖前沿与其把 p 提到 20不如用后续章节会讲的两层参考点生成策略让边界和内部都有点又不至于把参考点总数撑爆。2.3 归一化为什么不能省把高维目标拉到同一尺度参考点是在单位单纯形上生成的而真实目标函数值的量纲和范围可能完全不在一个数量级第一个目标可能是 0.1 量级第二个目标可能是 10000 量级。如果不做归一化关联计算里垂直距离会被量纲大的目标主导那些在量纲小的目标上分布均匀的解会被错误地归到同一个参考点小生境计数完全失真。NSGA3 的论文里归一化分三步用理想点 z_min 做平移用 ASF 函数找每个目标的极值点用极值点构造超平面求截距把截距作为每个目标维度的缩放标准。完整实现需要求解一个线性方程组而且要处理超平面退化的异常情况。下面的代码是教学版实现用极值个体的最大值替代超平面截距跑通算法逻辑没问题做严格对比实验时建议换回论文的截距版本function [Fn, zmin] Normalize(F) % Normalize: 教学版归一化用极值个体估计缩放范围 % 输入 F: 种群目标值矩阵n 行 M 列默认极小化问题 % 输出 Fn: 归一化后的目标值zmin: 理想点 zmin min(F, [], 1); F F - zmin; % 平移到正象限 [~, idx] max(F, [], 1); % 每个目标的最大值所在个体 zmax max(F(idx, :), [], 1); % 收集每个维度的极值个体 zmax(zmax 1e-10) 1; % 防止除零 Fn F ./ zmax; end平移那一步对应论文里用理想点把目标空间原点挪到当前种群的“最好点”位置。为什么要用所有个体的目标值而不仅是非支配解的目标值因为 NSGA3 的环境选择在每代要用合并种群计算归一化参数如果只用当前非支配解归一化参数会在迭代过程中剧烈抖动参考点的相对位置不稳定选择方向会飘。zmax的三行代码是为了让每个目标的缩放比例反映种群当前实际占有的范围代替论文里由截距给出的理想缩放遇到退化情况时用 1 兜底而不是抛错对调试更友好。2.4 关联与小生境计数NSGA3 的第二次挑选归一化之后每个解要找到离自己“方向”最近的参考点。注意是方向最近不是距离参考点最近。对每个参考点 z沿原点做一条射线方向向量 wz/||z||把解 f 投影到这条射线上投影点与 f 之间的欧氏距离就是垂直距离解的参考点编号就是垂直距离最小的那个参考点。这一步计算量是 O(n * M * R)n 是合并种群个体数R 是参考点数量。手写代码时常犯的错误是把 w 方向忘记归一化或者把投影点误写成(f * z) * z而不是(f * w) * w导致关联结果整体偏移。记住参考线是射线不是参考点与原点之间的向量差。关联完成之后每个参考点维护一个计数 rho_j表示当前已经选入下一代、并且关联到该参考点的个体数量。做环境选择的最后一层时流程是选 rho_j 最小的参考点从这个参考点关联的、还没有被选入下一代的候选个体里挑一个如果这个参考点没有候选个体就换下一个 rho_j 最小的参考点。这个“越空的方向越优先填”的策略就是 NSGA3 能覆盖整个前沿的关键。3. 在Matlab里最小实现NSGA3核心算子3.1 两条实现路径直接用 PlatEMO 还是自己写Matlab 里落地 NSGA3 有两条路。第一条是直接用开源的多目标优化平台 PlatEMO它把 NSGA3、NSGA2、MOEA/D 这些算法都实现好了还打包了 DTLZ、WFG、ZDT 测试问题集命令行和 GUI 都能用。做对比实验、调参、看前沿收敛效率最高。第二条是自己动手写核心算子适合你要改算法、投稿期刊、或者只是想彻底搞懂 NSGA3 内部机制的人。我一般建议研究初期两条路都走先在 PlatEMO 里跑通 NSGA3拿到一份可靠的结果作为参照再自己写一个最小实现用同一组测试问题和参数做对比。如果自己的代码结果在 HV 指标上和 PlatEMO 的结果量级一致基本可以确定核心逻辑没写错。下面给出的 Normalize 和 Associate 两个函数是手写实现里最容易出错、也最重要的两块。3.2 直接可用的关联算子Associate.m把上一章讲的垂直距离流程写成 Matlab 代码就是下面这个函数。它接受归一化后的目标值 Fn 和参考点集合 Z返回每个解关联的参考点编号以及对应的最小垂直距离function [refIdx, dmin] Associate(Fn, Z) % Associate: 计算每个归一化解与各参考线的垂直距离 % 输入 Fn: 归一化目标值n 行 M 列 % Z: 参考点R 行 M 列已经归一化到和为 1 % 输出 refIdx: n 行每个解关联的参考点编号 % dmin: n 行对应的最小垂直距离 nP size(Fn, 1); nZ size(Z, 1); d zeros(nP, nZ); for i 1:nP for j 1:nZ w Z(j, :) / norm(Z(j, :)); % 参考线单位方向 proj (Fn(i, :) * w) * w; % 解在参考线上的投影点 d(i, j) norm(Fn(i, :) - proj); end [dmin(i), refIdx(i)] min(d(i, :)); end refIdx refIdx; dmin dmin; end代码里Fn(i, :) * w是解向量与单位方向向量 w 的点积结果是一个标量再乘 w 得到投影点坐标。norm(Fn(i,:) - proj)就是垂直距离。两层循环在种群规模 500、参考点 300 时大约是十几万次距离计算Matlab 单次运行能接受如果种群上千建议改成矩阵化写法或用 C Mex否则每代都要等。输出转置是为了让 refIdx 和 dmin 都是列向量和 Matlab 里种群个体的组织习惯保持一致。3.3 一个 60 秒的冒烟测试随机解是否均匀关联写完整环境选择之前可以先做一个冒烟测试用随机解验证 GenerateReferencePoints、Normalize、Associate 三个函数能不能协同工作% smoke_test.m Z GenerateReferencePoints(3, 12); % 91 个参考点 F rand(2000, 3); F F ./ sum(F, 2); % 投影到单纯形模拟归一化目标值 Fn Normalize(F); % 随机解本身量纲一致归一化影响很小 [refIdx, ~] Associate(Fn, Z); % 统计每个参考点关联了多少解 counts accumarray(refIdx, 1, [size(Z,1), 1]); bar(1:size(Z,1), counts); xlabel(参考点编号); ylabel(关联解数量);对随机生成的 2000 个解理想情况下每个参考点附近关联到的解数量应该比较接近柱状图不会出现某个参考点关联数为 0 而另一个超过 100 的极端情况。如果关联分布严重偏向某个角落先检查参考点是否真的和为 1再检查 Associate 里投影公式是否写对。这 60 秒能帮你省下后面找 bug 的两个小时。3.4 环境选择怎么串起来主循环的骨架有了关联算子NSGA3 的完整环境选择就能串起来了。主循环每一代做四件事交叉变异生成子代合并父代和子代做非支配排序从第一层开始逐层把个体放入下一代的候选集直到放满最后一层放不下时用参考点小生境计数做挑选。非支配排序可以直接用 PlatEMO 里的 NDSort或者自己实现一遍。小生境挑选这段核心逻辑是取 rho_j 最小的参考点从这个参考点关联的、仍在关键层里的个体里随机选一个把该参考点的 rho_j 加 1循环直到填满下一代。这一步写起来不复杂但它是最容易出隐蔽 bug 的地方——“优先选空参考点”和“在非空参考点里挑个体”两个分支要严格按论文算法 4 写。4. NSGA3调参与实战用DTLZ2跑出可解释的结果4.1 一分钟跑起实验PlatEMO 的命令行入口按前面说的第一步先在 PlatEMO 里拿到 NSGA3 的基准结果。Matlab 命令行里切到 PlatEMO 目录输入platemo打开 GUI左侧选算法 NSGA3右侧选问题 DTLZ2设置种群大小和评估次数就能跑。脚本化的写法是main(-algorithm, NSGA3, -problem, DTLZ2, ... -N, 92, -M, 3, -D, 12, -evaluation, 40000);-algorithm传算法函数句柄-problem传测试问题句柄-N是种群规模-M是目标维数-D是决策变量个数-evaluation是总的评估次数。PlatEMO 不同小版本里评估次数的参数名可能略有出入有的版本写法是-maxFE具体以这个版本 GUI 默认显示为准。DTLZ2 的前沿是单位球面在第一象限的那一片目标之间相互耦合是验证 NSGA3 参考点分布最合适的入门问题。4.2 5个必调参数照着这张表设不踩常见的坑NSGA3 的参数敏感性比 NSGA2 更集中真正需要花时间调的项不多但这几项几乎决定了算法的成败参数推荐值作用调参提示种群规模 N92 左右环境选择填充目标影响每代评估预算不要明显低于参考点数量常见组合是三目标配 91 个参考点参考点划分 p12决定参考线密度和方向覆盖p 太小前沿中部稀疏p 太大参考点爆炸交叉分布指数 eta_c20SBX 交叉的聚集程度值越大子代越接近父代收敛变慢变异分布指数 eta_m20多项式变异的扰动幅度值越大变异步长越小总评估次数30000~50000收敛预算DTLZ2 三目标 4 万次评估能看到清晰前沿N 和 p 要放在一起考虑。三目标 p12 有 91 个参考点N 取 92 让下一代恰好比参考点多一个人环境选择时小生境计数有缓冲空间如果 N 取 50参考点数量明显多于种群必然有很多参考点一个解都没有小生境选择等于失效。五目标以上p 往往要降到 6 或 8否则参考点数量比种群规模还大得多。4.3 判断运行是否正常的三条线索参数设好后不要只盯着最终前沿图看过程信息更有价值。以手写实现为例建议在每一代收集三个量当前第一非支配层个体数、各目标的最小值、参考点关联分布中 rho 为 0 的个数。手写循环里可以这样加一行输出if mod(g, 20) 0 fprintf(gen%4d, |F1|%3d, min[%.4f %.4f %.4f], emptyRef%d\n, ... g, length(Fronts{1}), min(Obj, [], 1), sum(rho(:, 2) 0)); end|F1|是第一非支配层规模迭代早期它应该接近种群规模后期逐步缩小min是三个目标各自的最小值它们应该单调下降或者至少不上升emptyRef是当前还没有任何解关联的参考点数量理想情况下它应该随着代数增加逐渐降到 0如果始终有一大片参考点空置多半是归一化出了问题或者测试问题的前沿本身就覆盖不到那些方向。这三条线索比单纯看最终 PF 图更早暴露问题。比如emptyRef长期不降几乎可以断定归一化里的缩放范围估错了参考点方向与实际目标分布不匹配min出现非单调上升则说明选择压力不够可能需要检查非支配排序后小生境选择是不是把已经选入的个体又覆盖掉了。5. 验证NSGA3实现正确性的两条硬路径5.1 用HV指标量化蒙特卡洛近似版超体积计算前沿图画得再好看也可能骗人HVHypervolume是少数几个能同时度量收敛性和多样性的指标。Matlab 没有内置 HV 函数精确计算需要专门实现调试阶段可以先写一个蒙特卡洛近似版逻辑清晰、十行内跑通function hv approxHV(F, refPoint, K) % approxHV: 蒙特卡洛估计超体积用于快速验证算法实现 % 输入 F: 非支配解的目标值n 行 M 列refPoint: 参考点向量各维大于所有解目标值 if nargin 3, K 200000; end S rand(K, size(F, 2)) .* refPoint; dominated false(K, 1); for i 1:size(F, 1) dominated dominated | all(S F(i, :), 2); end hv mean(dominated) * prod(refPoint); end对极小化问题随机采样点在所有维度上都比某个解差就认为它落在超体积区域内mean(dominated)是采样点落入该区域的占比再乘参考点定义的超立方体体积就是 HV 的估计值。K 取 20 万时HV 的相对误差基本能控制在几个百分点内用来对比“改参数前后结果有没有变好”完全够用。参考点 refPoint 取种群各维最大值的 1.1 倍就行注意要保证每个维度的参考值都比所有解的对应目标值大否则 HV 会截断。5.2 三维PF可视化把参考点一并画出来看问题所在三目标问题最适合直接看三维散点图。把最终非支配解画出来后把参考点也叠加上去是定位归一化和关联问题最快的办法scatter3(F(:,1), F(:,2), F(:,3), 12, F(:,1), filled); hold on; plot3(Z(:,1), Z(:,2), Z(:,3), ko, MarkerSize, 4); hold off; xlabel(f1); ylabel(f2); zlabel(f3); view(135, 30);观察两个方向解的分布是否沿着参考线方向铺开有没有某些方向上解明显缺失参考点本身是否落在目标值实际覆盖的范围内如果参考点整体集中在原点附近或者飘到前沿外侧归一化逻辑基本可以判定写错了。5.3 把HV曲线融进主循环较之最后一刻才看最终 PV更推荐把approxHV塞进主循环每 20 代算一次当前种群 HV把数值存下来运行结束后画一条 HV 随代数变化的曲线。HV 曲线的前段决定收敛速度后段的平滑度反映多样性维护是否稳定。用它配合前文的三条过程线索可以快速分辨曲线爬升慢是交叉变异参数问题曲线爬升后突然回落是环境选择 bug曲线始终很低且震荡则是归一化方向错了。NSGA3 在 Matlab 里的实现并不复杂复杂的是让每个算子的数值细节都站得住脚。参考点生成、归一化、关联三步里任何一步的小偏差都会在小生境选择里被放大成前沿覆盖的局部塌陷。先跑通最小冒烟测试再用 PlatEMO 结果锚定 HV 数值最后用三维图叠加参考点做人工核验——这套流程足够让你在写完代码的当天就知道NSGA3 到底写对了没有。本文还有配套的精品资源点击获取
返回列表