这个专栏以工具实践为主,因此我们先讲进化算法工具包geatpy的使用。
关于遗传算法的推导原理我们举一个最简单的袋鼠爬山的例子,然后看遗传算法从头到尾怎么“编码-计算适应度-选择淘汰-基因交叉-基因突变-不断进化” 的。
一、geatpy框架
Geatpy2整体上看由工具箱内核函数(内核层)和面向对象进化算法框架(框架层)两部分组成。其中面向对象进化算法框架主要有四个大类:Problem问题类、Algorithm算法模板类、Population种群类和PsyPopulation多染色体种群类。UML图如下所示:
1、Population 类是一个表示种群的类
一个种群包含很多个个体,而每个个体都有一条染色体(若要用多染色体,则使用多个种群、并把每个种群对应个体关联起来即可)。除了染色体外,每个个体都有一个译码矩阵Field(或俗称区域描述器) 来标识染色体应该如何解码得到表现型,同时也有其对应的目标函数值以及适应度。种群类就是一个把所有个体的这些数据统一存储起来的一个类。比如里面的Chrom 是一个存储种群所有个体染色体的矩阵,它的每一行对应一个个体的染色体;ObjV 是一个目标函数值矩阵,每一行对应一个个体的所有目标函数值,每一列对应一个目标。对于Population 类中各属性的详细含义可查看Population.py 源码以及下一章“Geatpy 数据结构”。
PsyPopulation类是继承了Population的支持多染色体混合编码的种群类。一个种群包含很多个个体,而每个个体都有多条染色体。用Chroms列表存储所有的染色体矩阵(Chrom);Encodings列表存储各染色体对应的编码方式(Encoding);Fields列表存储各染色体对应的译码矩阵(Field)。
2、Algorithm 类是进化算法的核心类
它既存储着跟进化算法相关的一些参数,同时也在其继承类中实现具体的进化算法。比如Geatpy 中的moea_NSGA3_templet.py 是实现了多目标优化NSGA-III 算法的进化算法模板类,它是继承了Algorithm 类的具体算法的模板类。关于Algorithm 类中各属性的含义可以查看Algorithm.py 源码。这些算法模板通过调用Geatpy 工具箱提供的进化算法库函数实现对种群的进化操作,同时记录进化过程中的相关信息,其基本层次结构如下图:
二、例子1:单目标优化
对于求解一个优化问题,无论是单目标优化、还是多目标优化、抑或是组合优化、约束优化等,需要做两件工作:(1)自定义问题类;(2)编写执行代码。一般来说自定义问题类和执行代码可以专门分别写在独立的文件中,下面的代码中为了方便直接把两者写在同一个地方。
自定义问题类是把与问题模型有关的内容在此处进行定义和设置,比如变量范围、待优化的目标、约束条件的处理等;执行代码是调用算法模板进行进化优化。
下面代码有详尽的注释。对于实际问题,只需要更改问题类中变量的设置以及目标函数aimFunc()的定义,然后编写执行代码调用算法模板进行求解即可。
对于约束条件,Geatpy提供两种方式来进行处理:罚函数法和可行性法则。其中罚函数法比较自由,只需在问题类的“aimFunc()”函数定义里面把非可行解找到,然后对相应的个体的目标函数值作出惩罚即可。如果使用可行性法则,则需要生成一个CV矩阵(种群个体违反约束程度矩阵),该矩阵的每一行对应一个个体,每一列对应一个约束条件。
1、数学模型
该案例展示了一个带等式约束的连续型决策变量最大化目标的单目标优化问题。
该函数存在多个欺骗性很强的局部最优点。
max f = 4*x1 + 2*x2 + x3
s.t.
2*x1 + x2 - 1 <= 0
x1 + 2*x3 - 2 <= 0
x1 + x2 + x3 - 1 == 0
0 <= x1,x2 <= 1
0 < x3 < 2
2、定义决策变量/目标函数/约束条件
# -*- coding: utf-8 -*-
"""MyProblem.py"""
import numpy as np
import geatpy as ea
class MyProblem(ea.Problem): # 继承Problem父类
def __init__(self):
name = 'MyProblem' # 初始化name(函数名称,可以随意设置)
M = 1 # 初始化M(目标维数)
maxormins = [-1] # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)
Dim = 3 # 初始化Dim(决策变量维数)
varTypes = [0] * Dim # 这是一个list,初始化varTypes(决策变量的类型,元素为0表示对应的变量是连续的;1表示是离散的)
lb = [0,0,0] # 决策变量下界
ub = [1,1,2] # 决策变量上界
lbin = [1,1,0] # 决策变量下边界(0表示不包含该变量的下边界,1表示包含)
ubin = [1,1,0] # 决策变量上边界(0表示不包含该变量的上边界,1表示包含)
# 调用父类构造方法完成实例化
ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)
def aimFunc(self, pop): # 目标函数
Vars = pop.Phen # 得到决策变量矩阵
x1 = Vars[:, [0]] # 取出第一列得到所有个体的x1组成的列向量
x2 = Vars[:, [1]] # 第二列
x3 = Vars[:, [2]] # 第三列
pop.Ob