走进XGBoost
什么是XGBoost?
全称:eXtreme Gradient Boosting
作者:陈天奇(华盛顿大学博士)
基础:GBDT
所属:boosting迭代型、树类算法。
适用范围:分类、回归
优点:速度快、效果好、能处理大规模数据、支持多种语言、支持自定义损失函数等等。
缺点:算法参数过多,调参负责,对原理不清楚的很难使用好XGBoost。不适合处理超高维特征数据。
XGBoost是陈天奇等人开发的一个开源机器学习项目,高效地实现了GBDT算法并进行了算法和工程上的许多改进,被广泛应用在Kaggle竞赛及其他许多机器学习竞赛中并取得了不错的成绩。
说到XGBoost,不得不提GBDT(Gradient Boosting Decision Tree)。因为XGBoost本质上还是一个GBDT,但是力争把速度和效率发挥到极致,所以叫X (Extreme) GBoosted。
XGBoost树的定义
先来举个例子,我们要预测一家人对电子游戏的喜好程度,考虑到年轻和年老相比,年轻更可能喜欢电子游戏,以及男性和女性相比,男性更喜欢电子游戏,故先根据年龄大小区分小孩和大人,然后再通过性别区分开是男是女,逐一给各人在电子游戏喜好程度上打分,如下图所示。
就这样,训练出了2棵树tree1和tree2,类似gbdt的原理,两棵树的结论累加起来便是最终的结论,所以小孩的预测分数就是两棵树中小孩所落到的结点的分数相加:2 + 0.9 = 2.9。爷爷的预测分数同理:-1 + (-0.9)= -1.9。具体如下图所示:
这个图形也可以使用XGBoost自带方法可视化,和上面那个预测图一样的意义
如果你对gbdt有所了解,你会发现这不是一样的吗?
事实上,如果不考虑工程实现、解决问题上的一些差异,XGBoost与GBDT比较大的不同就是目标函数的定义。XGBoost的目标函数如下图所示:
如果光看这一张图片,你也许会有所疑惑,这些到底是什么?
红色箭头所指向的L 即为损失函数(比如平方损失函数:l(yi,yi)=(yi−yi)2)
红色方框所框起来的是正则项(包括L1正则、L2正则)
红色圆圈所圈起来的为常数项
对于f(x),XGBoost利用泰勒展开三项,做一个近似。f(x)表示的是其中一颗回归树。
再举一个简单的例子
首先我们初始化三个样例的考试成绩预测值为0,属性为(天赋高、不恋爱、每天学习时间有16个小时),(天赋低、不恋爱、每天学习时间有16个小时),(天赋高、不恋爱、每天学习时间有6个小时),真实成绩分别为100,70,86分。
Xgboost系统的每次迭代都会构建一颗新的决策树,决策树通过与真实值之间残差来构建,(天赋高、不恋爱、每天学习时间有16个小时)的学霸考了100分,(天赋低、不恋爱、每天学习时间有16个小时)的小学霸考了70分,(天赋高、不恋爱、每天学习时间有6个小时)的小天才考了86分,那么三位同学通过第一个决策树后预测结果分别为90分,60分,和90分。
在构建第二颗决策树时就会考虑残差(100-90=10),(70-60)=10,(86-90=-4)来构建一颗新的树,我们通过最小化残差学习到一个通过学习时间属性来构建的决策树的得到90+5,60+5,90-5的预测值,再继续通过(100-95=5)(70-65)(86-85)的残差构建下一个决策树,以此类推,当迭代次数达到上限或是残差不再减小是停止,就得到一个拥有多个(迭代次数)决策树的强分类器。
当然分类器是需要考虑更多样本的,我们可以把新加入的决策树fi(x)看作是在N维空间(因为有N个样本)中p(m)相对于点p(m-1)的增量。
XGBoost核心算法
不断地添加树,不断地进行特征分裂来生长一棵树,每次添加一个树,其实是学习一个新函数f(x),去拟合上次预测的残差。
当我们训练完成得到k棵树,我们要预测一个样本的分数,其实就是根据这个样本的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点就对应一个分数
最后只需要将每棵树对应的分数加起来就是该样本的预测值。
为了保证预测值和真实值的匹配
XGBoost也是需要将多棵树的得分累加得到最终的预测得分(每一次迭代,都在现有树的基础上,增加一棵树去拟合前面树的预测结果与真实值之间的残差),在前面也提到过。
那接下来,我们如何选择每一轮加入什么 f 呢?答案是非常直接的,选取一个 f 来使得我们的目标函数尽量最大地降低。这里 f 可以使用泰勒展开公式近似。
实质是把样本分配到叶子结点会对应一个obj,优化过程就是obj优化。也就是分裂节点到叶子不同的组合,不同的组合对应不同obj,所有的优化围绕这个思想展开。
到目前为止我们讨论了目标函数中的第一个部分:训练误差;下面就开始讨论目标函数的第二个部分:正则项,即如何定义树的复杂度。
正则项:树的复杂程度
XGBoost的复杂程度包含了两个重要的部分,一个是树里面叶子节点的个数T,一个是树上叶子节点的得分w的L2模平方(对w进行L2正则化,相当于针对每个叶结点的得分增加L2平滑,目的是为了避免过拟合)
XGBoost的目标函数(损失函数揭示训练误差 + 正则化定义复杂度):
总而言之,XGBoost使用了和CART回归树一样的想法,利用贪婪算法,遍历所有特征的所有特征划分点,不同的是使用的目标函数不一样。具体做法就是分裂后的目标函数值比单子叶子节点的目标函数的增益,同时为了限制树生长过深,还加了个阈值,只有当增益大于该阈值才进行分裂。从而继续分裂,形成一棵树,再形成一棵树,每次在上一次的预测基础上取最优进一步分裂/建树。
XGBoost与GBDT有什么不同
GBDT是机器学习算法,XGBoost是该算法的工程实现。
在使用CART作为基分类器时,XGBoost显式地加入了正则项来控制模 型的复杂度,有利于防止过拟合,从而提高模型的泛化能力。
GBDT在模型训练时只使用了代价函数的一阶导数信息,XGBoost对代价函数进行二阶泰勒展开,可以同时使用一阶和二阶导数。
传统的GBDT采用CART作为基分类器,XGBoost支持多种类型的基分类器,比如线性分类器。
传统的GBDT在每轮迭代时使用全部的数据,XGBoost则采用了与随机森林相似的策略,支持对数据进行采样。
传统的GBDT没有设计对缺失值进行处理,XGBoost能够自动学习出缺 失值的处理策略。
XGBoost需要注意的点
是否需要做特征筛选?
XGBoost可以做特征筛选,也就是重要性特征排序,那么我们的模型算法需要做特征筛选吗?根据经验来说是不一定,有时候不去剔除反而是最好的,看到网上帖子有一个生动形象的表达:
对于有惩罚项的机器学习算法,不需要,特征选择更多见到的是逻辑回归这种有考虑置信区间的算法,因为要考虑置信区间(达不到95%以上的可信度我就不要了),所以要做特征取舍,因为要做特征取舍,所以要考虑多重共线性问题,因为共线性会直接影响特征的重要性表现。
事实上也是如此,在前期的几个文章里面,我们基于决策树,随机森林等做的特征筛选,反而没有提高模型的效果,还降低了。
对于你不清楚的特征,需要谨慎。因为这有可能是只有短期有效的垃圾特征。
比如瓜长得大不大,跟国足踢球进没进,就大概率没关系。但有可能在特定的农场里有用,比如这个农场的管理员是个球迷,国足踢球踢进了,他就开心,就额外多施肥。那么“国足踢球进没进”这个特征在这个农场里就是一个有力的特征,但放到其他农场里就没用了,这就是一个泛化能力很弱的特征。这样100个不同类型的特征进行删减,可能只有两三个是有用的。
是否需要对数据集归一化?
“答案是不需要。首先,归一化是对连续特征来说的。那么连续特征的归一化,起到的主要作用是进行数值缩放。数值缩放的目的是解决梯度下降时,等高线是椭圆导致迭代次数增多的问题。而xgboost等树模型是不能进行梯度下降的,因为树模型是阶越的,不可导。树模型是通过寻找特征的最优分裂点来完成优化的。由于归一化不会改变分裂点的位置,因此xgboost不需要进行归一化。”
XGBoost重要参数详解
1、booster [default=gbtree]
选择每次迭代过程中需要运行的模型,一共有两种选择:gbtree:;tree-based models,但是一般选择是树,线性不是很好
2、eta [default=0.3, alias: learning_rate]
学习率,可以缩减每一步的权重值,使得模型更加健壮:典型值一般设置为:0.01-0.2
越大,迭代的速度越快,算法的极限很快被达到,有可能无法收敛到真正的最佳。 越小,越有可能找到更精确的最佳值,更多的空间被留给了后面建立的树,但迭代速度会比较缓慢。
3、min_child_weight [default=1]
定义了一个子集的所有观察值的最小权重和。这个可以用来减少过拟合,但是过高的值也会导致欠拟合,因此可以通过CV来调整min_child_weight。
4、max_depth [default=6]
树的最大深度,值越大,树越复杂。这个可以用来控制过拟合,典型值是3-10。
5、gamma [default=0, alias: min_split_loss]
这个指定了一个结点被分割时,所需要的最小损失函数减小的大小。这个值一般来说需要根据损失函数来调整。
6、alpha [default=0, alias: reg_alpha]
L1正则化(与lasso回归中的正则化类似:传送门)这个主要是用在数据维度很高的情况下,可以提高运行速度。
7、objective [default=reg:linear]
这个主要是指定学习目标的:而分类,还是多分类或者回归
“reg:linear” –linear regression:回归
“binary:logistic”:二分类
“multi:softmax” :多分类,这个需要指定类别个数
8、subsample有放回随机抽样
我们都知道树模型是天生过拟合的模型,并且如果数据量太过巨大,树模型的计算会非常缓慢,因此,我们要对我们的原始数据集进行有放回抽样(bootstrap)。有放回的抽样每次只能抽取一个样本,若我们需要总共N个样本,就需要抽取N次。每次抽取一个样本的过程是独立的,这一次被抽到的样本会被放回数据集中,下一次还可能被抽到,因此抽出的数据集中,可能有一些重复的数据。
sklearn的随机森林类中也有名为boostrap的参数来帮助我们控制这种随机有放回抽样。同时,这样做还可以保证集成算法中的每个弱分类器(每棵树)都是不同的模型,基于不同的数据建立的自然是不同的模型,而集成一系列一模一样的弱分类器是没有意义的。
在sklearn中,我们使用参数subsample来控制我们的随机抽样。在xgb和sklearn中,这个参数都默认为1且不能取到0,这说明我们无法控制模型是否进行随机有放回抽样,只能控制抽样抽出来的样本量大概是多少。
从这个角度来看,我们的subsample参数对模型的影响应该会非常不稳定,大概率应该是无法提升模型的泛化能力的,但也不乏提升模型的可能性。
数据集过少,降低抽样的比例反而让数据的效果更低,不如就让它保持默认
算法参数过多,调参复杂,对原理不清楚的很难使用好XGBoost,这也是为什么XGBoost好,但是很多人却用不好的原因之一,首先调参就是一个难题