├── mopso ├── __init__.py ├── __pycache__ │ ├── MOCNN.cpython-38.pyc │ ├── init.cpython-38.pyc │ ├── plot.cpython-38.pyc │ ├── pareto.cpython-38.pyc │ ├── update.cpython-38.pyc │ ├── __init__.cpython-38.pyc │ ├── archiving.cpython-38.pyc │ └── fitness_funs.cpython-38.pyc ├── fitness_funs.py ├── init.py ├── pareto.py ├── MOCNN.py ├── update.py ├── plot.py └── archiving.py ├── requirements.txt ├── README.md └── OMOPSO.py /mopso/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/24 4 | -------------------------------------------------------------------------------- /mopso/__pycache__/MOCNN.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wood-wolf/OMOPSO/HEAD/mopso/__pycache__/MOCNN.cpython-38.pyc -------------------------------------------------------------------------------- /mopso/__pycache__/init.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wood-wolf/OMOPSO/HEAD/mopso/__pycache__/init.cpython-38.pyc -------------------------------------------------------------------------------- /mopso/__pycache__/plot.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wood-wolf/OMOPSO/HEAD/mopso/__pycache__/plot.cpython-38.pyc -------------------------------------------------------------------------------- /mopso/__pycache__/pareto.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wood-wolf/OMOPSO/HEAD/mopso/__pycache__/pareto.cpython-38.pyc -------------------------------------------------------------------------------- /mopso/__pycache__/update.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wood-wolf/OMOPSO/HEAD/mopso/__pycache__/update.cpython-38.pyc -------------------------------------------------------------------------------- /mopso/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wood-wolf/OMOPSO/HEAD/mopso/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /mopso/__pycache__/archiving.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wood-wolf/OMOPSO/HEAD/mopso/__pycache__/archiving.cpython-38.pyc -------------------------------------------------------------------------------- /mopso/__pycache__/fitness_funs.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wood-wolf/OMOPSO/HEAD/mopso/__pycache__/fitness_funs.cpython-38.pyc -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib==3.3.4 2 | numpy==1.20.1 3 | thop==0.0.31.post2005241907 4 | torch==1.10.0+cu102 5 | torchvision==0.11.0+cu102 6 | tqdm==4.59.0 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OMOPSO 2 | 这是Evolving Deep Neural Networks by Multi-objective Particle的复现;多目标优化粒子群算法+CNN网络;实现调参。 3 | 智能控制理论与技术的小组作业 4 | # 使用方法 5 | OMOPSO.py文件是主函数文件可以直接运行,需要修改的参数有particals;generations;另外MOCNN.py中的epoch也要改。 6 | ## pytorch 中Desnet.py修改 7 | 192行把 growth_rate -> growth_rate[i] 8 | 187行把 growth_rate -> growth_rate[i] 9 | 160行变成 growth_rate: [int,int,int,int] = [32,32,32,32], 10 | ## thop库 中 profile.py修改 11 | 173行变成 pass;#print("[INFO] Register %s() for %s." % (fn.__qualname__, m_type)) 可以消除 [INFO] 12 | 176行变成 pass;#prRed("[WARN] Cannot find rule for %s. Treat it as zero Macs and zero Params." % m_type) 可以消除[WARN] 13 | ## 注意事项: 14 | 第二个和第三个是为了符合论文作者中的编码规则进行修改的,因为我是调用了pytorch中的Desnet库进行操作的。你要是用其他的神经网络进行训练也是可行的。 15 | -------------------------------------------------------------------------------- /mopso/fitness_funs.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/24 4 | import numpy as np 5 | from mopso import MOCNN 6 | 7 | 8 | # 为了便于图示观察,试验测试函数为二维输入、二维输出 9 | # 适应值函数:实际使用时请根据具体应用背景自定义 [[num_layers,growth]*4] 10 | def fitness(in_, dim): 11 | growthRate = [] 12 | blockConfig = [] 13 | for i in range(dim): 14 | blockConfig.append(round(in_[i][0])) # 把block的层数变成整数 15 | growthRate.append(int(round(in_[i][1]))) # 把 growthRate变成整数并且四舍五入取整 16 | # 输入增长率集合和4个块中每个块层数集合 17 | acc, flops = MOCNN.mocnn(growthRate, tuple(blockConfig)) 18 | return [acc, flops] 19 | 20 | 21 | def fitness_plot(in_): 22 | degree_45 = ((in_[0]-in_[1])**2/2)**0.5 23 | degree_135 = ((in_[0]+in_[1])**2/2)**0.5 24 | fit_1 = 1-np.exp(-(degree_45)**2/0.5)*np.exp(-(degree_135-np.sqrt(200))**2/250) 25 | fit_2 = 1-np.exp(-(degree_45)**2/5)*np.exp(-(degree_135)**2/350) 26 | return [fit_1,fit_2] 27 | -------------------------------------------------------------------------------- /mopso/init.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/24 4 | import random 5 | import numpy as np 6 | from mopso import archiving 7 | from mopso import pareto 8 | from tqdm import tqdm 9 | 10 | 11 | # xx初始化粒子种群层数和增长率 12 | def init_designparams(particals, range_list, dim=4): 13 | in_ = [] # P粒子群大小3D[2,4,2]结构 14 | for i in tqdm(range(particals), desc='CNNs粒子初始化中:'): 15 | ind = [[] for i in range(4)] 16 | for j in range(dim): 17 | layer_num = random.randint(range_list[j][0], range_list[j][1]) # 层数的变化 18 | growth_rate = random.randint(range_list[4][0], range_list[4][1]) # 增长率的变化 19 | ind[j] = [layer_num, growth_rate] 20 | in_.append(ind) 21 | return np.array(in_) 22 | 23 | 24 | def init_v(particals, v_max, v_min, dim=4): 25 | in_v = [] 26 | for i in tqdm(range(particals), desc='CNNs粒子速度初始化中:'): 27 | ind_v = [[] for i in range(4)] 28 | for j in range(dim): 29 | growth_rate = random.uniform(0, 1) * (v_max[4] - v_min[4]) + v_min[4] # 增长率的速度变化 30 | ind_v[j] = [1, growth_rate] 31 | in_v.append(ind_v) 32 | return np.array(in_v) 33 | 34 | 35 | def init_pbest(in_, fitness_): 36 | return in_, fitness_ 37 | 38 | 39 | def init_archive(in_, fitness_): 40 | pareto_c = pareto.Pareto_(in_, fitness_) # pareto实例化 41 | curr_archiving_in, curr_archiving_fit = pareto_c.pareto() # 42 | return curr_archiving_in, curr_archiving_fit 43 | 44 | 45 | def init_gbest(curr_archiving_in, curr_archiving_fit, mesh_div, range_list, particals): 46 | get_g = archiving.get_gbest(curr_archiving_in, curr_archiving_fit, mesh_div, range_list, particals) 47 | return get_g.get_gbest() 48 | -------------------------------------------------------------------------------- /mopso/pareto.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/24 4 | import numpy as np 5 | 6 | 7 | def compare_(fitness_curr, fitness_ref): 8 | # 判断fitness_curr是否可以被fitness_ref完全支配 9 | for i in range(len(fitness_curr)): 10 | if fitness_curr[i] < fitness_ref[i]: 11 | return True 12 | return False 13 | 14 | 15 | def judge_(fitness_curr, fitness_data, cursor): 16 | # 当前粒子的适应值fitness_curr与数据集fitness_data进行比较,判断是否为非劣解 17 | for i in range(len(fitness_data)): 18 | if i == cursor: 19 | continue 20 | # 如果数据集中存在一个粒子可以完全支配当前解,则证明当前解为劣解,返回False 21 | if not compare_(fitness_curr, fitness_data[i]): 22 | return False 23 | return True 24 | 25 | 26 | class Pareto_(object): 27 | def __init__(self, in_data, fitness_data): 28 | self.in_data = in_data # 粒子群坐标信息 29 | self.fitness_data = fitness_data # 粒子群适应值信息 30 | self.cursor = -1 # 初始化游标位置 31 | self.len_ = in_data.shape[0] # 粒子群的数量 32 | self.bad_num = 0 # 非优解的个数 33 | 34 | def next(self): 35 | # 将游标的位置前移一步,并返回所在检索位的粒子坐标、粒子适应值 36 | self.cursor = self.cursor + 1 37 | return self.in_data[self.cursor], self.fitness_data[self.cursor] 38 | 39 | def hasNext(self): 40 | # 判断是否已经检查完了所有粒子 41 | return self.len_ > self.cursor + 1 + self.bad_num 42 | 43 | def remove(self): 44 | # 将非优解从数据集删除,避免反复与其进行比较。 45 | self.fitness_data = np.delete(self.fitness_data, self.cursor, axis=0) 46 | self.in_data = np.delete(self.in_data, self.cursor, axis=0) 47 | # 游标回退一步 48 | self.cursor = self.cursor - 1 49 | # 非优解个数,加1 50 | self.bad_num = self.bad_num + 1 51 | 52 | def pareto(self): 53 | while self.hasNext(): 54 | # 获取当前位置的粒子信息 55 | in_curr, fitness_curr = self.next() 56 | # 判断当前粒子是否pareto最优 57 | if not judge_(fitness_curr, self.fitness_data, self.cursor): 58 | self.remove() 59 | return self.in_data, self.fitness_data 60 | -------------------------------------------------------------------------------- /mopso/MOCNN.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/24 4 | import torchvision 5 | from torch.utils.data import DataLoader 6 | import torch 7 | import torch.nn as nn 8 | from tqdm import tqdm 9 | from thop import profile 10 | import numpy as np 11 | 12 | 13 | def mocnn(growthRate, blockConfig): 14 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 15 | # 模型导入blockConfig=(6,12,24,16);growthRate=[32,32,32,32] 16 | densenet121 = torchvision.models.DenseNet(growth_rate=growthRate, num_classes=10, block_config=blockConfig) 17 | # 数据集获取 18 | train_data = torchvision.datasets.CIFAR10("./CIFAR_Dataset", train=True, download=False, 19 | transform=torchvision.transforms.ToTensor()) 20 | test_data = torchvision.datasets.CIFAR10("./CIFAR_Dataset", train=False, download=False, 21 | transform=torchvision.transforms.ToTensor()) 22 | test_data_size = len(test_data) 23 | # 训练集和测试集转换格式 24 | train_loader = DataLoader(dataset=train_data, batch_size=128) 25 | test_loader = DataLoader(dataset=test_data, batch_size=128) 26 | 27 | densenet121 = densenet121.to(device) # 模型调用GPU 28 | 29 | optimizer = torch.optim.Adam(densenet121.parameters()) # 优化参数均采用默认值 30 | # 损失函数调用GPU 31 | loss_fn = nn.CrossEntropyLoss().to(device) 32 | ave_acc = 0 # 测试集平均精度 33 | # 设置网络参数 34 | # 训练的轮数 35 | epoch = 1 # 300xx 36 | for i in range(epoch): 37 | print("------Desnet121-CIFAR10,第{}轮训练开始!------".format(i + 1)) 38 | 39 | # 训练步骤开始 40 | densenet121.train() 41 | for data in tqdm(train_loader, desc='Desnet-121 模型训练中:'): 42 | imgs, targets = data 43 | if torch.cuda.is_available(): 44 | imgs = imgs.to(device) 45 | targets = targets.to(device) 46 | output1 = densenet121(imgs) 47 | loss = loss_fn(output1, targets) 48 | 49 | optimizer.zero_grad() 50 | loss.backward() 51 | optimizer.step() 52 | 53 | # 测试步骤开始 54 | densenet121.eval() 55 | total_test_loss = 0 56 | total_accuracy = 0 57 | with torch.no_grad(): 58 | for data in tqdm(test_loader, desc='Desnet-121 模型测试中:'): 59 | imgs, targets = data 60 | 61 | imgs = imgs.to(device) # 图片调用GPU 62 | targets = targets.to(device) # 图片调用GPU 63 | output1 = densenet121(imgs) 64 | loss = loss_fn(output1, targets) 65 | total_test_loss = total_test_loss + loss.item() 66 | # 求出预测准确的数量 67 | accuracy = (output1.argmax(1) == targets).sum() 68 | total_accuracy = total_accuracy + accuracy 69 | ave_acc_curr = total_accuracy / test_data_size # 当前代的精确度 70 | # 确定最高精度的模型 71 | if ave_acc < ave_acc_curr: 72 | ave_acc = ave_acc_curr 73 | # FLOPs计算 74 | input1 = torch.randn(128, 3, 32, 32).to(device) # 128的batch size 75 | flops, params = profile(densenet121, inputs=(input1,)) 76 | return np.float64(ave_acc), np.float64(flops) 77 | -------------------------------------------------------------------------------- /mopso/update.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/24 4 | # encoding: utf-8 5 | import numpy as np 6 | import random 7 | from mopso import pareto 8 | from mopso import archiving 9 | 10 | 11 | def update_v(v_, v_min, v_max, in_, in_pbest, in_gbest, w, c1, c2): 12 | # 更新速度值 13 | v_temp = w * v_ + c1 * (in_pbest - in_) + c2 * (in_gbest - in_) 14 | # 如果粒子的新速度大于最大值,则置为最大值;小于最小值,则置为最小值 15 | for i in range(v_temp.shape[0]): 16 | for j in range(v_temp.shape[1]): 17 | if v_temp[i, j, 1] < v_min[4]: 18 | v_temp[i, j, 1] = v_min[4] 19 | if v_temp[i, j, 1] > v_max[4]: 20 | v_temp[i, j, 1] = v_max[4] 21 | return v_temp 22 | 23 | 24 | def update_in(in_, v_, in_range_list): 25 | # 更新位置参数 26 | in_temp = in_ + v_ 27 | # 大于最大值,则置为最大值;小于最小值,则置为最小值 28 | for i in range(in_temp.shape[0]): 29 | for j in range(in_temp.shape[1]): 30 | # 层数变化 31 | if in_temp[i, j, 0] < in_range_list[j][0]: 32 | in_temp[i, j, 0] = in_range_list[j][0] 33 | if in_temp[i, j, 0] > in_range_list[j][1]: 34 | in_temp[i, j, 0] = in_range_list[j][1] 35 | # 增长率变化 36 | if in_temp[i, j, 1] < in_range_list[4][0]: 37 | in_temp[i, j, 1] = in_range_list[4][0] 38 | if in_temp[i, j, 1] > in_range_list[4][1]: 39 | in_temp[i, j, 1] = in_range_list[4][1] 40 | return in_temp 41 | 42 | 43 | def compare_pbest(in_indiv, pbest_indiv): 44 | num_greater = 0 45 | num_less = 0 46 | for i in range(len(in_indiv)): 47 | if in_indiv[i] > pbest_indiv[i]: 48 | num_greater = num_greater + 1 49 | if in_indiv[i] < pbest_indiv[i]: 50 | num_less = num_less + 1 51 | # 如果当前粒子支配历史pbest,则更新,返回True 52 | if (num_greater > 0 and num_less == 0): 53 | return True 54 | # 如果历史pbest支配当前粒子,则不更新,返回False 55 | elif (num_greater == 0 and num_less > 0): 56 | return False 57 | else: 58 | # 如果互不支配,则按照概率决定是否更新 59 | random_ = random.uniform(0.0, 1.0) 60 | if random_ > 0.5: 61 | return True 62 | else: 63 | return False 64 | 65 | 66 | def update_pbest(in_, fitness_, in_pbest, out_pbest): 67 | for i in range(out_pbest.shape[0]): 68 | # 通过比较历史pbest和当前粒子适应值,决定是否需要更新pbest的值。 69 | if compare_pbest(fitness_[i], out_pbest[i]): 70 | out_pbest[i] = fitness_[i] 71 | in_pbest[i] = in_[i] 72 | return in_pbest, out_pbest 73 | 74 | 75 | def update_archive(in_, fitness_, archive_in, archive_fitness, thresh, mesh_div, range_list, particals): 76 | # 首先,计算当前粒子群的pareto边界,将边界粒子加入到存档archiving中 77 | pareto_1 = pareto.Pareto_(in_, fitness_) 78 | curr_in, curr_fit = pareto_1.pareto() 79 | # 其次,在存档中根据支配关系进行第二轮筛选,将非边界粒子去除 80 | in_new = np.concatenate((archive_in, curr_in), axis=0) 81 | fitness_new = np.concatenate((archive_fitness, curr_fit), axis=0) 82 | pareto_2 = pareto.Pareto_(in_new, fitness_new) 83 | curr_archiving_in, curr_archiving_fit = pareto_2.pareto() 84 | # 最后,判断存档数量是否超过了存档阀值。如果超过了阀值,则清除掉一部分(拥挤度高的粒子被清除的概率更大) 85 | if ((curr_archiving_in).shape[0] > thresh): 86 | clear_ = archiving.clear_archiving(curr_archiving_in, curr_archiving_fit, mesh_div, range_list, particals) 87 | curr_archiving_in, curr_archiving_fit = clear_.clear_(thresh) 88 | return curr_archiving_in, curr_archiving_fit 89 | 90 | 91 | def update_gbest(archiving_in, archiving_fit, mesh_div, range_list, particals): 92 | get_g = archiving.get_gbest(archiving_in, archiving_fit, mesh_div, range_list, particals) 93 | return get_g.get_gbest() 94 | -------------------------------------------------------------------------------- /mopso/plot.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/24 4 | # encoding: utf-8 5 | import numpy as np 6 | import os 7 | import matplotlib.pyplot as plt 8 | import mopso.fitness_funs as fit 9 | 10 | 11 | class Plot_pareto: 12 | def __init__(self): 13 | # 绘制测试函数的曲面,(x1,x2)表示两位度的输入,(y1,y2)表示两位的适应值, 14 | self.x1 = np.linspace(0, 10, 100) 15 | self.x2 = np.linspace(0, 10, 100) 16 | self.x1, self.x2 = np.meshgrid(self.x1, self.x2) 17 | self.m, self.n = np.shape(self.x1) 18 | self.y1, self.y2 = np.zeros((self.m, self.n)), np.zeros((self.m, self.n)) 19 | self.dim = 4 20 | # for i in range(self.m): 21 | # for j in range(self.n): 22 | # [self.y1[i, j], self.y2[i, j]] = fit.fitness_plot([self.x1[i, j], self.x2[i, j]]) 23 | if not os.path.exists('./img_txt'): 24 | os.makedirs('./img_txt') 25 | print('创建文件夹img_txt:保存粒子群每一次迭代的图片') 26 | 27 | def show(self, in_, fitness_, archive_in, archive_fitness, i): # 28 | # accurate = [] 29 | # flops = [] 30 | # with open('./img_txt/pareto_fitness.txt', 'r', encoding='utf8') as f: 31 | # fitness_list = f.readlines() 32 | # for fitness in fitness_list: 33 | # accurate.append(eval(fitness.lstrip('\n').split(' ')[0])) 34 | # flops.append(eval(fitness.lstrip('\n').split(' ')[1])) 35 | # f.close() 36 | flops = fitness_[:, 1] 37 | accurate = fitness_[:, 0] 38 | # 共3个子图,第1、2/子图绘制输入坐标与适应值关系,第3图展示pareto边界的形成过程 39 | 40 | fig = plt.figure(13, figsize=(17, 5)) 41 | 42 | # ax1 = fig.add_subplot(131, projection='3d') 43 | # ax1.set_xlabel('input_x1') 44 | # ax1.set_ylabel('input_x2') 45 | # ax1.set_zlabel('fitness_y1') 46 | # ax1.plot_surface(self.x1, self.x2, self.y1, alpha=0.6) 47 | # ax1.scatter(in_[:, 0], in_[:, 1], fitness_[:, 0], s=20, c='blue', marker=".") 48 | # ax1.scatter(archive_in[:, 0], archive_in[:, 1], archive_fitness[:, 0], s=50, c='red', marker=".") 49 | # ax2 = fig.add_subplot(132, projection='3d') 50 | # ax2.set_xlabel('input_x1') 51 | # ax2.set_ylabel('input_x2') 52 | # ax2.set_zlabel('fitness_y2') 53 | # ax2.plot_surface(self.x1, self.x2, self.y2, alpha=0.6) 54 | # ax2.scatter(in_[:, 0], in_[:, 1], fitness_[:, 1], s=20, c='blue', marker=".") 55 | # ax2.scatter(archive_in[:, 0], archive_in[:, 1], archive_fitness[:, 1], s=50, c='red', marker=".") 56 | 57 | # ax1 = fig.add_subplot(131) 58 | # ax1.set_xlabel('迭代数') 59 | # ax1.set_ylabel('Accuracy') 60 | # ax1.scatter(in_[:, 0], in_[:, 1], fitness_[:, 0], s=10, c='blue', marker=".") 61 | # ax1.scatter(archive_in[:, 0], archive_in[:, 1], archive_fitness[:, 0], s=50, c='red', marker=".") 62 | # 63 | # ax2 = fig.add_subplot(132, projection='3d') 64 | # ax2.set_xlabel('迭代数') 65 | # ax2.set_ylabel('FLOPs') 66 | # ax2.scatter(in_[:, 0], in_[:, 1], fitness_[:, 1], s=20, c='blue', marker=".") 67 | # ax2.scatter(archive_in[:, 0], archive_in[:, 1], archive_fitness[:, 1], s=50, c='red', marker=".") 68 | 69 | ax3 = fig.add_subplot(133) 70 | ax3.set_xlim((0, 10 ** 10)) 71 | ax3.set_ylim((0, 1)) 72 | ax3.set_xlabel('FLOPs') 73 | ax3.set_ylabel('accuracy') 74 | ax3.scatter(archive_fitness[:, 1], archive_fitness[:, 0], s=30, c='red', marker=".", 75 | alpha=1.0) # archive_fitness[:, 0], archive_fitness[:, 1] 76 | ax3.scatter(fitness_[:, 1], fitness_[:, 0], s=10, c='blue', marker='.') 77 | # plt.show() 78 | plt.savefig('./img_txt/第' + str(i + 1) + '次迭代.png') 79 | print('第' + str(i + 1) + '次迭代的图片保存于 img_txt 文件夹') 80 | plt.close() 81 | -------------------------------------------------------------------------------- /OMOPSO.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/25 4 | import numpy as np 5 | from mopso import fitness_funs 6 | from mopso import init 7 | from mopso import update 8 | from mopso import plot 9 | from tqdm import tqdm 10 | 11 | import os 12 | 13 | os.environ['KMP_DUPLICATE_LIB_OK'] = 'True' # 避免libiomp5md.dll初始化错误 14 | 15 | 16 | class Mopso(object): 17 | def __init__(self, particals, w, c1, c2, range_list, blocks, thresh, mesh_div=10): 18 | self.w, self.c1, self.c2 = w, c1, c2 19 | self.mesh_div = mesh_div 20 | self.particals = particals 21 | self.thresh = thresh 22 | self.range_list = range_list 23 | self.dim = blocks # 块的个数等于维度 24 | self.max_v = [1, 1, 1, 1, (range_list[4][1] - range_list[4][0]) * 0.05] # 速度上限 25 | self.min_v = [1, 1, 1, 1, (range_list[4][1] - range_list[4][0]) * 0.05 * (-1)] # 速度下限 26 | self.plot_ = plot.Plot_pareto() 27 | 28 | def initialize(self): 29 | # 初始化粒子 30 | self.in_ = init.init_designparams(self.particals, self.range_list, self.dim) 31 | # 初始化粒子速度 32 | self.v_ = init.init_v(self.particals, self.min_v, self.max_v, self.dim) 33 | # 计算适应值 34 | self.evaluation_fitness() 35 | # 初始化个体最优 36 | self.in_p, self.fitness_p = init.init_pbest(self.in_, self.fitness_) 37 | # 初始化外部存档 38 | self.archive_in, self.archive_fitness = init.init_archive(self.in_, self.fitness_) 39 | print(self.archive_in.shape) 40 | # 初始化全局最优 41 | self.in_g, self.fitness_g = init.init_gbest(self.archive_in, self.archive_fitness, self.mesh_div, 42 | self.range_list, 43 | self.particals) 44 | 45 | def evaluation_fitness(self): 46 | # 计算适应值 47 | fitness_curr = [] 48 | for i in range(self.in_.shape[0]): 49 | fitness_curr.append(fitness_funs.fitness(self.in_[i], self.dim)) 50 | self.fitness_ = np.array(fitness_curr) # 适应值 51 | 52 | def update_(self): 53 | # 更新粒子坐标、粒子速度、适应值、个体最优、外部存档、全局最优 54 | self.v_ = update.update_v(self.v_, self.min_v, self.max_v, self.in_, self.in_p, self.in_g, self.w, self.c1, 55 | self.c2) 56 | self.in_ = update.update_in(self.in_, self.v_, self.range_list) 57 | self.evaluation_fitness() 58 | self.in_p, self.fitness_p = update.update_pbest(self.in_, self.fitness_, self.in_p, self.fitness_p) 59 | self.archive_in, self.archive_fitness = update.update_archive(self.in_, self.fitness_, self.archive_in, 60 | self.archive_fitness, self.thresh, self.mesh_div, 61 | self.range_list, self.particals) 62 | self.in_g, self.fitness_g = update.update_gbest(self.archive_in, self.archive_fitness, self.mesh_div, range_list 63 | , self.particals) 64 | 65 | def done(self, generations): 66 | self.initialize() 67 | self.plot_.show(self.in_, self.fitness_, self.archive_in, self.archive_fitness, -1) 68 | for i in tqdm(range(generations), desc='OMOPSO训练迭代中:'): 69 | self.update_() 70 | # print(self.archive_fitness) 71 | self.plot_.show(self.in_, self.fitness_, self.archive_in, self.archive_fitness, i) 72 | return self.archive_in, self.archive_fitness 73 | 74 | 75 | if __name__ == '__main__': 76 | w = 0.8 # 惯性因子 77 | c1 = 0.1 # 局部速度因子 78 | c2 = 0.1 # 全局速度因子 79 | 80 | particals = 3 # 粒子群的数量 20 50 81 | generations = 1 # 迭代次数 20 10 82 | 83 | mesh_div = 10 # 网格等分数量 84 | thresh = 300 # 外部存档阀值 85 | blocks = 4 # 粒子的维度(网络的块数量) 86 | range_list = [[4, 6], [4, 12], [4, 24], [4, 16], [8, 32]] # 前面四个是block中层数的范围,第五个是growth_rate的范围 87 | mopso_ = Mopso(particals, w, c1, c2, range_list, blocks, thresh, mesh_div) # 粒子群实例化 88 | pareto_in, pareto_fitness = mopso_.done(generations) # 经过epochs轮迭代后,pareto边界粒子 89 | pareto = pareto_in.reshape(len(pareto_in), 8) # 把三维pareto边界粒子转成二维 90 | np.savetxt("./img_txt/pareto_in.txt", pareto) # 保存pareto边界粒子的坐标 91 | np.savetxt("./img_txt/pareto_fitness.txt", pareto_fitness) # 打印pareto边界粒子的适应值 92 | # plot_ = plot.Plot_pareto() # txt画图1 93 | # plot_.show() # txt画图2 94 | print("\n", "pareto边界的坐标保存于:./img_txt/pareto_in.txt") 95 | print(" pareto边界的适应值保存于:./img_txt/pareto_fitness.txt") 96 | print("\n", "迭代结束,over") 97 | -------------------------------------------------------------------------------- /mopso/archiving.py: -------------------------------------------------------------------------------- 1 | # -*- 编码格式:utf-8 -*- 2 | # 作者:常冥 3 | # 创建时间:2022/5/24 4 | import numpy as np 5 | import random 6 | 7 | 8 | # d 9 | class mesh_crowd(object): 10 | def __init__(self, curr_archiving_in, curr_archiving_fit, mesh_div, range_list, particals): 11 | self.curr_archiving_in = curr_archiving_in # 当前存档中所有粒子的坐标 12 | self.curr_archiving_fit = curr_archiving_fit # 当前存档中所有粒子的适应值 13 | self.mesh_div = mesh_div # 等分因子,默认值为10等分 14 | 15 | self.num_ = self.curr_archiving_in.shape[0] # 存档中粒子数量 16 | 17 | self.particals = particals 18 | 19 | self.id_archiving = np.zeros(self.num_) # 各个粒子的id编号,检索位与curr_archiving的检索位为相对应 20 | self.crowd_archiving = np.zeros(self.num_) # 拥挤度矩阵,用于记录当前粒子所在网格的总粒子数,检索位与curr_archiving的检索为相对应 21 | self.probability_archiving = np.zeros(self.num_) # 各个粒子被选为gbest的概率,检索位与curr_archiving的检索位为相对应 22 | self.gbest_in = np.zeros((self.particals, self.curr_archiving_in.shape[1],curr_archiving_in.shape[2])) # 初始化gbest矩阵_坐标 23 | self.gbest_fit = np.zeros((self.particals, self.curr_archiving_fit.shape[1])) # 初始化gbest矩阵_适应值 24 | self.range_list = np.array(range_list) 25 | 26 | def cal_mesh_id(self, in_): 27 | # 计算网格编号id 28 | # 首先,将每个维度按照等分因子进行等分离散化,获取粒子在各维度上的编号。按照10进制将每一个维度编号等比相加(如过用户自定义了mesh_div_num的值,则按照自定义),计算出值 29 | id_ = 0 30 | for i in range(self.curr_archiving_in.shape[1]): # 四个block 31 | id_dim = int( 32 | (in_[i][0] - self.range_list[i][0]) * self.num_ / (self.range_list[i][1] - self.range_list[i][0])) 33 | id_ = id_ + id_dim * (self.mesh_div ** i) 34 | return id_ 35 | 36 | def divide_archiving(self): # 调用了网格序号生成的内部函数 37 | # 进行网格划分,为每个粒子定义网格编号 38 | for i in range(self.num_): 39 | self.id_archiving[i] = self.cal_mesh_id(self.curr_archiving_in[i]) 40 | 41 | def get_crowd(self): 42 | index_ = (np.linspace(0, self.num_ - 1, self.num_)).tolist() # 定义一个数组存放粒子集的索引号,用于辅助计算 43 | index_ = list(map(int, index_)) 44 | while (len(index_) > 0): 45 | index_same = [index_[0]] # 存放本次子循环中与index[0]粒子具有相同网格id所有检索位 46 | for i in range(1, len(index_)): 47 | if self.id_archiving[index_[0]] == self.id_archiving[index_[i]]: 48 | index_same.append(index_[i]) 49 | number_ = len(index_same) # 本轮网格中的总粒子数 50 | for i in index_same: # 更新本轮网格id下的所有粒子的拥挤度 51 | self.crowd_archiving[i] = number_ 52 | index_.remove(i) # 删除本轮网格所包含的粒子对应的索引号,避免重复计算 53 | 54 | 55 | class get_gbest(mesh_crowd): 56 | def __init__(self, curr_archiving_in, curr_archiving_fit, mesh_div_num, range_list, particals): 57 | super(get_gbest, self).__init__(curr_archiving_in, curr_archiving_fit, mesh_div_num, range_list, particals) 58 | self.divide_archiving() 59 | self.get_crowd() 60 | 61 | def get_probability(self): 62 | for i in range(self.num_): 63 | self.probability_archiving = 10.0 / (self.crowd_archiving ** 3) 64 | self.probability_archiving = self.probability_archiving / np.sum(self.probability_archiving) # 所有粒子的被选概率的总和为1 65 | 66 | def get_gbest_index(self): 67 | random_pro = random.uniform(0.0, 1.0) # 生成一个0到1之间的随机数 68 | for i in range(self.num_): 69 | if random_pro <= np.sum(self.probability_archiving[0:i + 1]): 70 | return i # 返回检索值 71 | 72 | def get_gbest(self): 73 | self.get_probability() 74 | for i in range(self.particals): 75 | gbest_index = self.get_gbest_index() 76 | for j in range(self.gbest_in.shape[1]): # gbest_in(2,4,2) 4 77 | for k in range(self.gbest_in.shape[2]): # 2 78 | self.gbest_in[i][j][k] = self.curr_archiving_in[gbest_index][j][k] # gbest矩阵_坐标 79 | self.gbest_fit[i] = self.curr_archiving_fit[gbest_index] # gbest矩阵_适应值 80 | return self.gbest_in, self.gbest_fit 81 | 82 | 83 | class clear_archiving(mesh_crowd): 84 | def __init__(self, curr_archiving_in, curr_archiving_fit, mesh_div_num, range_list, particals): 85 | super(get_gbest, self).__init__(curr_archiving_in, curr_archiving_fit, mesh_div_num, range_list) 86 | self.divide_archiving() 87 | self.get_crowd() 88 | 89 | def get_probability(self): 90 | for i in range(self.num_): 91 | self.probability_archiving = self.crowd_archiving ** 2 92 | 93 | def get_clear_index(self): # 按概率清除粒子,拥挤度高的粒子被清除的概率越高 94 | len_clear = (self.curr_archiving_in).shape[0] - self.thresh # 需要清除掉的粒子数量 95 | clear_index = [] 96 | while (len(clear_index) < len_clear): 97 | random_pro = random.uniform(0.0, np.sum(self.probability_archiving)) # 生成一个0到1之间的随机数 98 | for i in range(self.num_): 99 | if random_pro <= np.sum(self.probability_archiving[0:i + 1]): 100 | if i not in clear_index: 101 | clear_index.append(i) # 记录检索值 102 | return clear_index 103 | 104 | def clear_(self, thresh): 105 | self.thresh = thresh 106 | self.get_probability() 107 | clear_index = self.get_clear_index() 108 | gbest_index = self.get_gbest_index() 109 | self.curr_archiving_in = np.delete(self.curr_archiving_in[gbest_index], clear_index, axis=0) # 初始化gbest矩阵_坐标 110 | self.curr_archiving_fit = np.delete(self.curr_archiving_fit[gbest_index], clear_index, axis=0) # 初始化gbest矩阵_适应值 111 | return self.curr_archiving_in, self.curr_archiving_fit 112 | --------------------------------------------------------------------------------