本文主要比较了几种python的遗传算法框架,并简单介绍使用方法
gaft
代码框架:https://github.com/PytLab/gaft
安装:
pip install gaft
使用方法:
from gaft import GAEngine
from gaft.components import BinaryIndividual
from gaft.components import Population
from gaft.operators import RouletteWheelSelection
from gaft.operators import UniformCrossover
from gaft.operators import FlipBitMutation
from gaft.analysis.fitness_store import FitnessStore
from gaft.plugin_interfaces.analysis import OnTheFlyAnalysis
# Define population.
# 变量范围:x in [-2, 2] y in [-2, 2]
indv_template = BinaryIndividual(ranges=[(-2, 2), (-2, 2)], eps=0.001)
population = Population(indv_template=indv_template, size=50)
population.init()
# Create genetic operators.
selection = RouletteWheelSelection()
crossover = UniformCrossover(pc=0.8, pe=0.5)
mutation = FlipBitMutation(pm=0.1)
# Create genetic algorithm engine.
engine = GAEngine(population=population, selection=selection,
crossover=crossover, mutation=mutation,
analysis=[FitnessStore])
# Define fitness function.
@engine.fitness_register
@engine.minimize # 注释这一行为找最大值
def fitness(indv):
x, y = indv.solution # 对应【16行】的变量顺序
return x * x + y
# 输出结果:如果不要直接注释掉
@engine.analysis_register
class ConsoleOutput(OnTheFlyAnalysis):
master_only = True
interval = 20 # 每隔多少次迭代 打印一条结果
def register_step(self, g, population, engine):
best_indv = population.best_indv(engine.fitness)
msg = 'Generation: {}, best fitness: {:.3f}'.format(g, engine.fmax)
engine.logger.info(msg)
if '__main__' == __name__:
engine.run(ng=100) # 迭代100次后停止
结果输出保存在best_fit.py中。
特点
- 可以实现 R n ⟶ f R R^n\stackrel{f}\longrightarrow R Rn⟶fR的函数优化
- 每个自变量可以有定义区间
- 交叉发生概率为pc,每个结点为交叉结点概率为pe。
- 每一位发生变异的概率为pm
geatpy
代码框架:http://geatpy.com/index.php/2019/07/28/3-%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/
教程:http://geatpy.com/index.php/2019/07/28/3-%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/
安装
pip install geatpy
使用方法:参见上面教程和代码框架里的demo,这里列出第一个demo。
import numpy as np
import geatpy as ea
import time
"""=======================目标函数====================="""
def aim(Phen): # 传入种群染色体矩阵解码后的基因表现型
x1 = Phen[:, [0]] # 取出第一列,得到所有个体的第一个自变量
x2 = Phen[:, [1]] # 取出第二列,得到所有个体的第二个自变量
return np.sin(x1+x2) + (x1-x2)**2 - 1.5*x1 + 2.5*x2 + 1
"""=======================变量范围====================="""
x1 = [-1.5, 4] # 第一个决策变量范围
x2 = [-3, 4] # 第二个决策变量范围
b1 = [1, 1] # 第一个决策变量边界,1表示包含范围的边界,0表示不包含
b2 = [1, 1] # 第二个决策变量边界,1表示包含范围的边界,0表示不包含
# 生成自变量的范围矩阵,使得第一行为所有决策变量的下界,第二行为上界
ranges=np.vstack([x1, x2]).T
# 生成自变量的边界矩阵
borders=np.vstack([b1, b2]).T
varType = np.array([0, 0]) # 决策变量的类型,0表示连续,1表示离散
"""=====================染色体编码设置==================="""
Encoding = 'BG' # 'BG'表示采用二进制/格雷编码
codes = [1, 1] # 决策变量的编码方式,两个1表示变量均使用各类编码
precisions = [6, 6] # 决策变量的编码精度,表示解码后能表示的决策变量的精度可达小数点6位
scales = [0, 0] #0表示采用算术刻度,1表示采用对数刻度
# 调用函数创建译码矩阵
FieldD = ea.crtfld(Encoding, varType, ranges, borders, precisions, codes, scales)
"""====================遗传算法参数设置=================="""
NIND = 20 # 种群个体数目
MAXGEN = 100 # 最大遗传代数
maxormins = [1] #表示目标函数是最小化,元素为-1则表示对应的目标函数是最大化
selectStyle = 'sus' # 采用随机抽样选择
recStyle = 'xovdp' # 采用两点交叉
mutStyle = 'mutbin' # 采用二进制染色体的变异算子
pc = 0.9 # 交叉概率
pm = 1 #整条染色体的变异概率(每一位的变异概率=pm/染色体长度)
Lind = int(np.sum(FieldD[0, :])) # 计算染色体长度
print("种群个体数目:", NIND)
print("最大遗传代数:",MAXGEN)
print("交叉概率", pc)
print("染色体长度:",Lind,"\n每一位的变异概率:",pm/Lind)
obj_trace = np.zeros((MAXGEN, 2)) # 定义目标函数值记录器
var_trace = np.zeros((MAXGEN, Lind)) # 染色体记录器,记录历代最优个题的染色体
"""====================开始遗传算法进化=================="""
start_time = time.time() # 开始计时
Chrom = ea.crtpc(Encoding,NIND,FieldD) # 生成种群染色体矩阵
variable = ea.bs2real(Chrom, FieldD) # 对初始种群进行解码
ObjV = aim(variable) # 计算初始种群个体的目标函数值
best_ind = np.argmin(ObjV) # 计算当代最优个体序号,最大:argmax
# 开始进化
for gen in range(MAXGEN):
FitnV = ea.ranking(maxormins * ObjV) # 根据目标函数大小分配适应度值
SelCh = Chrom[ea.selecting(selectStyle, FitnV, NIND-1), :] # 选择
SelCh = ea.recombin(recStyle, SelCh, pc) # 重组
SelCh = ea.mutate(mutStyle, Encoding, SelCh, pm) #变异
# 把父代精英个体与子代的染色体进行合并,得到新一代种群
Chrom = np.vstack([Chrom[best_ind, :], SelCh])
Phen = ea.bs2real(Chrom, FieldD) # 对种群进行解码(二进制转十进制)
ObjV = aim(Phen) # 求种群个体的目标函数值
# 记录
best_ind = np.argmin(ObjV) # 计算当代最优个体的序号
obj_trace[gen,0]=np.sum(ObjV)/ObjV.shape[0]
# 记录当代种群的目标函数值
obj_trace[gen,1]=ObjV[best_ind] # 记录当代种群最优个体的染色体
var_trace[gen,:]=Chrom[best_ind,:] # 记录当代种群最优个体的染色体
# 进化完成
end_time = time.time() #结束计时
ea.trcplot(obj_trace, [['种群个体平均目标函数值','种群最优个体目标函数值']]) #绘图
"""=======================输出结果====================="""
best_gen = np.argmin(obj_trace[:, [1]])
print('最优解的目标函数值:', obj_trace[best_gen, 1])
variable = ea.bs2real(var_trace[[best_gen], :], FieldD) # 解码得到的表现型(即对应的决策变量值)
print('最优解的决策变量值为:')
for i in range(variable.shape[1]):
print('x'+str(i)+'=',variable[0,i])
print('用时:',end_time - start_time,"秒")
特点
- 可以实现 R m ⟶ f R n R^m\stackrel{f}\longrightarrow R^n Rm⟶fRn的函数优化
- 输出结果比较直观,自定义方便,运算比较块。
- 文档比较丰富。
- 只有发生交叉的概率,没有具体哪一个结点交叉
- 每位发生变异的概率依赖于整条发生变异的概率和这条染色体的长度
- 在类继承方法(推荐方法)中需要写两个文件,而且可以实现约束和惩罚
scikit-opt
代码框架:https://github.com/guofei9987/scikit-opt
教程:https://scikit-opt.github.io/#/docs/zh
安装
pip install scikit-opt
或者直接下载源代码调用
使用方法
# 定义目标函数
def demo_func(x):
x1, x2, x3 = x
return x1 ** 2 + (x2 - 0.05) ** 2 + x3 ** 2
# 调入遗传算法求解器
from sko.GA import GA
ga = GA(func=demo_func, lb=[-1, -10, -5], ub=[2, 10, 2], max_iter=500)
best_x, best_y = ga.fit()
# 绘图
import pandas as pd
import matplotlib.pyplot as plt
FitV_history = pd.DataFrame(ga.FitV_history)
fig, ax = plt.subplots(2, 1)
ax[0].plot(FitV_history.index, FitV_history.values, '.', color='red')
plt_max = FitV_history.max(axis=1)
ax[1].plot(plt_max.index, plt_max, label='max')
ax[1].plot(plt_max.index, plt_max.cummax())
plt.show()
特点
- 非常简单轻便,有点像sklearn里的使用方法。
- 交叉一定发生,随机发生在某一位,每一位变异用GA类中的参数prob_mut控制。
- 该框架针对旅行商问题做了优化。
- 该框架还包含了粒子群算法、模拟退火。
总结
- scikit-opt最轻便,但是不方便控制参数
- gaft能够控制遗传算法中的许多细节,但是设计略有复杂,参数意义不明显。
- geatpy功能强大,调参方便,结果输出直观。虽然在细节控制上不如gaft,但是基本的交叉概率和变异概率都可以调整。比较推荐这个框架。