Python集成学习算法

Python集成学习算法---XgBoost

转载原文
在讲XGBoost之前,先讲一下GBDT,以及与Adaboost的区别所谓集成学习,是指构建多个分类器(弱分类器)对数据集进行预测,然后用某种策略将多个分类器预测的结果集成起来,作为最终预测结果。它要求每个弱分类器具备一定的“准确性”,分类器之间具备“差异性”。

集成学习根据各个弱分类器之间有无依赖关系,分为Boosting和Bagging两大流派: Boosting流派,各分类器之间有依赖关系,必须串行,比如Adaboost、GBDT(Gradient Boosting Decision Tree)、Xgboost Bagging流派,各分类器之间没有依赖关系,可各自并行,比如随机森林(Random Forest) 而著名的Adaboost作为boosting流派中最具代表性的一种方法。

Adaboost AdaBoost的自适应在于:前一个基本分类器分错的样本会得到加强,加权后的全体样本再次被用来训练下一个基本分类器。同时,在每一轮中加入一个新的弱分类器,直到达到某个预定的足够小的错误率或达到预先指定的最大迭代次数。具体说来,整个Adaboost 迭代算法就3步: 1、初始化训练数据的权值分布。如果有N个样本,则每一个训练样本最开始时都被赋予相同的权值:1/N。

2、训练弱分类器。具体训练过程中,如果某个样本点已经被准确地分类,那么在构造下一个训练集中,它的权值就被降低;相反,如果某个样本点没有被准确地分类,那么它的权值就得到提高。然后,权值更新过的样本集被用于训练下一个分类器,整个训练过程如此迭代地进行下去。

3、将各个训练得到的弱分类器组合成强分类器。各个弱分类器的训练过程结束后,加大分类误差率小的弱分类器的权重,使其在最终的分类函数中起着较大的决定作用,而降低分类误差率大的弱分类器的权重,使其在最终的分类函数中起着较小的决定作用。换言之,误差率低的弱分类器在最终分类器中占的权重较大,否则较小。

GBDT(Gradient Boost Decision Tree) 另一种boosting方法GBDT(Gradient Boost Decision Tree),则与AdaBoost不同,GBDT每一次的计算是都为了减少上一次的残差,进而在残差减少(负梯度)的方向上建立一个新的模型。 boosting集成学习由多个相关联的决策树联合决策,什么叫相关联?举个例子1、有一个样本[数据->标签]是:[(2,4,5)->4] 2、第一棵决策树用这个样本训练的预测为3.33、那么第二棵决策树训练时的输入,这个样本就变成了:[(2,4,5)->0.7] 4、也就是说,下一棵决策树输入样本会与前面决策树的训练和预测相关 5、很快你会意识到,Xgboost为何也是一个boosting的集成学习了。

而一个回归树形成的关键点在于: (i)分裂点依据什么来划分(如前面说的均方误差最小,loss); (ii)分类后的节点预测值是多少(如前面说,有一种是将叶子节点下各样本实际值得均值作为叶子节点预测误差,或者计算所得)

至于另一类集成学习方法,比如Random Forest(随机森林)算法,各个决策树是独立的、每个决策树在样本堆里随机选一批样本,随机选一批特征进行独立训练,各个决策树之间没有啥关系。 说到×gboost,不得不先从GBDT(Gradient Boosting Decision Tree)说起。而且前面说过,两者都是boosting方法(如图所示:Y=Y1+Y2+Y3)

image.png

图中Y1,Y2,Y3为每次分类器的预测值,每次通过减少预测误差的方法,最终实现与真实值无误差。

咱们来看个年龄预测的例子。 简单起见,假定训练集只有4个人:A,B,C,D,他们的年龄分别是14,16,24,26。其中A、B分别是高一和高三学生;C,D分别是应届毕业生和工作两年的员工。 现在我们使用GBDT来做这件事,由于数据太少,我们限定叶子节点最多有两个,即每棵树都只有一个分枝,并且限定只学两棵树。 我们会得到如下图所示结果:


image.png

在第一棵树分枝和图1一样,由于A,B年龄较为相近,C,D年龄较为相近,他们被分为左右两拨,每拨用平均年龄作为预测值。

(i)此时计算残差(残差的意思就是:A的实际值-A的预测值=A的残差),所以A的残差就是实际值14-预测值15=残差值-1。 (ii)注意,A的预测值是指前面所有树累加的和,这里前面只有一棵树所以直接是15,如果还有树则需要都累加起来作为A的预测值。

残差在数理统计中是指实际观察值与估计值(拟合值)之间的差。“残差”蕴含了有关模型基本假设的重要信息。 如果回归模型正确的话,我们可以将残差看作误差的观测值。进而得到A,B,C,,D的残差分别为-1,1,-1,1。 然后拿它们的残差代替ABCD的原值-1、1、-1、1,到第二棵树去学习,第二棵树只有两个值1和-1,直接分成两个节点,即A和C分在左边,B和D分在右边,经过计算(比如A,实际值-1-预测值-1=残差0,比如C,实际值-1-预测值-1=0),此时所有人的残差都是0。 残差值都为0,相当于第二棵树的预测值和它们的实际值相等,则只需把第二棵树的结论累加到第一棵树上就能得到真实年龄了,即每个人都得到了真实的预测值。 换句话说,现在A,B,C,D的预测值都和真实年龄一致了。Perfect!

A:14岁高一学生,购物较少,经常问学长问题,预测年龄A=15-1=14

B:16岁高三学生,购物较少,经常被学弟问问题,预测年龄B=15+1=16

C:24岁应届毕业生,购物较多,经常问师兄问题,预测年龄C=25-1=24

D:26岁工作两年员工,购物较多,经常被师弟问问题,预测年龄D=25+1=26

Gradient Boosting 和其它Boosting算法一样,通过将表现一般的数个模型(通常是深度固定的决策树)组合在一起来集成一个表现较好的模型。抽象地说,模型的训练过程是对一任意可导目标函数的优化过程。通过反复地选择一个指向负梯度方向的函数,该算法可被看做在函数空间里对目标函数进行优化。因此可以说Gradient Boosting=Gradient Descent+Boosting。和AdaBoost一样,Gradient Boosting也是重复选择一个表现一般的模型并且每次基于先前模型的表现进行调整。不同的是,AdaBoost是通过提升错分数据点的权重来定位模型的不足而Gradient Boosting是通过算梯度(gradient)来定位模型的不足。因此相比AdaBoost,Gradient Boosting可以使用更多种类的目标函数。

Gradient Boosting for Regression:

有一组数据(x1,y1).…,(xm,ym)和一个基础模型F就像你说的我们想要最小化预测值F(x:)和真实值y:之间的square loss。对于任意i,使得1≤i≤m,我们称hi=yy-F(x;)为关于xi的残差。我们可以训练一个回归树h来拟合数据组(x1,y1-F(x1)),…,(xm,ym-F(xm))。这样我们就得到了一个更好的横型F+h,重复这一过程,我们最终得到了一个让人满意的模型。

用回归树去拟合残差其实就是用回归树去拟合目标方程关于F(xi)的梯度。

对于任意,便得1≤i≤m,预测值和真实值之间的square loss为(y;一F(x)),我们注意到 argminp(y-F(z)2=argminp(gy-F(z)2/2令L(,F(Ei)=(h-F(E)2/2.目标函数J=2(%,F(=)),)关手F(x;)的偏导数由链式法则可得正好是F(x;)一y;,则y;一F(xi)倍好是y:一F(x:)=-/,因此在这里用回归树拟合残差实际上就是用回归树拟合负梯度8F(xi) (当损失函数不为square loss时残差并不一定等于负梯度!),我们实际上是在通过梯度下降法对横型参数进行更新,这样理解的好处在于我们可以把这一算法推广到其它的损失函数上。


image.png

要注意regression并不一定会用square loss。square loss的优点是便于理解和实现,缺点在于对于异常值它的鲁棒性较差,如下图

一个异常值造成的损失由于二次幂而被过分放大,会影响到最后得到模型在测试集上的表现。 因此我们有时候会选择其它的损失函数,如absolute loss或Huber loss(标红色星号的数据为异常值):


image.png

梯度下降法的思想使得我们可以非常轻易地改用不同的损失函数设计Gradient Boosting算法。另外在使用某些其它损失函数时(如Huber loss),残差相比负梯度更容易受到异常值的影响。

Gradient Boosting for Classification基于回归树的Gradient Boosting除了回归问题外还可以做分类问题、排序问题等。 对于分类问题,很多时候我们是要去获得一个概率分布去逼近真正的概率分布,因此很多时候就会基于1og loss来构建目标函数,如KL-divergence或cross entropy。但有时候也可能使用其它损失函数,可以参考一下Loss functions for classification。 除了损失函数的区别外,分类问题和回归问题的区别还在于当我有多个类的时候,我可能会训练多个分类器。比如如果要去识别手写字母的话,我可能会训26个分类器来分别去求该手写字母为A/.../Z的概率。 由于决策树是非线性的(并且随着深度的加深非线性越来越强),基于决策树的GBDT也是非线性的。

AdaBoost Vs.GBDT

最主要的区别在于两者如何识别模型的问题。AdaBoost用错分数据点来识别问题,通过调整错分数据点的权重来改进模型。Gradient Boosting通过负梯度来识别问题,通过计算负梯度来改进模型。

XGBoost树的定义 举个例子,我们要预测一家人谁是谁,则可以先通过年龄区分开小孩和大人,然后再通过性别区分开是男是女,如下图所示


image.png

就这样,训练出了2棵树tree1和tree2,类似之前gbdt的原理,两棵树的结论累加起来便是最终的结论,所以小孩的预测分数就是两棵树中小孩所落到的结点的分数相加:2+0.9=2.9。爷爷的预测分数同理:-1+(-0.9)=-1.9。具体如下图所示


image.png

这其实与gbdt原理差不多? 事实上,如果不考虑工程实现、解决问题上的一些差异,xgboost与gbdt比较大的不同就是目标函数的定义。 XGBoost的目标函数如下图所示:

image.png

其中红色箭头所指向的L即为损失函数(比如平方损失函数:,或logistic损失函数或hinge损失函数或者自定义的损失函数(调用XGBoost包时候则需要输入该自定义损失函数的一阶、二阶损失函数)红色方框所框起来的是正则项(包括L1正则、L2正则)红色圆圈所圈起来的为常数项 对于f(x),xgboost利用泰勒展开三项,做一个近似我们可以很清晰地看到,最终的目标函数只依赖于每个数据点的在误差函数上的一阶导数和二阶导数。

XGBoost目标函数 首先,再次明确下我们的目标,是希望建立K个回归树,使得树群的预测值尽量接近真实值(准确率)而且有尽量大的泛化能力(更为本质的东西)。 从数学角度看这是一个泛函最优化,多目标,把目标函数简化下:

image.png

T表示叶子节点的个数,w表示节点的数值。直观上看,目标要求预测误差尽量小,且叶子节点T尽量少,节点数值w尽量不极端。

总结

1、Adaboost与GBDT两者boosting的不同策略是两者的本质区别。 2、Adaboost强调Adaptive(自适应),通过不断修改样本权重(增大分错样本权重,降低分对样本权重),不断加入弱分类器进行boosting。 3、而GBDT则是旨在不断减少残差(回归),通过不断加入新的树旨在在残差减少(负梯度)的方向上建立一个新的模型。—一即损失函数是旨在最快速度降低残差。

4、而XGBoost的boosting策略则与GBDT类似,区别在于GBDT旨在通过不断加入新的树最快速度降低残差,而XGBoost则可以人为定义损失函数(可以是最小平方差、logistic loss function、hinge loss function或者人为定义的loss function),只需要知道该loss function对参数的一阶、二阶导数便可以进行boosting,其进一步增大了模型的泛华能力,其贪婪法寻找添加树的结构以及loss function中的损失函数与正则项等一系列策略也使得XGBoost预测更准确。 5、GBDT每一次的计算是都为了减少上一次的残差,进而在残差减少(负梯度)的方向上建立一个新的模型。 6、XGBoost则可以自定义一套损失函数,借助泰勒展开(只需知道损失函数的一阶、二阶导数即可求出损失函数)转换为一元二次函数,得到极值点与对应极值即为所求。

image.png

8、XGBoost,GBDT均支持自定义损失函数,但XGBoost 进行基分类器拟合的时候需要一阶、二阶梯度信息,故而需要自定义损失函数提供一阶、二阶梯度信息,而GBDT因为只用到一阶梯度信息,故而只需提供自定义损失函数的一阶梯度信息;

9、XGBoost可以看做是GBDT的一种升级版实现,其中需要明确的一个概念是,XGBoost是Boosting框架的一种实现结构,lightgbm也是一种框架实现结构,而GBDT则是一种算法实现,其基分类器为CART树,可以处理分类、回归任务,处理分类任务则是将分类任务利用softmax转换为回归任务进行求解,

你可能感兴趣的:(Python集成学习算法)