在之前的一篇文章中,从 GBDT 一直说到当下最流行的梯度提升树模型之一 XGBoost,今天这里主要说应用XGB这个算法包的一些参数问题,在实际应用中,我们并不会自己动手去实现一个XGB,了解更多的XGB的算法原理,也是为了我们在工程实践中更好的应对各种问题,了解每一个参数背后的意义,有助我们训练出来更好的模型
普通参数 [General Parameters]
1. booster [default: gbtree]
a: 表示应用的弱学习器的类型, 推荐用默认参数
b: 可选的有gbtree, dart, gblinear
gblinear是线性模型,表现很差,接近一个LASSO
dart是树模型的一种,思想是每次训练新树的时候,随机从前m轮的树中扔掉一些,来避免过拟合
gbtree即是论文中主要讨论的树模型,推荐使用
2. silent [default: 0] [不推荐]
a: 不推荐使用,推荐使用verbosity参数来代替,功能更强大
3. verbosity [default: 1]
a: 训练过程中打印的日志等级,0 (silent), 1 (warning), 2 (info), 3 (debug)
4. nthread [default: 最大可用线程数][alias: n_jobs]
a: 训练过程中的并行线程数
b: 如果用的是sklearn的api,那么使用n_jobs来代替
每个分类器算法参数 [Booster Parameters]
1. eta [default: 0.3] [alias: learning_rate] [range: [0, 1]]
a: 就是常说的学习速率,控制每一次学习的权重缩减,给后来的模型提供更多的学习空间
2. gamma [default: 0] [alias: min_split_loss] [range: [0, inf]]
a: 叶子节点分裂时所需要的最小的损失减少量,这个值越大,叶子节点越难分裂,所以算法就越保守
gamma ( \( \gamma\) )就是 从GBDT到XGBoost 中的正则化项控制叶子节点数量复杂度( \(\gamma T\) )的系数,在文中最后寻找分裂节点 \( j\) 的时候,实际是用分裂后的 左子树的损失+右子树的损失-分列前的损失
$$
Gain=\frac{G_L^2}{H_L+\lambda}+\frac{G_R^2}{H_R+\lambda}-\frac{(G_L+G_R)^2}{H_L+H_R+\lambda}-\gamma
$$
看 \( Gain\) 是否减少了,也就是判断 \( Gain > 0 \space ?\) ,如果减少了则在 \( j\) 处分裂,反过来看也就是
$$
\frac{G_L^2}{H_L+\lambda}+\frac{G_R^2}{H_R+\lambda}-\frac{(G_L+G_R)^2}{H_L+H_R+\lambda} \> \gamma\\
$$
gamma ( \( \gamma\) ) 就是表示的这个数值,意思就是最少要减少 \(\gamma\) 这些损失,否则不分裂
3. max_depth [default: 6] [range: [0, inf]]
a: 树的最大深度
b: 这个值对结果的影响算是比较大的了,值越大,树的深度越深,模型的复杂度就越高,就越容易过拟合
c: 注意如果这个值被设置的较大,会吃掉大量的内存
d: 一般来说比价合适的取值区间为[3, 10]
4. min_child_weight [default: 1] [range: [0, inf]]
a: 最小的叶子节点权重
b: 在普通的GBM中,叶子节点样本没有权重的概念,其实就是等权重的,也就相当于叶子节点样本个数
c: 越小越没有限制,容易过拟合,太高容易欠拟合
min_child_weight 源码中的 文档 是这么解释的,“Minimum sum of instance weight(hessian) needed in a child.” 实际上知晓XGBoost的原理的话,其实这个参数就是
$$
H_j = \sum_{i\in I_j}^{}{h^{(i)}}
$$
$$
h^{(i)}= \partial^2_{H_m(x^{(i)})}[l(y^{(i)}, H_m(x^{(i)})]
$$
也就是叶子节点中的样本二阶导求和,更一般的情况假如是均方差损失 \( \frac{1}{2}(y-h_\theta(x))^2\) 的话,二阶导之后的 \( h^{(i)}=1\) ,所以此时 min_child_weight 其实就是叶子节点中的样本个数
min_child_weight 数值越大的话,就越不容易形成叶子节点,算法就越保守,越不容易过拟合,其实在XGBoost中,在分裂节点的时候,每个样本是有一个“权重”的概念的,用于在分裂节点的时候减少计算量,权重就是 \( h^{(i)}\)
5. max_delta_step [default: 0] [range: [0, inf]]
a: 适用于正负样本不均衡情况下,控制学习速率(类似eta)最大为某个值,不能超过这个阈值
b: 首先我们有参数eta来控制学习速率,为了后面学习到更多,每一步在权重上乘上这个因子,降低速度
c: 但是在正负样本不均衡的情况下eta不足够,因为此时由于二阶导接近于0的原因,权重会特别大
d: 这个参数就是用来控制学习速率最大不能超过这个数值
6. sub_sample [default: 1] [range: (0, 1]]
a: 样本抽样比例
b: 在每次训练的随机选取sub_sample比例的样本来作为训练样本
7. colsample_by* [default: 1]
a: 这里实际上有3个参数,借助了随机森林的特征抽样的思想,3个参数可以同时使用
b: colsample_bytree 更常用,每棵树的特征抽样比例
c: colsample_bylevel 每一层深度的树特征抽样比例
d: colsample_bynode 每一个节点的特征抽样比例
8. lambda [default: 1] [alias: reg_lambda]
a: 损失函数中的L2正则化项的系数,类似RidgeRegression,减轻过拟合
9. alpha [default: 0] [alias: reg_alpha]
a: 损失函数中的L1正则化项的系数,类似LASSO,减轻过拟合
10. scale_pos_weight [default: 1]
a: 在正负样本不均衡的情况下,此参数需要设置,通常为: sum(负样本) / sum(正样本)
TreeBooster 的参数整体来说主要为以上这些,当然不止于这些,而是这些是最为常用的,如果想要了解更多的话,官方文档是个好地方
学习目标参数 [Learning Task Parameters]
1. objective [default: reg:squarederror(均方误差)]
a: 目标函数的选择,默认为均方误差损失,当然还有很多其他的,这里列举几个主要的
b: reg:squarederror 均方误差
c: reg:logistic 对数几率损失,参考对数几率回归(逻辑回归)
d: binary:logistic 二分类对数几率回归,输出概率值
e: binary:hinge 二分类合页损失,此时不输出概率值,而是0或1
f: multi:softmax 多分类softmax损失,此时需要设置num_class参数
2. eval_metric [default: 根据objective而定]
a: 模型性能度量方法,主要根据objective而定,也可以自定义一些,下面列举一些常见的
b: rmse : root mean square error 也就是平方误差和开根号
c: mae : mean absolute error 误差的绝对值再求平均
d: auc : area under curve roc曲线下面积
e: aucpr: area under the pr curve pr曲线下面积
工具包参数 [XGB Packages Parameters]
1. num_boost_round
a: 迭代次数,这货其实跟sklearn中的n_estimators是一样的
b: sklearn的api中用n_estimators,原始xgb中用num_boost_round
2. evals
a: 训练过程中通过计算验证集的指标,观察模型性能的数据集
b: 指标就是通过eval_metric参数来制定的
3. early_stopping_rounds
a: 在num_boost_round的轮训练中,如果过程中指标经过early_stopping_rounds轮还没有减少
那么就停止训练
b: 指标是通过evals的验证集,计算eval_metric的指标
这里主要说一下 early_stopping_rounds 这个参数, 首先触发这个参数(也就是确实提前停止了)的时候返回的变量会带有3个属性: best_score, best_iteration, best_ntree_limit , 这里best_ntree_limit 就是最好的模型的树的个数
但是在文档和 源码 中,有这么一句话 The method returns the model from the last iteration (not the best one).
就是说如果触发了这个参数的话,那么结果返回的并不是最好的模型,而是最后一轮的模型,那这不坑爹呢?!
但是后续再深入测试的时候发现,用各种指标去验证(比如 rmse
)的时候,结果却和最好的模型是一样的,并不是和最后一轮的模型一样,再深入的研究之后在源码中发现了 这么一段代码 ,XGBoost在调用 predict 的时候 tree_limit 参数如果没指定默认用的就是 best_ntree_limit 也就是在预测时候,用的还是最好的模型
总结
整体来说,对于一个机器学习问题如果用XGBoost这个算法包来实现的话,算法层面上的参数主要应用大概是如上这些,当然从0到1实现一个模型的话,这些参数可能是不足够的,还包括工程上的实现,这里只是说在算法方面的一般场景,更明确的其实是 gbtree 这个核心算法,目前来说在实际应用中树模型的表现要好于线性模型,在树模型中提升树表现的还要更好,其实提升树模型中不止 XGBoost 这一种,同样是 GBDT 的升级版还有微软开源的 LightGBM 和 CatBoost ,这个以后有机会再研究。
😛