最近因为非线性规划问题,学习了geatpy包,使用遗传算法求解非线性规划问题。想到可以用遗传算法优化机器学习参数。
这里使用的是单目标差分进化算法,对随机森林算法参数进行调优。
建议在阅读本文前详细查看 geatpy官方API ,有助于理解算法原理。
我尽量在代码中穿插注释,如果有疑问或者认为有问题的地方请私信我
参考资料:geatpy入门、 sklearn计算分类效果指标 、 sklearn生成各类数据集
geatpy包安装
pip install geatpy
#加载包 import numpy as np import pandas as pd from plotnine import* import seaborn as sns from scipy import stats import matplotlib as mpl import matplotlib.pyplot as plt #中文显示问题 plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unicode_minus'] = False # notebook嵌入图片 %matplotlib inline # 提高分辨率 %config InlineBackend.figure_format='retina' # 切分数据 from sklearn.model_selection import train_test_split # 评价指标 from sklearn.metrics import mean_squared_error # 交叉检验 from sklearn.model_selection import cross_val_score # 忽略警告 import warnings warnings.filterwarnings('ignore')
分类数据生成
from sklearn.datasets import make_classification x, y = make_classification(n_samples=1000, # 样本个数 n_features=10, # 特征个数 n_informative=4, # 有效特征个数 n_redundant=3, # 冗余特征个数(有效特征的随机组合) n_repeated=3, # 重复特征个数(有效特征和冗余特征的随机组合) n_classes=4, # 样本类别 n_clusters_per_class=2, # 簇的个数 random_state=42) # 可视化分类数据集,只选择前两个特征 plt.figure(dpi = 600,figsize = (6,4)) plt.scatter(x[:,0],x[:,1],c = y)
划分数据集
训练集占比80%(800个样本),测试集20%(200个样本)
from sklearn.model_selection import train_test_split x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=45, test_size=0.2)
随机森林分类
使用默认参数建立随机森林分类模型,输出算法在训练集与测试集上准确率
# 随机森林分类 from sklearn.ensemble import RandomForestClassifier rf_ori = RandomForestClassifier(oob_score=True, random_state=42) rf_ori.fit(x_train,y_train) from sklearn.metrics import accuracy_score from sklearn.metrics import f1_score print('训练集准确率:{:.2f}'.format(accuracy_score(y_train, rf_ori.predict(x_train)))) print('测试集准确率:{:.2f}'.format(accuracy_score(y_test, rf_ori.predict(x_test))))
输出
训练集准确率:1.00 测试集准确率:0.79
从结果来看貌似出现了过拟合现象
遗传算法优化参数
定义优化目标
multiprocessing
多进程包,借助这个包,可以完成从单进程到并发执行的转换。可以加速遗传算法优化参数的运行速度
需要注意的是 self.data
和 self.dataTarget
这个地方一定要将训练集传到类定义中,否则会报错
目标函数为随机森林算法在训练集上10折交叉检验 f1_macro
的平均值,优化目标为最大化
import geatpy as ea from multiprocessing import Pool as ProcessPool import multiprocessing as mp from multiprocessing.dummy import Pool as ThreadPool class MyProblem(ea.Problem): # 继承Problem父类 def __init__(self,PoolType): name = 'MyProblem' # 初始化name(函数名称,可以随意设置) M = 1 # 初始化M(目标维数) maxormins = [-1] # 初始化目标最小最大化标记列表,1:min;-1:max Dim = 5 # 初始化Dim(决策变量维数) varTypes = [1] * Dim # 初始化决策变量类型,0:连续;1:离散 lb = [100,2,2,100,2] # 决策变量下界 ub = [600,50,50,1000,10] # 决策变量上界 lbin = [1,1,1,1,1] # 决策变量下边界 ubin = [1,1,1,1,1] # 决策变量上边界 # 调用父类构造方法完成实例化 ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb,ub, lbin, ubin) # 这个地方一定要将训练集传给类变量 self.data = x_train self.dataTarget = y_train # 设置用多线程还是多进程 self.PoolType = PoolType if self.PoolType == 'Thread': self.pool = ThreadPool(2) # 设置池的大小 elif self.PoolType == 'Process': num_cores = int(mp.cpu_count()) # 获得计算机的核心数 self.pool = ProcessPool(num_cores) # 设置池的大小 def evalVars(self, Vars): # 目标函数,采用多线程加速计算 N = Vars.shape[0] args = list(zip(list(range(N)),[Vars] * N,[self.data] * N,[self.dataTarget] * N)) if self.PoolType == 'Thread': f = np.array(list(self.pool.map(subAimFunc, args))) elif self.PoolType == 'Process': result = self.pool.map_async(subAimFunc, args) result.wait() f = np.array(result.get()) return f def subAimFunc(args): i = args[0] Vars = args[1] data = args[2] dataTarget = args[3] rf_model = RandomForestClassifier(n_estimators = Vars[i, 0], min_samples_split = Vars[i, 1], min_samples_leaf = Vars[i, 2], max_leaf_nodes = Vars[i, 3], max_features = Vars[i, 4], random_state=42, oob_score = True).fit(data,dataTarget) scores = cross_val_score(rf_model,data,dataTarget,scoring='f1_macro',cv=10) # 计算交叉验证的得分 ObjV_i = [scores.mean()] # 把交叉验证的平均得分作为目标函数值 return ObjV_i
定义算法模板
这里我选择的是单目标差分进化算法,也有其他算法模板可以选择,具体可以参考 官方算法模板说明
算法中各参数作用与选择可以参考 官方API ,也可以直接读模板源代码,这里建议查看算法源代码,对参数调节会有更深理解, GitHub地址
算法源代码在geatpy-master → \to → geatpy → \to → algorithms下,其中mops文件夹中存储多目标优化模板,sops文件夹中存储单目标优化模板
"""============================实例化问题对象========================""" problem = MyProblem(PoolType='Thread') # 实例化问题对象 """==============================种群设置===========================""" Encoding = 'RI' # 编码方式 NIND = 50 # 种群规模 Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges,problem.borders) # 创建区域描述器 population = ea.Population(Encoding, Field, NIND) # 实例化种群对象(此时种群还没被真正初始化,仅仅是生成一个种群对象) """===========================算法参数设置==========================""" myAlgorithm = ea.soea_DE_best_1_L_templet(problem, population) # 实例化算法模板对象 myAlgorithm.selFunc = 'ecs' # 采用精英复制选择 myAlgorithm.MAXGEN = 30 # 最大进化代数 myAlgorithm.mutOper.F = 0.5 # 差分进化中的参数F myAlgorithm.recOper.XOVR = 0.7 # 设置交叉概率 myAlgorithm.logTras = 1 # 设置每隔多少代记录日志,若设置成0则表示不记录日志 myAlgorithm.verbose = True # 设置是否打印输出日志信息 myAlgorithm.trappedValue=1e-6 # 单目标优化陷入停滞的判断阈值。 myAlgorithm.maxTrappedCount=10 # 进化停滞计数器最大上限值。 myAlgorithm.drawing = 1 # 设置绘图方式(0:不绘图;1:绘制结果图;2:绘制目标空间过程动画;3:绘制决策空间过程动画) """==========================调用算法模板进行种群进化===============""" [BestIndi, population] = myAlgorithm.run() # 执行算法模板,得到最优个体以及最后一代种群 BestIndi.save() # 把最优个体的信息保存到文件中 """=================================输出结果=======================""" print('评价次数:%s' % myAlgorithm.evalsNum) print('时间花费 %s 秒' % myAlgorithm.passTime) if BestIndi.sizes != 0: print('最优的目标函数值为:%s' % BestIndi.ObjV[0][0]) print('最优的控制变量值为:') count = 0 for i in range(BestIndi.Phen.shape[1]): print(BestIndi.Phen[0, i]) else: print('此次未找到可行解。')
输出:
================================================================================== gen| eval | f_opt | f_max | f_avg | f_min | f_std ---------------------------------------------------------------------------------- 0 | 50 | 7.63755E-01 | 7.63755E-01 | 6.73293E-01 | 5.72781E-01 | 4.88142E-02 1 | 100 | 7.75028E-01 | 7.75028E-01 | 7.21321E-01 | 5.95548E-01 | 4.23839E-02 2 | 150 | 7.75028E-01 | 7.75028E-01 | 7.40628E-01 | 6.33135E-01 | 3.29428E-02 3 | 200 | 7.75028E-01 | 7.75028E-01 | 7.54157E-01 | 6.43969E-01 | 2.56128E-02 4 | 250 | 7.75028E-01 | 7.75028E-01 | 7.61468E-01 | 6.50528E-01 | 2.12210E-02 5 | 300 | 7.75028E-01 | 7.75028E-01 | 7.65539E-01 | 6.50528E-01 | 1.80670E-02 6 | 350 | 7.75028E-01 | 7.75028E-01 | 7.70115E-01 | 7.59079E-01 | 3.64143E-03 7 | 400 | 7.75028E-01 | 7.75028E-01 | 7.70977E-01 | 7.60948E-01 | 2.66358E-03 8 | 450 | 7.75028E-01 | 7.75028E-01 | 7.71782E-01 | 7.64347E-01 | 2.11301E-03 9 | 500 | 7.75028E-01 | 7.75028E-01 | 7.72191E-01 | 7.67464E-01 | 1.56715E-03 10| 550 | 7.75028E-01 | 7.75028E-01 | 7.72412E-01 | 7.67464E-01 | 1.55444E-03 11| 600 | 7.75028E-01 | 7.75028E-01 | 7.72624E-01 | 7.69829E-01 | 1.36533E-03
评价次数:600 时间花费 10897.077802181244 秒 最优的目标函数值为:0.7750278687764522 最优的控制变量值为: 544 6 2 185 5
由此得到最优参
使用最优参
rf_new = RandomForestClassifier(n_estimators = 544, min_samples_split = 6, min_samples_leaf = 2, max_leaf_nodes = 185, max_features = 5, random_state=42, oob_score = True) rf_new.fit(x_train,y_train) from sklearn.metrics import accuracy_score print('训练集准确率:{:.2f}'.format(accuracy_score(y_train, rf_new.predict(x_train)))) print('测试集准确率:{:.2f}'.format(accuracy_score(y_test, rf_new.predict(x_test))))
输出:
训练集准确率:0.97 测试集准确率:0.81
可以看到训练集准确率有所下降,测试集准确率上升。在一定程度上避免了过拟合。同时说明经过遗传算法优化后参数,提高了模型泛化能力,优化效果显着。
Be First to Comment