决策树模型系列——8、XGBoost算法原理分析
XGBoost全称是Extreme Gradient Boosting(极限梯度提升算法),是梯度提升树算法(GBDT)的改良版本,属于集成学习技术框架中Boosting方法之一,用于监督学习,善于处理不同类型表格数据。

该算法是2016年陈天奇在其毕业博士论文《 XGBoost:A Scalable Tree Boosting System》(https://arxiv.org/pdf/1603.02754.pdf ) 中提出,是在GBDT算法基础上进行了许多改进,使其运算速度更快,并更适合工程上应用。后来,陈天奇等一些机器学习爱好者在这基础上开发的一个独立、开源的机器学习库XGBoost库,即xgboost,可以专门提供梯度提升树和XGBoost算法,并提供与C、Python、R、Julia等编程语言的API接口,同时支持CPU和GPU运算、分布式布置使用。
详细官网文档介绍: XGBoost Documentation — xgboost 2.1.0-dev documentation。
1. XGBoost算法模型
XGBoost原则上也是一个提升树模型,所以也需要使用加法模型构建XGBoost的算法模型。
假设一个数据集\(\mathcal{D} = {(x_i, y_i)} (|\mathcal{D}| = n, x_i \in R^m, y_i \in R)\),n为样本个数,m为样本特征维度,加法模型为:
XGBoost的算法模型为:
其中\(\mathcal{F} = {f(x) = w_{q(x)}} (q: R^m \rightarrow T, w \in R^T)\)代表回归树样本空间(一般使用CART树);\(q\)为将样本映射到叶结点索引的每一颗树结构,T为每一颗树的叶结点个数,w为每一颗树的叶结点权重(\(w_i\)代表第i片叶子的权重);\(f_k\)代表对应q(树结构)和w(叶结点权重)的独立树。
GBDT中的预测值是所有弱学习器的预测结果的加权求和,即每个样本的预测结果为样本所在叶子结点的均值,而XGB中的预测值是所有弱学习器上每个叶子权重\(w_j\)直接求和所得。如下图所示一个提升树模型使用K棵独立树的预测结果(叶结点权重)相加得到最终模型输出。

2. 目标函数
XGB的目标函数设计由两部分组成,也可叫”结构分数“:
其中\(l\)代表可微的凸损失函数(例如均方误差\((y_i - \hat y_i)^2\));\(\Omega(f)\)为正则项:
1、损失函数\(\Sigma_{i}l(y_i, \hat y_i)\):传统梯度提升树的损失函数,计算出真实标签值\(y_i\)与预测值\(\hat y_i\)之间的差异大小,用于衡量模型在训练数据上的预测效果。
一般可根据下面几种情况进行选择:
-
reg: linear - 使用线性回归的损失函数,均方误差,回归时使用;
-
binary:logistic - 使用逻辑回归的损失函数,对数损失log_loss,二分类时使用;
-
binary:hinge - 使用支持向量机的损失函数,Hinge Loss,二分类时使用;
-
multi:softmax- 使用softmax损失函数,多分类时使用。
2、正则化项\(\Sigma_{k} \Omega(f_k)\):用于控制模型的复杂度,避免模型出现过拟合,其中XGB的正则化更加简单和易于平行计算。
模型优化的目标是使目标函数取最小值,即求解泛化误差最小点,需平衡偏差和方差。

3. 目标函数变换
在逻辑回归或支持向量机中,将目标函数转化为比较容易求解的方式(比如对偶),然后使用梯度下降或SMO(Sequential Minimal Optimization,用于解决凸二次规划问题)方法实现目标函数最优求解,获得对应的模型参数。上一节式中的XGB的损失函数包含了函数作为参数,因此不能直接用传统的优化方法在欧拉空间求解最优值,因此需要将目标函数进行更简单的转化,转化与树结构相关的形式:
-
一次性求取所有树模型几乎是不可能,因此可根据加法模型将\(\hat y_i^{(t)}\)拆分为\(\hat y_i^{(t-1)} + f_t(x_i)\),每次迭代只学习一个树模型\(f_t\),利用局部最优求解所有模型,第\(t\)棵树\(f_t\)中的第i个实例的目标函数表示为:
\[\mathcal {L^{(t)}} = \Sigma_{i=1}^n l(y_i, \hat y_i^{(t-1)} + f_t(x_i)) + \Omega(f_t) \] -
根据泰勒公式获取\(\hat y_i^{(t-1)} + f_t(x_i)\)的二阶展示式,则目标函数变换为:
\[\mathcal {L^{(t)}} \simeq \Sigma_{i=1}^n [l(y_i, \hat y_i^{(t-1)}) + g_if_t(x_i) + \frac{1}{2}h_if_t^2(x_i)] + \Omega(f_t) \]其中每个样本的一阶梯度统计量为\(g_i = \frac{\partial l(y_i,\hat y^{(t-1)})}{\partial \hat y^{(t-1)}}\), 二阶梯度统计量为\(h_i = \frac{\partial^2 l(y_i,\hat y^{(t-1)})}{\partial \hat {(y^{(t-1)})^2}}\)。
删除常数项\(l(y_i, \hat y_i^{(t-1)})\)得到的目标函数\(\mathcal {\tilde L^{(t)}}\) 只和树结构 \(f_{t}\)相关:
\[\mathcal {\tilde L^{(t)}} = \Sigma_{i=1}^n [g_if_t(x_i) + \frac{1}{2}h_if_t^2(x_i)] + \Omega(f_t) \] -
将每个样本\(x_i\)映射到对应的叶结点j(\(I_j = \{i|q(x_i) = j\}\)),\(q(x_i) = f_t(x_i) \rightarrow w_j\),得到的目标函数$\mathcal {\tilde L^{(t)}} $:
\[\mathcal {\tilde L^{(t)}} = \Sigma_{j=1}^T [(\Sigma_{i \in I_j} \large{g_i}) \normalsize w_j + \frac{1}{2}(\Sigma_{i \in I_j}h_i+\lambda)w^2_j] + \gamma T \]其中设置模型复杂度为\(\Omega(f) = \gamma T + \frac{1}{2}\lambda||w||^2\),也可使用其它方式,只要能表达模型复杂度即可。
令\(G_j = \Sigma_{i \in I_j}g_i, H_j = \Sigma_{i \in I_j}h_i\),则对于固定的树结构q,可通过微分求极值计算叶结点j对应的最优权重\(w_j^{*}\):
\[\frac {\partial (G_j w_j + \frac{1}{2}w_j^2(H_j + \lambda))}{ \partial w_j} = G_j + w_j(H_j + \lambda) \\ \\ 令\partial (G_j w_j + \frac{1}{2}w_j^2(H_j + \lambda))/ \partial w_j = 0 \\ 0 = G_j + w_j^{*}(H_j + \lambda) \\ w_j^{*} = - \frac{G_j}{H_j + \lambda} \] -
最终将目标函数与结点个数T、样本的一阶统计量\(G_j\)和二阶统计量\(H_j\)连接起来,较为方便计算每个节点的损失值,得到第t棵树\(f_t\)的目标函数简化格式:
\[\mathcal {\tilde L^{(t)}}(q) = -\frac{1}{2} \frac{(G_j)^2}{H_j + \lambda} + \gamma T \]

4. 分叉选择算法
假设\(I_L\)和\(I_R\)是\(I\)在某个内部叶结点拆分后的叶结点空间,即\(I = I_L \bigcup I_R\),则CART树分叉后的结构分数下降值,可认为是分叉后的增益值:
其中
分叉后左叶结点的结构分数:\(G_L = \Sigma_{i \in I_L}g_i, H_L = \Sigma_{i \in I_L}h_i\)
分叉后右叶结点的结构分数:\(G_R = \Sigma_{i \in I_R}g_i, H_j = \Sigma_{i \in I_R}h_i\)
分叉前叶结点的结构分数:\(G = \Sigma_{i \in I}g_i, H = \Sigma_{i \in I}h_i\)
正则项:\(\gamma\),当增益小于\(\gamma\)时,就停止分叉(功能类似于决策树的剪枝功能)。
Algorithm 1:基于贪心算法
精确贪婪算法(exact greedy algorithm):计算所有特征k和所有的特征值拆分所得的结构分数差值Gain,然后选择最优特征和特征值进行拆分,通过局部最优实现全局拆分选择:

其中k表示x的特征,j表示x根据特征k排序后的特征值,为了提高算法效率,会提前对特征k中的特征值j进行排序,使用排序后的特征值进行分叉选择。
与决策树对比如下:

在XGB中,首先让树从根结点开始生长,每进行一次分枝,计算对应的损失函数减少值,如果减少的值小于某个设定的阈值,则树停止增长。
Algorithm 2: 基于分位数对特征值分组
精确贪婪算法(exact greedy algorithm),计算所有特征k和所有的特征值拆分所得的结构分数差值,这种枚举式的方法计算耗费内存,同时效率较低。因此,可考虑使用分位数策略(quantile strategy)对特征值提前进行分组,使用近似计算方法(approximate algorithm)提高计算效率,精度却还是可以和精确贪婪算法能达到一个水平。

AUC比较:

Algorithm 3 :稀疏敏感算法
数据的稀疏性主要包含三种模式:1、数据中存储缺失值;2、数据中存在较多0值;3、数据中存在类似one-hot编码的数据类型。XGB在分组时只处理非缺失的数据,对于缺失值先全部分配到右侧计算得到增益\(Gain_R\),然后再全部分配到左侧计算得到增益\(Gain_L\),比较选取获得最大Gain的方向。

XGB对稀疏数据敏感的方法,计算效率也更快:

5. 效率提升设计
使用块结构存储
进行树模型学习过程中,最耗时的是对各个列特征进行排序,因此为了降低排序的运算消耗,块结构(block structure)从以下几方面进行优化:
- 内存优化:使用内存单元(in-mermory units)以块(block)为单位进行存储,并且块数据使用CSC(compressed column)格式,然后通过相应的特征值进行排序,同时只排序一次,后面的迭代过程可以不断调用。
- 搜索优化:与精确贪婪算法比较,列块并行计算将所有数据存在一个单独块中并通过线性搜索(linear scanning)实现快速分叉。
- 并行计算:各特征可以同步进行统计(collecting statistics)和分叉(split finding)。
- 特征抽样:支持对列(特征)进行子集抽样(column subsampling)。
缓存感知
使用块结构(block structure)在特征值拆分选择上降低了计算的消耗,而缓存感知(cache-aware access)在每个进出中分配内部缓冲区,从里面提取梯度统计值(gradient statistics,g和h值),然后通过小批量(mini-batch)方式进行累计计算得到(G和H值),在数据量较大时,可看到明显提高了计算效率,如下图所示:

块大小(block size)对于计算效率也有很大影响,一般需要与CPU进行匹配,设置太小块无法更有效平行计算,太大块容易造成缓存损失。

核外计算
核外计算(blocks for out-of-core computation)是将数据拆分为多个块(block),并将这些块存储到硬盘(disk),在计算过程中利用一个独立的进程提前从硬盘中提取块进入缓存中,使得计算与硬盘读取并发进行,减少了硬盘读取数据过程等候的时间。
6.时间复杂度比较
假设d为树的深度,K为树的个数,n为样本个数,\(||x||_0\)为非缺失值个数,q为潜在分叉点(比n小)
- 原始的精确贪婪算法(exact greedy algorithm):\(O(Kd||x||_0)logn\)
- 分组的近似算法(approximate algorithm with binary search):\(O(Kd||x||_0)logq\)
- 块结构(block structure):\(O(Kd||x||_0 + ||x||_0logn)\),使用块结构后时间消耗降低到\(O(Kd||x||_0 + ||x||_0logB)\),B为块中最大行数。
$ $
参考资料


浙公网安备 33010602011771号