NSGA2、NSGA-II实现、基于分配的多目标进化-Python

算法流程:

P:父辈种群
Q:子辈种群
R:P并上Q -》 之后依据偏序关系进行排序

在实际上,能在原来数组上改就到原来数组上改,要产生新的那就产生新的,分配一次内存时间应该影响不大,以后再考虑底层优化。!
在函数调用上,一律认为创建了一个新的数组

初始化:P
计算P适应度:F
根据适应度度计算层次关系:rank、L
rank为P对应的等级数组,L标记每层的元素数量
根据F、rank计算拥挤距离,越大越好:crowding_distance
更具rank, crowding_distance对dna进行排序:得到新P
对P按序号两两进行单点交叉:得Q
对Q按概率做一遍变异:新Q
将P、Q复制到R中
重复计算适应度开始重新

交叉使用单点交叉: pc = 0.9
变异概率:1/决策变量数 或 1/二进制编码长度
250代

  1 # nsga2.py
  2 import numpy as np
  3
  4
  5 class individual:
  6     def __init__(self, dna):
  7         self.dna = dna
  8         self.dna_len = len(dna)
  9
 10         self.f = None
 11         self.rank = -1
 12         self.crowding_distance = -1  # 越远 多样性越好
 13
 14     def __gt__(self, other):
 15         if self.rank > other.rank:
 16             return True
 17         if self.rank == other.rank and self.crowding_distance < other.crowding_distance:
 18             return True
 19         return False
 20
 21     def get_fitness(self, F):
 22         len_F = len(F)
 23         fitness = np.zeros(shape=len_F)
 24         for i in range(len_F):
 25             fitness[i] = F[i](self.dna)
 26         return fitness
 27
 28     def cross_newObj(self, p1, p2):
 29         # 不能修改之前的,需要生成一个新的对象
 30         p1_dna = p1.dna.copy()
 31         p2_dna = p2.dna.copy()
 32         cut_i = np.random.randint(1, self.dna_len - 1)
 33         temp = p1_dna[cut_i:].copy()
 34         p1_dna[cut_i:] = p2_dna[cut_i:]
 35         p2_dna[cut_i:] = temp
 36
 37         q1 = individual(p1_dna)
 38         q2 = individual(p2_dna)
 39
 40         return q1, q2
 41
 42     def cross(self, p1, p2):
 43         cut_i = np.random.randint(1, self.dna_len - 1)
 44         temp = p1[cut_i:].copy()
 45         p1[cut_i:] = p2[cut_i:]
 46         p2[cut_i:] = temp
 47         return p1, p2
 48
 49     def mutation(self, domain):
 50         point = np.random.randint(0, self.dna_len)
 51         low = domain[point][0]
 52         high = domain[point][1]
 53         self.dna[point] = np.random.rand() * (high - low) + low
 54         return self.dna
 55
 56     def dominate(self, p2):
 57         p1 = self
 58         # p1 是否支配p2
 59         dominated = True
 60         for i in range(len(p1.f)):
 61             if p1.f[i] > p2.f[i]:
 62                 dominated = False
 63                 break
 64         return dominated
 65
 66
 67 class nsga2:
 68     def __init__(self, pop_size, dna_size, pc, pm, f_funcs, domain):
 69         self.pop_size = pop_size
 70         self.dna_size = dna_size
 71         self.f_size = len(f_funcs)
 72         self.pc = pc
 73         self.pm = pm
 74         self.f_funcs = f_funcs
 75         self.domain = domain
 76
 77     def ini_pop(self):
 78         P_dna = np.random.random(size=(self.pop_size, self.dna_size))
 79         for i in range(len(self.domain)):
 80             item = self.domain[i]
 81             P_dna[:, i] = P_dna[:, i] * (item[1] - item[0]) + item[0]
 82
 83         P = []  # 初始种群P
 84         for dna in P_dna:
 85             P.append(individual(dna))
 86
 87         return P
 88     def calculate_pop_fitness(self, pop):
 89         # 计算Pop适应度,存储到individual中
 90         for p in pop:
 91             p.f = p.get_fitness(self.f_funcs)
 92         return pop
 93
 94     def fast_non_dominated_sort(self, R):
 95         # 非支配排序操作
 96         # 默认调用之前,改执行的动作已经执行过了
 97         R_len = len(R)
 98         n = np.zeros(shape=R_len)  # 被支配数
 99         S = []  # 支配了那些成员
100         F = [[]]  # 每层个体编号(对R)
101         for i in range(R_len):
102             Sp = []
103             n[i] = 0
104             for j in range(R_len):
105                 if i == j:
106                     continue
107                 if R[i].dominate(R[j]):
108                     Sp.append(j)
109                 elif R[j].dominate(R[i]):
110                     n[i] += 1
111             S.append(Sp)
112             if n[i] == 0:
113                 R[i].rank = 0
114                 F[0].append(i)
115
116         i = 0
117         while len(F[i]) != 0:
118             Q = []  # i+1层
119             for p in F[i]:
120                 for q in S[p]:
121                     n[q] -= 1
122                     if n[q] == 0:
123                         R[q].rank = i + 1
124                         Q.append(q)
125             i += 1
126             F.append(Q)
127         F.pop()
128
129         # 转化为实际的人
130         F_obj = []
131         for rank in F:
132             rank_obj = [R[p_idx] for p_idx in rank]
133             F_obj.append(rank_obj)
134
135         return F_obj  # 每层包含的实际的人
136
137     def crowding_distance_pop(self, F):
138         for rank in F:
139             self.crowding_distance_assignment(rank)
140
141     def crowding_distance_assignment(self, rank):
142         # 初始化该组所有人的距离为0
143         pop_fitness = []
144         for p in rank:
145             p.crowding_distance = 0
146             pop_fitness.append(p.f)
147         pop_fitness = np.array(pop_fitness)
148         sorted_idx = np.argsort(pop_fitness, axis=0)
149         f_dimension = len(self.f_funcs)
150         for i in range(f_dimension):
151             # 在f1下是适应度的排序
152             fi_sort_idx = sorted_idx[:, i]
153             fi_sort_p = [rank[i] for i in fi_sort_idx]
154             # fi_sort_p = rank[fi_sort_idx]
155             inf = 9999999999999
156             fi_sort_p[0].crowding_distance = fi_sort_p[-1].crowding_distance = float(inf)
157             diff = fi_sort_p[-1].f[i] - fi_sort_p[0].f[i]
158             for j in range(1, len(fi_sort_p) - 1):
159                 a = fi_sort_p[j + 1].f[i]
160                 # fi_sort_p[j].crowding_distance += (fi_sort_p[j + 1].f[i] - fi_sort_p[j - 1].f[i]) / diff
161                 fi_sort_p[j].crowding_distance += (fi_sort_p[j + 1].f[i] - fi_sort_p[j - 1].f[i])
162
163         # 打印看下
164         return rank
165
166     def select(self, R):
167         R.sort()
168         # print(len(R))
169         # print([[p.rank, p.crowding_distance] for p in R])
170         return R[:self.pop_size]
171
172     def pop_cross(self, P):
173         new_Q = []
174         i = 0
175         P_len = len(P)
176         while i < P_len:
177             q1, q2 = P[i].cross_newObj(P[i], P[i + 1])
178             new_Q.append(q1)
179             new_Q.append(q2)
180             i += 2
181         return new_Q
182
183     def pop_mutation(self, Q):
184         for q in Q:
185             if np.random.rand() < self.pm:
186                 q.mutation(self.domain)
187         return Q
1 # zdt_funcs.py
 2 import numpy as np
 3
 4
 5 def zdt4_f1(x_list):
 6     return x_list[0]
 7
 8
 9 def zdt4_gx(x_list):
10     sum = 1 + 10 * (10 - 1)
11     for i in range(1, 10):
12         sum += x_list[i] ** 2 - 10 * np.cos(4 * np.pi * x_list[i])
13     return sum
14
15
16 def zdt4_f2(x_list):
17     gx_ans = zdt4_gx(x_list)
18     if x_list[0] < 0:
19         print("????: x_list[0] < 0:", x_list[0])
20     if gx_ans < 0:
21         print("gx_ans < 0", gx_ans)
22     if (x_list[0] / gx_ans)  0:
23         print("x_list[0] / gx_ans", x_list[0] / gx_ans)
24
25     ans = 1 - np.sqrt(x_list[0] / gx_ans)
26     return ans
27
28
29 def zdt3_f1(x):
30     return x[0]
31
32
33 def zdt3_gx(x):
34     if x[:].sum() < 0:
35         print(x[1:].sum(), x[1:])
36     ans = 1 + 9 / 29 * x[1:].sum()
37     return ans
38
39
40 def zdt3_f2(x):
41     g = zdt3_gx(x)
42     ans = 1 - np.sqrt(x[0] / g) - (x[0] / g) * np.sin(10 * np.pi * x[0])
43     return ans
44
45
46 def get_zdt(name="zdt4"):
47     if name == "zdt4":
48         f_funcs = [zdt4_f1, zdt4_f2]
49         domain = [[0, 1]]
50         for i in range(9):
51             domain.append([-5, 5])
52         return f_funcs, domain
53
54     if name == "zdt3":
55         f_funcs = [zdt3_f1, zdt3_f2]
56         domain = [[0, 1] for i in range(30)]
57         return f_funcs, domain
1 # 实现zdt.py
 2 import numpy as np
 3 from nsga2 import *
 4 import matplotlib.pyplot as plt
 5 from zdt_funcs import *
 6
 7
 8 # 画图
 9 def draw(P: object) -> object:
10     for t in P:
11         # 每level
12         x = [p.f[0] for p in P]
13         y = [p.f[1] for p in P]
14         # plt.clf()
15         plt.xlabel("f0")
16         plt.ylabel("f1")
17         plt.scatter(x, y, s=5)  # s 点的大小  c 点的颜色 alpha 透明度
18
19     plt.show()
20
21
22 def draw_rank(F):
23     for t in F:
24         # 每level
25         x = [p.f[0] for p in t]
26         y = [p.f[1] for p in t]
27         plt.scatter(x, y, s=15)  # s 点的大小  c 点的颜色 alpha 透明度
28
29     plt.show()
30
31
32 # 准备目标问题
33
34
35 f_funcs, domain = get_zdt("zdt3")
36 # 准备参数
37 pop_size = 300
38 dna_size = len(domain)
39 pm = 0.2
40
41 # 开始操作
42 nsga2 = nsga2(pop_size, dna_size, pc=1, pm=0.7
43               , f_funcs=f_funcs, domain=domain)
44
45 # x1 [0,1] xi [-5,5]
46 P = nsga2.ini_pop()
47 # 计算初始适应度
48 nsga2.calculate_pop_fitness(P)
49 # 进行非支配排序
50 F = nsga2.fast_non_dominated_sort(P)
51 # 计算拥挤距离
52 nsga2.crowding_distance_pop(F)
53 R = P
54
55 N = 100
56 for i in range(N):
57     print(i)
58     # 选择
59     P = nsga2.select(R)
60     # 交叉
61     Q = nsga2.pop_cross(P)
62     # 变异
63     Q = nsga2.pop_mutation(Q)
64     # 合并
65     R = P + Q
66     # 计算适应度
67     nsga2.calculate_pop_fitness(R)
68     # 非支配排序
69     F = nsga2.fast_non_dominated_sort(R)
70     # 画个图
71     # draw_rank(F)
72     # 计算拥挤距离
73     nsga2.crowding_distance_pop(F)
74
75 P = nsga2.select(R)
76 # 画图
77 draw(P)

实现效果:

增加种群数量以及迭代次数,能够使曲线更加的完美。我电脑i3 10105f。在代码中参数的配置下,能够很快运行完成。

NSGA2、NSGA-II实现、基于分配的多目标进化-Python

Original: https://www.cnblogs.com/Twobox/p/16416460.html
Author: Wei_Xiong
Title: NSGA2、NSGA-II实现、基于分配的多目标进化-Python

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/805093/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球