Geatpy遗传算法在曲线寻优上的初步探究
园子里关于遗传算法的教案不少,但基于geatpy框架的并未多见,故分享此文以作参考,还望广大园友多多指教!
Geatpy出自三所名校联合团队之手,是遗传算法领域的权威框架(python),其效率之高、应用领域之广远胜诸多第三方工具,此处不作赘述,直接上链接:
官网:http://www.geatpy.com/start
源码:https://github.com/geatpy-dev/geatpy/tree/master/geatpy
使用Geatpy需要安装geatpy模块(pip install geatpy),linux下如果装完后import时出现报错,可以下载我帖尾链接里的wheel文件进行安装。
言归正传,根据经典的遗传算法流程,无外乎这几个步骤:种群初始化 ->(适应度评价 -> 遴选 -> 交叉 -> 变异)<- 循环进化直至终止条件达标。
当然,有关遗传算法的原理和过程不做深讨,本文旨在剖析遗传算法在阵曲线寻优上的高效应用,我写了一个简单示例来帮助大家更好的理解,代码如下:
# -*- coding: utf-8 -*-
"""punishing.py - 罚函数demo""" import numpy as np def punishing(LegV, FitnV):
FitnV[np.where(LegV == 0)[0]] = 0
return FitnV
# -*- coding: utf-8 -*-
""" aimfc.py即目标函数,本例通过输入每一代的染色体,由自定义评价函数计算与目标曲线的面积差,作为目标函数值ObjV来输出 """ import numpy as np def MakeObjCurve(width):
''' 创建目标曲线,此处定义为一组正弦波拼接序列 '''
n1 = width//3
n2 = width - 2*n1
x1=np.cos(np.arange(0,n1)) * 1
x2=np.cos(np.arange(0,n1)) * 4
x3=np.cos(np.arange(0,n2)) * 2
ObjCurve=np.hstack((x1,x2,x3))
return ObjCurve def CalScore(chrom):
''' 返回染色体与目标曲线之间的面积的倒数作为评分值 '''
objCurve = MakeObjCurve(len(chrom))
area = chrom - objCurve
area *= 10**5 #调整系数确保分值不受小数项干扰
score = 1 / np.dot(area, area) #计算差值的平方和以简化求面积过程
return score def myEvaFunc(chroms):
''' 自定义评价函数,以评分值作为目标函数值 '''
scores = []
for chrom in chroms:
score = CalScore(chrom)
scores.append(score)
scores = np.array([scores]).T
return scores def aimfuc(Phen, LegV): ObjV = myEvaFunc(Phen)
exIdx = np.argmin(ObjV[:, 0]) # 惩罚方法2: 标记非可行解在可行性列向量中对应的值为0,并编写punishing罚函数来修改非可行解的适应度。
# 也可以不写punishing,因为Geatpy内置的算法模板及内核已经对LegV标记为0的个体的适应度作出了修改。
# 使用punishing罚函数实质上是对非可行解个体的适应度作进一步的修改
LegV[exIdx] = 0 # 对非可行解作出标记,使其在可行性列向量中对应的值为0,此处标记的是得分最小项 return [ObjV, LegV]
# -*- coding: utf-8 -*-
""" main.py即主函数,本例仅用于演示“已知曲线寻优”的过程 """ import numpy as np
import geatpy as ga
import time
import matplotlib.pyplot as plt def search_objects(directory):
directory=os.path.normpath(directory) #规格化,防止分隔符造成的差异
if not os.path.isdir(directory):
raise IOError("The directory '"+"' doesn't exist!")
objects={}
for curdir,substrs,files in os.walk(directory):
for jpeg in (file for file in files if file.endswith('.csv')):
path=os.path.join(curdir,jpeg)
label=path.split(os.path.sep)[-2]
if label not in objects:
objects[label]=[]
objects[label].append(path)
return objects def sga_mps_real_templet(AIM_M, AIM_F, PUN_M, PUN_F, FieldDRs, problem, maxormin, MAXGEN, NIND, SUBPOP, GGAP, selectStyle, recombinStyle, recopt, pm, distribute, drawing = 1):
""" 基于多种群独立进化单目标编程模板(实值编码),各种群独立将父子两代合并进行选择,采取精英保留机制 """
#==========================初始化配置===========================
GGAP = 0.5 # 因为父子合并后选择,因此要将代沟设为0.5以维持种群规模
# 获取目标函数和罚函数
aimfuc = getattr(AIM_M, AIM_F) # 获得目标函数
if PUN_F is not None:
punishing = getattr(PUN_M, PUN_F) # 获得罚函数
NVAR = FieldDRs[0].shape[1] # 得到控制变量的个数
# 定义全局进化记录器,初始值为nan
pop_trace = (np.zeros((MAXGEN ,2)) * np.nan)
pop_trace[:, 0] = 0
# 定义变量记录器,记录控制变量值,初始值为nan
var_trace = (np.zeros((MAXGEN ,NVAR)) * np.nan)
"""=========================开始遗传算法进化======================="""
start_time = time.time() # 开始计时
# 对于各个网格分别进行进化,采用全局进化记录器记录最优值
for index in range(len(FieldDRs)): # 遍历各个子种群,各子种群独立进化,互相不竞争
FieldDR = FieldDRs[index]
if problem == 'R':
Chrom = ga.crtrp(NIND, FieldDR) # 生成初始种群
elif problem == 'I':
Chrom = ga.crtip(NIND, FieldDR)
LegV = np.ones((NIND, 1)) # 初始化种群的可行性列向量
[ObjV, LegV] = aimfuc(Chrom, LegV) # 求初始种群的目标函数值
repnum = 0 # 初始化重复个体数为0
ax = None # 存储上一帧图形
gen = 0
badCounter = 0 # 用于记录在“遗忘策略下”被忽略的代数
# 开始进化!!
while gen < MAXGEN:
if badCounter >= 10 * MAXGEN: # 若多花了10倍的迭代次数仍没有可行解出现,则跳出
break
# 进行遗传算子,生成子代
SelCh = ga.recombin(recombinStyle, Chrom, recopt, SUBPOP) # 重组
if problem == 'R':
SelCh = ga.mutbga(SelCh,FieldDR, pm) # 变异
if repnum > Chrom.shape[0] * 0.01: # 当最优个体重复率高达1%时,进行一次高斯变异
SelCh = ga.mutgau(SelCh, FieldDR, pm) # 高斯变异
elif problem == 'I':
SelCh = ga.mutint(SelCh, FieldDR, pm)
LegVSel = np.ones((SelCh.shape[0], 1)) # 初始化育种种群的可行性列向量
[ObjVSel, LegVSel] = aimfuc(SelCh, LegVSel) # 求育种种群的目标函数值
# 父子合并
Chrom = np.vstack([Chrom, SelCh])
ObjV = np.vstack([ObjV, ObjVSel])
LegV = np.vstack([LegV, LegVSel])
FitnV = ga.ranking(maxormin * ObjV, LegV, None, SUBPOP) # 适应度评价
if PUN_F is not None:
FitnV = punishing(LegV, FitnV) # 调用惩罚函数
repnum = len(np.where(ObjV[np.argmax(FitnV)] == ObjV)[0]) # 计算最优个体重复数
# 记录进化过程
bestIdx = np.argmax(FitnV)
if (LegV[bestIdx] != 0) and ((np.isnan(pop_trace[gen,1])) or ((maxormin == 1) & (pop_trace[gen,1] >= ObjV[bestIdx])) or ((maxormin == -1) & (pop_trace[gen,1] <= ObjV[bestIdx]))):
feasible = np.where(LegV != 0)[0] # 排除非可行解
pop_trace[gen,0] += np.sum(ObjV[feasible]) / ObjV[feasible].shape[0] / len(FieldDRs) # 记录种群个体平均目标函数值
pop_trace[gen,1] = ObjV[bestIdx] # 记录当代目标函数的最优值
var_trace[gen,:] = Chrom[bestIdx, :] # 记录当代最优的控制变量值
# 绘制动态图
if drawing == 2:
ax = ga.sgaplot(pop_trace[:,[1]],'子种群'+str(index+1)+'各代种群最优个体目标函数值', False, ax, gen)
badCounter = 0 # badCounter计数器清零
else:
gen -= 1 # 忽略这一代(遗忘策略)
badCounter += 1
if distribute == True: # 若要增强种群的分布性(可能会造成收敛慢)
idx = np.argsort(ObjV[:, 0], 0)
dis = np.diff(ObjV[idx,0]) / (np.max(ObjV[idx,0]) - np.min(ObjV[idx,0]) + 1)# 差分计算距离的修正偏移量
dis = np.hstack([dis, dis[-1]])
dis = dis + np.min(dis) # 修正偏移量+最小量=修正绝对量
FitnV[idx, 0] *= np.exp(dis) # 根据相邻距离修改适应度,突出相邻距离大的个体,以增加种群的多样性
[Chrom, ObjV, LegV] = ga.selecting(selectStyle, Chrom, FitnV, GGAP, SUBPOP, ObjV, LegV) # 选择
gen += 1
end_time = time.time() # 结束计时
times = end_time - start_time
# 后处理进化记录器
delIdx = np.where(np.isnan(pop_trace))[0]
pop_trace = np.delete(pop_trace, delIdx, 0)
var_trace = np.delete(var_trace, delIdx, 0)
if pop_trace.shape[0] == 0:
raise RuntimeError('error: no feasible solution. (有效进化代数为0,没找到可行解。)')
# 输出结果
if maxormin == 1:
best_gen = np.argmin(pop_trace[:, 1]) # 记录最优种群是在哪一代
best_ObjV = np.min(pop_trace[:, 1])
elif maxormin == -1:
best_gen = np.argmax(pop_trace[:, 1]) # 记录最优种群是在哪一代
best_ObjV = np.max(pop_trace[:, 1])
print('最优的目标函数值为:%s'%(best_ObjV))
print('最优的控制变量值为:')
for i in range(NVAR):
print(var_trace[best_gen, i])
print('有效进化代数:%s'%(pop_trace.shape[0]))
print('最优的一代是第 %s 代'%(best_gen + 1))
print('时间已过 %s 秒'%(times))
# 绘图
if drawing != 0:
ga.trcplot(pop_trace, [['种群个体平均目标函数值', '种群最优个体目标函数值']])
# 返回进化记录器、变量记录器以及执行时间
return [pop_trace, var_trace, times, best_gen] # 获取函数接口地址
AIM_M = __import__('aimfuc')
PUN_M = __import__('punishing')
POP_SIZE = 300 # 种群高度
CHROM_LENGTH = 20 # 染色体宽度
max_generation = 150 # 进化代数
chrom_bottom = -4 #染色体数值下限
chrom_top = 4 #染色体数值上限 # 变量设置
x = []; b = []
for i in range(CHROM_LENGTH):
x.append([chrom_bottom, chrom_top]) # 自变量的范围
b.append([0, 0]) # 自变量是否包含下界
ranges=np.vstack(x).T # 生成自变量的范围矩阵
borders = np.vstack(b).T # 生成自变量的边界矩阵
precisions = [1]*CHROM_LENGTH # 在二进制/格雷码编码中代表自变量的编码精度,当控制变量是连续型时,根据crtfld参考资料,该变量只表示边界精度,故设置为一定的正数即可
# 生成网格化后的区域描述器集合
FieldDRs = []
for i in range(1):
FieldDRs.append(ga.crtfld(ranges, borders, precisions)) # 调用编程模板(设置problem = 'R'处理实数型变量问题,详见该算法模板的源代码)
[pop_trace, var_trace, times, best_gen] = sga_mps_real_templet(AIM_M, 'aimfuc', PUN_M, 'punishing',
FieldDRs, problem = 'R', maxormin = -1, MAXGEN = max_generation, NIND = POP_SIZE, SUBPOP = 1, GGAP = 0.9, \
selectStyle = 'tour', recombinStyle = 'xovdprs', recopt = 0.9, pm = 0.3, distribute = True, drawing = 1) bstChrom = var_trace[best_gen]
objCurve = AIM_M.MakeObjCurve(CHROM_LENGTH) plt.ion()
fig = plt.figure('曲线寻优演示',facecolor='lightgray')
ax1 = fig.add_subplot(2, 1, 1)
ax2 = fig.add_subplot(2, 1, 2)
ax1.set_title("Evaluation Map")
ax1.grid(axis='y', linestyle=':')
for i in range(max_generation):
if i%5==0:
ax1.plot(var_trace[i], 'o-')
ax2.cla()
ax2.set_title("最优染色体[gen:%i]"%(i+1))
ax2.plot(var_trace[i], 'o-', c='dodgerblue')
plt.pause(0.001)
ax2.cla()
ax2.grid(axis='y', linestyle=':')
ax2.plot(objCurve, 'o-', c='orangered', label='目标曲线')
ax2.plot(bstChrom, 'o-', c='dodgerblue', label='最优染色体[gen:%i]'%(best_gen+1))
plt.legend()
plt.ioff()
plt.show()
请注意,此处我已将模板函数单独放到主函数中以便大家更好的理解,返回值中增加了最优代数以便后续图例的显示。
本例采用的进化模板是sga_mps_real_templet,基于多种群进化单目标(实数值),用于实现寻找目标曲线。
目标曲线的定义函数在aimfc.py的MakeObjCurve函数中,本例为3段振幅不同的cos函数拼接而成的模拟曲线,宽度20。
种群初始值设置:种群高度300、染色体宽度20(与目标曲线宽度保持一致)、进化代数150、染色体数值上下限[-4,4](与目标曲线的上下限保持一致)。
接着我们开始寻优,通过CalScore函数计算每代种群的每条染色体与目标函数之间的差值,经过一定的系数转换得到评分值(差值越大,评分越低),以单目标(1列)形式输出,由geatpy的ranking函数来决定适应度评价,然后继续遴选、交叉、变异,如此循环往复,直至达到近似目标值时终止(本例设定为150代时终止),寻优过程如下图:

最优的目标函数值为:7.287567405118076e-09
有效进化代数:150
最优的一代是第 148 代
时间已过 1.3259999752044678 秒
可以看到从50代左右优化曲线开始显著上扬,直到130代左右逐渐平缓,并且耗时非常少,来看寻优结果图:

可以看到除了第6个点的数值有细微差异之外,其他点几乎都是吻合的,基本实现了目标曲线的寻求。
之所以抛出本例,最重要的一点在于geatpy遗传算法不仅能寻求已知的目标函数,还可以通过自定义的评分体系或第三方接口来参与实现寻优过程,只需将CalScore函数稍作改动即可,以上。
【wheel文件】: https://pan.baidu.com/s/1BwLq_m3Dd5RMqatvTYXrAw 提取码: vgkz
Geatpy遗传算法在曲线寻优上的初步探究的更多相关文章
- MATLAB神经网络(4) 神经网络遗传算法函数极值寻优——非线性函数极值寻优
4.1 案例背景 \[y = {x_1}^2 + {x_2}^2\] 4.2 模型建立 神经网络训练拟合根据寻优函数的特点构建合适的BP神经网络,用非线性函数的输入输出数据训练BP神经网络,训练后的B ...
- 小程序组件化框架 WePY 在性能调优上做出的探究
作者:龚澄 导语 性能调优是一个亘古不变的话题,无论是在传统H5上还是小程序中.因为实现机制不同,可能导致传统H5中的某些优化方式在小程序上并不适用.因此必须另开辟蹊径找出适合小程序的调估方式. 本文 ...
- 模拟退火算法SA原理及python、java、php、c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径
模拟退火算法SA原理及python.java.php.c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径 模拟退火算法(Simulated Annealing,SA)最早的思 ...
- paper 36 :[教程] 基于GridSearch的svm参数寻优
尊重原创~~~ 转载出处:http://www.matlabsky.com/thread-12411-1-1.html 交叉验证(Cross Validation)方法思想简介http://www.m ...
- grid search 超参数寻优
http://scikit-learn.org/stable/modules/grid_search.html 1. 超参数寻优方法 gridsearchCV 和 RandomizedSearchC ...
- 吴裕雄 python 机器学习——模型选择参数优化暴力搜索寻优GridSearchCV模型
import scipy from sklearn.datasets import load_digits from sklearn.metrics import classification_rep ...
- 吴裕雄 python 机器学习——模型选择参数优化随机搜索寻优RandomizedSearchCV模型
import scipy from sklearn.datasets import load_digits from sklearn.metrics import classification_rep ...
- 体验CoreCLR的stack unwinding特性在Linux/Mac上的初步实现
有了stack unwinding特性,才能在.NET程序中获取调用堆栈(call stack)信息,才能在异常时显示调用堆栈信息.这个特性之前只在Windows上有实现,Linux/Mac上的实现最 ...
- kinect在ros上的初步测试---17
摘要: 原创博客:转载请表明出处:http://www.cnblogs.com/zxouxuewei/ 1.在使用本贴前必须先按照我的上一个博文正确在ubuntu上安装kinect驱动:http:// ...
随机推荐
- [HNOI2002]营业额统计(splay基础)
嘟嘟嘟 这几天开始搞平衡树了,\(splay\)理解起来感觉还行,然而代码看了半天才勉强看懂. 我这篇博客应该不算什么入门讲解,因为我觉得我讲不明白,所以只能算自己的学习笔记吧. 这道题就是有\(n\ ...
- Hive学习之路 (二十)Hive 执行过程实例分析
一.Hive 执行过程概述 1.概述 (1) Hive 将 HQL 转换成一组操作符(Operator),比如 GroupByOperator, JoinOperator 等 (2)操作符 Opera ...
- pom xml testng
<dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId ...
- sencha 2.3中自己定义PullRefreshFn给PullRefresh加入下拉刷新事件
Sencha removed the refreshFn from the pullrefresh plugin in ST 2.2. Here is an user extension with g ...
- Linux磁盘与文件系统管理(一)
fdisk 常用的磁盘分区工具,受mbr分区表的限制,只能给小于2TB的磁盘划分分区,如果使用fdisk对大于2TB的磁盘进行分区,虽然可以分区,但只能识别2T的空间,一般使用parted分区工具 - ...
- mapreduce二次排序详解
什么是二次排序 待排序的数据具有多个字段,首先对第一个字段排序,再对第一字段相同的行按照第二字段排序,第二次排序不破坏第一次排序的结果,这个过程就称为二次排序. 如何在mapreduce中实现二次排序 ...
- 一个简单python爬虫的实现——爬取电影信息
最近在学习网络爬虫,完成了一个比较简单的python网络爬虫.首先为什么要用爬虫爬取信息呢,当然是因为要比人去收集更高效. 网络爬虫,可以理解为自动帮你在网络上收集数据的机器人. 网络爬虫简单可以大致 ...
- 【树形DP】洛谷P1352_没有上司的舞会
本人第一篇Blog,初学树形DP,心情别样鸡冻... 好了废话不多说,我们来看看题目[传送门] 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是 ...
- Hibernate第一天——入门和基本操作
第一个接触的框架就是这个Hibernate框架了,Hibernate本意是 冬眠 ,这里有必要引用CSDN上某位网友某个帖子的评论先引出框架的概念: 框架:一个软件半成品,帮你做了一些基础工作,你就可 ...
- 20155236范晨歌 Exp2后门原理与实践
## 实验二 后门原理与实践 1.Windows获得Linux Shell 在windows下,打开CMD,使用ipconfig指令查看本机IP ncat.exe -l -p 5236监听本机的523 ...