LightGBM的安装
LightGBM CLI 版本的构建可参考LightGBM安装指南,python版本我们只需要通过pip下载安装即可,更多python版本的安装可参见LightGBM/python-package/。
构建普通版本:
1 | pip install lightgbm |
构建GPU版本:
1 | pip install lightgbm --install-option=--gpu |
LightGBM的使用
使用LightGBM的一般流程:
- 特征工程:尽可能多地构造新特征,再通过特征选择筛选出有价值的特征,可以交给模型训练过程自动选择也可以通过其他方式手动选择;
- 模型选择:对于分类问题有lightgbm.LGBMClassifier,对于回归问题有lightgbm.LGBMRegressor;
- 参数选择:迭代次数和一般超参数要分开来优化
- 通过“早停止”确定最优的迭代次数;
- 通过“网格搜索”确定最优的超参数;
- 训练预测:使用最后的模型对全量训练集进行训练,预测测试集标签;
其中 1、2、4 步骤是一般机器学习的基本流程,步骤3才是使用LightGBM的关键所在,下面我们将详细讨论LightGBM的调参之法,并给出基于Python API的相应实例。
特征工程
假设我们已经通过特征工程构建了如下的数据集;
1 | import pandas as pd |
更多数据接口参见这里。
模型选择
以回归模型lightgbm.LGBMRegressor为例。
模型参数
1 | class lightgbm.LGBMRegressor(boosting_type='gbdt', # 模型类型 |
控制类型的参数:
- boosting_type=’gbdt’: 模型类型,default=gbdt, type=enum, options=gbdt, rf, dart, goss;
- objective=None:问题类型,default=regression, type=enum, options=regression, regression_l1, huber, fair, poisson, quantile, quantile_l2, binary, multiclass, multiclassova, xentropy, xentlambda, lambdarank;
控制学习效果的参数:
- num_leaves=31:叶节点数,这是控制树模型复杂度的主要参数,理论上, 借鉴 depth-wise 树, 我们可以设置 num_leaves = 2^(max_depth) 但是, 这种简单的转化在实际应用中表现不佳. 这是因为, 当叶子数目相同时, leaf-wise 树要比 depth-wise 树深得多, 这就有可能导致过拟合. 因此, 当我们试着调整 num_leaves 的取值时, 应该让其小于 2^(max_depth)。越大越容易过拟合;
- max_depth=-1:最大深度,虽然Leaf-wise 只需要控制num_leaves就可以间接控制树的深度,但你也可以利用 max_depth 来显式地限制树的深度。越大越容易过拟合;
- learning_rate=0.1:学习率,一般取值范围是
[0.01,0.1]
,需要和n_estimators配合使用;较小的 learning_rate 和较大的 num_iterations一般会有更好的准确率; - n_estimators=100:迭代次数;越大越容易过拟合;
- max_bin=255:特征值的脂肪bins个数,LightGBM 将根据 max_bin 自动压缩内存。 例如, 如果 maxbin=255, 那么 LightGBM 将使用 uint8t 的特性值;越大越容易过拟合;
- subsample_for_bin=200000:用来构建直方图的样本数量,在设置更大的数据时, 会提供更好的训练效果, 但会增加数据加载时间;
- class_weight=None:样本权重,可以是dict、’balanced’或 None, dict只用于多分类任务,可以通过{class_label: weight}为每种标签设置权值;对于二分类可以使用is_unbalance or scale_pos_weight参数;’balanced’会自动根据标签比例调整样本权重;
- min_split_gain=0.0:分裂的最小增益,越大越限制节点分裂越容易欠拟合;
- min_child_weight=0.001:一个叶子上的最小 hessian 和,类似于 min_child_samples,越大越容易欠拟合;
- min_child_samples=20:叶子中的最小样本数,越大越容易欠拟合;
- subsample=1.0:行抽样比例,越大越容易过拟合;
- subsample_freq=0:抽样频率,每k次迭代进行一次采样,等于0时不采样,越大越容易过拟合;
- colsample_bytree=1.0:列抽样比例,越大越容易过拟合;
- reg_alpha=0.0:L1正则项系数,越大越不容易过拟合;
- reg_lambda=0.0:L2正则项系数,越大越不容易过拟合;
控制辅助的参数:
- random_state=None:随机种子
- n_jobs=-1:线程数
- silent=True:迭代过程是否打印信息
其它控制的参数:
- verbose=1:int,控制日志打印级别, <0 = 致命的, =0 = 错误 (警告), >0 = 信息
假设我们的回归模型初始化参数为:
1 | lgb_reg = lgb.LGBMRegressor(num_leaves=5, |
模型的属性
- nfeatures:int ,特征数
- classes_:array of shape = [n_classes] ,分类标签
- nclasses:int,分类标签数
- bestscore:dict or None,模型最好的得分
- bestiteration:int or None,如果训练时指定了early_stopping_rounds,模型最优迭代次数
- objective:string or callable,训练时所使用的objective
- evalsresult:dict or None,如果训练时指定了early_stopping_rounds,评估结果
- featureimportances:array of shape = [n_features],特征重要度
早停止——确定最优迭代次数
原理:LightGBM是一种迭代的Boosting方法,我们可以在模型训练时设置验证集,通过在每轮迭代评估当前模型在验证集上的性能来监控模型训练过程,通过早停止的方式确定出在给定步长下的最优迭代次数。
早停止涉及到以下几个核心概念:
- 步长:也称学习率
learning_rate
,最优迭代次数和步长的设置息息相关,必须先固定步长,一般取值范围为[0.01,0.1]
; - 验证集:必须在模型
fit
时提供相应的验证集; - 评估指标:必须使用合适的评估指标
eval_metric
;
fit方法
1 | fit(X, # 特征空间 |
- X:特征空间,array-like or sparse matrix of shape = [n_samples, n_features],一般为ndarray或者DataFrame
- y:标签空间,array-like of shape = [n_samples],一般为ndarray或者Series
- sample_weight=None:样本权重,array-like of shape = [n_samples] or None
- init_score=None:训练数据的初始得分,array-like of shape = [n_samples] or None
- eval_set=None:验证集,为了早停止而用作验证集的(X, y) 元组列表
- eval_names=None:验证集名称,list of strings or None
- eval_sample_weight=None:验证集样本权重,list of arrays or None
- eval_init_score=None:验证集初始得分,list of arrays or None
- eval_metric=’l2’:评估指标,string, list of strings, callable or None,如果为字符串,则必须是内置的评估指标,如l1, l2, ndcg, auc, binary_logloss, binary_error …;如果是函数,必须满足func(y_true, y_pred),返回 (指标名eval_name, 指标得分eval_result, 是否越大越好is_bigger_better);
- early_stopping_rounds=None:早停止迭代次数,当评估指标停止提升early_stopping_rounds多轮时,训练会提前停止;
- verbose=True:是否打印详情,bool值,且至少有一个验证集
- feature_name=’auto’:特征名,当取’auto’且输入的数据为DataFrame时,会自动识别特征名;
- categorical_feature=’auto’:类别特征,list of strings or int, or ‘auto’,如果是字符串,特征名会被使用,如果是整型,特征索引会被使用,如果是’auto’且数据为DataFrame时,会自动识别特征名,被自动识别类别特征;
- callbacks=None:callback函数列表,函数列表中的每个函数都会在每次迭代结束时被调用,详见python的回调函数;
自定义早停止中的评估函数
自定义函数遵循以下接口协议:func(y_true, y_pred)
, func(y_true, y_pred, weight) or func(y_true, y_pred, weight, group)
,Returns (eval_name, eval_result, is_bigger_better) or list of (eval_name, eval_result, is_bigger_better)
。
- y_true:真实标签,array-like of shape = [n_samples]
- y_pred:预测标签,array-like of shape = [n_samples]
- weight:样本权重
- group:Group/query data, used for ranking task
- eval_name: 评估指标名称,str
- eval_result: 评估结果,float
- is_bigger_better: 是否越大代表模型越好,bool
一个将Gini系数作为评估指标的例子:
1 | def gini(actual, pred): |
通过早停止确定最优迭代次数
通过早停止能够得到在当前参数下的最佳迭代次数,需要重新修正模型的迭代次数参数n_estimators,一个例子:
1 | def early_stopping(lgb_reg, X_train, y_train, X_valid, y_valid, online): |
打印日志监控迭代过程:
1 | [1] gini's l2: 12.5645 gini's gini_normalized: 0.709833 |
网格搜索
在确定了最优的迭代次数之后,需要通过网格搜索来确定其他超参数的最优取值,网格搜索包含以下几个核心:
- 模型:用于训练的模型
- 参数空间:由各个待调节参数的取值范围共同组成的参数空间
- 搜索机制:参数空间的搜索或抽样机制
- 交叉验证:通过交叉验证留出验证集来验证不同参数备选解的效果
- 评估函数:评估模型训练效果的函数
搜索机制
常用的有两种网格搜索机制:
- 一般网格搜索:给出每个待调节的超参数的若干备选解,它们的任意组合构成很多组解,在所有的备选解空间中搜索最优参数;因为备选解空间刚好都是各轴交点,它们一起构成了一个解的网格,因此称为网格搜索;
- 随机网格搜索:给出每个待调节的超参数的若干备选解或分布,每次从各个超参数备选解中抽样出一个值组成一组解,在有限抽样次数中寻找最优解;
关于二者的比较可以参见这篇文章Smarter Parameter Sweeps (or Why Grid Search Is Plain Stupid)。
随机网格搜索更加高效,接下来将主要介绍scilit-learn提供的RandomizedSearchCV方法,GridSearchCV方法也大同小异。
1 | class sklearn.model_selection.RandomizedSearchCV(estimator, |
参数说明:
- estimator:模型对象,模型中需要指定score方法,否则必须传递scoring参数;
- param_distributions:dict,参数名作为key,参数分布或者列表作为value;如果所有参数都以列表作为value,那么不会出现重复抽样;否则,可能出现重复抽样,参数分布必须支持rvs方法用于抽样;
- n_iter=10:抽样次数
- scoring=None:string, callable, list/tuple, dict or None,应用于验证集上的评估方法。如果是字符串,必须是内置的评估指标,如’f1’,’roc_auc’,参见predefined values;如果是函数,可以通过
sklearn.metrics.make_scorer(score_func)
方法构造一个scorer,其中score_func(y, y_pred, **kwargs)
,return eval_result; - fit_params=None:dict,传给fit方法的参数
- n_jobs=1:线程数
- iid=True:是否独立同分布
- refit=True:是否使用最优参数重新在全量数据集行进行训练,refit后的模型可以使用bestestimator返回,可以直接在该模型上应用predict方法;
- cv=None:int,cross-validation generator or an iterable,默认采用3折交叉验证。如果为整数k,采用k折交叉验证;如果为交叉验证生成器或可迭代的训练测试集划分,具体可参考Cross-validation;
- verbose=0:大于等于0的整数,控制日志打印详细程度,越大越详细;
- pre_dispatch=‘2*n_jobs’:int, or string控制线程数,默认立刻创建所有线程;
- random_state=None:随机种子;
- error_score=’raise’:fit时出错的处理机制
- returntrain_score=’warn’:boolean,是否在cv_results中包含训练集得分,默认为True;
属性:
- cvresults:网格搜索结果,dict of numpy (masked) ndarrays(keys作为header,values作为列,可以传给DataFrame显示)。包含以下项目:
- params:存储历次尝试的参数字典列表
- rank_test_score:存储各次参数的得分排名
1 | # 包含两组备选解的搜索结果 |
- bestestimator:返回最佳模型,参见refit
- bestscore:返回最佳模型的平均交叉验证得分
- bestparams:返回最佳参数
- bestindex:返回最佳参数在cvresults[params]中的索引
- scorer_:返回所使用的评估函数
- nsplits:int,交叉验证的折数
参数空间
参数空间由字典表示,参数名为key,参数取值列表或分布(具有rvs方法的对象)作为values,一个例子:
1 | from scipy.stats import randint as sp_randint |
自定义网格搜索中的评估函数
网格搜索自定义评估函数(可作为scoring参数)的接口协议:
1 | sklearn.metrics.make_scorer(score_func, greater_is_better=True, needs_proba=False, needs_threshold=False, **kwargs)[source]¶ |
一个例子:
1 | # 首先定义一个score_func |
随机网格搜索确定最优超参数
一个例子:
1 | def random_search(lgb_reg, params_dist, X_train, y_train, n_iter=10, nfold=3): |
打印网格搜索结果:
1 | GridSearchCV took 100 candidate parameter settings. |
训练预测
LightGBM训练预测过程与一般机器学习无异:
1 | # lgb_reg是网格搜索refit后的最优模型 |
LightGBM示例完整代码
1 | #!/usr/bin/env python |