机器学习算法总结(四)——GBDT与XGBOOST

  Boosting方法其实是采用加法模型与前向分布算法。在上一篇提到的Adaboost算法也能够用加法模型和前向分布算法来表示。以决策树为基学习器的提高方法称为提高树(Boosting Tree)。对分类问题决策树是CART分类树,对回归问题决策树是CART回归树。算法

一、前向分布算法多线程

  引入加法模型dom

  

  在给定了训练数据和损失函数$L(y, f(x))$ 的条件下,能够经过损失函数最小化来学习加法模型机器学习

  

  然而对于这个问题是个很复杂的优化问题,并且要训练的参数很是的多,前向分布算法的提出就是为了解决模型的优化问题,其核心思想是由于加法模型是由多各模型相加在一块儿的,并且在Boosting中模型之间又是有前后顺序的,所以能够在执行每一步加法的时候对模型进行优化,那么每一步只须要学习一个模型和一个参数,经过这种方式来逐步逼近全局最优,每一步优化的损失函数:函数

  

  具体算法流程以下:性能

  1)初始化$f_0(x) = 0$;学习

  2)第m次迭代时,极小化损失函数优化

  

  3)更新模型,则$f_m (x)$:spa

  

  4)获得最终的加法模型线程

  

  Adaboost算法也能够用前向分布算法来描述,在这里输入的数据集是带有权重分布的数据集,损失函数是指数损失函数。

二、GBDT算法

  GBDT是梯度提高决策树(Gradient Boosting Decision Tree)的简称,GBDT能够说是最好的机器学习算法之一。GBDT分类和回归时的基学习器都是CART回归树,由于是拟合残差的。GBDT和Adaboost同样能够用前向分布算法来描述,不一样之处在于Adaboost算法每次拟合基学习器时,输入的样本数据是不同的(每一轮迭代时的样本权重不一致),由于Adaboost旨在重点关注上一轮分类错误的样本,GBDT算法在每一步迭代时是输出的值不同,本轮要拟合的输出值是以前的加法模型的预测值和真实值的差值(模型的残差,也称为损失)。用于一个简单的例子来讲明GBDT,假如某人的年龄为30岁,第一次用20岁去拟合,发现损失还有10岁,第二次用6岁去拟合10岁,发现损失还有4岁,第三次用3岁去拟合4岁,依次下去直到损失在咱们可接受范围内。

  以平方偏差损失函数的回归问题为例,来看看以损失来拟合是个什么样子,采用前向分布算法:

  

  在第$m$次迭代时,咱们要优化的损失函数:

  

  此时咱们采用平方偏差损失函数为例:

  

  则上面损失函数变为:

  

  问题就成了对残差r的拟合了

  然而对于大多数损失函数,却没那么容易直接得到模型的残差,针对该问题,大神Freidman提出了用损失函数的负梯度来拟合本轮损失的近似值,拟合一个回归树

  

  关于GBDT通常损失函数的具体算法流程以下:

  1)初始化$f_0(x)$:

  

  2)第$m$次迭代时,计算当前要拟合的残差$r_{mi}$:

   

  以$r_{mi}$为输出值,对$r_{mi}$拟合一个回归树(此时只是肯定了树的结构,可是还未肯定叶子节点中的输出值),而后经过最小化当前的损失函数,并求得每一个叶子节点中的输出值$c_{mj}$,$j$表示第$j$个叶子节点

  

  更新当前的模型$f_m(x)$为:

  

  3)依次迭代到咱们设定的基学习器的个数$M$,获得最终的模型,其中$M$表示基学习器的个数,$J$表示叶子节点的个数

  

  GBDT算法提供了众多的可选择的损失函数,经过选择不一样的损失函数能够用来处理分类、回归问题,好比用对数似然损失函数就能够处理分类问题。大概的总结下经常使用的损失函数:

  1)对于分类问题能够选用指数损失函数、对数损失函数。

  2)对于回归问题能够选用均方差损失函数、绝对损失函数。

  3)另外还有huber损失函数和分位数损失函数,也是用于回归问题,能够增长回归问题的健壮性,能够减小异常点对损失函数的影响。

三、GBDT的正则化

  在Adaboost中咱们会对每一个模型乘上一个弱化系数(正则化系数),减少每一个模型对提高的贡献(注意:这个系数和模型的权重不同,是在权重上又乘以一个0,1之间的小数),在GBDT中咱们采用一样的策略,对于每一个模型乘以一个系数λ (0 < λ ≤ 1),下降每一个模型对拟合损失的贡献,这种方法也意味着咱们须要更多的基学习器。

  第二种是每次经过按比例(推荐[0.5, 0.8] 之间)随机抽取部分样原本训练模型,这种方法有点相似Bagging,能够减少方差,但一样会增长模型的误差,可采用交叉验证选取,这种方式称为子采样。采用子采样的GBDT有时也称为随机梯度提高树(SGBT)。

  第三种就是控制基学习器CART树的复杂度,能够采用剪枝正则化。

四、GBDT的优缺点

  GBDT的主要优势:

  1)能够灵活的处理各类类型的数据

  2)预测的准确率高

  3)使用了一些健壮的损失函数,如huber,能够很好的处理异常值

  GBDT的缺点:

  1)因为基学习器之间的依赖关系,难以并行化处理,不过能够经过子采样的SGBT来实现部分并行。

 

五、XGBoost算法

  事实上对于树模型为基学习器的集成方法在建模过程当中能够分为两个步骤:一是肯定树模型的结构,二是肯定树模型的叶子节点中的输出值。

5.1 定义树的复杂度

  首先把树拆分红结构部分$q$和叶子节点输出值$w$,在这里$w$是一个向量,表示各叶子节点中的输出值。在这里就囊括了上面提到的两点,肯定树结构$q$和叶子结点的输出值$w$。从下图中能够看出,$q(x)$其实是肯定输入值最终会落到哪一个叶子节点上,而$w$将会给出相应的输出值。

  

  具体表现示例以下,引入正则化项 $\Omega (f_t)$来控制树的复杂度,从而实现有效的控制模型的过拟合,这是xgboost中的第一个重要点。式子中的$T$为叶子节点数

  

5.2 XGBoost中的Boosting Tree模型

  

  和GBDT方法同样,XGBoost的提高模型也是采用残差,不一样的是分裂结点选取的时候不必定是最小平方损失,其损失函数以下,较GBDT其根据树模型的复杂度加入了一项正则化项:

  

5.3 对目标函数进行改写

  

  上面的式子是经过泰勒展开式将损失函数展开为具备二阶导的平方函数。

  在GBDT中咱们经过求损失函数的负梯度(一阶导数),利用负梯度替代残差来拟合树模型。在XGBoost中直接用泰勒展开式将损失函数展开成二项式函数(前提是损失函数一阶、二阶都连续可导,并且在这里计算一阶导和二阶导时能够并行计算),假设此时咱们定义好了树的结构(在后面介绍,和GBDT中直接用残差拟合不一样),假设咱们的叶节点区域为:

  

  上面式子中$i$表明样本$i$,$j$表明叶子节点$j$。

  则咱们的目标优化函数能够转换成(由于$l(y_i, y_i^{t-1})$是个已经肯定的常数,能够舍去):

  

  上面式子把样本都合并到叶子节点中了。

  此时咱们对$w_j$求导并令导数为0,可得:

   

  其中 $ G_j = \sum_{i \in I_j} g_i,  H_j = \sum_{i \in T_j} h_j $。 

5.4 树结构的打分函数

  上面的Obj值表明当指定一个树结构时,在目标上面最多减小多少,咱们能够把它称为结构分数。能够认为这是一个相似与基尼指数同样更通常的对树结构进行打分的函数。以下面的例子所示

  

  对于求得Obj分数最小的树结构,咱们能够枚举全部的可能性,而后对比结构分数来得到最优的树结构,然而这种方法计算消耗太大,更经常使用的是贪心法(事实上绝大多数树模型都是这样的,只考虑当前节点的划分最优),每次尝试对已经存在的叶节点(最开始的叶节点是根节点)进行分割,而后得到分割后的增益为:

  

  在这里以$Gain$做为判断是否分割的条件,这里的$Gain$能够看做是未分割前的Obj减去分割后的左右Obj,所以若是$Gain < 0$,则此叶节点不作分割,然而这样对于每次分割仍是须要列出全部的分割方案(对于特征的值的个数为n时,总共有2^n - 2 种划分)。而实际中是采用近似贪心方法,咱们先将全部样本按照$g_i$从小到大排序,而后进行遍历,查看每一个节点是否须要分割(对于特征的值的个数为$n$时,总共有$n-1$种划分),具体示例以下:

  

  最简单的树结构就是一个节点的树。咱们能够算出这棵单节点的树的好坏程度obj*。假设咱们如今想按照年龄将这棵单节点树进行分叉,咱们须要知道:

  1)按照年龄分是否有效,也就是是否减小了obj的值

  2)若是可分,那么以哪一个年龄值来分。

  此时咱们就是先将年龄特征从小到大排好序,而后再从左到右遍历分割

  这样的分割方式,咱们就只要对样本扫描一遍,就能够分割出$G_L$,$G_R$,而后根据$Gain$的分数进行分割,极大地节省了时间。因此从这里看,XGBoost中重新定义了一个划分属性,也就是这里的$Gain$,而这个划分属性的计算是由其目标损失决定obj的。

5.5 XGBoost中其余的正则化方法

  1)像Adaboost和GBDT中同样,对每个模型乘以一个系数$\lambda(0 < \lambda ≤ 1)$,用来下降每一个模型对结果的贡献。

  2)采用特征子采样方法,和RandomForest中的特征子采样同样,能够下降模型的方差

六、XGBoost和GBDT的区别

   1)将树模型的复杂度加入到正则项中,来避免过拟合,所以泛化性能会因为GBDT。

   2)损失函数是用泰勒展开式展开的,同时用到了一阶导和二阶导,能够加快优化速度。

  3)和GBDT只支持CART做为基分类器以外,还支持线性分类器,在使用线性分类器的时候可使用L1,L2正则化。

  4)引进了特征子采样,像RandomForest那样,这种方法既能下降过拟合,还能减小计算。

  5)在寻找最佳分割点时,考虑到传统的贪心算法效率较低,实现了一种近似贪心算法,用来加速和减少内存消耗,除此以外还考虑了稀疏数据集和缺失值的处理,对于特征的值有缺失的样本,XGBoost依然能自动找到其要分裂的方向。

  6)XGBoost支持并行处理,XGBoost的并行不是在模型上的并行,而是在特征上的并行,将特征列排序后以block的形式存储在内存中,在后面的迭代中重复使用这个结构。这个block也使得并行化成为了可能,其次在进行节点分裂时,计算每一个特征的增益,最终选择增益最大的那个特征去作分割,那么各个特征的增益计算就能够开多线程进行。