本文为博客GBDT算法原理深入解析和博客xgboost的原理没你想像的那么难的摘抄,这两篇文章写得通俗易懂,所以就将两个文章写得较好的部分整合摘抄形成本博文。
一、前言
1.1 梯度提升
梯度提升(Gradient boosting)是一种用于回归、分类和排序任务的机器学习技术,属于Boosting算法族的一部分。Boosting是一族可将弱学习器提升为强学习器的算法,属于集成学习(ensemble learning)的范畴。Boosting方法基于这样一种思想:对于一个复杂任务来说,将多个专家的判断进行适当的综合所得出的判断,要比其中任何一个专家单独的判断要好。通俗地说,就是“三个臭皮匠顶个诸葛亮”的道理。梯度提升同其他boosting方法一样,通过集成(ensemble)多个弱学习器,通常是决策树,来构建最终的预测模型。
1.2 集成学习
Boosting、bagging和stacking是集成学习的三种主要方法。不同于bagging方法,boosting方法通过分步迭代(stage-wise)的方式来构建模型,在迭代的每一步构建的弱学习器都是为了弥补已有模型的不足。Boosting族算法的著名代表是AdaBoost,AdaBoost算法通过给已有模型预测错误的样本更高的权重,使得先前的学习器做错的训练样本在后续受到更多的关注的方式来弥补已有模型的不足。与AdaBoost算法不同,梯度提升方法在迭代的每一步构建一个能够沿着梯度最陡的方向降低损失(steepest-descent)的学习器来弥补已有模型的不足。经典的AdaBoost算法只能处理采用指数损失函数的二分类学习任务,而梯度提升方法通过设置不同的可微损失函数可以处理各类学习任务(多分类、回归、Ranking等),应用范围大大扩展。另一方面,AdaBoost算法对异常点(outlier)比较敏感,而梯度提升算法通过引入bagging思想、加入正则项等方法能够有效地抵御训练数据中的噪音,具有更好的健壮性。这也是为什么梯度提升算法(尤其是采用决策树作为弱学习器的GBDT算法)如此流行的原因,有种观点认为GBDT是性能最好的机器学习算法,这当然有点过于激进又固步自封的味道,但通常各类机器学习算法比赛的赢家们都非常青睐GBDT算法,由此可见该算法的实力不可小觑。
1.3 基于梯度提升算法的学习器
基于梯度提升算法的学习器叫做GBM(Gradient Boosting Machine)。理论上,GBM可以选择各种不同的学习算法作为基学习器。现实中,用得最多的基学习器是决策树。为什么梯度提升方法倾向于选择决策树(通常是CART树)作为基学习器呢?这与决策树算法自身的优点有很大的关系。决策树可以认为是if-then规则的集合,易于理解,可解释性强,预测速度快。同时,决策树算法相比于其他的算法需要更少的特征工程,比如可以不用做特征标准化,可以很好的处理字段缺失的数据,也可以不用关心特征间是否相互依赖等。决策树能够自动组合多个特征,它可以毫无压力地处理特征间的交互关系并且是非参数化的,因此你不必担心异常值或者数据是否线性可分(举个例子,决策树能轻松处理好类别A在某个特征维度x的末端,类别B在中间,然后类别A又出现在特征维度x前端的情况)。不过,单独使用决策树算法时,有容易过拟合缺点。所幸的是,通过各种方法,抑制决策树的复杂性,降低单颗决策树的拟合能力,再通过梯度提升的方法集成多个决策树,最终能够很好的解决过拟合的问题。由此可见,梯度提升方法和决策树学习算法可以互相取长补短,是一对完美的搭档。至于抑制单颗决策树的复杂度的方法有很多,比如限制树的最大深度、限制叶子节点的最少样本数量、限制节点分裂时的最少样本数量、吸收bagging的思想对训练样本采样(subsample),在学习单颗决策树时只使用一部分训练样本、借鉴随机森林的思路在学习单颗决策树时只采样一部分特征、在目标函数中添加正则项惩罚复杂的树结构等。现在主流的GBDT算法实现中这些方法基本上都有实现,因此GBDT算法的超参数还是比较多的,应用过程中需要精心调参,并用交叉验证的方法选择最佳参数。
本文对GBDT算法原理进行介绍,从机器学习的关键元素出发,一步一步推导出GBDT算法背后的理论基础,读者可以从这个过程中了解到GBDT算法的来龙去脉。对于该算法的工程实现,本文也有较好的指导意义,实际上对机器学习关键概念元素的区分对应了软件工程中的“开放封闭原则”的思想,基于此思想的实现将会具有很好的模块独立性和扩展性。
二、机器学习的关键元素
先复习下监督学习的关键概念:模型(model)、参数(parameters)、目标函数(objective function)
模型就是所要学习的条件概率分布或者决策函数,它决定了在给定特征向量$x$时如何预测出目标$y$。定义$x_{i} \in R^{d}$为训练集中的第$i$个训练样本,则线性模型(linear model)可以表示为:$\hat{y_{i}}=\sum_{j}w_{j}x_{ij}$。模型预测的分数$\hat{y}_{i}$在不同的任务中有不同的解释。例如在逻辑回归任务中,$1/(exp(-\hat{y}_{i})$表示模型预测为正例的概率;而在排序学习任务中,$\hat{y}_{i}$表示排序分。参数就是我们要从数据中学习得到的内容。模型通常是由一个参数向量决定的函数。例如,线性模型的参数可以表示为:$\Theta = {w_{i}|j=1,….,s}$。目标函数通常定义为如下形式:
$$
Obj(\Theta)=L(\Theta)+\Omega(\Theta)
$$
其中,$L(\Theta)$是损失函数,用来衡量模型拟合训练数据的好坏程度;$\Omega(\Theta)$称之为正则项,用来衡量学习到的模型的复杂度。训练集上的损失(Loss)定义为:$L=\sum_{i=1}^{n}l(y_{i},\hat{y}_{i})$。
常用的损失函数有如下两个:
- 平方损失函数(square loss):$l(y_{i},\hat{y}_{i})=(y_{i}-\hat{y}_{i})^2$;
- Logistic损失:$l(y_{i},\hat{y}_{i})=y_{i}ln(\hat{y}_{i})+(1-y_{i})ln(1-\hat{y}_{i})$
正则项有L1正则和L2正则:
- L1正则:$\Omega(\Theta)=\lambda||w||_{1} $
- L2正则:$\Omega(\Theta)=\lambda||w||_{2} $
Ridge regression就是指使用平方损失和L2范数正则项的线性回归模型;Lasso regression就是指使用平方损失和L1范数正则项的线性回归模型;逻辑回归(Logistic regression)指使用logistic损失和L2范数或L1范数正则项的线性模型。
目标函数之所以定义为损失函数和正则项两部分,是为了尽可能平衡模型的偏差和方差(Bias Variance Trade-off)。最小化目标函数意味着同时最小化损失函数和正则项,损失函数最小化表明模型能够较好的拟合训练数据,一般也预示着模型能够较好地拟合真实数据(groud true);另一方面,对正则项的优化鼓励算法学习到较简单的模型,简单模型一般在测试样本上的预测结果比较稳定、方差较小(奥坎姆剃刀原则)。也就是说,优化损失函数尽量使模型走出欠拟合的状态,优化正则项尽量使模型避免过拟合。
从概念上区分模型、参数和目标函数给学习算法的工程实现带来了益处,使得机器学习的各个组成部分之间耦合尽量松散。
三、加法模型(additive model)
加法模型本质上是一个元算法,适用于所有的加法模型,它是一种启发式算法。GBDT算法可以看成是由K棵树组成的加法模型:
$$
\hat{y}_{i}=\sum_{k=1}^{K}f_{k}(x_{i}),f_{k} \in F
$$
其中$F$为所有树组成的函数空间,以回归任务为例,回归树可以看作为一个把特征向量映射为某个score的函数。该模型的参数为:$\Theta={f_{1},f_{2},f_{3},….,f_{k}}$。于一般的机器学习算法不同的是,加法模型不是学习d维空间中的权重,而是直接学习函数(决策树)集合。
上述加法模型的目标函数定义为:
$$
Obj=\sum_{i=1}^{n}l(y_{i},\hat{y}^{i})+\sum_{k=1}^{K}\Omega(f_{k})
$$
其中$\Omega$表示决策树的复杂度,那么该如何定义树的复杂度呢?比如,可以考虑树的节点数量、树的深度或者叶子节点所对应的分数的L2范数等等。
如何来学习加法模型呢?
解这一优化问题,可以用前向分布算法(forward stagewise algorithm)。因为学习的是加法模型,如果能够从前往后,每一步只学习一个基函数及其系数(结构),逐步逼近优化目标函数,那么就可以简化复杂度。这一学习过程称之为Boosting。具体地,我们的目标不再是直接优化整个目标函数,这已经被我们证明是行不通的。而是分步骤优化目标函数,首先优化第一棵树,完了之后再优化第二棵树,直至优化完K棵树。整个过程如下图所示:
$$
\begin{split}
{} & \hat{y}_{i}^{(0)}=0 \\
{} & \hat{y}_{i}^{(1)}=f_{1}(x_{i})=\hat{y}_{i}^{(0)}+f_{1}(x_{1}) \\
{} & \hat{y}_{i}^{(2)}=f_{1}(x_{i})+f_{2}(x_{i})=\hat{y}_{i}^{(1)}+f_{2}(x_{i}) \\
{} & ……\\
{} & \hat{y}_{i}^{(t)}=\sum_{k=1}^{t}f_{k}(x_{i}) = \hat{y}_{i}^{(t-1)}+f_{t}(x_{i})
\end{split}
$$
那么,在每一步如何决定哪一个函数f被加入呢?指导原则还是最小化目标函数。
在第t步,模型对xi的预测为:$\hat{y}_{i}^{t}=\hat{y}_{i}^{t-1}+f_{t}(x_{i})$,其中$f_{t}(x_{i})$为这一轮我们要学习的函数(决策树)。这个时候目标函数可以写为:
$$
\begin{split}
Obj^{(t)} {} & = \sum_{i=1}^{n}l(y_{i},\hat{y}_{i}^{t})+\sum_{i=1}^{t}\Omega(f_{i}) \\
{} & = \sum_{i=1}^{n}l(y_{i},\hat{y}_{i}^{t-1}+f_{t}(x_{i}))+\Omega(f_{t})+constant
\end{split}
$$
constant就是前t-1棵树的复杂度,后面会如何衡量树的复杂度。假如我们使用的损失函数时MSE,那么上述表达式会变成这个样子:
$$
\begin{split}
Obj^{(t)} {} & = \sum_{i=1}^{n}(y_{i}-(\hat{y}_{i}^{t-1}+f_{t}(x_{i})))^2+\Omega(f_{t})+constant \\
{} & = \sum_{i=1}^{n}[2(\hat{y}_{i}^{t-1}-y_{i})f_{t}(x_{i})+f_{t}(x_{i})^2]+\Omega(f_{t})+constant
\end{split}
$$
其中,$\hat{y}_{i}^{t-1}-y_{i}$称之为残差(residual)。因此,使用平方损失函数时,GBDT算法的每一步在生成决策树时只需要拟合前面的模型的残差。
$f_t(x_i)$是什么?它其实就是$f_t$的某个叶子节点的值。之前我们提到过,叶子节点的值是可以作为模型的参数的。
泰勒公式: 设n是一个正整数,如果定义在一个包含a的区间上的函数f在a点处n+1次可导,那么对于这个区间上的任意x都有:
$$
f(x)=\sum_{n=0}^{N}\frac{f^{(n)}(a)}{n!}(x-a)^n+R_{n}(x)
$$
其中的多项式称为函数在a处的泰勒展开式,$R_{n}(x)$是泰勒公式的余项且是$(x−a)^n$的高阶无穷小。
根据泰勒公式把函数f(x+Δx)在点x处二阶展开,可得到如下等式:
$$
f(x+\Delta{x})≈f(x)+f^{′}(x)\Delta{x}+\frac{1}{2}f^{″}(x)\Delta{x}^{2}
$$
有目标函数的公式可知,目标函数是关于变量$\hat{y}_{i}^{t-1}+f_{t}(x_{i})$的函数,若把变量$\hat{y}_{i}^{t-1}$看成是泰勒展开式中的x,把变量$f_{t}(x_{i})$看成是泰勒展开式中的$\Delta{x}$,则目标函数可转为:
$$
Obj^{(t)}=\sum_{i=1}^{n}[l(y_{i},\hat{y}_{i}^{t-1})+g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega(f_{t})+constant
$$
其中:
$$
g_{i}=\frac{\partial l(y_{i},\hat{y}_{i}^{(t-1)})}{\partial \hat{y}_{i}^{(t-1)}} \\
h_{i}=\frac{\partial^2 l(y_{i},\hat{y}_{i}^{(t-1)})}{\partial \hat{y}_{i}^{(t-1)}}
$$
简要说明下$g_{i}$和$h_{i}$的含义。$g_{i}$怎么理解呢?现有t-1棵树是不是?这t-1棵树组成的模型对第i个训练样本有一个预测值$\hat{y}_{i}$是不是?这个$\hat{y}_{i}$与第i个样本的真实标签$y_i$肯定有差距是不是?这个差距可以用$l(y_i,\hat{y}_{i})$这个损失函数来衡量是不是?现在gi和hi的含义你已经清楚了是不是?
假设损失函数为平方损失函数,则有:
$$
\begin{split}
{} & g_{i}=\frac{\partial l(y_{i},\hat{y}_{i}^{(t-1)})}{\partial \hat{y}_{i}^{(t-1)}}=\frac{\partial (\hat{y}^{t-1}-y_{i})^2}{\partial \hat{y}_{i}^{(t-1)}}=2(\hat{y}_{i}^{(t-1)}-y_{i}) \\
{} & h_{i}=\frac{\partial^2 l(y_{i},\hat{y}_{i}^{(t-1)})}{\partial \hat{y}_{i}^{(t-1)}}=\frac{\partial^{2} (\hat{y}^{t-1}-y_{i})^2}{\partial \hat{y}_{i}^{(t-1)}}=2
\end{split}
$$
我们的目标是让这个目标函数最小化,常数项显然没有什么用,我们把它们去掉,就变成了下面这样:
$$
Obj^{(t)}≈\sum_{i=1}^{N}[g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega(f_{t})
$$
由于要学习的函数仅仅依赖于目标函数,从上式可以看出只需为学习任务定义好损失函数,并为每个训练样本计算出损失函数的一阶导数和二阶导数,通过在训练样本集上最小化目标函数即可求得每步要学习的函数f(x),从而根据加法模型可得最终要学习的模型。
四、模型正则化项
上面的式子已然很漂亮,但是,后面的$\Omega(f_t)$仍然是云遮雾罩,不清不楚。现在我们就来定义如何衡量一棵树的正则化项。这个事儿并没有一个客观的标准,可以见仁见智。为此,我们先对CART树作另一番定义,如下所示:
$$
f_{t}(x)=w_{q(x)},w \in R^{T},q:R^{d} \rightarrow {1,2,3,…..,T}
$$
一颗生成好的CART树,假设其叶子节点个数为T,该CART树是由所有叶子节点对应的值组成的向量$w \in R^{T}$,以及一个把特征向量映射到叶子节点索引(Index)的函数$q:R^{d} \rightarrow {1,2,3,…..,T}$组成的。因此,CART树可以定义为$f_{t}(x)=w_{q(x)}$。
CART树复杂度可以由正则项$\Omega(f_{t})=\gamma T +\frac{1}{2}\lambda \sum_{j=1}^{T}w_{j}^{2}$来定义,即CART树模型的复杂度由生成的树的叶子节点数量和叶子节点对应的值向量的L2范数决定。
注意:这里出现了$\lambda$和$\gamma$,显然,$\gamma$越大,表示越希望获得结构简单的树,因为此时对较多叶子节点的树的惩罚越大。$\lambda$越大也是越希望获得结构简单的树。
xgboost选择的就是这样的正则化,为什么呢?很简单,好使!效果好才是真的好。
五、GBDT算法
至此,我们关于第t棵树的优化目标已然很清晰,下面我们对它做下变形,将正则项加入其中则有:
$$
\begin{split}
Obj^{(t)} {} &≈\sum_{i=1}^{N}[g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega(f_{t}) \\
{} &=\sum_{i=1}^{N}[g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\gamma T +\frac{1}{2}\lambda \sum_{j=1}^{T}w_{j}^{2} \\
{} &=\sum_{j=1}^{T}\Bigg[(\sum_{i \in I_{j}}g_{i})w_{i}+\frac{1}{2}(\sum_{i \in I_{j}}h_{i}+\lambda)w_{j}^{2}\Bigg]+\gamma T
\end{split}
$$
定义:
$$
\begin{split}
{} & G_{i} = \sum_{i \in I_{j}}g_{i} \\
{} & H_{i} = \sum_{i \in I_{j}}h_{}
\end{split}
$$
$I_j$代表什么?它代表一个集合,集合中每个值代表一个训练样本的序号,整个集合就是被第t棵CART树分到了第j个叶子节点上的训练样本。
则上述目标等式可写为:
$$
Obj^{(t)}=\sum_{j=1}^{T}\Bigg[G_{i}w_{j}+\frac{1}{2}(H_{i}+\lambda)w_{j}^{2}\Bigg]+\gamma T
$$
假设树的结构是固定的,即函数$q(x)$确定,令函数$Obj^{(t)}$的一阶导数等于0,即可求得叶子节点j对应的值为:
$$
w_{j}^{ * }=-\frac{G_{i}}{H_{i}+\lambda}
$$
此时,目标函数的值为:
$$
Obj^{ * }=-\frac{1}{2}\sum_{j=1}^{T}\frac{G_{i}^{2}}{H_{i}+\lambda}+\gamma T
$$
$Obj^{*}$它表示了这棵树的结构有多好,值越小,代表这样结构越好,也就是说,它是衡量第t棵CART树的结构好坏的标准。注意~注意~注意~,这个值仅仅是用来衡量结构的好坏的,与叶子节点的值可是无关的。为什么?请再仔细看一下$Obj^{*}$的推导过程。$Obj^{*}$只和$G_j$和$H_j$和T有关,而它们又只和树的结构(q(x))有关,与叶子节点的值可是半毛关系没有。如下图所示:
这里,我们对$w^{*}_j$给出一个直觉的解释,以便能获得感性的认识。我们假设分到j这个叶子节点上的样本只有一个。那么,$w^{*}_j$就变成如下这个样子:
这个式子告诉我们,$w^{*}_j$的最佳值就是负的梯度乘以一个权重系数,该系数类似于随机梯度下降中的学习率。观察这个权重系数,我们发现,$h_j$越大,这个系数越小,也就是学习率越小。$h_j$越大代表什么意思呢?代表在该点附近梯度变化非常剧烈,可能只要一点点的改变,梯度就从10000变到了1,所以,此时,我们在使用反向梯度更新时步子就要小而又小,也就是权重系数要更小。
综上,为了便于理解,单颗决策树的学习过程可以大致描述为:
- 枚举所有可能的树结构q
- 用等式$Obj^{*}$为每个q计算其对应的分数$Obj^{*}$,分数越小说明对应的树结构越好
- 根据上一步的结果,找到最佳的树结构,用等式$w^{*}_j$为树的每个叶子节点计算预测值
然而,可能的树结构数量是无穷的,所以实际上我们不可能枚举所有可能的树结构。通常情况下,我们采用贪心策略来生成决策树的每个节点。
- 从深度为0的树开始,对每个叶节点枚举所有的可用特征
- 针对每个特征,把属于该节点的训练样本根据该特征值升序排列,通过线性扫描的方式来决定该特征的最佳分裂点,并记录该特征的最大收益(采用最佳分裂点时的收益)
- 选择收益最大的特征作为分裂特征,用该特征的最佳分裂点作为分裂位置,把该节点生长出左右两个新的叶节点,并为每个新节点关联对应的样本集
- 回到第1步,递归执行到满足特定条件为止
在上述算法的第二步,样本排序的时间复杂度为$O(nlogn)$,假设公用K个特征,那么生成一颗深度为K的树的时间复杂度为$O(dKnlogn)$。具体实现可以进一步优化计算复杂度,比如可以缓存每个特征的排序结果等。
如何计算每次分裂的收益呢?假设当前节点记为C,分裂之后左孩子节点记为L,右孩子节点记为R,则该分裂获得的收益定义为当前节点的目标函数值减去左右两个孩子节点的目标函数值之和:$Gain=Obj^{*}_{C}-Obj^{*}_{L}-Obj^{*}_{R}$,带入式子之后有:
$$
Gain=\frac{1}{2}\Bigg[\frac{G^{2}_{L}}{H_{L}+\lambda}+\frac{G^{2}_{R}}{H_{R}+\lambda}-\frac{(G_{L}+G_{R})^{2}}{H_{L}+H_{R}+\lambda}\Bigg]-\gamma
$$
其中,$-\gamma$项表示因为增加了树的复杂性(该分裂增加了一个叶子节点)带来的惩罚。
这个Gain实际上就是单节点的$obj^{*}$减去切分后的两个节点的树obj,Gain如果是正的,并且值越大,表示切分后obj越小于单节点的$obj^{*}$,就越值得切分。同时,我们还可以观察到,Gain的左半部分如果小于右侧的γ,则Gain就是负的,表明切分后obj反而变大了。γ在这里实际上是一个临界值,它的值越大,表示我们对切分后obj下降幅度要求越严。
注意:xgboost的切分操作和普通的决策树切分过程是不一样的。普通的决策树在切分的时候并不考虑树的复杂度,而依赖后续的剪枝操作来控制。xgboost在切分的时候就已经考虑了树的复杂度,就是那个γ参数。所以,它不需要进行单独的剪枝操作。
最后,总结一下GBDT的学习算法:
- 算法每次迭代生成一颗新的决策树
- 在每次迭代开始之前,计算损失函数在每个训练样本点的一阶导数$g_i$和二阶导数$h_i$
- 通过贪心策略生成新的决策树,通过计算$w^{*}_{i}$计算每个叶节点对应的预测值
- 把新生成的树$f_{t}(x)$添加到模型中:$\hat{y}^{(t)}_{i}=\hat{y}^{(t-1)}_{i}+f_{t}(x)$
通常在第四步,我们把模型更新公式替换为:$\hat{y}^{(t)}_{i}=\hat{y}^{(t-1)}_{i}+\varepsilon f_{t}(x)$,其中$\varepsilon$称之为步长或者学习率。增加$\varepsilon$因子的目的是为了避免模型过拟合。
六、GBDT小结
GDBT本身并不复杂,不过要吃透的话需要对集成学习的原理,决策树原理和各种损失函树有一定的了解。由于GBDT的卓越性能,只要是研究机器学习都应该掌握这个算法,包括背后的原理和应用调参方法。目前GBDT的算法比较好的库是xgboost。当然scikit-learn也可以。
最后总结下GBDT的优缺点。
GBDT主要的优点有:
- 可以灵活处理各种类型的数据,包括连续值和离散值。
- 在相对少的调参时间情况下,预测的准确率也可以比较高。这个是相对SVM来说的。
- 使用一些健壮的损失函数,对异常值的鲁棒性非常强。比如 Huber损失函数和Quantile损失函数。
GBDT的主要缺点有: - 由于弱学习器之间存在依赖关系,难以并行训练数据。不过可以通过自采样的SGBT来达到部分并行。
七、XGB与GBDT
XGB模型属于GBDT模型的升级,其主要与GBDT的区别:
- XGBoost在代价函数中加入了正则项,用于控制模型的复杂度(降低叶子节点个数及叶子节点的权重)。从权衡方差偏差来看,它降低了模型的方差,使学习出来的模型更加简单,防止过拟合,这也是XGBoost优于传统GBDT的一个特性;
- 传统的GBDT在优化的时候只用到一阶导数信息,XGBoost则对代价函数进行了二阶泰勒展开,得到一阶和二阶导数;
- 和GBDT只支持CART作为基分类器之外,还支持线性分类器,在使用线性分类器的时候可以使用L1,L2正则化。
- 列抽样。XGBoost借鉴了随机森林的做法,支持列抽样,不仅防止过拟合,还能减少计算;
- shrinkage(缩减),相当于学习速率(XGBoost中的eta)。XGBoost在进行完一次迭代时,会将叶子节点的权值乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。(GBDT也有学习速率);
- 在寻找最佳分割点时,考虑到传统的贪心算法效率较低,实现了一种近似贪心算法,用来加速和减小内存消耗,除此之外还考虑了稀疏数据集和缺失值的处理,对于特征的值有缺失的样本,XGBoost依然能自动找到其要分裂的方向。
- XGBoost支持并行处理,XGBoost的并行不是在模型上的并行,而是在特征上的并行,将特征列排序后以block的形式存储在内存中,在后面的迭代中重复使用这个结构。这个block也使得并行化成为了可能,其次在进行节点分裂时,计算每个特征的增益,最终选择增益最大的那个特征去做分割,那么各个特征的增益计算就可以开多线程进行。