写在最前
文章有很多被转载,我看了,重要的地方由于格式问题,转载网站上并未适配,请大家戳原文链接看原文,有最完整描述。
LightGBM,XGBoost作为非常经典的GBDT模型,网上原理和实战代码都一大堆。但是看了几个公式,写了几行代码。是不是总觉得心里空空的。直到有一次被面试官问道。给你一堆数据,让你用GBDT模型去处理。这些数据在模型内部是如何运行的呢,最终答案如何得到的呢?虽然现场可以结结巴巴的答出来,但是觉得面试官一针见血,其实这个问题才是学习过程中真正需要先弄懂的吧。多方面查了资料,主要以XGBoost为基础,把这个问题给大家陈述一下。大家可以先戳这个链接看看XGBoost的一些核心参数,对于原理部分,这里有GBDT的原理解释。当然,XGBoost的原理解释也有,建议先从GBDT开始。
我用步骤的方式来阐述这一过程
- 先给学习器定一些宏大的目标,这时候需要通用参数的支持。使用
booster
定义我们采取树还是线性方法来作为训练的结构。使用silent
定义系统要不要实时输出训练的内容,比如树的深度和叶子数量什么的。使用nthread
来设置使用多少线程来运行此模型。 - 接下来是学习目标参数。我们要定义
objective
,是我们要拿此模型做回归还是分类?多分类还是二分类?这是总体目标。接下来是eval_metric
,这是评估函数,也是我们的损失函数。我们都知道,模型优化的方向是按照损失函数值下降的方向进行的,所以这很重要。在xgboost的原论文中,我们在XGBoost中的目标函数由两部分构成:其中第一部分便是这个损失函数。第二部分是正则化函数,后面细谈。
- 有了损失函数,那我们还需要定义一下正则化参数,正则化函数总体上是衡量树的复杂度,正则化的原理可以戳此链接:
这里需要三个参数,gamma
, alpha
,lambda
。值得注意的是,我查遍了xgboost原论文和各类博客,发现并没有关于在数学公式里面的描述。但是它作为l1正则化参数确实有意义并且存在。我猜想可能其大部分情况都为0,导致公式里没有很重视。这里我大胆的将其加了进来。
首先,一棵树有个叶子节点,这个叶子节点的值组成了一个维向量,这分别对应上面的正则化函数。
我对(
gamma
)这个参数详细解释一下,这段有兴趣可看看
gamma
,作为的正则化参数,它的存在是让尽可能小,gamma
值设置的越大,算法越发保守。原参数介绍是这么说的,gamma
是在树的叶节点进一步分裂所需的最小损失减少量。我尽可能不用太多公式来解释此描述:我们看到正则化函数中实际上可以写作这样:
而xgboost分裂节点的策略是左右两个字节点的目标函数值之和比父节点小则分裂。这里就会用到一个差值,两个子节点的目标函数减去父节点的目标函数值要大于0。这里gamma算是一个常数,则有:
这其中函数代表的是目标函数去掉常数 的部分。是目标函数的差,基本上是由损失函数的减少量贡献的。 我这里做了一个简单的阐述,具体的大家可以看看原论文解释的更清楚。如果有什么疑问或者高见的话,欢迎来评论区吐槽。
- 现在已经确定了目标函数,那就拿数据来训练呗。我们训练基本使用GBDT作为核心来训练的。
- 先对这部分数据进行一个CART训练。训练完一个结果之后,一棵树训练一定是不准确的。那么经过评估函数评估误差之后,就进入 第二棵树的生成过程中。
- 第二棵树的目标并不是原目标,而是目标与上一棵树预测值的残差.残差的取法就是GBDT命名的方式一样,以目标函数的负梯度来作为第二棵树的学习目标.
- 第三棵树的学习目标是前两棵树学习的总残差.这样学习,理论上说我们总的误差在一步步减少.
- …….
- 在如此反复迭代n次之后,有个
n_estimator
参数限制最大生成树的数目,也是最大迭代次数.n到达这个数字停止生成新树.- 将之前每次训练的树的值加起来作为最终训练结果.我们会看到,将每棵的值加起来,实际上是从原目标函数中每次都加上一个负梯度,也就相当于减去一个梯度.这样GBDT的优化过程其实是梯度下降的原理.这可能就是GBDT的核心吧.
这个训练过程我们用到有: learning_rate
:每次迭代的梯度下降步长,max_depth
:代表最大树深,colsample_bytree
:每次生成树的时候随机选取列数的比例,colsample_bylevel
:这个就相比于前一个更加细致了,它指的是每棵树每次节点分裂的时候列采样的比例。subsample
:每次训练的时候随机采样的比例,我觉得这应该指的是行采样吧。还有一些我个人不太常用的,比如max_delta_step
:这个参数限制了每棵树权重改变的最大步长。
以上都是些核心的参数,比较常用的。
这篇文章中比较重点的就是树的生成方式,核心的东西讲出来了,面试官应该不会不满意吧。