【机器学习】LightGBM+遗传算法调参优化
基于遗传算法改进LightGBM
背景介绍
LightGBM作为新流绍⾏起来的算法模型,其发布时间短,相对前沿,且LightGBM模型较复杂,国内对LightGBM的研究尚处于应⽤阶段,⽽针对LightGBM进⾏改进的相关⼯作较少。由于参数对模型性能表现有着决定性的影响,加之LightGBM模型参数众多,因此优化LightGBM预测模型的参数对于提升其在景区产品销量预估的表现尤为重要。但模型参数的调整往往依赖于⼈⼯经验和迭代试错,且LightGBM的参数众多、意义多样(如表4-7),使得调整参数⼯作就显得尤为繁琐和庞⼤,极可能落⼊局部最优的调参范围,事倍功半,精度提升不⾼。此外,最优参数组极⼤程度依赖于训练样本数据,噪声的存在会影响模型学习和演算进程,降低训练模型的泛化能⼒和稳定性。
breaststrokeLightGBM部分参数情况
参数默认取
值
取值范
围
说明
num_boost_round--[10, +]训练迭代次数learning_rate0.01(0, 1)学习步长
max_depth5[2, +]树的最⼤深度min_child_weight5[1, +]最⼩样本权重和awkwardly
bagging_fraction1(0, 1]创建树的样本采样⽐例
feature_fraction1(0, 1]创建树时特征采样⽐例
early_stopping_round--[10, +]提早终⽌训练,防⽌过拟合
lambda0{0 , 1}指定L2正则化
virginia woolfalpha0{0 , 1}指定L1正则化
............
然⽽利⽤遗传算法GA的优势可以很好改善这⼀不⾜。遗传算法(Genetic Algorithm, GA)是基于进化
理论和种群遗传理论,通过计算机模拟⽣物界⾃然选择和遗传的机制,利⽤遗传复制、交叉变异的思想,进化得到适应于指定环境下的最优成果,具有随机性、并⾏性和全局性,能⾃动的积攒全局范围内的空间信息,⾃适应的达到逼近最优值的状态。遗传仿⽣算法的出现,使其迅速在各领域如⽬标优化、调度⽅案、模式识别、机器学习等得到⼴泛的应⽤,成为了⼀种鲁棒性强、效率⾼的优化⽅法,
其基本算法流程如图:
chaps
遗传算法作为⼀种具有随机性、并⾏性和全局性的优化⽅法,其个体适应性函数(即求解问题的⽬标函数),能⾃动化确定和缩⼩最优参数组搜索的⽅向与规模。同时,交叉复制和信息突变的技术,能有效帮助跳出局部搜索范围,避免落⼊局部最优情况。最后,多个搜索信息点的同步执⾏,可⾼效确定全局最优解,从⽽获取最优预测模型参数组,获得性能最好的预测模型。
⽬前,国内已有许多学者将遗传算法优异特性运⽤于提升各类机器学习的性能,如李响等⼈结合遗传算法优化SVM的参数,应⽤在海洋中钢材腐蚀速率预测中,结果表明经GA优化的SVM有更精确的预测结果;李瀚阳等⼈提出了⼀种基于GA-BP的⼿⾜⼝病预估模型,利⽤遗传算法对BP⽹络的初始权值及阈值进⾏优化,证明了经优化的BP相⽐传统BP模型有更准确的⼿⾜⼝病预测精度;雷雪梅等⼈运⽤遗传算法对XGBoost进⾏改进,发现优化的XGBoost在⾼⾎压菜谱判定上⽐传统的XGBoost有更优秀的识别准确性[22]。结合国内利⽤遗传算改进机器学习模型并取得优异成果的研究现状,本⽂提出⼀种基于遗传算法的LightGBM销量预测改进模型GA_LightGBM,将LightGBM参数组的寻优问题看作遗传算法的搜索⽬标,利⽤遗传算法GA对LightGBM进⾏调参优化,解决在对LightGBM调参时出现的因低效率、慢收敛以及局部最优⽽导致的低准确率的问题,得到LightGBM的⽆偏差、全局最优参数组,从⽽提升景区产品销量预估模型的准确性。此外,GA 算法的⾼鲁棒性,对于去除样本噪声的⼲扰,提⾼销量估测模型的泛化能⼒和稳健性也有⼀定帮助。
算法设计
结合遗传算法基本流程图见图4-3,对LightGBM进⾏参数优化,设计改进的算法流程如下图4-4所⽰:
基于遗传算法融合的LightGBM改进⽅案
大概八点二十分发本改进算法的实现流程为:
(1)初始化GA种群与LightGBM参数
(2)训练LightGBM预测模型,将求解RMSE过程定义为⽬标函数
(3)求解当前种群各个体的⽬标函数值,即不同参数组下的LightGBM预测模型的RMSE取值⼤⼩
(4)对中群内各个体进⾏⼆进制编码,即对每个待寻优参数进⾏编码
(5)计算种群内各个体的适应度函数值,即⽬标函数值RMSE的倒数
(6)找出适应度函数值最⼤时对应的参数组,并记录结果
(7)运⽤轮盘赌算法进⾏⾃然选择,保留被选中的参数组
(8)对剩余参数组按交叉率进⾏⼆进制位交换操作
(9)对剩余参数组按变异率进⾏⼆进制位变异操作
(10)对记录的适应度最⼤的⼆进制参数组进⾏解码操作,更新当前最优参数组
(11)重复上述(2)~(10)操作,直⾄⼦代数量达到初始设定值
(12)输出最优参数组和预测值RMSE表现
代码实现
#coding=utf-8
from __future__ import division
import numpy as np
import pandas as pd
import random
import math
from sklearn import metrics
del_lection import train_test_split
import xgboost as xgb
import lightgbm as lgb
from random import randint
# from xgboost.sklearn import XGBClassifiers
'''
群体⼤⼩,⼀般取20~100;终⽌进化代数,⼀般取100~500;交叉概率,⼀般取0.4~0.99;变异概率,⼀般取0.0001~0.1。'''
# generations = 400 # 繁殖代数 100
pop_size = 500 # 种群数量 500capm
# max_value = 10 # 基因中允许出现的最⼤值(可防⽌离散变量数⽬达不到2的幂的情况出现,限制最⼤值,此处不⽤)chrom_length = 15 # 染⾊体长度
pc = 0.6 # 交配概率
pm = 0.01 # 变异概率
results = [] # 存储每⼀代的最优解,N个三元组(auc最⾼值, n_estimators, max_depth)
fit_value = [] # 个体适应度
fit_mean = [] # 平均适应度
# pop = [[0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] for i in range(pop_size)] # 初始化种群中所有个体的基因初始序列
random_ed = 20
cons_value = 0.19 / 31 # (0.20-0.01)/ (32 - 1)
'''要调试的参数有:(参考:adthedocs.io/en/latest/parameter.html)
太棒了英文tree_num:基树的棵数 ----------------(要调的参数)
eta: 学习率(learning_rate),默认值为0.3,范围[0,1] ----------------(要调的参数)
max_depth: 最⼤树深,默认值为6 ----------------(要调的参数)
min_child_weight:默认值为1,范围[0, 正⽆穷],该参数值越⼩,越容易 overfitting,当它的值较⼤时,可以避免模型学习到局部的特殊样本。 ----------(要调的参数 gamma:默认值为0,min_split_loss,范围[0, 正⽆穷]
subsample:选择数据集百分之多少来训练,可以防⽌过拟合。默认值1,范围(0, 1],理想值0.8
colsample_bytree:subsample ratio of columns when constructing each tree,默认值1,范围(0, 1],理想值0.8,太⼩的值会造成⽋拟合
lambda:L2 regularization term on weights, increa this value will make model more conrvative.参数值越⼤,模型越不容易过拟合
alpha:L1 regularization term on weights, increa this value will make model more conrvative.参数值越⼤,模型越不容易过拟合
努力学习的英文
上述参数,要调的有4个,其他的采⽤理想值就可以
tree_num: [10、 20、 30、......150、160] ⽤4位⼆进制, 0000代表10
eta: [0.01, 0.02, 0.03, 0.04, 0.05, ...... 0.19, 0.20] 0.2/0.01=20份,⽤5位⼆进制表⽰⾜够(2的4次⽅<20<2的5次⽅)
00000 -----> 0.01
11111 -----> 0.20
0.01 + 对应⼗进制*(0.20-0.01)/ (2的5次⽅-1)
max_depth:[3、4、5、6、7、8、9、10] ⽤3位⼆进制
min_child_weight: [1, 2, 3, 4, 5, 6, 7, 8] ⽤3位⼆进制
⽰例: 0010, 01001, 010, 110 (共15位)
tree_num eta max_depth min_child_weight
(1+2)*10=30 0.01+9*0.005939=0.06 3+2=5 1+6=7
'''
#定义评价函数rmspe 均⽅根对数误差
female是什么意思def rmspe(y, yhat):
return np.an((yhat/y-1) ** 2))
def xgboostModel(tree_num, eta, max_depth, min_child_weight, random_ed):
#---------------------------数据准备------------------------
data = pd.read_csv('UnFeature.csv',low_memory=Fal)
#因为销售额为0的记录不计⼊评分,所以只采⽤店铺为开,且销售额⼤于0的数据进⾏训练
data = data[data["Open"] != 0]
data = data[data["Sale"] > 0]
data = data[:500000]
data.fillna(0, inplace=True)
#不带特征⼯程
data.drop(['Date','Open','PromoInterval','monthStr','Customers'],axis=1,inplace =True)
#不带特征⼯程
#data.drop(['Date','Customers','Open','PromoInterval','monthStr'],axis=1,inplace =True)
target = np.log1p(data.Sale)
data.drop(['Sale'],axis=1,inplace =True)
X_train,X_test,y_train,y_test =train_test_split(data,target,test_size=0.2)
#--------------------------------------------------------------
# 创建成lgb特征的数据集格式
lgb_train = lgb.Datat(X_train, y_train) # 将数据保存到LightGBM⼆进制⽂件将使加载更快
lgb_eval = lgb.Datat(X_test, y_test, reference=lgb_train) # 创建验证数据
params = {
'task': 'train',
'boosting_type': 'gbdt', # 设置提升类型
'objective': 'regression', # ⽬标函数
'early_stopping_rounds': 100,
'eval_metric': 'rm', # 评估函数
#'num_leaves': 31, # 叶⼦节点数
'learning_rate': eta, # 学习速率
'feature_fraction': 0.8, # 建树的特征选择⽐例
'bagging_fraction': 0.8, # 建树的样本采样⽐例
#'bagging_freq': 5, # k 意味着每 k 次迭代执⾏bagging
'verbo': 1, # <0 显⽰致命的, =0 显⽰错误 (警告), >0 显⽰信息
上海军训
'max_depth': max_depth,
'max_depth': max_depth,
'min_child_weight': min_child_weight,
'ed': randint(1,10)
}
print('')
# 训练 cv and train
model = ain(params,lgb_train,num_boost_round=tree_num,valid_ts=lgb_eval) # 训练数据需要参数列表和数据集 yhat = model.predict(X_test, num_iteration=model.best_iteration)
error = rmspe( np.expm1(yhat), np.expm1(y_test))
return error
def loadFile(filePath):
fileData = pd.read_csv(filePath)
return fileData
# Step 1 : 对参数进⾏编码(⽤于初始化基因序列,可以选择初始化基因序列,本函数省略)
def geneEncoding(pop_size, chrom_length):
pop = [[]]
for i in range(pop_size):
temp = []
for j in range(chrom_length):
temp.append(random.randint(0, 1))
pop.append(temp)
return pop[1:]
# Step 2 : 计算个体的⽬标函数值
def cal_obj_value(pop):
objvalue = []
variable = decodechrom(pop)
for i in range(len(variable)):
tempVar = variable[i]
tree_num_value = (tempVar[0] + 1)* 70 #*10
eta_value = 0.01 + tempVar[1] * cons_value
max_depth_value = 3 + tempVar[2]
min_child_weight_value = 1 + tempVar[3]
#aucValue = xgboostModel(tree_num_value, eta_value, max_depth_value, min_child_weight_value, random_ed) error = xgboostModel(tree_num_value, eta_value, max_depth_value, min_child_weight_value, random_ed)
#objvalue.append(aucValue)
objvalue.append(error)
return objvalue #⽬标函数值objvalue[m] 与个体基因 pop[m] 对应
# 对每个个体进⾏解码,并拆分成单个变量,返回 tree_num(4)、eta(5)、max_depth(3)、min_child_weight(3)def decodechrom(pop):
variable = []
for i in range(len(pop)):
res = []
# 计算第⼀个变量值,即 0101->10(逆转)
temp1 = pop[i][0:4]
v1 = 0
for i1 in range(4):
v1 += temp1[i1] * (math.pow(2, i1))
res.append(int(v1))
# 计算第⼆个变量值
temp2 = pop[i][4:9]
v2 = 0
for i2 in range(5):