机器学习系列(9)——XGBoost算法

XGBoost算法挺复杂的,要多读几遍论文,梳理算法思路。本文希望有助于理解这个算法。

 

0x01、XGBoost简介

XGBoost即 Extreme Gradient Boosting,极端梯度提升,或者叫极值梯度提升。XGBoost所应用的算法就是GBDT的改进,既可用于分类也可用于回归问题中。作为GBDT的高效实现,XGBoost是一个上限特别高的算法。

1、XGBoost与GBDT的比较

 XGBoost算法

GBDT算法 

基分类器使用CART回归树作为基分类器,还支持线性的基学习器。使用CART树作为基分类器
结点分类方法依据影响Loss的程度来确定连续特征的切分值使用基尼指数
损失函数使用了二阶泰勒展开,有利于梯度下降得更快更准确只用到一阶导数
防止过拟合在代价函数里加入了正则项用于控制模型复杂度,降低了过拟合的可能性无正则项,可能出现过拟合
模型实现

在训练之前预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,可以大大减少计算量。

能够实现在特征粒度的并行。

串行,不支持并行

2、XGBoost的优缺点

优点:速度快,效果好,能处理大规模数据,支持多种基学习器,支持自定义损失函数等;

缺点:算法参数过多,调参复杂,不适合处理超高维特征数据。

 

0x02、XGBoost的模型和框架

1、XGBoost的模型

(回归树)给定一个包含个样本,M 个特征的数据集 T=\left\{ (x_1,y_1),(x_2,y_2),...,(x_N,y_N) \right\},集成模型的预测输出表示为:

        \hat{y}_i = \phi(x_i) = \sum_{k=1}^{K} f_k(x_i)  

其中,f_k 表示回归树,K 为回归树的数量。整个公式表示给定一个输入 x_i,输出值为 K 棵回归树的预测值相加。

2、XGBoost算法的基本框架

输入:训练数据集 T=\left\{ (x_1,y_1),(x_2,y_2),...,(x_N,y_N) \right\}, ~x_i \in \chi \sqsubseteq R^n, ~ y_i \in \gamma \sqsubseteq R,最大迭代次数 T,损失函数 L正则化系数 \lambda\gamma

输出:强学习器 f_M(x) 。

对迭代轮数 m=1,2,...,M

(1)计算一阶二阶导:计算第 i 个样本(i=1,2,...N)在当前轮的损失函数 L 基于 f_{m-1}(x_i) 的一阶导数 g_{mi},二阶导数 h_{mi},并计算所有样本的一阶导数和  G_{t} = \sum _{i=1}^{N} g_{mi},二阶导数和  H_{t} = \sum _{i=1}^{N} h_{mi} ; 

(2)基于当前结点尝试分裂决策树,默认分数 score=0,G 和 H 为当前需要分裂的结点的一阶二阶导数之和,

         对特征序号 k=1,2,...,K

        a. 先将左子树的一阶二阶导数和均置为零:G_{L}=0H_{L}=0

        b. 将样本按特征 k 从小到大排序,依次取出第 i 个样本,依次计算当前样本放入左子树后,左右子树一阶二阶导数和为:

                G_{L} = G_{L} + g_{mi}, ~ G_{R}=G-G_{L}  

               H_{L}=H_{L}+h_{mi},~ H_{R}=H-H_{L}  

        c. 尝试更新最大的分数:

                 score = max ( score, \frac{1}{2} [\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} ]- \gamma )   

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

(4)如果最大score为0,则当前决策树建立完毕,计算所有叶子区域的 w_{mj},得到弱学习器 h_m(x),更新强学习器 f_m(x),进入下一轮弱学习器迭代;如果最大score不为0,则转到第(2)步继续尝试分裂决策树。

XGBoost对GBDT的改进主要在目标函数(即损失函数和正则项)的改进上,例如向原损失函数中加入了正则项,使用了损失函数的二阶导数等,下面将详细分析这些改进点。

 

0x03、XGBoost的知识点分解

主要是在损失函数上的改进。

1、XGBoost损失函数推导

(1)XGBoost模型

        \hat{y}_i = \phi (x_i) = \sum_{k=1}^K f_k(x_i) ,F=\left\{ f_t(x) = w_{q(x)} \right\}, ~w \in R^T,~q:R^d \rightarrow \left\{ 1,2,...,T \right\}

其中 K 为树的总个数,f_k 表示第 k 棵树, \hat{y}_i 表示 样本 x_i 的预测结果;w为叶子结点的得分值,q(x)表示样本 x 对应的叶子结点,T为该树的叶子结点个数。

(2)XGBoost损失函数

        L(\phi ) = \sum_{i} l(\hat{y}_i, y_i) + \sum_{k} \Omega (f_k) ,\Omega (f) = \gamma T + \frac{1}{2} \lambda||w||^2  ,

其中 l(y_i,\hat{y}_i) 为样本 x_i 的训练误差,表示模型拟合数据的程度,\Omega (f_k) 表示第 k 棵树的正则项,用来惩罚复杂模型。

(3)XGBoost目标函数的改写过程

使用前向分步算法优化目标函数。设 \hat{y}_{i}^{(t)} 是第 i 个样本在第 t 次迭代的预测值,则有:\hat{y}_{i}^{(t)} = \hat{y}_{i}^{(t-1)} + t_{t}(x_i) ,表示样本 i 在第 t次迭代后的预测值为样本 i 在第 (t-1) 次迭代后的预测值与当前第t棵树的预测值的和。因此目标函数可以表示为:

        L^{(t)} = \sum_{i=1}^{n} l(y_i, \hat{y}_i^{(t)} ) + \sum_{i}^{t} \Omega (f_i) \\ ~~~~~~~~~~= \sum_{i=1}^{n} l \left\(y_i, ~\hat{y}_i^{(t-1)}+f_t(x_i) \right\) + \Omega(f_t) + constant     (3-1)   

上式表示贪心地添加使得模型提升最大  ,其中 constant表示前t (t-1) 次迭代的惩罚项,是一个常数项,即公式中  \sum_{i}^{t} \Omega (f_i)  部分。在第 t 次迭代时,前 (t-1) 次迭代产生的 (t-1) 棵树已经完全确定,则 (t-1) 棵树的叶结点和权重都已经确定,所以变成了常数。

如果在式(3-1)中考虑平方损失函数,则目标函数可以表示为:

        L^{(t)} = \sum_{i=1}^{n} \left\( y_i - (\hat{y}_i^{(t-1)} + f_t(x_i)) \right\)^2 + \Omega(f_t) + constant \\ ~~~~~~~~~~ = \sum_{i=1}^{n} \left\(y_i - \hat{y}_{i}^{(t-1)} - f_t(x_i) \right\)^2 + \Omega(f_t) + constant     (3-2)  

上式中,y_i - \hat{y}_{i}^{(t-1)} 表示残差,即经过 (t-1) 棵树的预测之后与真实值之间的差距,与GBDT中的残差概念相同。

定义 g_ih_i 分别为损失函数 l(y_i, \hat{y}^{(t-1)}) 的一阶导和二阶导,即: g_i = \partial _{\hat{y}^{(t-1)}} l(y_i, \hat{y}^{(t-1)}) , h_i = \partial _{\hat{y}^{(t-1)}}^2 l(y_i, \hat{y}^{(t-1)})  。而 l(y_i, \hat{y}^{(t-1)}) 表示的是前 (t-1) 次迭代的损失函数,对当前的第 t 次迭代来说已经是一个确定的常数了,可以省略。因此使用二阶泰勒展开式近似表示式(3-1)为:

        L^{(t)} \approx \sum_{i=1}^{n} \left\[ l(y_i, \hat{y}_i^{(t-1)}) + g_if_t(x_i) +0.5\cdot h_{i} f_{t}^{2}(x_i) \right\] + \Omega(f_t) + constant \\ ~~~~~~~~~~ = \sum_{i=1}^{n} \left\[ g_i f_t (x_i) + 0.5 \cdot h_i f_t^2(x_i) \right\] + \Omega(f_t)     (3-3)

接下来分析式(3-3)。首先定义集合  为树的第 j 个叶结点上的所有样本点的集合,即给定一棵树,所有按照决策规则被划分到第

j 个叶结点的样本集合,将复杂度惩罚项带入式(3-3),可得:

        L^{(t)} = \sum_{i=1}^{n} \left\[ g_i f_t (x_i) + 0.5 \cdot h_i f_t^2(x_i) \right\] + \Omega(f_t) \\ ~~~~~~~~~~ = \sum_{i=1}^{n} [ g_i f_t(x_i) + 0.5 \cdot h_i f_{t}^2(x_i) ] + \gamma T + \frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^2 \\ ~~~~~~~~~~ = \sum_{j=1}^{T} [ (\sum_{i \in I_j} g_i)w_j + \frac{1}{2}( \sum_{i \in I_j} h_i + \lambda) w_{j}^2 ] + \gamma T    (3-4) 

对上式求导,先令 \partial L^{(t)} / \partial w_j= 0,有:

        \frac{ \partial L^{(t)}}{ \partial w_j} = 0 \\ \Rightarrow (\sum_{i \in I_j} g_i) + (\sum_{i \in I_j} h_i + \lambda) w_j = 0 \\ \Rightarrow (\sum_{i \in I_j}h_i + \lambda )w_j = - \sum_{i \in I_j} g_i \\ \Rightarrow w_{j}^* = \frac{- \sum_{i \in I_j} g_i }{ \sum_{i \in I_j} h_i +\lambda}     (3-5) 

将式(3-5)代入式(3-4)中,得到:

       L^{(t)} = - \frac{1}{2} \sum_{j=1}^{T} \frac{ (\sum_{i \in I_j} g_i)^2}{ \sum_{i \in I_j} h_i + \lambda} + \gamma T    (3-6) 

令 G_i = \sum_{i \in I_j} g_i ,H_i = \sum_{i \in I_j} h_i  ,则将式(3-6)简化为: L^{(t)} = -\frac{1}{2} \sum_{j=1}^{T} \frac{G_{j}^2}{H_j + \lambda } + \gamma T  (3-7) 。

XGBoost根据式(3-7)作为损失函数判断的标准。

2、树的分裂查找算法

(1)XGBoost的特征选择和切分点选择指标

判断切分增益:

      Gain = \frac{1}{2} [ \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} ] - \gamma      (3-8)

中括号里的三个式子依次表示:某个结点按条件切分后左结点、右结点、和切分前的得分。\gamma 表示切分后模型复杂度的增加量。Gain值越大,说明分裂后能使目标函数减少越多,就越好。现在有了判断增益的方法,就需要使用该方法去查找最优特征和最优切分点。

(2)分裂查找算法

XGBoost提供了两个算法,用来选取最优特征和最优切分点:精确贪心算法(Basic Exact Greedy Algorithm),近似算法(Approximate Algorithm)。

 【精确贪心算法】通过遍历每个特征下的每个可能的切分点取值,计算切分后的增益,选择增益最大的特征及切分点。但因为精确贪心算法需要遍历所有特征和取值,当数据量非常大,无法将所有数据同时加载进内存时,该算法非常耗时。

【近似算法】对特征值进行了近似处理,即根据每个特征k的特征值分布,确定出候选切分点 S_k = \left\{ s_{k1}, s_{k2}, ..., s_{kl} \right\},即按特征分布将连续的特征值划分到 l 个候选点对应的桶中,并对每个桶中每个样本的 G_iH_i 进行累加。划分好候选切分点后,按照精确贪心算法的步骤选择最优切分特征和最优切分点,不同的是切分点被上述候选切分点所代替。

提取候选切分点的方式有两种:global和local。global表示在生成树之前进行候选切分点的提取,即开始之前为整棵树做一次提取即可,在每次的结点划分时都是用已经提取好的候选切分点。而local则是在每次结点划分时才进行候选切分点的提取。 global方式进行候选切分点提取的次数少:因为只是在初始化阶段进行一次,以后的结点切分均使用同一个,而local方式是在每次结点切分时才进行,需要很多次提取。global方式需要更多的候选点(对候选点提取数量比local多):因为没有像local方式一样每次结点划分时,对当前结点的样本进行细化,local方式更适合树深度较大的情况。

(3)加权分为数略图(Weighted Quantile Sketch)

这是为了处理带权重的候选切分点的选取。加权分位数略图算法提出一种支持merge和prune操作的数据结构。

加权分位数略图法对候选点的选取方式:

设数据集 D_k =\left\{ (x_{1k},h_1),(x_{2k},h_2),...,(x_{nk},h_n) \right\} 表示每个样本的第 k 个特征值(x_{nk})和二阶导数(h_{nk})的集合,定义排名函数 r_k

        r_k(z) = \frac{1}{\sum_{(x,h) \in D_k}h} \sum_{(x,h) \in D_k, x < z} h     (3-9)

上式表示数据集中第 k 个特征值小于z 的样本所在比例(特征值小于z的样本的权重和,占所有样本权重总和的百分比)。

可以根据下式进行候选点的选取:|r_k(s_k,j)-r_k(s_k,j+1)| < \epsilon,此式表示落在两个相邻的候选切分点之间的样本占比小于某个很小的常数 \epsilon 那么我们就有 1/\epsilon 个候选切分点。

3、其他优化方法

(1)稀疏值处理(Sparsity-aware Split Finding)

实际工程中一般会出现输入值稀疏的情况,如数据缺失、one-hot编码等。XGBoost的思想是:对于缺失数据让模型自动学习默认的划分方向。在该算法中,在每次切分中,让缺失值分别被切分到左、右结点中,通过计算得分值比较两种切分方法哪一个更优,则会对每个特征的缺失值都学到一个最优的默认切分方向。

(2)分块并行(Column Block for Parallel Learning)

在树生成过程中,需要花费大量的时间在特征选择和切分点选择上,并且这部分时间大部分又花费在对特征值排序上。XGBoost通过按特征进行分块并排序,在块里保存排序后的特征值及对应样本的引用,以便于获取样本的一阶二阶导数值。通过顺序访问排序后的块遍历样本特征的特征值,方便进行切分点的查找。分块存储后多个特征之间互不干涉,可以使用多线程同时对不同的特征进行切分点查找,即特征的并行化处理。

但在顺序访问特征值时,访问的是一块连续的内存空间,但通过特征值持有的索引(样本索引)访问样本获取一阶二阶导数时,这个访问操作访问的内存空间并不连续,这样可能造成CPU缓存命中率低,影响算法效率。有以下几种方法解决:

    (a)缓存访问(Cache-aware Access):为了减小非连续内存的访问带来缓存命中率低的问题。解决思路是:既然是非连续内存访问带来问题,那么去掉非连续内存访问就可以解决。可以转非连续为连续——缓存预取,即提起将要访问的非连续内存空间中的梯度统计信息(一节二阶导数),放置到连续的内存空间中。具体的操作就是为每个线程在内存空间中分配一个连续的buffer缓存区,将需要的梯度统计信息存放到缓冲区中。这种方式对大数据量很有用,可以不用把所有样本都加入的内存中,仅动态地将相关信息加入到内存中。

    (b)“核外”块计算(Blocks for Out-of-core-Computation):当数据量非常大时不能把所有的样本都加载到内存中,那么必须将一部分需要加载进内存的数据先存放在硬盘中。需要时再加载进内存。但硬盘的I/O操作速度远远低于内存的速度,那么会存在大量等待硬盘I/O的情况。针对这个问题,“核外”计算的操作为:将数据集分成多个块存放在硬盘中,使用一个独立的线程专门从硬盘读取数据加载到内存中,这样算法在内存中处理数据就可以和从硬盘读取数据同时进行。论文提出两种方法:块压缩(Block Compression)和块分区(Block Sharding)。前者是按列压缩,读取的时候用另外的线程解压,对行索引只保存第一个索引值,然后用16位的整数保存与该block第一个索引的差值。后者是将特征block分区存放在不同的硬盘上,以此来增加硬盘I/O的吞吐量。

(3)防止过拟合

XGBoost为了防止过拟合加入了两项惩罚项 \gamma T 和  (1/2)\lambda ||w||^2 。除此之外还可以用学习率行列采样的方法防止过拟合:例如 \hat{y}^{(t)} = \hat{y}_{i}^{(t-1)} + \eta f_t(x_i) 中,\eta 就是学习率;而XGBoost也和随机森林一样支持对样本及特征进行采样,取采样后的样本和特征作为训练数据,进一步防止过拟合。

 

 

参考:

[1]. 李航. 统计学习方法(第二版)

[2]. 周志华. 机器学习

[3]. 陈天奇. XGBoost: A Scalable Tree Boosting System. SIGKDD 2016

[4]. 集成学习之Xgboost - 博客园

[5]. 机器学习算法之XGBoost - 标点符

[6]. 对xgboost的理解 -知乎

[7]. XGBoost算法原理小结 - 博客园

[8]. xgboost - github

[9]. XGBoost调参详解 - 知乎

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值