微软的lightgbm已经成为了数据挖掘竞赛的必用工具,运行速度快,准确率高等等各种优点。
调参也是竞赛中不可缺少的一步,常规的调参方法有网格搜索,贝叶斯调参等,或者就是部分大佬的手动直接调参,这种级别需要大量的经验累积,23333。
今天介绍一个调参包----hyperopt,可以对lgb进行自动调参,本次先介绍使用hyperopt对lightgbm进行自动调参,下次再更交叉验证~
关于Hyperopt
hyperopt:是python中的一个用于"分布式异步算法组态/超参数优化"的类库。使用它我们可以拜托繁杂的超参数优化过程,自动获取最佳的超参数。广泛意义上,可以将带有超参数的模型看作是一个必然的非凸函数,因此hyperopt几乎可以稳定的获取比手工更加合理的调参结果。尤其对于调参比较复杂的模型而言,其更是能以远快于人工调参的速度同样获得远远超过人工调参的最终性能。
使用iris数据集来进行演示:
导入数据
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
import lightgbm as lgb
from sklearn.model_selection import train_test_split
#导入数据
data_iris=load_iris()
X,y=data_iris.data,data_iris.target
#划分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=100)
train_data = lgb.Dataset(data=X_train,label=y_train)
test_data = lgb.Dataset(data=X_test,label=y_test)
迁移一:自动调参
定义参数空间
使用hyperopt自带的函数定义参数空间,但是因为其randint()方法产生的数组范围是从0开始的,所以我额外定义了一个数据转换方法,对原始参数空间进行一次转换。
from hyperopt import fmin, tpe, hp, partial
# 自定义hyperopt的参数空间
space = {"max_depth": hp.randint("max_depth", 15),
"num_trees": hp.randint("num_trees", 300),
'learning_rate': hp.uniform('learning_rate', 1e-3, 5e-1),
"bagging_fraction": hp.randint("bagging_fraction", 5),
"num_leaves": hp.randint("num_leaves", 6),
}
def argsDict_tranform(argsDict, isPrint=False):
argsDict["max_depth"] = argsDict["max_depth"] + 5
argsDict['num_trees'] = argsDict['num_trees'] + 150
argsDict["learning_rate"] = argsDict["learning_rate"] * 0.02 + 0.05
argsDict["bagging_fraction"] = argsDict["bagging_fraction"] * 0.1 + 0.5
argsDict["num_leaves"] = argsDict["num_leaves"] * 3 + 10
if isPrint:
print(argsDict)
else:
pass
return argsDict
创建模型工厂与分数获取器
lightgbm模型工厂用于生产我们需要的model,而分数获取器则是为了解耦。这样在实际的测试工作中可以更加方便地套用代码。
from sklearn.metrics import mean_squared_error
def lightgbm_factory(argsDict):
argsDict = argsDict_tranform(argsDict)
params = {'nthread': -1, # 进程数
'max_depth': argsDict['max_depth'], # 最大深度
'num_trees': argsDict['num_trees'], # 树的数量
'eta': argsDict['learning_rate'], # 学习率
'bagging_fraction': argsDict['bagging_fraction'], # bagging采样数
'num_leaves': argsDict['num_leaves'], # 终点节点最小样本占比的和
'objective': 'regression',
'feature_fraction': 0.7, # 样本列采样
'lambda_l1': 0, # L1 正则化
'lambda_l2': 0, # L2 正则化
'bagging_seed': 100, # 随机种子,light中默认为100
}
#rmse
params['metric'] = ['rmse']
model_lgb = lgb.train(params, train_data, num_boost_round=300, valid_sets=[test_data],early_stopping_rounds=100)
return get_tranformer_score(model_lgb)
def get_tranformer_score(tranformer):
model = tranformer
prediction = model.predict(X_test, num_iteration=model.best_iteration)
return mean_squared_error(y_test, prediction)
调用Hyperopt开始调参
之后我们调用hyperopt进行自动调参即可,同时通过返回值获取最佳模型的结果。
# 开始使用hyperopt进行自动调参
algo = partial(tpe.suggest, n_startup_jobs=1)
best = fmin(lightgbm_factory, space, algo=algo, max_evals=20, pass_expr_memo_ctrl=None)
结果:
Early stopping, best iteration is:
[125] valid_0's rmse: 0.220095
展示结果
展示我们获取的最佳参数,以及该模型在训练集上的最终表现,如果想要使用交叉验证请参考其他教程。
RMSE = lightgbm_factory(best)
print('best :', best)
print('best param after transform :')
argsDict_tranform(best,isPrint=True)
print('rmse of the best lightgbm:', np.sqrt(RMSE))
结果:
Early stopping, best iteration is:
[135] valid_0's rmse: 0.21918
best : {'bagging_fraction': 0.5, 'learning_rate': 0.050207534543127846, 'max_depth': 17, 'num_leaves': 25, 'num_trees': 250}
best param after transform :
{'bagging_fraction': 0.55, 'learning_rate': 0.05100415069086256, 'max_depth': 22, 'num_leaves': 85, 'num_trees': 400}
rmse of the best lightgbm: 0.219180276662925
通过以上操作,就可以得到最终最优的参数啦,同时也得到了最佳的rmse值。
至于使用hyperopt对lightgbm进行交叉验证调参,我们下次再见啦~