XGBoost 算法原理

一、简介

XGBoost(eXtreme Gradient Boosting)是GBDT(Gradient Boosting Decision Tree)的一种实现。GBDT又是提升树(Boosting Tree)的一种优化模型。Boosting则是集成学习的一种算法。
enssemble learning

1.1 梯度提升树(Gradient Boosting Decison Tree, GBDT)

之前提到的 Bagging 的思想比较简单,即每一次从原始数据中根据均匀概率分布有放回的抽取和原始数据大小相同的样本集合,样本点可能出现重复,然后对每一次产生的训练集构造一个分类器,再对分类器进行组合。

Boosting的每一次抽样的样本分布都是不一样的。每一次迭代,都根据上一次迭代的结果,增加被错误分类的样本的权重,使得模型能在之后的迭代中更加注意到难以分类的样本,这是一个不断学习的过程,也是一个不断提升的过程,这也就是Boosting思想的本质所在。迭代之后,将每次迭代的基分类器进行集成。那么如何进行样本权重的调整和分类器的集成是我们需要考虑的关键问题。

GB(Gradient Boosting) 是一种 Boosting 算法,因此它也试图利用一群弱学习器来创建一个强学习器。该算法与 AdaBoost算法 算法相似,但在某些方面有所不同。在这种方法中,我们试图将增强问题形象化为一个优化问题。我们采用一个损失函数,并试图优化它。

提升树是一种以分类树或者回归树作为基本分类器的算法。提升树一般采用加法模型,即基函数的线性组合与前向分步算法。对分类问题决策树是二叉分类树,对回归问题决策树是二叉回归树。对于提升树,当损失函数是平方误差损失函数和指数损失函数时,每一步优化比较简单,但是对于一般损失函数,往往每一步优化没那么容易。通常优化损失函数,是希望损失函数能够不断的减小,而且是损失函数能够尽可能快的减小。而梯度提升算法针对这问题 ,让损失函数沿着梯度方向最速下降的方法。

GBDT是以决策树(CART)为基学习器的GB算法。在GBDT的迭代中,假设我们前一轮迭代得到的强学习器是 f t − 1 ( x ) f_{t-1}(x) ft1(x), 损失函数是 L ( y , f t − 1 ( x ) ) L(y,f_{t-1}(x)) L(y,ft1(x)) , 我们本轮迭代的目标是找到一个CART回归树模型的弱学习器 h t ( x ) h_t(x) ht(x),让本轮的损失函数 L ( y , f t − 1 ( x ) + h t ( x ) ) L(y,f_{t-1}(x)+h_t(x)) L(y,ft1(x)+ht(x)) 最小(这里也可以看出来,其实GBDT就是一个学习残差的过程)。也就是说,本轮迭代找到决策树,要让样本的损失尽量变得更小。

GBDT的核心就在于:每一棵树学的是之前所有树结论和的残差,这个残差就是一个加预测值后能得真实值的累加量。比如A的真实年龄是18岁,但第一棵树的预测年龄是12岁,差了6岁,即残差为6岁。那么在第二棵树里我们把A的年龄设为6岁去学习,如果第二棵树真的能把A分到6岁的叶子节点,那累加两棵树的结论就是A的真实年龄;如果第二棵树的结论是5岁,则A仍然存在1岁的残差,第三棵树里A的年龄就变成1岁,继续学习。

GBDT的目标函数:
L ( y , f t − 1 ( x ) + h t ( x ) ) L(y,f_{t-1}(x)+h_t(x)) L(y,ft1(x)+ht(x))

GBDT用于分类算法的损失函数:

  • 指数损失函数
  • 对数损失函数

GBDT用于回归算法的损失函数:

  • 均方损失函数
  • 绝对值损失函数
  • Huber损失函数
  • 分位数损失函数

GBDT的算法描述如下:
(1)初始化弱学习器:
f 0 ( x ) = a r g   m i n c ∑ i = 1 m L ( y i , c ) f_0(x)=arg\ min_c \sum_{i=1}^m L(y_i,c) f0(x)=arg minci=1mL(yi,c)

(2)对t=1,2,…,T有:
  (a)首先对样本i=1,2,…,m,计算损失函数在当前模型 f t − 1 ( x ) f_{t−1}(x) ft1(x)的负梯度值,作为残差
r t i = − [ ∂ L ( y i , f ( x i ) ) ) ∂ f ( x i ) ] f ( x ) = f t − 1      ( x ) r_{ti} = -\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)} rti=[f(xi)L(yi,f(xi)))]f(x)=ft1(x)
  (b)将上一步得到的残差作为样本新的真实值,利用新的训练样本( x i x_i xi, r t i r_{ti} rti)(i=1,2,…,m), 拟合一颗CART回归树,得到第t颗回归树,其对应的叶子节点区域为对应的叶节点区域为 R t j , j = 1 , 2 , . . . , J R_{tj}, j =1,2,..., J Rtj,j=1,2,...,J
   (c)最小化损失函数,从而在叶结点区域 j = 1 , 2 , . . . , J j=1,2,...,J j=1,2,...,J上计算最佳拟合值:
c t j = a r g    m i n ⏟ c ∑ x i ∈ R t j L ( y i , f t − 1 ( x i ) + c ) c_{tj} = \underbrace{arg\; min}_{c}\sum\limits_{x_i \in R_{tj}} L(y_i,f_{t-1}(x_i) +c) ctj=c argminxiRtjL(yi,ft1(xi)+c)
这样我们就得到了本轮的决策树拟合函数如下(ct是叶子节点的输出,I是指示函数):
h t ( x ) = ∑ j = 1 J c t j I ( x ∈ R t j ) h_t(x) = \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj}) ht(x)=j=1JctjI(xRtj)
  (d)更新强学习器:
f t ( x ) = f t − 1 ( x ) + ∑ j = 1 J c t j I ( x ∈ R t j ) f_{t}(x) = f_{t-1}(x) + \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj}) ft(x)=ft1(x)+j=1JctjI(xRtj)

(3)得到最终学习器:
f ( x ) = f T ( x ) = f 0 ( x ) + ∑ t = 1 T ∑ j = 1 J c t j I ( x ∈ R t j ) f(x)=f_T(x)=f_0(x)+\sum_{t=1}^T \sum_{j=1}^{J} c_{tj}I(x \in R_{tj}) f(x)=fT(x)=f0(x)+t=1Tj=1JctjI(xRtj)

上面步骤二中的第一步是得到负梯度,或者是泰勒展开式的一阶导数。第二步是第一个优化求解,即基于残差拟合一颗CART回归树,得到J个叶子节点区域。第三步是第二个优化求解,在第二步优化求解的结果上,对每个节点区域再做一次线性搜索,得到每个叶子节点区域的最优取值。最终得到当前轮的强学习器。

1.2 XGBoost

作为GBDT的高效实现,XGBoost是一个上限特别高的算法,因此在算法竞赛中比较受欢迎。简单来说,对比原算法GBDT,XGBoost主要从下面三个方面做了优化:

  • 一是算法本身的优化:在算法的弱学习器模型选择上,对比GBDT只支持决策树,XGBoost还支持很多其他的弱学习器。在算法的损失函数上,除了本身的损失,还加上了正则化部分。在算法的优化方式上,GBDT的损失函数只对误差部分做负梯度(一阶泰勒)展开,而XGBoost损失函数对误差部分做二阶泰勒展开,更加准确。算法本身的优化是我们后面讨论的重点。
  • 二是算法运行效率的优化:对每个弱学习器,比如决策树建立的过程做并行选择,找到合适的子树分裂特征和特征值。在并行选择之前,先对所有的特征的值进行排序分组,方便前面说的并行选择。对分组的特征,选择合适的分组大小,使用CPU缓存进行读取加速。将各个分组保存到多个硬盘以提高IO速度。
  • 三是算法健壮性的优化:对于缺失值的特征,通过枚举所有缺失值在当前节点是进入左子树还是右子树来决定缺失值的处理方式。算法本身加入了L1和L2正则化项,可以防止过拟合,泛化能力更强。

在上面三方面的优化中,第一部分算法本身的优化是重点也是难点。现在我们就来看看算法本身的优化内容。

二、XGboost的目标函数推导

从上面GBDT的算法描述可以看出,GBDT求最优解的方法是分两步走:求解当前决策树最优的所有叶子节点区域和每个叶子节点区域的最优解 c t j c_{tj} ctj

对于XGBoost,它期望把第2步和第3步合并在一起做,即一次求解出决策树最优的所有叶子节点区域和每个叶子节点区域的最优解。在讨论如何求解前,我们先看看XGBoost的损失函数的形式。

2.1 重新定义一棵树

我们将一颗树 f t ( x ) = w q ( x ) f_t(x)=w_{q(x)} ft(x)=wq(x)重新定义,包括两个部分:

  • 叶子结点的权重向量w;
  • (实例 -> 叶子结点)的映射关系q(本质是树的分支结构);
    一棵树的表达形式定义如下:
    tree
    通俗的讲就是给定一个输入x之后,先通过q(x)从树中找到相应地叶子节点leaf,然后以这个节点作为下标从权重向量得到对应的权重,所以 f t ( x ) f_t(x) ft(x)其实是权重值。
2.2 重新定义树的复杂度

我们将一颗树的复杂度重新定义为 Ω ( f t ) = γ T + 1 2 λ ∑ j = 1 T w j 2 \Omega(f_t)=\gamma T+\frac{1}{2} \lambda \sum_{j=1}^T w_j^2 Ω(ft)=γT+21λj=1Twj2,它由两部分组成:

  • 叶子结点的数量T;
  • 叶子结点权重向量的L2范数 w j 2 w_j^2 wj2

实例如下:
复杂度

2.3 XGBoost 的目标函数

首先,为了防止过拟合,XGBoost的目标函数在GBDT损失函数 ∑ i = 1 n l ( y i , y ^ i ) \sum\limits_{i=1}^n l(y_i, \hat y_i) i=1nl(yi,y^i) 的基础上,将全部 K 棵树的复杂度进行累加求和作为正则项加入,最终XGBoost目标函数定义如下:
O b j = ∑ i = 1 n l ( y i , y ^ i ) + ∑ k = 1 K Ω ( f k ) (2.3.1) Obj=\sum\limits_{i=1}^n l(y_i, \hat y_i) + \sum\limits_{k=1}^K \Omega(f_k) \tag{2.3.1} Obj=i=1nl(yi,y^i)+k=1KΩ(fk)(2.3.1)
这里的 l l l 是训练损失, y ^ i \hat y_i y^i 是第 i 个样本 x i x_i xi 的预测值。由于XGBoost是一个加法模型,因此,预测得分是每棵树打分的累加之和:
y_hat
假设我们第 t 次迭代要训练的树模型是 f t ( x i ) f_t(x_i) ft(xi) ,也就是要学习第 t 棵树,由于XGBoost 是一个加法模型,所以此时前 t-1 次的训练结果 y ^ i ( t − 1 ) \hat y_i^{(t-1)} y^i(t1) 其实是已经通过之前累加得出来了,可以将其看做一个常数值,于是样本i的第t棵树的预测结果 y ^ i ( t ) \hat y_i^{(t)} y^i(t) 可表示为:
在这里插入图片描述
还要注意的一点是,这里我们将正则化项进行了拆分,和上面的 y ^ \hat y y^一样,由于前 t-1棵树的结构已经确定,因此,前 t-1 棵树的复杂度之和可以用一个常量表示const,而 Ω ( f t ) \Omega(f_t) Ω(ft)则是未知的第t棵树的复杂度,于是所有t棵树的复杂度之和可以表示为:
在这里插入图片描述
将上面两式带入等式(2.3.1)中的目标函数 Obj ,可以得到等式(2.3.2):
t_tree
注意上式中,只有一个变量,那就是第 t 棵树: f t ( x i ) f_t(x_i) ft(xi),其余的都是已知量或可通过已知量可以计算出来的(注意要理解哦!)。

最终我们要极小化上面这个损失函数,得到第t个决策树最优的所有J个叶子节点区域和每个叶子节点区域的最优解。XGBoost没有和GBDT一样去拟合泰勒展开式的一阶导数,而是期望直接基于损失函数的二阶泰勒展开式来求解。下面我们来看看这个损失函数的二阶泰勒展开式。

2.4 二阶泰勒展开

首先简单回忆一下,泰勒公式:

泰勒公式是将一个在 x = x0 处具有n阶导数的函数 f(x) 利用关于 (x-x0) 的n次多项式来逼近函数的方法。

泰勒公式的二阶展开形式如下:
tale
回到我们的问题上来, f ( x ) f(x) f(x) 对应于我们的损失函数 l l l ,x 对应于前 t-1 棵树的预测值, Δ x \Delta x Δx 对应于我们正在训练的第 t 棵树。

首先定义损失函数 l l l 关于 y ^ ( t − 1 ) \hat y^{(t-1)} y^(t1) 的一阶偏导数 g i g_i gi和二阶偏导数 h i h_i hi
partial
那么,我们的损失函数就可以转化为下式(标出了与泰勒公式中x和Δx的对应关系)。
loss function
将上述二阶展开式,带入到目标函数(2.3.2)中,可以得到目标函数 Obj 的近似值:
obj
去掉全部常数项,得到目标函数(2.4.1):
obj

2.5 叶子结点归组

我们将属于第 j 个叶子结点的所有样本 xi , 划入到一个叶子结点样本集中,数学表示如下:
I
然后,将【2.1】和【2.2】中一棵树及其复杂度的定义,带入到泰勒展开后的目标函数Obj(2.4.1)中,具体推导如下:
obj
可以看出,对叶子节点归组是为了把损失函数求和中的n个结点和树的复杂度求和中的T个叶子节点进行统一,结点权重 w q w_q wq也因此统一成了 w j w_j wj,方便了化简公式。

为进一步简化该式,我们进行如下定义:
G j = ∑ i ∈ I j g i , H j = ∑ i ∈ I j h i G_j=\sum_{i \in I_j}g_i,H_j=\sum_{i \in I_j} h_i Gj=iIjgi,Hj=iIjhi

含义如下:

  • G j G_j Gj:叶子结点 j 所包含样本的一阶偏导数累加之和,是一个常量;
  • H j H_j Hj:叶子结点 j 所包含样本的二阶偏导数累加之和,是一个常量;

G j G_j Gj H j H_j Hj 带入目标式Obj,得到我们最终的目标函数(注意,此时式中的变量只剩下第t棵树的权重向量W):
obj

三、XGBoost目标函数的优化求解

XGBoost目标函数的优化求解涉及到2个问题:

  • 如果我们已经求出了第t个决策树的J个最优的叶子节点区域,如何求出每个叶子节点区域的最优解 w ∗ w^* w
  • 对当前决策树做子树分裂决策时,应该如何选择哪个特征和特征值进行分裂,使最终我们的目标函数最小化?
3.1 求每个叶子节点区域的最优解

回忆一下高中数学知识。假设有一个一元二次函数,形式如下:
G x + 1 2 H x 2 , H > 0 Gx+\frac{1}{2}Hx^2,H>0 Gx+21Hx2,H>0
我们可以套用一元二次函数的最值公式轻易地求出最值点:
在这里插入图片描述
那回到我们的目标函数 Obj,该如何求出它的最值呢?
在这里插入图片描述
先简单分析一下上面的式子,对于每个叶子结点 j , 可以将其从目标式 Obj 中拆解出来:
在这里插入图片描述
在【2.5】中我们提到, G j G_j Gj H j H_j Hj 相对于第 t 棵树来说是可以计算出来的。那么,这个式子就是一个只包含一个变量 叶子结点权重 w j w_j wj 的一元二次函数,上面也提到了,我们可以通过最值公式求出它的最值点。

再次分析一下目标函数Obj,可以发现,各个叶子结点的目标子式是相互独立的,也就是说,当每个叶子结点的子式都达到最值点时,整个目标函数式Obj才达到最值点(下面Obj中对每个叶子节点的求和)。

那么,假设目前树的结构已经固定,套用一元二次函数的最值公式,我们可以轻易求出,每个叶子结点的权重 w j ∗ w_j^* wj 及其此时达到最优的 Obj 的目标值:
在这里插入图片描述
实例演示:
在这里插入图片描述

3.2 如何选择特征和特征值进行分裂
3.2.1 分裂一个结点

在GBDT里面,我们是直接拟合的CART回归树,所以树节点分裂使用的是均方误差。XGBoost这里不使用均方误差,而是使用贪心法,即每次分裂都期望最小化我们的损失函数的误差。在实际训练过程中,当建立第 t 棵树时,从树深为0时开始:

  • 对树中的每个叶子结点尝试进行分裂
  • 每次分裂后,原来的一个叶子结点继续分裂为左右两个子叶子结点,原叶子结点中的样本集将根据该结点的判断规则分散到左右两个叶子结点中;
  • 新分裂一个结点后,我们需要检测这次分裂是否会给损失函数带来增益,增益的定义如下:
    在这里插入图片描述

也就是说,我们的决策树分裂标准不再使用CART回归树的均方误差,而是上式了。如果增益Gain>0,即分裂为两个叶子节点后,目标函数下降了,那么我们会考虑此次分裂的结果。

但是,在一个结点分裂时,可能有很多个分裂点,每个分裂点都会产生一个增益,如何才能寻找到最优的分裂点呢?接下来会讲到。

3.2.2 寻找最佳分裂点

在分裂一个结点时,我们会有很多个候选分割点,寻找最佳分割点的大致步骤如下:

  • 遍历每个结点的每个特征;
  • 对每个特征,按特征值大小将特征值排序;
  • 线性扫描,找出每个特征的最佳分裂特征值;

在所有特征中找出最好的分裂点(分裂后增益最大的特征及特征值)

上面是一种贪心的方法,每次进行分裂尝试都要遍历一遍全部候选分割点,也叫做全局扫描法

但当数据量过大导致内存无法一次载入或者在分布式情况下,贪心算法的效率就会变得很低,全局扫描法不再适用。

基于此,XGBoost提出了一系列加快寻找最佳分裂点的方案:

  • 特征预排序+缓存:XGBoost在训练之前,预先对每个特征按照特征值大小进行排序,然后保存为block结构,后面的迭代中会重复地使用这个结构,使计算量大大减小。
  • 分位点近似法:对每个特征按照特征值排序后,采用类似分位点选取的方式,仅仅选出常数个特征值作为该特征的候选分割点,在寻找该特征的最佳分割点时,从候选分割点中选出最优的一个。
  • 并行查找:由于各个特性已预先存储为block结构,XGBoost支持利用多个线程并行地计算每个特征的最佳分割点,这不仅大大提升了结点的分裂速度,也极利于大规模训练集的适应性扩展。
3.2.3 停止生长

一棵树不会一直生长下去,下面是一些常见的限制条件。

(1) 当新引入的一次分裂所带来的增益Gain<0时,放弃当前的分裂。这是训练损失和模型结构复杂度的博弈过程。
gain

(2) 当树达到最大深度时,停止建树,因为树的深度太深容易出现过拟合,这里需要设置一个超参数max_depth

(3) 当引入一次分裂后,重新计算新生成的左、右两个叶子结点的样本权重和。如果任一个叶子结点的样本权重低于某一个阈值,也会放弃此次分裂。这涉及到一个超参数:最小样本权重和,是指如果一个叶子节点包含的样本数量太少也会放弃分裂,防止树分的太细,这也是过拟合的一种措施。

每个叶子结点的样本权值和计算方式如下:
在这里插入图片描述
具体如何分裂呢?举个简单的年龄特征的例子如下,假设我们选择年龄这个特征的值a作为决策树的分裂标准,则可以得到左子树2个人,右子树三个人,这样可以分别计算出左右子树的一阶和二阶导数和,进而求出最终的上式的值。我们可以发现对于所有的a,我们只要做一遍从左到右的扫描就可以枚举出所有分割的梯度和 G L G_L GL G R G_R GR。然后用上面的公式计算每个分割方案的分数就可以了。

在这里插入图片描述

四、XGBoost算法主流程

这里我们总结下XGBoost的算法主流程,基于决策树弱分类器。不涉及运行效率的优化和健壮性优化的内容。

输入:训练集样本 I = { ( x , y 1 ) , ( x 2 , y 2 ) , . . . ( x m , y m ) } I=\{(x_,y_1),(x_2,y_2), ...(x_m,y_m)\} I={(x,y1),(x2,y2),...(xm,ym)}, 最大迭代次数T, 损失函数L, 正则化系数 λ , γ \lambda, \gamma λ,γ
输出:强学习器f(x)

对迭代轮数t=1,2,…T有:

(1) 计算第i个样本(i=1,2,…m)在当前轮损失函数L基于 f t − 1 ( x i ) f_{t-1}(x_i) ft1(xi) 的一阶导数 g t i g_{ti} gti,二阶导数 h t i h_{ti} hti,计算所有样本的一阶导数和 G t = ∑ i = 1 m g t i G_t=\sum_{i=1}^m g_{ti} Gt=i=1mgti,二阶导数和 H t = ∑ i = 1 m h t i H_t=\sum_{i=1}^m h_{ti} Ht=i=1mhti

(2) 基于当前节点尝试分裂决策树,默认分数score=0
对特征序号 k=1,2…K:
  (a) G L = 0 , H L = 0 G_L=0, H_L=0 GL=0,HL=0
  (b.1) 将样本按特征k从小到大排列,依次取出第i个样本,依次计算当前样本放入左子树后,左右子树一阶和二阶导数和:
G L = G L + g t i , G R = G − G L G_L = G_L+ g_{ti}, G_R=G-G_L GL=GL+gti,GR=GGL
H L = H L + h t i , H R = H − H L H_L = H_L+ h_{ti}, H_R=H-H_L HL=HL+hti,HR=HHL
  (b.2) 尝试更新最大的分数:
s c o r e = m a x ( s c o r e , 1 2 G L 2 H L + λ + 1 2 G R 2 H R + λ − 1 2 ( G L + G R ) 2 H L + H R + λ − γ ) score = max(score, \frac{1}{2}\frac{G_L^2}{H_L + \lambda} + \frac{1}{2}\frac{G_R^2}{H_R+\lambda} - \frac{1}{2}\frac{(G_L+G_R)^2}{H_L+H_R+ \lambda} -\gamma ) score=max(score,21HL+λGL2+21HR+λGR221HL+HR+λ(GL+GR)2γ)

(3) 基于最大score对应的划分特征和特征值分裂子树。

(4) 如果最大score为0,则当前决策树建立完毕,计算所有叶子区域的 w t j w_{tj} wtj, 得到弱学习器 h t ( x ) h_t(x) ht(x),更新强学习器 f t ( x ) f_t(x) ft(x),进入下一轮弱学习器迭代.如果最大score不是0,则转到第2)步继续尝试分裂决策树。

五、XGBoost算法优化

5.1 XGBoost算法运行效率的优化

上面我们重点讨论了XGBoost算法本身的优化,在这里我们再来看看XGBoost算法运行效率的优化。

首先明确的是:XGBoost的并行,并不是说每棵树可以并行训练,XGBoost本质上仍然采用 Boosting 思想,Boosting算法的弱学习器是没法并行迭代的,每棵树训练前需要等前面的树训练完成才能开始训练。但是单个弱学习器里面最耗时的是决策树的分裂过程,XGBoost针对这个分裂做了比较大的并行优化。对于不同的特征的特征划分点,XGBoost分别在不同的线程中并行选择分裂的最大增益。

XGBoost的特征维度的并行是这样实现的:在训练之前,每个特征按特征值对样本进行预排序,并以Block结构存储在内存中,在后面查找特征分割点时可以重复使用,减少计算量。计算量的减少是指:默认所有的样本都在右子树,然后从小到大迭代,依次放入左子树,并寻找最优的分裂点。这样做可以减少很多不必要的比较。而且特征已经被存储为一个个block结构,那么在寻找每个特征的最佳分割点时,可以利用多线程对每个block并行计算。

此外,通过设置合理的分块的大小,充分利用了CPU缓存进行读取加速(cache-aware access)。使得数据读取的速度更快。另外,通过将分块进行压缩(block compressoin)并存储到硬盘上,并且通过将分块分区到多个硬盘上实现了更大的IO。

5.2 XGBoost算法健壮性的优化

首先我们来了解一下一般情况下对存在缺失值的特征的解决方法是:

  • 离散型变量:用出现次数最多的特征值填充;
  • 连续型变量:用中位数或均值填充;

一些模型如SVM和KNN,其模型原理中涉及到了对样本距离的度量,如果缺失值处理不当,最终会导致模型预测效果很差。
树模型对缺失值的敏感度低,大部分时候可以在数据缺失时时使用。原因就是,一棵树中每个结点在分裂时,寻找的是某个特征的最佳分裂点(特征值),完全可以不考虑存在特征值缺失的样本,也就是说,如果某些样本缺失的特征值缺失,对寻找最佳分割点的影响不是很大。

因此,对于有缺失值的数据在经过缺失处理后:

  • 当数据量很小时,优先用朴素贝叶斯
  • 数据量适中或者较大,用树模型,优先XGBoost
  • 数据量较大,也可以用神经网络
  • 避免使用距离度量相关的模型,如KNN和SVM

XGBoost模型的一个优点就是允许特征存在缺失值,XGBoost对特征的缺失值的处理方式是这样的:

  • 在特征k上寻找最佳 split point 时,不会对该列特征 missing 的样本进行遍历,而只对该列特征值为 non-missing 的样本上对应的特征值进行遍历,通过这个技巧来减少了为稀疏离散特征寻找 split point 的时间开销。
  • 在逻辑实现上,为了保证完备性,XGBoost没有假设缺失值一定进入左子树还是右子树,会将该特征值missing的样本分别分配到左叶子结点和右叶子结点,两种情形都计算一遍后,选择分裂后增益最大的那个方向(左分支或是右分支),作为预测时特征值缺失样本的默认分支方向,这样处理起来更加的灵活和合理。
  • 如果在训练中没有缺失值而在预测中出现缺失,那么会自动将缺失值的划分方向放到右子结点

也就是说,其实每次都是针对没有缺失值的特征k的样本走上述第四节的一般流程。而对应有缺失值的特征,上面第4节的算法的步骤(a),(b.1)和(b.2)会执行2次,第一次假设特征k所有有缺失值的样本都走左子树,第二次假设特征k所有有缺失值的样本都走右子树

  • 如果是所有的缺失值走右子树,使用上面第4节的(a),(b.1)和(b.2)即可。
  • 如果是所有的样本走左子树,则上面第4节的(a)步要变成: G R = 0 , H R = 0 G_R=0, H_R=0 GR=0,HR=0
    (b.1)步要更新为:
    G R = G R + g t i , G L = G − G R G_R = G_R+g_{ti}, G_L=G-G_R GR=GR+gti,GL=GGR
    H R = H R + h t i , H L = H − H R H_R = H_R+h_{ti}, H_L=H-H_R HR=HR+hti,HL=HHR

六、面试题

  1. 珍藏版 | 20道XGBoost面试题,你会几个?(上篇)
  2. 珍藏版 | 20道XGBoost面试题,你会几个?(下篇)

参考
【0】 https://xgboost.ai
【1】【ML】XGBoost超详细推导,终于有人讲明白了!
【2】 机器学习经典算法(4)——Xgboost
【3】 机器学习总结(一) Adaboost,GBDT和XGboost算法
【4】 ID3、C4.5、CART、随机森林、bagging、boosting、Adaboost、GBDT、xgboost算法总结
【5】 梯度提升树(GBDT)原理小结
【6】 XGBoost算法原理小结
【7】 AdaBoost, GBDT和XGBoost的区别和联系

### XGBoost算法工作原理详解 #### 一、概述 XGBoost是一种高效的梯度提升决策树(GBDT)实现方式,由华盛顿大学博士陈天奇开发。该算法通过一系列弱预测模型(通常是决策树),逐步迭代改进最终形成强预测模型。 #### 二、目标函数定义 为了衡量模型的好坏并指导后续的学习过程,XGBoost引入了一个特定形式的目标函数: \[ \text{Obj}(\theta)=\sum_{i=1}^{n}\ell(y_i,\hat{y}_i)+\sum_k\Omega(f_k) \] 其中\( y_i \)表示真实标签值,而 \( \hat{y}_i=\phi(x_i;\Theta)\approx y_i \)代表预测得分;第一项为训练误差部分,第二项是对每棵新加入的基分类器施加正则化的惩罚项[^3]。 #### 三、泰勒展开近似求解 考虑到直接最小化上述复杂表达式的难度较大,在实际操作过程中会利用泰勒公式对损失函数做二次逼近处理: \[ L(\tilde{\mathbf{x}}+\Delta\mathbf{x})\simeq f(\tilde{\mathbf{x}})+f'(\tilde{\mathbf{x}})^T\cdot\Delta\mathbf{x}+\frac{1}{2}\Delta\mathbf{x}^TH_f(\tilde{\mathbf{x}})\cdot\Delta\mathbf{x} \] 这里取到了Hessian矩阵即二阶导数的信息来提高精度,从而使得每次更新都能更加贴近全局最优方向[^4]。 #### 四、结构参数量化与优化策略 对于新增加进来的一颗子树而言,除了要确定具体的分裂节点外还需要评估整棵树所带来的增益情况。为此设定了如下公式用于计算某个潜在分割点处可能产生的收益变化量: \[ Gain=\frac{1}{2}\left[\frac{G_L^2}{H_L+\lambda}+\frac{G_R^2}{H_R+\lambda}-\frac{(G_L+G_R)^2}{H_L+H_R+\lambda}\right]-\gamma \] 这里的GL,HL分别对应左分支样本集上的梯度平方和以及海森行列式之和;GR,HR同理适用于右支路;λ用来控制L2范数系数大小以防止过拟合现象发生;γ则是提前剪枝阈值参数设置。 最后采用贪心法遍历所有候选切分位置寻找局部最大Gain值得方案实施建模直至满足终止条件为止。 ```python import xgboost as xgb from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 创建模拟数据集 X,y = make_classification(n_samples=100,n_features=20) # 划分训练测试集合 X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=.2) # 定义DMatrix对象作为输入源 dtrain=xgb.DMatrix(data=X_train,label=y_train) dtest=xgb.DMatrix(data=X_test,label=y_test) param={ 'max_depth':6, 'eta':.3, 'objective':'binary:logistic' } num_round=100 bst=xgb.train(param,dtrain,num_round) pred_prob=bst.predict(dtest) print(pred_prob[:5]) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值