多目标优化(二)简单的 NSGA-Ⅲ
写在前面:
1、最近好多人收藏了我的文章啊!那就把其他多目标优化学习内容也整理一下!!
2、本文主要介绍自己理解的 NSGA-III Matlab 程序实现,在学习中迷糊的地方都会点出来!!
3、本文程序基于 上一篇 NSGA-II 改编;
4、欢迎指正。
算法概要:
1、基础 Pareto 概念请转:
2、算法目标:我们知道遗传算法会构建一个带有 NP 个个体的种群,单目标输出的就是迭代后种群中最好的 个体。但在多目标中,输出的是迭代后整个种群作为 NP 个 Pareto 最优解,构成一个 Pareto 实际前沿面。(单目标是要找最优个体,多目标是要找最接近理论前沿面的实际前沿面)
3、快速非支配排序:将当代种群根据各个目标函数值进行 Pareto 等级分层(用到非支配解的概念)
4、基于参考点分类个体:在空间中(几个目标就是几维空间),生成一组参考点(参考点个数自定,建议选择和种群大小相同,每个解对应一个参考点是最理想的)
5、选择、交叉、变异:GA基本操作,NSGA-III论文并没有限定选用哪种方法,我觉得可以随意选用
6、精英保留:就是将生成的候选种群和原种群合并,重新 Pareto 分层和计算各个个体归属的参考点,然后按照 Pareto 层级和参考点所拥有的个体数选择前NP 个个体进入下一循环(就是让新种群中个体归属的参考点尽可能均匀分布)
NSGA-II 与 NSGA-III 的区别:
NSGA-II中根据拥挤度来选择个体,NSGA-III根据参考点来选择个体;
拥挤度是一个数值呀,一维上能精确的表示长度,二维上能表示无数种形状的面积,三维上能表示无数的无数种的体积,所以对于多目标(大于3)是不合理的。
参考点是在空间中均匀的选择点位置,画出两目标和三目标的图,应该能理解了吧??
主程序:
clear; close all; clc global NP x_num f_num iter_max fun bound f_min % 定义全局变量 % NP:种群大小 % iter_max:最大迭代次数 % f_num:目标个数 % x_num:解得维数 % f_min: 当前寻找到的各目标函数的最小值 %% 测试ZDT1函数,参数如下: f_num = 2; % 目标函数个数 x_num = 30; % 决策变量个数 x_min = zeros(1,x_num); % 决策变量的最小值 x_max = ones(1,x_num); % 决策变量的最大值 f_min = ones(1, f_num) * inf; NP = 100; iter_max = 500; % 初始化种群 chromo_ori = x_min + rand(NP, x_num) .* (x_max - x_min); % 初始化种群 chromo_ori = fun(chromo_ori); % 计算适应度函数值 [F0, chromo] = non_domination_sort(chromo_ori); % 子函数:非支配排序 Ref_Points = Gen_RefPoints(); % 子函数:生成参考点 % 开始迭代 for i = 1:iteration chromo_parent = chromo(:,1:x_num+f_num+2); % 定义父代种群,最后两列放pareto等级和参考点归属类 % 交叉 变异 chromo_cro = crossover(pick_sel, chromo_parent); chromo_mut = mutation(chromo_parent); chromo_off = [chromo_cro; chromo_mut]; % 种群合并 chromo_co = [chromo_parent(:,1:x_num+f_num); chromo_off(:,1:x_num+f_num)]; % 种群个数 2*NP [F_i, chromo_co] = non_domination_sort(chromo_co); % 非支配排序 f_min_new = min(chromo_co(:, x_num+1:x_num+f_num)); % 看看各目标的最小值有没有更新 if sum(f_min > f_min_new) f_min = min(f_min, f_min_new); % 更新各目标最小值 end % 精英保留 chromo_co = Classify_RefPoint(); % 将合并后的种群个体分属于参考点中 chromo = elitism(chromo_co); % 按 Pareto 等级和拥挤度从2*NP个个体中选出NP个个体进入下一代 end
子程序原理:
1、 大部分子程序原理同NSGA-II一样,请转https://blog.csdn.net/weixin_41942547/article/details/102527352
2、 Gen_RefPoints:
① 参考点生成一次就行了,默认是各目标均等的分成几份,然后在多维空间上均匀分布参考点。
② 有几个目标,参考点就是一个几维向量。
function RefPoints = Gen_RefPoints()
global f_num
n_div = floor(f_num * 3); % 各目标分成几份
RefPoints = Get_Matrix(f_num, n_div)' / n_div;
end
function A = Get_Matrix(f_num, n_div)
if f_num < 1
error('f_num cannot be less than 1.');
end
if floor(f_num) ~= f_num
error('f_num must be an integer.');
end
if f_num == 1
A = n_div;
return;
end
A = [];
for i = 0:n_div
B = Get_Matrix(f_num-1, n_div-i);
A = [A; i*ones(size(B,1),1) B];
end
end
3、 Classify_RefPoint:
① 这部分就是NSGA-III的核心也是难点。
② 由于参考点是按标准化计算的,所以我们需要将各目标所在的目标空间转换成和参考点的空间一致。
③ 首先,是改变“原点”,将当前寻找到的个目标值的最小值作为原点,即将目标空间进行平移变换。
④ 其次,由于各目标的取值的区间差异很大,所以将其归一化。(因为参考点也相当于是归一化后取得的)
上两步骤可参考(标准化目标空间)https://zhuanlan.zhihu.com/p/124917159
⑤ 经过上述步骤,就能将参考点完全匹配到目标空间上啦,接下来就是计算每个个体所属的参考点。
⑥ 判断归属的参考点就是计算每个个体到各个参考点向量(“原点”与参考点的连线)的距离,选择最短的那个。
function chromo = Classify_RefPoint(chromo)
global f_min x_num f_num RefPoints habitat
fit = chromo(:, x_num+1:x_num+f_num);
fit = fit - f_min; % 函数目标坐标变换
for i = 1:f_num
w = 10-6 * ones(1, f_num); w(i) = 1;
[f_max(i), ~] = min(max(fit ./ w, [], 2));
end
fit = fit ./ f_max; % 坐标归一化
for i = 1:numel(habitat)
w = RefPoints(:, i) / norm(RefPoints(:, i));
for j = 1:size(chromo, 1)
dis(j, i) = norm(fit(j, :)' - w'*fit(j, :)'*w);
end
end
[~, idx] = min(dis, [], 2);
chromo(:, x_num+f_num+2) = idx; % 各个个体归属到哪个参考点
end
4、 elitism:
① 变异后的种群就是子代种群了,和父代种群合并以下吧(变成 2NP 个个体了)
② 将这 2NP 个个体 Pareto 分层和计算拥挤度吧,
③ 先将 Pareto 等级为1的选中,再选 Pareto 等级为2的,……
④ 选 NP 个就够啦,比如 Pareto 等级为1的个体有 NP/2 个,Pareto等级为2的个体有NP个,那么就需要从Pareto等级为2的个体中挑选出NP/2个。
⑤ 首先,挑选出Pareto等级为1的个体有NP/2个,统计这NP/2个个体归属的参考点各有几个。
⑥ 选择归属的参考点个数最少的参考点,从Pareto等级为2的个体中挑选出一个归属于那个参考点的个体。重复这个过程直到挑出NP/2个个体。
function chromo = elitism(chromo_co)
global NP f_num x_num habitat
habitat = min(habitat, 0);
chromo_co_sort = sortrows(chromo_co, x_num+f_num+1); % 按 pareto_rank 排序
chromo = [];
pareto_rank = 1;
while size(chromo, 1) < NP
% 取出 pareto_rank 整层
temp = chromo_co_sort(chromo_co_sort(:,x_num+f_num+1)==pareto_rank,:);
if (size(temp, 1) + size(chromo, 1)) < NP
chromo = [chromo; temp];
for i = 1:size(temp, 1)
habitat(temp(i, x_num+f_num+2)) = habitat(temp(i, x_num+f_num+2)) + 1;
end
else
while size(chromo, 1) < NP
[~, idx] = min(habitat); % 找出小生境中数量最少的
pick = find(temp(:, x_num+f_num+2)==idx);
if isempty(pick)
habitat(idx) = inf;
else
chromo = [chromo; temp(pick(1), :)];
temp(pick(1), :) = [];
habitat(idx) = habitat(idx) + 1;
end
end
end
pareto_rank = pareto_rank + 1;
end
end
可执行的完整代码见:https://download.csdn.net/download/weixin_41942547/15467105