• 快速入门

    Geatpy2整体上看由工具箱内核函数(内核层)和面向对象进化算法框架(框架层)两部分组成。其中面向对象进化算法框架主要有四个大类:Problem问题类、Algorithm算法模板类、Population种群类和PsyPopulation多染色体种群类。UML图如下所示:

     

Problem 类定义了与问题相关的一些信息,如问题名称name、优化目标的维数M、决策变量的个数Dim、决策变量的范围ranges、决策变量的边界borders 等。maxormins是一个记录着各个目标函数是最小化抑或是最大化的行向量,其中元素为1 表示对应的目标是最小化目标;为-1 表示对应的是最大化目标。例如M=3,maxormins=np.array([1,-1,1]),此时表示有三个优化目标,其中第一、第三个是最小化目标,第二个是最大化目标。varTypes 是一个记录着决策变量类型的行向量,其中的元素为0 表示对应的决策变量是连续型变量;为1 表示对应的是离散型变量。待求解的目标函数定义在aimFunc() 的函数中。calBest() 函数则用于从理论上计算目标函数的理论最优值。在实际使用时,不是直接在Problem 类的文件中修改相关代码使用的,而是通过定义一个继承Problem的子类来完成对问题的定义的。这些在后面的章节中会详细讲述。对于Problem 类中各属性的详细含义可查看Problem.py 源码。

Population 类是一个表示种群的类。一个种群包含很多个个体,而每个个体都有一条染色体(若要用多染色体,则使用多个种群、并把每个种群对应个体关联起来即可)。除了染色体外,每个个体都有一个译码矩阵Field(或俗称区域描述器) 来标识染色体应该如何解码得到表现型,同时也有其对应的目标函数值以及适应度。种群类就是一个把所有个体的这些数据统一存储起来的一个类。比如里面的Chrom 是一个存储种群所有个体染色体的矩阵,它的每一行对应一个个体的染色体;ObjV 是一个目标函数值矩阵,每一行对应一个个体的所有目标函数值,每一列对应一个目标。对于Population 类中各属性的详细含义可查看Population.py 源码以及下一章“Geatpy 数据结构”。

PsyPopulation类是继承了Population的支持多染色体混合编码的种群类。一个种群包含很多个个体,而每个个体都有多条染色体。用Chroms列表存储所有的染色体矩阵(Chrom);Encodings列表存储各染色体对应的编码方式(Encoding);Fields列表存储各染色体对应的译码矩阵(Field)。

Algorithm 类是进化算法的核心类。它既存储着跟进化算法相关的一些参数,同时也在其继承类中实现具体的进化算法。比如Geatpy 中的moea_NSGA3_templet.py 是实现了多目标优化NSGA-III 算法的进化算法模板类,它是继承了Algorithm 类的具体算法的模板类。关于Algorithm 类中各属性的含义可以查看Algorithm.py 源码。这些算法模板通过调用Geatpy 工具箱提供的进化算法库函数实现对种群的进化操作,同时记录进化过程中的相关信息,其基本层次结构如下图:

其中“算子类”是2.2.2版本之后增加的新特性,通过实例化算子类来调用低级操作函数,可以利用面向对象编程的优势使得对传入低级操作函数的一些参数的修改变得更加方便。目前内置的“算子类”有“重组算子类”和“变异算子类”。用户可以绕开对底层的低级操作函数的修改而直接通过新增“算子类”来实现新的算子例如自适应的重组、变异算子等。

下面以一个多目标优化问题为例:对于求解一个优化问题,无论是单目标优化、还是多目标优化、抑或是组合优化、约束优化等,需要做两件工作:(1)自定义问题类;(2)编写执行代码。一般来说自定义问题类和执行代码可以专门分别写在独立的文件中,下面的代码中为了方便直接把两者写在同一个地方。

自定义问题类是把与问题模型有关的内容在此处进行定义和设置,比如变量范围、待优化的目标、约束条件的处理等;执行代码是调用算法模板进行进化优化。

QuickStart
In [1]:
# -*- coding: utf-8 -*-
""" QuickStart """
import numpy as np
import geatpy as ea
# 自定义问题类
class MyProblem(ea.Problem): # 继承Problem父类
    def __init__(self, M):
        name = 'DTLZ1' # 初始化name(函数名称,可以随意设置)
        maxormins = [1] * M # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)
        Dim = M + 4 # 初始化Dim(决策变量维数)
        varTypes = np.array([0] * Dim) # 初始化varTypes(决策变量的类型,0:实数;1:整数)
        lb = [0] * Dim # 决策变量下界
        ub = [1] * Dim # 决策变量上界
        lbin = [1] * Dim # 决策变量下边界
        ubin = [1] * Dim # 决策变量上边界
        # 调用父类构造方法完成实例化
        ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)
    def aimFunc(self, pop): # 目标函数
        Vars = pop.Phen # 得到决策变量矩阵
        XM = Vars[:,(self.M-1):]
        g = 100 * (self.Dim - self.M + 1 + np.sum(((XM - 0.5)**2 - np.cos(20 * np.pi * (XM - 0.5))), 1, keepdims = True))
        ones_metrix = np.ones((Vars.shape[0], 1))
        f = 0.5 * np.fliplr(np.cumprod(np.hstack([ones_metrix, Vars[:,:self.M-1]]), 1)) * np.hstack([ones_metrix, 1 - Vars[:, range(self.M - 2, -1, -1)]]) * np.tile(1 + g, (1, self.M))
        pop.ObjV = f # 把求得的目标函数值赋值给种群pop的ObjV
    def calBest(self): # 计算全局最优解
        uniformPoint, ans = ea.crtup(self.M, 10000) # 生成10000个在各目标的单位维度上均匀分布的参考点
        globalBestObjV = uniformPoint / 2
        return globalBestObjV
In [2]:
# 编写执行代码
"""===============================实例化问题对象=============================="""
M = 3                     # 设置目标维数
problem = MyProblem(M)    # 生成问题对象
"""==================================种群设置================================="""
Encoding = 'RI'           # 编码方式
NIND = 100                # 种群规模
Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders) # 创建区域描述器
population = ea.Population(Encoding, Field, NIND) # 实例化种群对象(此时种群还没被初始化,仅仅是完成种群对象的实例化)
"""================================算法参数设置==============================="""
myAlgorithm = ea.moea_NSGA3_templet(problem, population) # 实例化一个算法模板对象
myAlgorithm.MAXGEN = 500  # 最大进化代数
myAlgorithm.drawing = 1   # 设置绘图方式(0:不绘图;1:绘制结果图;2:绘制过程动画)
"""==========================调用算法模板进行种群进化=========================
调用run执行算法模板,得到帕累托最优解集NDSet。NDSet是一个种群类Population的对象。
NDSet.ObjV为最优解个体的目标函数值;NDSet.Phen为对应的决策变量值。
详见Population.py中关于种群类的定义。
"""
NDSet = myAlgorithm.run() # 执行算法模板,得到非支配种群
NDSet.save()              # 把结果保存到文件中
# 输出
print('用时:%f 秒'%(myAlgorithm.passTime))
print('评价次数:%d 次'%(myAlgorithm.evalsNum))
print('非支配个体数:%d 个'%(NDSet.sizes))
print('单位时间找到帕累托前沿点个数:%d 个'%(int(NDSet.sizes // myAlgorithm.passTime)))
# 计算指标
PF = problem.getBest() # 获取真实前沿,详见Problem.py中关于Problem类的定义
if PF is not None and NDSet.sizes != 0:
    GD = ea.indicator.GD(NDSet.ObjV, PF)       # 计算GD指标
    IGD = ea.indicator.IGD(NDSet.ObjV, PF)     # 计算IGD指标
    HV = ea.indicator.HV(NDSet.ObjV, PF)       # 计算HV指标
    Spacing = ea.indicator.Spacing(NDSet.ObjV) # 计算Spacing指标
    print('GD',GD)
    print('IGD',IGD)
    print('HV', HV)
    print('Spacing', Spacing)
"""============================进化过程指标追踪分析==========================="""
if PF is not None:
    metricName = [['IGD'], ['HV']]
    [NDSet_trace, Metrics] = ea.indicator.moea_tracking(myAlgorithm.pop_trace, PF, metricName, problem.maxormins)
    # 绘制指标追踪分析图
    ea.trcplot(Metrics, labels = metricName, titles = metricName)
种群信息导出完毕。
用时:0.781933 秒
评价次数:45500 次
非支配个体数:91 个
单位时间找到帕累托前沿点个数:116 个
GD 0.000198173830767891
IGD 0.02059977212182223
HV 0.841122031211853
Spacing 0.0012533325944347649
正在进行进化追踪指标分析,请稍后......
指标追踪分析结束,进化记录器中有效进化代数为: 500

上面代码有详尽的注释。对于实际问题,只需要更改问题类中变量的设置以及目标函数aimFunc()的定义,然后编写执行代码调用算法模板进行求解即可。

对于约束条件,Geatpy提供两种方式来进行处理:罚函数法和可行性法则。其中罚函数法比较自由,只需在问题类的“aimFunc()”函数定义里面把非可行解找到,然后对相应的个体的目标函数值作出惩罚即可。如果使用可行性法则,则需要生成一个CV矩阵(种群个体违反约束程度矩阵),该矩阵的每一行对应一个个体,每一列对应一个约束条件。详见详见“Geatpy教程

更多案例详见“案例源码”。每个案例均有详尽的代码注释,可以帮助您以最快速度掌握Geatpy的基本用法,并且把Geatpy与实际项目相结合。