监督学习集成模型——对比与调参
一、三大Boosting算法对比
XGBoost、LightGBM和CatBoost都是目前经典的SOTA(state of the art)Boosting算法,都可以归入梯度提升决策树算法系列。这三个模型都是以决策树为支撑的集成学习框架,其中XGBoost是对原始版本GBDT算法的改进,而LightGBM和CatBoost则是在XGBoost基础上做了进一步的优化,在精度和速度上各有所长。
三大模型之间的区别主要体现在两个方面:
模型树的构造方式有所不同,XGBoost使用按层生长(level-wise)的决策树构建策略,LightGBM则使用按叶子生长(leaf-wise)的构建策略,而CatBoost使用对称树结构,其决策树都是完全二叉树。
对于类别特征的处理有较大区别,XGBoost本身不具备自动处理类别特征的能力,对于数据中的类别特征,需要我们手动处理变换成数值后才能输入到模型中;LightGBM中则需要指定类别特征名称,算法即可自动对其进行处理;CatBoost以处理类别特征而闻名,通过目标变量统计等特征编码方式也能实现高效处理类别特征。
下面以Kaggle 2015年的flights数据集为例,分别用XGBoost、LightGBM、CatBoost模型进行实验。
https://www.kaggle.com/datasets/usdot/flight-delays
1.1 数据预处理
该数据集共有500多万条航班数据记录,特征有31个。仅演示用的情况下,我们采用抽样的方式从原始数据集中抽样1%的数据,并筛选11个特征,经预处理后重新构建训练集,目标是构建对航班是否有延误的二分类模型。数据读取和简单预处理代码如下:
import numpy as np
import pandas as pd
import time
from sklearn.model_selection import train_test_split
data = pd.read_csv("flights.csv")
# 数据抽样1%
data = data.sample(frac=0.01, random_state=10)
# 特征抽样,获取指定的11个特征
data = data[["MONTH","DAY","DAY_OF_WEEK","AIRLINE","FLIGHT_NUMBER","DESTINATION_AIRPORT",
"ORIGIN_AIRPORT","AIR_TIME", "DEPARTURE_TIME","DISTANCE","ARRIVAL_DELAY"]]
# 重新设置索引
data = data.reset_index(drop=True)
# 对标签进行离散化,延误10分钟以上才算延误
data["ARRIVAL_DELAY"] = (data["ARRIVAL_DELAY"]>10)*1
# 类别特征
cols = ["AIRLINE","FLIGHT_NUMBER","DESTINATION_AIRPORT","ORIGIN_AIRPORT"]
# 类别特征编码
for item in cols:
data[item] = data[item].astype("category").cat.codes +1
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(data.drop(["ARRIVAL_DELAY"], axis=1), data["ARRIVAL_DELAY"],
random_state=10, test_size=0.3)
# 打印划分后数据集大小
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
(40733, 10) (40733,) (17458, 10) (17458,)
得到40733条训练样本,17458条测试样本,查看了正例反例的分布情况。
y_train.value_counts()
0 32125
1 8608
Name: ARRIVAL_DELAY, dtype: int64
y_test.value_counts()
0 13691
1 3767
Name: ARRIVAL_DELAY, dtype: int64
1.2 XGBoost测试
import xgboost as xgb
from sklearn.metrics import f1_score
from sklearn.metrics import accuracy_score, roc_auc_score
# 设置模型参数
params = {
'booster': 'gbtree',
'objective': 'binary:logistic',
'gamma': 0.1,
'max_depth': 8,
'lambda': 2,
'subsample': 0.7,
'colsample_bytree': 0.7,
'min_child_weight': 3,
'eta': 0.001,
'seed': 1000,
'nthread': 4,
}
t0 = time.time()
# plst = params.items()
num_rounds = 500
dtrain = xgb.DMatrix(X_train, y_train)
model_xgb = xgb.train(params, dtrain, num_rounds)
print('training spend {} seconds'.format(time.time()-t0))
# 对测试集进行预测
t1 = time.time()
dtest = xgb.DMatrix(X_test)
y_pred = model_xgb.predict(dtest)
print('testing spend {} seconds'.format(time.time()-t1))
y_pred_train = model_xgb.predict(dtrain)
print(roc_auc_score(y_train, y_pred_train))
print(roc_auc_score(y_test, y_pred))
training spend 7.261053085327148 seconds
testing spend 0.0786886215209961 seconds
0.7520467070718759
0.6965243744827456
我们测试了XGBoost在flights数据集上的表现,导入相关模块并设置模型超参数,基于训练集进行XGBoost 模型拟合,最后将训练好的模型用于测试集预测,得到测试集AUC为0.69
1.3 LightGBM测试
import lightgbm as lgb
d_train = lgb.Dataset(X_train, label=y_train)
params = {"max_depth": 5, "learning_rate" : 0.05, "num_leaves": 500, "n_estimators": 300}
#With Catgeorical Features
cate_features_name = ["MONTH","DAY","DAY_OF_WEEK","AIRLINE","DESTINATION_AIRPORT",
"ORIGIN_AIRPORT"]
t0 = time.time()
model_lgb = lgb.train(params, d_train, categorical_feature = cate_features_name)
print('training spend {} seconds'.format(time.time()-t0))
t1 = time.time()
y_pred = model_lgb.predict(X_test)
print('testing spend {} seconds'.format(time.time()-t1))
y_pred_train = model_lgb.predict(X_train)
print(roc_auc_score(y_train, y_pred_train))
print(roc_auc_score(y_test, y_pred))
training spend 0.41118717193603516 seconds
testing spend 0.06251025199890137 seconds
0.8864882165535997
0.7026932971667874
LightGBM得到测试集AUC为0.69,与XGBoost表现差不多
1.4 CatBoost测试
import catboost as cb
cat_features_index = [0,1,2,3,4,5,6]
t0 = time.time()
model_cb = cb.CatBoostClassifier(eval_metric="AUC", one_hot_max_size=50,
depth=6, iterations=300, l2_leaf_reg=1, learning_rate=0.1)
model_cb.fit(X_train,y_train, cat_features= cat_features_index)
print('training spend {} seconds'.format(time.time()-t0))
t1 = time.time()
y_pred = model_cb.predict(X_test)
print('testing spend {} seconds'.format(time.time()-t1))
y_pred_train = model_cb.predict(X_train)
print(roc_auc_score(y_train, y_pred_train))
print(roc_auc_score(y_test, y_pred))
training spend 18.903005361557007 seconds
testing spend 0.04068255424499512 seconds
0.5656212355170469
0.5455274098689694
CatBoost得到测试集AUC为0.55,相比XGBoost和LightGBM表现要差很多。
LightGBM无论是精度还是在速度上,都要优于XGBoost和CatBoost。当然我们只是在数据集上直接用三个模型做了比较,没有做进一步的数据特征工程和超参数调优。
二、超参数调优方法
机器学习模型中有大量参数需要事先人为设定,比如神经网络训练的batch-size、XGBoost等集成学习模型的树相关参数,我们将这类不是经过模型训练得到的参数叫作超参数(hyperparameter)。人为调整超参数的过程就是我们熟知的调参。机器学习中常用的调参方法包括网格搜索法(grid search)、随机搜索法(random search)和贝叶斯优化(bayesian optimization)。
2.1 网格搜索法
网格搜索是一种常用的超参数调优方法,常用于优化三个或者更少数量的超参数,本质上是一种穷举法。对于每个超参数,使用者选择一个较小的有限集去探索,然后这些超参数笛卡儿乘积得到若干组超参数。网格搜索使用每组超参数训练模型,挑选验证集误差最小的超参数作为最优超参数。
sklearn中通过model_selection模块下的GridSearchCV来实现网格搜索调参。
class sklearn.model_selection.GridSearchCV(estimator, param_grid, scoring=None, fit_params=None,n_jobs=1, iid=True, refit=True, cv=None, verbose=0, pre_dispatch=‘2*n_jobs’, error_score=’raise’, return_train_score=’warn’)
GridSearchCV()的参数含义:
-
estimator
选择使用的分类器,并且传入除需要确定最佳的参数之外的其他参数。
每一个分类器都需要一个scoring参数,或者score方法: -
param_grid
需要最优化的参数的取值,值为字典或者列表. -
scoring=None
模型评价标准,默认None,这时需要使用score函数;或者如scoring='roc_auc',
根据所选模型不同,评价准则不同。字符串(函数名),或是可调用对象,
需要其函数签名形如:scorer(estimator, X, y);如果是None,则使用estimator的误差估计函数。 -
n_jobs=1
n_jobs: 并行数,int:个数,-1:跟CPU核数一致, 1:默认值 -
cv=None
交叉验证参数,默认None,使用三折交叉验证。指定fold数量,默认为3,也可以是yield产生训练/测试数据的生成器。
-
verbose=0, scoring=None
verbose:日志冗长度,int:冗长度,0:不输出训练过程,1:偶尔输出,>1:对每个子模型都输出。 -
pre_dispatch=‘2*n_jobs’
指定总共分发的并行任务数。当n_jobs大于1时,数据将在每个运行点进行复制,这可能导致OOM,
而设置pre_dispatch参数,则可以预先划分总共的job数量,使数据最多被复制pre_dispatch次 -
return_train_score=’warn’
如果“False”,cv_results_属性将不包括训练分数。 -
refit :默认为True,程序将会以交叉验证训练集得到的最佳参数,重新对所有可用的训练集与开发集进行,
作为最终用于性能评估的最佳模型参数。即在搜索参数结束后,用最佳参数结果再次fit一遍全部数据集。 -
iid:默认True,为True时,默认为各个样本fold概率分布一致,误差估计为所有样本之和,而非各个fold的平均。
下面使用网络搜索法对XGBoost模型进行最优参数搜索
### GridSearch
from sklearn.model_selection import GridSearchCV
model = xgb.XGBClassifier()
# 待搜索参数列表空间
param_lst = {"max_depth": [3,5,7],
"min_child_weight" : [1,3,6],
"n_estimators": [100,200,300],
"learning_rate": [0.01,0.05,0.1]
}
t0 = time.time()
grid_search = GridSearchCV(model, param_grid=param_lst, cv=3,
verbose=10, n_jobs=-1)
grid_search.fit(X_train, y_train)
print('gridsearch for xgb spend', time.time()-t0, 'seconds.')
Fitting 3 folds for each of 81 candidates, totalling 243 fits
gridsearch for xgb spend 101.64500069618225 seconds.
print(grid_search.best_estimator_)
XGBClassifier(base_score=0.5, booster='gbtree', callbacks=None,
colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,
early_stopping_rounds=None, enable_categorical=False,
eval_metric=None, gamma=0, gpu_id=-1, grow_policy='depthwise',
importance_type=None, interaction_constraints='',
learning_rate=0.1, max_bin=256, max_cat_to_onehot=4,
max_delta_step=0, max_depth=5, max_leaves=0, min_child_weight=6,
missing=nan, monotone_constraints='()', n_estimators=300,
n_jobs=0, num_parallel_tree=1, predictor='auto', random_state=0,
reg_alpha=0, reg_lambda=1, ...)
可以看出,当树最大深度为5,最小子树权重为6,且树的棵树为300时,模型能达到相对最优的效果。
2.2 随机搜索
随机搜索,即在指定超参数范围内或者分布上随机搜寻最优超参数。相较于网格搜索方法,给定超参数分布,并不是所有超参数都会进行尝试,而是会从给定分布中抽样固定数量的参数,实际仅对这些抽样到的超参数进行实验。相较于网格搜索,随机搜索有时候会是更高效的调参方法。
sklearn中通过model_selection模块下的RandomizedSearchCV方法进行随机搜索。
### RandomSearch
from sklearn.model_selection import RandomizedSearchCV
# from scipy.stats import uniform
model = xgb.XGBClassifier()
param_lst = {'max_depth': [3,5,7],
'min_child_weight': [1,3,6],
'n_estimators': [100,200,300],
'learning_rate': [0.01, 0.05, 0.1]
}
t0 = time.time()
random_search = RandomizedSearchCV(model, param_lst, random_state=0)
random_search.fit(X_train, y_train)
print(random_search.best_params_)
print('randomsearch for xgb spend', time.time()-t0, 'seconds.')
{'n_estimators': 300, 'min_child_weight': 6, 'max_depth': 5, 'learning_rate': 0.1}
randomsearch for xgb spend 66.17739844322205 seconds.
2.2 贝叶斯调参
贝叶斯优化是一种基于高斯过程(Gaussian process)和贝叶斯定理的参数优化方法,近年来广泛用于机器学习模型的超参数调优。贝叶斯优化其实跟其他优化方法一样,都是为了求目标函数取最大值时的参数值。作为序列优化问题,贝叶斯优化需要在每次迭代时选取一个最佳观测值,这是贝叶斯优化的关键问题,而这个关键问题正好被上述高斯过程完美解决。
关于贝叶斯优化的大量数学原理,包括高斯过程、采集函数、Upper Confidence Bound(UCB)和Expectation Improvements(EI)等概念原理,可参考相关论文表述。
贝叶斯调参可直接借用现成的第三方库BayesianOptimization来实现。
pip install bayesian-optimization
### Bayesian Opt
from bayes_opt import BayesianOptimization
from tqdm import tqdm
import random
# 定义目标优化函数
def xgb_evaluate(min_child_weight,colsample_bytree,max_depth,subsample,gamma,alpha):
num_rounds = 3000
random_state = 2021
params = {
'eta': 0.1,
'silent': 1,
'eval_metric': 'auc',
'verbose_eval': True,
'seed': random_state
}
params['min_child_weight'] = int(min_child_weight)
params['cosample_bytree'] = max(min(colsample_bytree, 1), 0)
params['max_depth'] = int(max_depth)
params['subsample'] = max(min(subsample, 1), 0)
params['gamma'] = max(gamma, 0)
params['alpha'] = max(alpha, 0)
# 定义xgb交叉验证结果
cv_result = xgb.cv(params, dtrain, num_boost_round=num_rounds, nfold=5,
seed=random_state)
return cv_result['test-auc-mean'].values[-1]
创建贝叶斯优化实例,并设定参数搜索范围
num_iter = 25
init_points = 5
xgbBO = BayesianOptimization(xgb_evaluate, {'min_child_weight': (1, 10),
'colsample_bytree': (0.1, 0.5),
'max_depth': (1, 10),
'subsample': (0.5, 1),
'gamma': (0, 5),
'alpha': (0, 8),
})
# 执行调优过程
xgbBO.maximize(init_points=init_points, n_iter=num_iter)
可以看到,贝叶斯优化在第13次迭代时达到了最优,当alpha=0.7061,列抽样比为0.3001,gamma参数为1.903,树最大深度为9,最小子树权重为2.257,以及子抽样比例为0.9207时,测试集AUC达到最优的0.7205
iter | target | alpha | colsam... | gamma | max_depth | min_ch... | subsample |
---|---|---|---|---|---|---|---|
4 | 0.7172 | 2.369 | 0.3095 | 2.263 | 8.802 | 4.177 | 0.663 |
7 | 0.7193 | 1.118 | 0.443 | 1.844 | 7.876 | 3.6 | 0.7404 |
13 | 0.7205 | 0.7061 | 0.3001 | 1.903 | 9.235 | 2.257 | 0.9207 |
本文作者:王陸
本文链接:https://www.cnblogs.com/wkfvawl/p/16628992.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2018-08-26 win10与linux双系统切换时间不一致的调整
2018-08-26 联想电脑深度系统(deepin)与win10双系统切换设置