遗传算法(GA/NSGA)优化神经网络 GA-BP
查了网上一些论文和代码,自己写了两个版本的GA-BP优化代码(实际应该为EGA-BP),从简到繁,从易到难。该文章代码基于Python3 pytorch进行编写。
版本说明:Part 1 为 利用 GA-BP 求神经网络最优的learning rate和隐藏层的神经元个数。Part 2 为利用 GA-BP 在神经网络训练中加入遗传变异等操作。Combined 即为结合 Part 1 and Part 2。
说明:主要便于方便代入自己的数据所以写了如下代码。自己用的时候主要可以修改Net中的网络结构,Train中的load_data变成自己要读的文件,选用合适的损失函数等等。geatpy为国内大佬写的遗传算法库,这里假设读者已经会用。关于GA和NSGA的区别只在于代码中运用模板的区别。
代码都有注释,可以试着读一读。因为作者是初学者的时候写的该代码,可能会有小错误。
可供测试data文件。测试文件说明:最后一列为label,除最后一列外为data。
两个版本对比:对上述测试文件对R^2 >= 0.96为指标,在我的破电脑上运行时间part1≈1min,combined≈20s,我没有测试过别的例子的速度,对于combined不能保证每个例子都能用,有一定缺陷,不同问题可以选用不同的版本进行使用。
GA-BP Part1: 利用 GA 求神经网络最优的learning rate和隐藏层的神经元个数
import torch.nn as nn
import torch
import geatpy as ea
import numpy as np
import os
from sklearn.model_selection import train_test_splitinput_dimension = 7
output_dimension = 1device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")# 自定义网络
class Net(nn.Module):def __init__(self, neurons_num):super(Net, self).__init__()self.hidden0 = torch.nn.Linear(input_dimension, neurons_num)self.hidden1 = torch.nn.Linear(neurons_num, neurons_num)self.hidden2 = torch.nn.Linear(neurons_num, neurons_num)self.hidden3 = torch.nn.Linear(neurons_num, output_dimension)def forward(self, x):x = torch.relu(self.hidden0(x))x = torch.relu(self.hidden1(x))x = torch.relu(self.hidden2(x))x = self.hidden3(x)return x# r^2 函数
def r2(y_test, y):return 1 - ((y_test - y) ** 2).sum(axis=0) / ((y.mean() - y) ** 2).sum(axis=0)# 神经网络训练
class Train:train_x, train_y, test_x, test_y, model, lr, neurons_num, x, y, optimizer = None, None, None, None, None, None, None, None, None, Nonedef __init__(self):self.use_gpu = torch.cuda.is_available()# 选用合适的 loss function# self.loss_fn = torch.nn.CrossEntropyLoss()self.loss_fn = torch.nn.MSELoss()self.load_data()# 自定义读入数据def load_data(self):with open('data.csv') as f:df = np.loadtxt(f, delimiter=",", skiprows=0)self.x = df[:, :-1]self.y = df[:, -1:]f.close()# 重新创建不一样的 train and test data setdef reload(self, learing_rate, neurons_num):train_x, test_x, train_y, test_y = train_test_split(self.x, self.y, test_size=0.3, random_state=42)self.train_x = torch.from_numpy(train_x).float().to(device)self.train_y = torch.from_numpy(train_y).float().to(device)self.test_x = torch.from_numpy(test_x).float().to(device)self.test_y = torch.from_numpy(test_y).float().to(device)self.model = Net(neurons_num).to(device)self.lr = learing_rateself.optimizer = torch.optim.Adam(self.model.parameters(), lr=self.lr)# 训练神经网络,返回 r^2 值def train(self, n=10):for epoch in range(n):model_output = self.model(self.train_x)loss = self.loss_fn(model_output, self.train_y)self.optimizer.zero_grad()loss.backward()self.optimizer.step()model_output = self.model(self.test_x)return float(r2(model_output.data, self.test_y).cpu())# 自定义 GA,对 learning rate and neurons num 经行改变
class My_nsga(ea.Problem):def __init__(self, epoch):if "result" not in os.listdir():os.makedirs("./result")name = 'GA-NET'M = 1maxormins = [-1] * MDim = 2varTypes = [1] * Dimlb = [10, 10]ub = [5000, 100]lbin = [1] * Dimubin = [1] * Dimself.epoch = epochself.train = Train()ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)# 目标函数即神经网络返回值def evalVars(self, Vars):ans = np.zeros(len(Vars), dtype=float).reshape(len(Vars), 1)for i in range(len(Vars)):self.train.reload(Vars[i][0] / 100000, Vars[i][1])# 括号内参数表示单个神经网络训练次数data = self.train.train(self.epoch)print("learning rate = {}, neurons num = {}, R^2 = {}".format(Vars[i][0] / 10000, Vars[i][1], data))torch.save(self.train.model, "./result/lr={}num={}epoch={}r2={}.pt".format(Vars[i][0] / 100000, Vars[i][1], self.epoch, round(data, 3)))# 达到一定准确率停止if data >= 1:torch.save(self.train.model, "lr{}=num={}epoch={}r2={}.pt".format(Vars[i][0] / 100000, Vars[i][1], self.epoch, round(data, 3)))exit("Find!")ans[i][0] = datareturn ans# 运行 GA
class Run_nsga:def __init__(self, epoch=10, ndind=10, maxgen=10):problem = My_nsga(epoch)myAlgorithm = ea.soea_EGA_templet(problem, ea.Population(Encoding='RI', NIND=ndind), MAXGEN=maxgen, logTras=0)myAlgorithm.drawing = 0res = ea.optimize(myAlgorithm, seed=1, verbose=False, drawing=0, outputMsg=True, drawLog=False, saveFlag=False, dirName='result')print(res)print(res['Vars'][0])if __name__ == "__main__":# 括号内参数表示单个神经网络训练次数,种群数,GA迭代数Run_nsga(10000, 10, 30)
GA-BP Part2 and Combined: 在神经网络中也加入GA改变网络权值来加快神经网络训练速度
参考代码原地址:遗传算法(GA) - 优化神经网络(CNN) - pytorch(亲测可用)_Vertira的博客-CSDN博客_遗传算法优化cnn
import random
import torch.nn as nn
import torch
import geatpy as ea
import numpy as np
import os
import copy
from sklearn.model_selection import train_test_split
from torch.distributions import Categoricalinput_dimension = 7
output_dimension = 1
# 该参数在数据较少的输入时也相应变少,最好使得 data_size / batch_size = NetGA_pop_size
batch_size = 100device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")# 自定义网络
class Net(torch.nn.Module):def __init__(self, neurons_num, lr):super(Net, self).__init__()self.layers = torch.nn.Sequential(torch.nn.Linear(input_dimension, neurons_num),torch.nn.ReLU(),torch.nn.Linear(neurons_num, neurons_num),torch.nn.ReLU(),torch.nn.Linear(neurons_num, neurons_num),torch.nn.ReLU(),torch.nn.Linear(neurons_num, output_dimension))self.optimizer = torch.optim.Adam(self.parameters(), lr=lr)def forward(self, x):return self.layers(x)def set_layer(self, layers):self.layers = layers# r^2 函数
def r2(y_test, y):return 1 - ((y_test - y) ** 2).sum(axis=0) / ((y.mean() - y) ** 2).sum(axis=0)# Data 类,以便带入自己的data
class Data(torch.utils.data.Dataset):def __init__(self, data, label):self.x = dataself.y = labelself.len = len(self.y)def __len__(self):return self.lendef __getitem__(self, item):return self.x[item], self.y[item]# GA 优化的神经网络训练
class NetTrainGA:def __init__(self, _pop_size=10, _r_mutation=0.1, _p_mutation=0.1, _elite_num=6, stddev=0.1):self.test_x, self.test_y, self.trainSetLoader, self.x, self.y = None, None, None, None, None # 数据存储self.pop_size = _pop_size # 种群数self.r_mutation = _r_mutation # 变异里,数据变异的概率self.p_mutation = _p_mutation # 变异概率self.elite_num = _elite_num # 精英数self.chroms = [] # 储存所有 modelself.stddev = stddev # 网络权值步进大小的最大值self.criterion = nn.MSELoss() # 计算 loss 的方法self.model = None # 全局最优解 modelself.load_data() # 加载数据self.lr = 0.001 # learning rate# 自定义读入数据def load_data(self):with open('data.csv') as f:df = np.loadtxt(f, delimiter=",", skiprows=0)self.x = df[:, :-1]self.y = df[:, -1:]self.x = torch.from_numpy(self.x).float().to(device)self.y = torch.from_numpy(self.y).float().to(device)f.close()# 创建 train and testself.re_data_split()# 重新创建不一样的 train and test data set,便于带入到 reload 函数中def re_data_split(self):train_x, self.test_x, train_y, self.test_y = train_test_split(self.x, self.y, test_size=0.3, random_state=42)trainSet = Data(train_x, train_y)# train 池化self.trainSetLoader = torch.utils.data.DataLoader(trainSet, batch_size=batch_size, shuffle=False)def reload(self, learning_rate, neurons_num):# 可选择是否重新分类 test and train# self.re_data_splitself.lr = learning_ratefor i in range(self.pop_size):net = Net(neurons_num, learning_rate).to(device)self.chroms.append(net)# 训练神经网络,返回R^2的值"""对下列博客代码进行改进https://blog.csdn.net/Vertira/article/details/122561056"""def train(self, n):for epoch in range(n):result = [{'pop': i, 'train_acc': float("-inf")} for i in range(self.pop_size)]# 为种群训练不同的数据for step, (batch_x, batch_y) in enumerate(self.trainSetLoader):self.netTrain(batch_x, batch_y, (step + epoch) % self.pop_size)# 计算 train accuracyfor i in range(self.pop_size):output = self.chroms[i](self.test_x)result[i]["train_acc"] = float(r2(output.data, self.test_y).cpu())result = sorted(result, key=lambda x: x['train_acc'], reverse=True)# self.model 即为类中最优解,可直接套用 test 经行预测self.model = self.chroms[result[0]['pop']]self.selection(result)model_output = self.model(self.test_x)return float(r2(model_output.data, self.test_y).cpu())def netTrain(self, batch_x, batch_y, now):model = self.chroms[now]optimizer = model.optimizer# 选择每次神经网络训练次数,这个参数影响了训练速度,但跟多时候会影响梯度,很多时候我也不知道为什么梯度就没了,所以太小梯度可能变0或None导致训练停滞,太大训练的有可能变慢for j in range(100):output = model(batch_x)optimizer.zero_grad()train_loss = self.criterion(output, batch_y).requires_grad_()train_loss.backward()optimizer.step()# 保留精英个数,并进行交叉操作至种群数满,最后进行变异操作def selection(self, result):elites = [e['pop'] for e in result[:self.elite_num]]# 保留 elites 个精英children = [copy.deepcopy(self.chroms[i]) for i in elites]# 轮盘赌来选择交配的个体,使用 softmax 处理负数问题prob = torch.softmax(torch.tensor([i["train_acc"] for i in result]), dim=0)m = Categorical(prob)# 随机选择两个交配直至达到种群大小while len(children) < self.pop_size:# 随机选择两个进行 self.crossover交配pair = [result[m.sample()]['pop'], result[m.sample()]['pop']]children.append(self.crossover(pair))del self.chroms[:]self.chroms[:] = children# 变异且不变异精英for i in range(self.elite_num, self.pop_size):# 满足变异概率if random.random() < self.p_mutation:mutated_child = self.mutation(i)del self.chroms[i]self.chroms.insert(i, mutated_child)def crossover(self, _selected_pop):if _selected_pop[0] == _selected_pop[1]:return copy.deepcopy(self.chroms[_selected_pop[0]])chrom1 = copy.deepcopy(self.chroms[_selected_pop[0]])chrom2 = copy.deepcopy(self.chroms[_selected_pop[1]])chrom1_layers = nn.ModuleList(chrom1.modules())chrom2_layers = nn.ModuleList(chrom2.modules())child = torch.nn.Sequential()for i in range(len(chrom1_layers)):layer1 = chrom1_layers[i]layer2 = chrom2_layers[i]# 对 Linear 层随机交换if isinstance(layer1, nn.Linear):child.add_module(str(i - 2), layer1 if random.random() < 0.5 else layer2)elif isinstance(layer1, (torch.nn.Sequential, Net)):passelse:child.add_module(str(i - 2), layer1)chrom1.set_layer(child)chrom1.optimizer = torch.optim.Adam(chrom1.parameters(), lr=self.lr)return chrom1def mutation(self, _selected_pop):child = torch.nn.Sequential()chrom = copy.deepcopy(self.chroms[_selected_pop])chrom_layers = nn.ModuleList(chrom.modules())# 变异比例,选择几层进行变异for i, layer in enumerate(chrom_layers):if isinstance(layer, nn.Linear):# 变异 Linear 层,且有一定变异比例if random.random() < self.r_mutation:# 提取权重weights = layer.weight.detach()# 更改权重w = weights + torch.normal(0, self.stddev, weights.shape).float().to(device)# 重新设置layer.weight = torch.nn.Parameter(w)child.add_module(str(i - 2), layer)elif isinstance(layer, (torch.nn.Sequential, Net)):passelse:child.add_module(str(i - 2), layer)chrom.set_layer(child)chrom.optimizer = torch.optim.Adam(chrom.parameters(), lr=self.lr)return chrom# 自定义 GA,对 learning rate and neurons num 经行改变
class My_nsga(ea.Problem):def __init__(self, epoch):if "result" not in os.listdir():os.makedirs("./result")name = 'GA-NET'M = 1maxormins = [-1] * MDim = 2varTypes = [1] * Dimlb = [10, 10]ub = [500, 100]lbin = [1] * Dimubin = [1] * Dimself.count = 1self.epoch = epochself.train = NetTrainGA()ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)# 目标函数即神经网络返回值def evalVars(self, Vars):ans = np.zeros(len(Vars)).reshape(len(Vars), 1)for i in range(len(Vars)):self.train.reload(Vars[i][0] / 10000, Vars[i][1])# 括号内参数表示单个神经网络训练次数data = self.train.train(self.epoch)print("learning rate = {}, neurons num = {}, R^2 = {}".format(Vars[i][0] / 10000, Vars[i][1], round(data, 3)))torch.save(self.train.model, "./result/lr{}num{}epoch{}r2{}.pt".format(Vars[i][0] / 10000, Vars[i][1], self.epoch, round(data, 3)))# 达到一定准确率停止if data >= 1:torch.save(self.train.model, "lr{}num{}epoch{}r2{}.pt".format(Vars[i][0] / 10000, Vars[i][1], self.epoch, round(data, 3)))return 0ans[i] = float(data)return ans# 运行 GA
class Run_nsga:def __init__(self, epoch=10, ndind=10, maxgen=10):problem = My_nsga(epoch)myAlgorithm = ea.soea_EGA_templet(problem, ea.Population(Encoding='RI', NIND=ndind), MAXGEN=maxgen, logTras=0)myAlgorithm.drawing = 0res = ea.optimize(myAlgorithm, seed=1, verbose=False, drawing=0, outputMsg=True, drawLog=False, saveFlag=False, dirName='result')print(res)print(res['Vars'][0])if __name__ == "__main__":# 括号内参数表示单个神经网络训练次数,种群数,GA迭代数Run_nsga(100, 10, 10)"""# 也可以单独调用 NetTrainGA,设置初始参数netga = NetTrainGA()# learning rate, neurons_numnetga.reload(0.001, 30)# epochprint(netga.train(1000))"""
以上均为回归损失计算方法,要计算分类损失只需要修改如下代码。
详情也可见该文章
# 修改输出 dimension
output_dimension = 28
# 修改 y 维度
self.y = df[:, -1]
# 修改损失函数
self.criterion = nn.CrossEntropyLoss()
# 修改 train_y 从 float 变成 long
train_y = torch.from_numpy(train_y).long()
# 修改 train_acc 计算方法
result[i]["train_acc"] = float((output.argmax(dim=1) == self.test_y).sum()) / len(self.test_y)
# 修改 train 输出
return float((model_output.argmax(dim=1) == self.test_y).sum() / len(self.test_y))
Part2 版本有缺陷,optimizer是整个GA优化神经网络权值项目的bug所在,该文章最后更新时间为 2023/4/15: 加入 gpu 训练。如果有更好的改进方法欢迎一起讨论。
个人博客地址,欢迎访问https://www.pancake2021.work/?p=1511
遗传算法(GA/NSGA)优化神经网络 GA-BP相关推荐
- 【验证码识别】基于遗传算法优化OUST结合BP神经网络实现数字验证码识别含Matlab源码
1 简介 本项目基于MATLAB完成数字验证码识别的GUI设计,图像处理,验证码生成.识别等功能.采用BP神经网络来实现对验证码图像的识别.验证码的识别,大概分为图片预处理.分割字符.识别字符三个过程 ...
- 遗传算法(四)MATLAB GA工具箱使用 附解TSP问题
笔记(一) 基本遗传算法 笔记(二) 遗传算法的优化改进 笔记(三) 遗传算法解组合优化 笔记(四) MATLAB遗传算法工具箱使用 基本使用 1. 直接参见函数ga 函数原型:[x fval] = ...
- 遗传算法(Genetic algorithm,GA)前世今生(python实现)
遗传算法(Genetic algorithm,GA)模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法. 19世纪40年代,有学者开始研究利 ...
- 【多目标优化】2. 非支配排序遗传算法 —(NSGA、NSGA-II)
[多目标优化]1. 多目标优化的相关基本概念 [多目标优化]2. 非支配排序遗传算法 -(NSGA.NSGA-II) [多目标优化]3. 基于分解的多目标进化算法 -(MOEAD) 1. 非支配排序遗 ...
- 【MATLAB教程案例12】基于GA遗传优化算法的函数极值计算matlab仿真及其他应用
FPGA教程目录 MATLAB教程目录 -------------------------------------------------------------------------------- ...
- 遗传算法优化神经网络实现数据预测
作者 | 李秋键 责编 | 寇雪芹 头图 | 下载于视觉中国 出品 | AI科技大本营(ID:rgznai100) 引言 随着人工智能和大数据的发展,大量实验和数据处理等流程对算法的要求也随之变得越来 ...
- 模糊聚类FCM算法和基于GA遗传优化的FCM聚类算法matlab仿真
目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 在各种糊聚类算法中 ,模糊C -均值聚类算法FCM (FuzzyC MeanClusteri ...
- 【负荷预测】基于蚂蚁优化算法的BP神经网络在负荷预测中的应用研究(Matlab完整代码实现)
目录 0 知识回顾 1 ACO-BP算法 2 ACO-BP算法基本思路 3 具体步骤 4 Matlab代码实现 5 运行结果 6 参考文献 7 写在最后 0 知识回顾 智能优化算法-蚁群算法(Pyth ...
- 蚁群算法优化神经网络matlab源程序,粒子群优化神经网络的程序大集合
粒子群程序集合 866867259psobp psobp.m pso(粒子群算法)优化神经网络 粒子群算法(PSO)应用于神经网络优化[matlab] PSOt A Particle Swarm Op ...
最新文章
- TortoiseSVN status cache占用CPU高
- .Net学习笔记----2015-07-10(GDI绘图)
- hbase异步客户端连接-非阻塞并发模式实验记录(还没弄完)
- oracle日期数据格式,oracle日期数据格式
- c语言链表常错,C语言链表,哪里错了?
- Codeforces 216D Spider#39;s Web 树状数组+模拟
- shiro访问html没有验证码,Shiro在web应用中实现验证码、回显登录失败信息
- 生产者消费者 java实现_Java生产者消费者的三种实现
- java 2 %3c%3c 3_Shiro 实战(二)-授权
- TLS1.3---密钥的计算
- 计算机专业理论试题答案最新,2017年计算机一级考试题库及答案
- python操作word文档,表格中插入文字及照片
- CSDN每日打卡已经2周,进展如何?,【2021Python最新学习路线】
- 人人开源 / renren-security/小记(二)
- 2022最新Java后端面试题(带答案),重点都给画出来了!你不看?
- 空客设立多个零碳研究机构 致力设计制造氢动力飞机
- 高德置地广场UTOPA HUB来了!弄潮玩家们的杭州新聚场!
- 数字孪生流域工程建设研究案例
- 【PPT模板53】紫色渐变商务风PPT模板
- 卯の花曇(うのはなぐもり) どんなお天気でも
热门文章
- 查找会议的地址和时间
- 6W字的Hive讲解只为你更懂它
- Roboware安装
- 基于百度AI Studio的药物设计——NAMD自由能微扰计算(FEP笔记本)
- Spring5 里边的新玩法!这种 URL 请求让我涨见识了!
- 【C语言】转义字符\xhh和\ddd到底如何判断?被兔子个数支配的恐惧你也有吗?(每日小细节001)
- Git常用命令(Git常用命令)
- Linux命令入门教程(二):目录基础篇
- QT tableview内置控件
- 所生成项目的处理器架构“MSIL”与引用“***”的处理器架构“x86”不匹配。这种不匹配可能会导致运行时失败。请考虑通过配置管理器更改您的项目的目标处理器架构,以使您的项目与引用间的处理器架构...