Geatpy是国内几所高校做的一个开源遗传算法包,是一个高性能实用型进化算法工具箱,提供许多已实现的进化算法中各项重要操作的库函数,并提供一个高度模块化、耦合度低的面向对象的进化算法框架,利用“定义问题类 + 调用算法模板”的模式来进行进化优化,可用于求解单目标优化、多目标优化、复杂约束优化、组合优化、混合编码进化优化等。
基础问题求解
该算法包寻找最优解的基本分两步:第一步,搭建问题框架,编写目标函数、约束函数等;第二步;问题求解。简单介绍一个单目标问题求解的样例。
编写问题框架
#定义问题importnumpyasnpimportgeatpyasea"""max f = x * np.sin(10 * np.pi * x) + 2.0s.t.-1 <= x <= 2"""classMyProblem(ea.Problem): # 继承Problem父类def__init__(self): name='MyProblem'# 初始化name(函数名称,可以随意设置)M=1# 初始化M(目标维数)maxormins= [-1] # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)Dim=1# 初始化Dim(决策变量维数)varTypes= [0] *Dim# 初始化varTypes(决策变量的类型,元素为0表示对应的变量是连续的;1表示是离散的)lb= [-1] # 决策变量下界ub= [2] # 决策变量上界lbin= [1] *Dim# 决策变量下边界(0表示不包含该变量的下边界,1表示包含)ubin= [1] *Dim# 决策变量上边界(0表示不包含该变量的上边界,1表示包含)# 调用父类构造方法完成实例化ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin) defevalVars(self, x): # 目标函数f=x*np.sin(10*np.pi*x) +2.0returnf
问题求解
"""该案例展示了一个简单的连续型决策变量最大化目标的单目标优化问题的求解。问题的定义详见MyProblem.py。"""if__name__=='__main__': # 实例化问题对象problem=MyProblem() # 构建算法algorithm=ea.soea_SEGA_templet(problem, ea.Population(Encoding='RI', NIND=10),#编码规则、种群个数MAXGEN=25, # 最大进化代数。logTras=5, # 表示每隔多少代记录一次日志信息,0表示不记录。trappedValue=1e-6, # 单目标优化陷入停滞的判断阈值。maxTrappedCount=10) # 进化停滞计数器最大上限值。# 求解res=ea.optimize(algorithm, verbose=True, drawing=1, outputMsg=True, drawLog=False, saveFlag=True) print(res)
其结果很清晰地展示了求解过程,每一代的平均值、最大值、最小值等。以上是一个简单的函数最大值求解案例。
更常见的单目标问题求解:
importnumpyasnpimportgeatpyasea""" 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"""classMyProblem(ea.Problem): # 继承Problem父类def__init__(self): name='MyProblem'# 初始化name(函数名称,可以随意设置)M=1# 初始化M(目标维数)maxormins= [-1] # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)Dim=3# 初始化Dim(决策变量维数)varTypes= [0] *Dim# 初始化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) defevalVars(self, Vars): # 目标函数x1=Vars[:, [0]] x2=Vars[:, [1]] x3=Vars[:, [2]] f=4*x1+2*x2+x3# 采用可行性法则处理约束CV=np.hstack([2*x1+x2-1, x1+2*x3-2, np.abs(x1+x2+x3-1)]) returnf, CVdefcalReferObjV(self): # 设定目标数参考值(本问题目标函数参考值设定为理论最优值)referenceObjV=np.array([[2.5]]) returnreferenceObjV""" 该案例展示了一个带等式约束的连续型决策变量最大化目标的单目标优化问题的求解。"""if__name__=='__main__': # 实例化问题对象problem=MyProblem() # 构建算法algorithm=ea.soea_DE_rand_1_bin_templet(problem, ea.Population(Encoding='RI', NIND=100), MAXGEN=500, # 最大进化代数。logTras=100) # 表示每隔多少代记录一次日志信息,0表示不记录。algorithm.mutOper.F=0.5# 差分进化中的参数Falgorithm.recOper.XOVR=0.7# 重组概率# 求解res=ea.optimize(algorithm, verbose=True, drawing=1, outputMsg=True, drawLog=False, saveFlag=True) print(res)
TSP问题求解
Geatpy提供了TSP问题求解的建模方法,同样是先建模,再求解的步骤。
importnumpyasnpimportgeatpyaseaimportmatplotlib.pyplotasplt""" 有十座城市:A, B, C, D, E, F, G, H, I, J,坐标如下: X Y [[0.4, 0.4439], [0.2439,0.1463], [0.1707,0.2293], [0.2293,0.761], [0.5171,0.9414], [0.8732,0.6536], [0.6878,0.5219], [0.8488,0.3609], [0.6683,0.2536], [0.6195,0.2634]] 某旅行者从A城市出发,想逛遍所有城市,并且每座城市去且只去一次,最后要返回出发地,而且需要从G地拿重要文件到D地,另外要从F地把公司的车开到E地,那么他应该如何设计行程方案,才能用最短的路程来满足他的旅行需求? 分析:在这个案例中,旅行者从A地出发,把其他城市走遍一次后回到A地,因此我们只需要考虑中间途径的9个城市的访问顺序即可。这9个城市需要排列组合选出满足约束条件的最优的排列顺序作为最终的路线方案。"""classMyProblem(ea.Problem): # 继承Problem父类def__init__(self): name='MyProblem'# 初始化name(函数名称,可以随意设置)M=1# 初始化M(目标维数)maxormins= [1] # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)Dim=9# 初始化Dim(决策变量维数)varTypes= [1] *Dim# 初始化varTypes(决策变量的类型,元素为0表示对应的变量是连续的;1表示是离散的)lb= [1] *Dim# 决策变量下界ub= [9] *Dim# 决策变量上界lbin= [1] *Dim# 决策变量下边界(0表示不包含该变量的下边界,1表示包含)ubin= [1] *Dim# 决策变量上边界(0表示不包含该变量的上边界,1表示包含)# 调用父类构造方法完成实例化ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin) # 新增一个属性存储旅行地坐标self.places=np.array([[0.4, 0.4439], [0.2439, 0.1463], [0.1707, 0.2293], [0.2293, 0.761], [0.5171, 0.9414], [0.8732, 0.6536], [0.6878, 0.5219], [0.8488, 0.3609], [0.6683, 0.2536], [0.6195, 0.2634]]) defevalVars(self, x): # 目标函数# 添加从0地出发且最后回到出发地X=np.hstack([np.zeros((x.shape[0], 1)), x, np.zeros((x.shape[0], 1))]).astype(int) ObjV= [] # 存储所有种群个体对应的总路程foriinrange(X.shape[0]): journey=self.places[X[i], :] # 按既定顺序到达的地点坐标distance=np.sum(np.sqrt(np.sum(np.diff(journey.T) **2, 0))) # 计算总路程ObjV.append(distance) f=np.array([ObjV]).T# 找到违反约束条件的个体在种群中的索引,保存在向量exIdx中(如:若0、2、4号个体违反约束条件,则编程找出他们来)exIdx1=np.where(np.where(x==3)[1] -np.where(x==6)[1] <0)[0] exIdx2=np.where(np.where(x==4)[1] -np.where(x==5)[1] <0)[0] exIdx=np.unique(np.hstack([exIdx1, exIdx2])) CV=np.zeros((x.shape[0], 1)) CV[exIdx] =1# 把求得的违反约束程度矩阵赋值给种群pop的CVreturnf, CV""" 该案例展示了一个带约束的单目标旅行商问题的求解。"""if__name__=='__main__': # 实例化问题对象problem=MyProblem() # 构建算法algorithm=ea.soea_SEGA_templet(problem, ea.Population(Encoding='P', NIND=50), MAXGEN=200, # 最大进化代数logTras=50) # 表示每隔多少代记录一次日志信息,0表示不记录。algorithm.mutOper.Pm=0.5# 变异概率# 求解res=ea.optimize(algorithm, verbose=True, drawing=1, outputMsg=True, drawLog=False, saveFlag=True) # 绘制路线图ifres['success']: print('最短路程为:%s'%res['ObjV'][0][0]) print('最佳路线为:') best_journey=np.hstack([0, res['Vars'][0, :], 0]) foriinrange(len(best_journey)): print(int(best_journey[i]), end=' ') print() # 绘图plt.figure() plt.plot(problem.places[best_journey.astype(int), 0], problem.places[best_journey.astype(int), 1], c='black') plt.plot(problem.places[best_journey.astype(int), 0], problem.places[best_journey.astype(int), 1], 'o', c='black') foriinrange(len(best_journey)): plt.text(problem.places[int(best_journey[i]), 0], problem.places[int(best_journey[i]), 1], chr(int(best_journey[i]) +65), fontsize=20) plt.grid(True) plt.xlabel('x') plt.ylabel('y') plt.savefig('roadmap.svg', dpi=600, bbox_inches='tight') plt.show() else: print('没找到可行解。')
用遗传算法聚类
geatpy也可以解决聚类问题。
"""该案例展示了如何利用进化算法进行仿k-means聚类(可称之为EA-KMeans算法)。问题的定义详见MyProblem.py。本案例采用与k-means类似的聚类方法,采用展开的聚类中心点坐标作为染色体的编码,基本流程大致如下:1) 初始化种群染色体。2) 迭代进化(循环第3步至第6步),直到满足终止条件。3) 重组变异,然后根据得到的新染色体计算出对应的聚类中心点。4) 计算各数据点到聚类中心点的欧式距离。5) 把与各中心点关联的数据点的坐标平均值作为新的中心点,并以此更新种群的染色体。6) 把各中心点到与其关联的数据点之间的距离之和作为待优化的目标函数值。注意:导入的数据是以列为特征的,即每一列代表一个特征(如第一列代表x,第二列代表y......)。"""importmatplotlib.pyplotaspltimportnumpyasnpimportgeatpyaseaclassMyProblem(ea.Problem): # 继承Problem父类def__init__(self): # 目标函数计算中用到的一些数据self.datas=np.loadtxt('data.csv', delimiter=',') # 读取数据self.k=4# 分类数目# 问题类设置name='MyProblem'# 初始化name(函数名称,可以随意设置)M=1# 初始化M(目标维数)maxormins= [1] # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)Dim=self.datas.shape[1] *self.k# 初始化DimvarTypes= [0] *Dim# 初始化varTypes(决策变量的类型,元素为0表示对应的变量是连续的;1表示是离散的)lb=list(np.min(self.datas, 0)) *self.k# 决策变量下界ub=list(np.max(self.datas, 0)) *self.k# 决策变量上界lbin= [1] *Dim# 决策变量下边界(0表示不包含该变量的下边界,1表示包含)ubin= [1] *Dim# 决策变量上边界(0表示不包含该变量的上边界,1表示包含)# 调用父类构造方法完成实例化ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin) defaimFunc(self, pop): # 目标函数centers=pop.Phen.reshape(int(pop.sizes*self.k), int(pop.Phen.shape[1] /self.k)) # 得到聚类中心dis=ea.cdist(centers, self.datas, 'euclidean') # 计算距离dis_split=dis.reshape(pop.sizes, self.k, self.datas.shape[0]) # 分割距离矩阵,把各个聚类中心到各个点之间的距离的数据分开labels=np.argmin(dis_split, 1)[0] # 得到聚类标签值uni_labels=np.unique(labels) foriinrange(len(uni_labels)): centers[uni_labels[i], :] =np.mean(self.datas[np.where(labels==uni_labels[i])[0], :], 0) # 直接修改染色体为已知的更优值,加快收敛pop.Chrom=centers.reshape(pop.sizes, self.k*centers.shape[1]) pop.Phen=pop.decoding() # 染色体解码(要同步修改Phen,否则后面会导致数据不一致)dis=ea.cdist(centers, self.datas, 'euclidean') dis_split=dis.reshape(pop.sizes, self.k, self.datas.shape[0]) pop.ObjV=np.sum(np.min(dis_split, 1), 1, keepdims=True) # 计算个体的目标函数值defdraw(self, centers): # 绘制聚类效果图dis=ea.cdist(centers, self.datas, 'euclidean') dis_split=dis.reshape(1, self.k, self.datas.shape[0]) labels=np.argmin(dis_split, 1)[0] colors= ['r', 'g', 'b', 'y'] fig=plt.figure() ax=fig.add_subplot(111, projection='3d') foriinrange(self.k): idx=np.where(labels==i)[0] # 找到同一类的点的下标datas=self.datas[idx, :] ax.scatter(datas[:, 0], datas[:, 1], datas[:, 2], c=colors[i]) if__name__=='__main__': # 实例化问题对象problem=MyProblem() # 构建算法algorithm=ea.soea_DE_rand_1_bin_templet(problem, ea.Population(Encoding='RI', NIND=10), MAXGEN=20, # 最大进化代数。logTras=5, # 表示每隔多少代记录一次日志信息,0表示不记录。trappedValue=1e-4, # 单目标优化陷入停滞的判断阈值。maxTrappedCount=20) # 进化停滞计数器最大上限值。# 求解res=ea.optimize(algorithm, verbose=True, drawing=1, outputMsg=True, drawLog=False, saveFlag=True) # 检验结果ifres['success']: print('最优的聚类中心为:') Vars=res['Vars'][0, :] centers=Vars.reshape(problem.k, int(len(Vars) /problem.k)) # 得到最优的聚类中心print(centers) """=================================检验结果==============================="""problem.draw(centers)
以上都是geapty官方的案例,其还可以实现多目标优化、并行计算等功能,感兴趣的可去其官网查看相关文档。