利用Python中geatpy库设计遗传/差分进化/NSGA算法的常用技巧与细节

上一篇文章Python遗传/差分进化算法(geatpy库)常见报错总结了2.7版本geatpy库的常见报错,意味着我们终于能够顺利使用geatpy库辣!!但是不同于开发者提供的各种demo,日常科研中碰到的优化问题往往规模庞大,并不单单只有两三个变量、四五个公式。因此本文将进一步介绍利用geatpy库建模、优化的技巧与细节

本文的代码样例主要为geatpy中给出的soea_demo2

一、算法选择

geatpy库以传统遗传算法为基础,开发并提供了大量的进化算法模板,以常见的遗传算法(Genetic Algorithm, GA)、差分进化算法(Differential Evolution Algorithm, DE)、非支配排序遗传算法(non-dominated sorting genetic algorithm-II, NSGA-II)为例,geatpy包含的模板有:

其中soea/moea字样的表示单目标优化/多目标优化,带有DE/GA/NSGA字样的分别对应遗传算法、差分进化算法、非支配排序遗传算法三类常见算法。不同算法模板的main文件中需要调整的参数不同,比如遗传算法主要调参对象为种群规模、迭代次数、交叉概率、变异概率,而差分进化算法主要对象为种群规模、迭代次数、交叉概率、差分因子。不同算法对不同问题的求解效果不同,可代入不同模板进行对比择优。

二、自定义模型与问题

soea_demo2为例,自定义的问题需要在Myproblem.py中表述,主要修改的地方如下:

1、确定目标维数(一般选择了soea的话,M就为1);

2、定义目标的min/max,就是定义你的问题是取目标的最小值还是最大值;

3、定义决策变量数量,这里的数量要与步骤4、5中的变量数统一;

4、定义每个决策变量的取值范围,如果没有限制可以输入一个较大的数/较小的数。此外,这里的取值是指决策变量自带的取值范围,与步骤7中的约束属于并集关系,个人建议能在步骤4中约束的就在4里输入;

5、生成决策变量,图中x1、x2、x3被定义为决策变量,可以直接代入到目标函数的构建中。决策变量的排序与步骤4列表中的顺序对应;

6、基于决策变量构建目标函数;

7、输入约束条件。若为不等式约束,则化为 ax1 + bx2 + cx3 + d ≦ 0 的形式,并输入左边的多项式,若为等式约束,则化为 ax1 + bx2 + cx3 + d = 0 的形式,并在np.abs( ) 中输入左边的多项式。

进一步调整main.py文件中的算法参数:

1、选择算法模板;

2、设置种群规模,规模越大,前期解的多样性越高,速度越慢;

3、设置最大进化代数;

4、设置算法参数,可通过调试来选取最佳参数;

5、输出绘图,通过查看ea.optimize()的函数说明可发现,drawing有0、1、2、3四种取值,分别代表不绘图、绘制最终结果图、绘制目标函数动态图、绘制决策变量动态图。

三、大规模问题的简化技巧

geatpy提供的所有demo都是小规模简单问题,因此很多问题的表述较为简单粗暴,当面临大规模优化问题时(例如几百个决策变量、上百个约束条件等),不能死板地按照demo里的表述来构建问题。geatpy对numpy、openpyxl、xlrd等常用库的兼容性较好,可以采用xlsx辅助、多套用循环、构建列表数组等方式来规避重复性表述。

1、在大规模问题中定义变量

假设该问题有100个变量,采用soea_demo2中的变量定义方式则需要手打100行代码,输入包含100个变量的矩阵,可以采用以下模板来简化问题表述:

Dim = 100 #变量维度

varTypes = [1] * 50 + [0] * 50  #初始化varTypes(50个离散变量,50个连续变量)

################建立变量上下界矩阵,50个离散变量范围[0,1],连续变量[0,10]################
lb=[]
for i in range(100):
    lb.append(0)

ub=[]
for i in range(50):
    ub.append(1)
for i in range(50):
    ub.append(10)

################建立变量边界包含情况的矩阵,0表示不包含,1表示包含################
lbin=[]
for i in range(100):
    lbin.append(1)

ubin=[]
for i in range(100):
    ubin.append(1)

定义决策变量时,假设前50个离散变量归纳为集合A,后50个连续变量归纳为集合B,若目标函数f为A元素间乘积与B元素间叠加的总和,则可用构建循环来简化模型:

def evalVars(self, Vars):  # 目标函数
   A = [Vars[:, [i]] for i in range(50)]
   B = [Vars[:, [i+50]] for i in range(50)]

   ###################### 调用决策变量 #######################
   a = 1
   for i in range(len(A)):
       a = a * A[i]

   b = 0
   for i in range(len(B)):
       b = b + B[i]

   f = a + b

2、在大规模问题中引入约束

大规模问题中的约束条件往往较多,需要进行分类、整合。例如等式约束与不等式约束的表述不同,需要区分。demo中给出的表述是在CV=np.hstack()中构造列表,并逐一输入约束条件,我们可将所有约束先分类,再采用循环进行整合,最后将列表元素传递给CV。

constraints =[]

################### 对集合B引入不等式约束:相邻变量之和不超过10 #####################
for i in range(1,50):
    constraints.append(B[i] + B[i-1] - 10)

################### 对集合A引入等式约束:相邻变量不同 #####################
for i in range(1,50):
    constraints.append(np.abs(A[i] + A[i-1] - 1))

################### 整合约束至CV #####################
CV = np.hstack(constraints)

四、问题建模过程中的一些坑

1、决策变量个数未知

由于启发式算法能够快速求解大规模复杂问题,geatpy往往被我用来做NP-hard问题的求解,但常规的LP、MIP、IP等问题一般不会考虑用遗传算法,而是用其他精确求解算法或者Gurobi、Cplex去做。因此在geatpy中建模的往往是一些复杂抽象、难以定义类型的问题。例如在geatpy社区中有很多人问的一个问题:

首先这个问题并不属于常见的LP、MIP、IP、MINLP等问题的范畴,在使用geatpy前要先梳理清楚优化问题的特征。本文给出求解这类问题的一种简单思路,可将该问题分解成两个子问题:1、决策变量的个数问题;2、已知变量个数下的问题。由于决策变量个数必然是一个整数,因此只要个数的取值范围不是很大,都可以通过两阶段优化去做,具体求解框架如下:

实际上就是在整个geatpy算法外面套了一层遍历Y的过程,很容易在main.py文件中修改实现。

2、建模中的判断语句

有些优化问题中往往存在判断过程,例如决策变量的不同取值范围会让函数产生差异

可在模型中用if语句来增加判断过程。但是在判断的过程中需要注意,等式判断语句的报错率很高,例如if x == 5,debug后可以发现,决策变量x在算法中是以数组的形式出现的,维度为N×1,N为种群规模,因此这种判断语句容易报错,需要规避。

五、总结

其实用geatpy库就意味着你的问题比较复杂、难以精确求解,所以demo列举的模板往往难以满足我们的建模需求,后续如果还有其他细节可以补充就再更新这个主题了,感谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值