Boostrap是靴子的带子的意思,名字来源于“pull up your ownboostraps”,意思是通过拉靴子提高自己,本来的意思是不可能发生的事情,但后来发展成通过自己的努力让事情变得更好。放在组合分类器这里,意思就是通过分类器自己提高分类的性能。
Boostrap只是提供了一种组合方法的思想,就是将基分类器的训练结果进行综合分析,而其它的名称如Bagging、Boosting是对组合方法的具体演绎。
Boostrap组合方法分为:Bagging算法与Boosting算法。
在看XGBoost本身的优化内容前,我们先回顾下GBDT的回归算法迭代的流程,对于GBDT的第 t t t 颗决策树,主要是走下面5步:
上面第一步是得到负梯度,或者是泰勒展开式的一阶导数。第二步是第一个优化求解,即基于残差拟合一颗CART回归树,得到 J J J 个叶子节点区域。第三步是第二个优化求解,在第二步优化求解的结果上,对每个节点区域再做一次线性搜索,得到每个叶子节点区域的最优取值。最终得到当前轮的强学习器。
从上面可以看出,我们要求解这个问题,需要求解当前决策树最优的所有 J J J 个叶子节点区域和每个叶子节点区域的最优解 c j ( t ) c^{(t)}_j cj(t)。GBDT采样的方法是分两步走,先求出最优的所有 J J J 个叶子节点区域,再求出每个叶子节点区域的最优解。
对于XGBoost,它期望把第2步和第3步合并在一起做,即一次求解出决策树最优的所有 J J J 个叶子节点区域和每个叶子节点区域的最优解 c j ( t ) c^{(t)}_j cj(t)。
在讨论如何求解前,我们先看看XGBoost的损失函数的形式,在GBDT损失函数 L ( y , F t ( x ) ) = L ( y , F t − 1 ( x ) + h t ( x ) ) \color{violet}{L(y,F_t(\textbf{x}))=L(y,F_{t−1}(\textbf{x})+h_t(\textbf{x}))} L(y,Ft(x))=L(y,Ft−1(x)+ht(x)) 的基础上,我们加入正则化项如下:
Ω ( h t ) = γ J + λ 2 ∑ j = 1 J w t j 2 \Omega(h_t) = \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 Ω(ht)=γJ+2λj=1∑Jwtj2
这里的 J J J 是叶子节点的个数,而 w t j w_{tj} wtj 是第 j j j 个叶子节点的最优值。这里的 w t j w_{tj} wtj 和我们GBDT里使用的 c t j c_{tj} ctj 是一个意思,只是XGBoost的论文里用的是 w w w 表示叶子区域的值,因此这里和论文保持一致。
最终XGBoost的损失函数可以表达为:
L t = ∑ i = 1 m L ( y i , f t − 1 ( x i ) + h t ( x i ) ) + γ J + λ 2 ∑ j = 1 J w t j 2 L_t=\sum\limits_{i=1}^mL(y_i, f_{t-1}(x_i)+ h_t(x_i)) +\gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 Lt=i=1∑mL(yi,ft−1(xi)+ht(xi))+γJ+2λj=1∑Jwtj2
最终我们要极小化上面这个损失函数,得到第 t t t 个决策树最优的所有 J J J 个叶子节点区域和每个叶子节点区域的最优解 w t j w_{tj} wtj。XGBoost没有和GBDT一样去拟合泰勒展开式的一阶导数,而是期望直接基于损失函数的二阶泰勒展开式来求解。
现在我们来看看这个损失函数的二阶泰勒展开式:
L t = ∑ i = 1 m L ( y i , f t − 1 ( x i ) + h t ( x i ) ) + γ J + λ 2 ∑ j = 1 J w t j 2 ≈ ∑ i = 1 m ( L ( y i , f t − 1 ( x i ) ) + ∂ L ( y i , f t − 1 ( x i ) ∂ f t − 1 ( x i ) h t ( x i ) + 1 2 ∂ 2 L ( y i , f t − 1 ( x i ) ∂ f t − 1 2 ( x i ) h t 2 ( x i ) ) + γ J + λ 2 ∑ j = 1 J w t j 2 \begin{aligned} L_t &=\sum\limits_{i=1}^mL(y_i, f_{t-1}(x_i)+ h_t(x_i)) +\gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \\ & \approx\sum\limits_{i=1}^m( L(y_i, f_{t-1}(x_i)) + \frac{\partial L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}(x_i)}h_t(x_i) + \frac{1}{2}\frac{\partial^2 L(y_i, f_{t-1}(x_i)}{\partial f_{t-1}^2(x_i)}h_t^2(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \end{aligned} Lt=i=1∑mL(yi,ft−1(xi)+ht(xi))+γJ+2λj=1∑Jwtj2≈i=1∑m(L(yi,ft−1(xi))+∂ft−1(xi)∂L(yi,ft−1(xi)ht(xi)+21∂ft−12(xi)∂2L(yi,ft−1(xi)ht2(xi))+γJ+2λj=1∑Jwtj2
为了方便,我们把第i个样本在第t个弱学习器的一阶和二阶导数分别记为:
g t i = ∂ L ( y i , f t − 1 ( x i ) ∂ f t − 1 ( x i ) , h t i = ∂ 2 L ( y i , f t − 1 ( x i ) ∂ f t − 1 2 ( x i ) g_{ti} = \frac{\partial L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}(x_i)}, \; h_{ti} =\frac{\partial^2 L(y_i, f_{t-1}(x_i)}{\partial f_{t-1}^2(x_i)} gti=∂ft−1(xi)∂L(yi,ft−1(xi),hti=∂ft−12(xi)∂2L(yi,ft−1(xi)
则我们的损失函数现在可以表达为:
L t ≈ ∑ i = 1 m ( L ( y i , f t − 1 ( x i ) ) + g t i h t ( x i ) + 1 2 h t i h t 2 ( x i ) ) + γ J + λ 2 ∑ j = 1 J w t j 2 L_t \approx\sum\limits_{i=1}^m( L(y_i, f_{t-1}(x_i)) + g_{ti}h_t(x_i) + \frac{1}{2} h_{ti}h_t^2(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 Lt≈i=1∑m(L(yi,ft−1(xi))+gtiht(xi)+21htiht2(xi))+γJ+2λj=1∑Jwtj2
损失函数里面 L ( y i , f t − 1 ( x i ) ) L(y_i,f_{t−1}(x_i)) L(yi,ft−1(xi))是常数,对最小化无影响,可以去掉,同时由于每个决策树的第 j j j 个叶子节点的取值最终会是同一个值 w t j w_{tj} wtj,因此我们的损失函数可以继续化简。
L t ≈ ∑ i = 1 m [ g t i h t ( x i ) + 1 2 h t i h t 2 ( x i ) ] + γ J + λ 2 ∑ j = 1 J w t j 2 = ∑ j = 1 J ( ∑ x i ∈ R t j g t i w t j + 1 2 ∑ x i ∈ R t j h t i w t j 2 ) + γ J + λ 2 ∑ j = 1 J w t j 2 = ∑ j = 1 J [ ( ∑ x i ∈ R t j g t i ) w t j + 1 2 ( ∑ x i ∈ R t j h t i + λ ) w t j 2 ] + γ J \begin{aligned} L_t & \approx\sum\limits_{i=1}^m[g_{ti}h_t(x_i) + \frac{1}{2} h_{ti}h_t^2(x_i)] + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2\\ & = \sum\limits_{j=1}^J (\sum\limits_{x_i \in R_{tj}}g_{ti}w_{tj} + \frac{1}{2} \sum\limits_{x_i \in R_{tj}}h_{ti}w_{tj}^2) +\gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \\ & =\sum\limits_{j=1}^J [(\sum\limits_{x_i \in R_{tj}}g_{ti})w_{tj} +\frac{1}{2}( \sum\limits_{x_i \in R_{tj}}h_{ti}+ \lambda) w_{tj}^2] +\gamma J \end{aligned} Lt≈i=1∑m[gtiht(xi)+21htiht2(xi)]+γJ+2λj=1∑Jwtj2=j=1∑J(xi∈Rtj∑gtiwtj+21xi∈Rtj∑htiwtj2)+γJ+2λj=1∑Jwtj2=j=1∑J[(xi∈Rtj∑gti)wtj+21(xi∈Rtj∑hti+λ)wtj2]+γJ
我们把每个叶子节点区域样本的一阶和二阶导数的和单独表示如下:
G t j = ∑ x i ∈ R t j g t i , H t j = ∑ x i ∈ R t j h t i G_{tj} = \sum\limits_{x_i \in R_{tj}}g_{ti},\; H_{tj} = \sum\limits_{x_i \in R_{tj}}h_{ti} Gtj=xi∈Rtj∑gti,Htj=xi∈Rtj∑hti
最终损失函数的形式可以表示为:
L t = ∑ j = 1 J [ G t j w t j + 1 2 ( H t j + λ ) w t j 2 ] + γ J L_t = \sum\limits_{j=1}^J [G_{tj}w_{tj} +\frac{1}{2}(H_{tj}+\lambda)w_{tj}^2] +\gamma J Lt=j=1∑J[Gtjwtj+21(Htj+λ)wtj2]+γJ
现在我们得到了最终的损失函数,那么回到前面讲到的问题,我们如何一次求解出决策树最优的所有 J J J 个叶子节点区域和每个叶子节点区域的最优解 w t j w_{tj} wtj 呢?
关于如何一次求解出决策树最优的所有 J J J 个叶子节点区域和每个叶子节点区域的最优解 w t j w_{tj} wtj,我们可以把它拆分成2个问题:
对于第一个问题,其实是比较简单的,我们直接基于损失函数对 w t j w_{tj} wtj 求导并令导数为0即可。这样我们得到叶子节点区域的最优解 w t j w_{tj} wtj 表达式为:
w t j = − G t j H t j + λ w_{tj} = - \frac{G_{tj}}{H_{tj} + \lambda} wtj=−Htj+λGtj
我们已经解决了第一个问题。现在来看XGBoost优化拆分出的第二个问题:如何选择哪个特征和特征值进行分裂,使最终我们的损失函数 L t L_t Lt 最小?
在GBDT里面,我们是直接拟合的CART回归树,所以树节点分裂使用的是均方误差。XGBoost这里不使用均方误差,而是使用贪心法,即每次分裂都期望最小化我们的损失函数的误差。
注意到在我们 w t j w_{tj} wtj 取最优解的时候,原损失函数对应的表达式为:
L t = − 1 2 ∑ j = 1 J G t j 2 H t j + λ + γ J L_t = -\frac{1}{2}\sum\limits_{j=1}^J\frac{G_{tj}^2}{H_{tj} + \lambda} +\gamma J Lt=−21j=1∑JHtj+λGtj2+γJ
如果我们每次做左右子树分裂时,可以最大程度的减少损失函数的损失就最好了。也就是说,假设当前节点左右子树的一阶二阶导数和为 G L G_L GL, H L H_L HL, G R G_R GR, H L H_L HL, 则我们期望最大化下式:
− 1 2 ( G L + G R ) 2 H L + H R + λ + γ J − ( − 1 2 G L 2 H L + λ − 1 2 G R 2 H R + λ + γ ( J + 1 ) ) -\frac{1}{2}\frac{(G_L+G_R)^2}{H_L+H_R+ \lambda} +\gamma J -( -\frac{1}{2}\frac{G_L^2}{H_L + \lambda} -\frac{1}{2}\frac{G_{R}^2}{H_{R} + \lambda}+ \gamma (J+1) ) −21HL+HR+λ(GL+GR)2+γJ−(−21HL+λGL2−21HR+λGR2+γ(J+1))
整理下上式后,我们期望最大化的是:
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 + λ − γ \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 21HL+λGL2+21HR+λGR2−21HL+HR+λ(GL+GR)2−γ
也就是说,我们的决策树分裂标准不再使用CART回归树的均方误差,而是上式了。
具体如何分裂呢?举个简单的年龄特征的例子如下,假设我们选择年龄这个 特征的值 a a a 作为决策树的分裂标准,则可以得到左子树2个人,右子树三个人,这样可以分别计算出左右子树的一阶和二阶导数和,进而求出最终的上式的值。
然后我们使用其他的不是值 a a a 的划分标准,可以得到其他组合的一阶和二阶导数和,进而求出上式的值。最终我们找出可以使上式最大的组合,以它对应的特征值来分裂子树。
至此,我们解决了XGBoost的2个优化子问题的求解方法。
这里我们总结下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 T T, 损失函数 L L L, 正则化系数 λ , γ \lambda,\gamma λ,γ。
输出是强学习器 f ( x ) f(x) f(x)
对迭代轮数 t=1,2,…T 有:
在第2,3,4节我们重点讨论了XGBoost算法本身的优化,在这里我们再来看看XGBoost算法运行效率的优化。
大家知道,Boosting算法的弱学习器是没法并行迭代的,但是单个弱学习器里面最耗时的是决策树的分裂过程,XGBoost针对这个分裂做了比较大的并行优化。对于不同的特征的特征划分点,XGBoost分别在不同的线程中并行选择分裂的最大增益。
同时,对训练的每个特征排序并且以块的的结构存储在内存中,方便后面迭代重复使用,减少计算量。计算量的减少参见上面第4节的算法流程,首先默认所有的样本都在右子树,然后从小到大迭代,依次放入左子树,并寻找最优的分裂点。这样做可以减少很多不必要的比较。
具体的过程如下图所示:
此外,通过设置合理的分块的大小,充分利用了CPU缓存进行读取加速(cache-aware access)。使得数据读取的速度更快。另外,通过将分块进行压缩(block compressoin)并存储到硬盘上,并且通过将分块分区到多个硬盘上实现了更大的IO。
最后我们再来看看XGBoost在算法健壮性的优化,除了上面讲到的正则化项提高算法的泛化能力外,XGBoost还对特征的缺失值做了处理。
XGBoost没有假设缺失值一定进入左子树还是右子树,则是尝试通过枚举所有缺失值在当前节点是进入左子树,还是进入右子树更优来决定一个处理缺失值默认的方向,这样处理起来更加的灵活和合理。
也就是说,上面第4节的算法的步骤a),b.1)和b.2)会执行2次,第一次假设特征k所有有缺失值的样本都走左子树,第二次假设特征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=G−GR 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=H−HR
不考虑深度学习,则XGBoost是算法竞赛中最热门的算法,它将GBDT的优化走向了一个极致。当然,后续微软又出了LightGBM,在内存占用和运行速度上又做了不少优化,但是从算法本身来说,优化点则并没有XGBoost多。
何时使用XGBoost,何时使用LightGBM呢?个人建议是优先选择XGBoost,毕竟调优经验比较多一些,可以参考的资料也多一些。如果你使用XGBoost遇到的内存占用或者运行速度问题,那么尝试LightGBM是个不错的选择。
当模型没有达到预期效果的时候,XGBoost就是数据科学家的最终武器。XGboost是一个高度复杂的算法,有足够的能力去学习数据的各种各样的不规则特征。
用XGBoost建模很简单,但是提升XGBoost的模型效果却需要很多的努力。因为这个算法使用了多维的参数。为了提升模型效果,调参就不可避免,但是想要知道参数怎么调,什么样的参数能够得出较优的模型输出就很困难了。
XGBoost的变量类型有三类:
虽然XGBoost有两种boosters,作者在参数这一块只讨论了tree booster,原因是tree booster的表现总是好于 linear booster
此类变量用于定义优化目标每一次计算的需要用到的变量
有些变量在Python的sklearn的接口中对应命名如下:
可能感到困惑的是这里并没有像GBM中一样提及n_estimators,这个参数实际存在于XGBClassifier中,但实际是通过num_boosting_rounds在我们调用fit函数事来体现的。
何谓样本不平衡——简单来说就是数据集中负样本的数量远远大于正样本的数量。在这个情况下,模型就会倾向于把样本预测为负样本,因为这是最便捷的降低损失、提高模型准确率的方法。例如:有一个正样本数量为1,负样本数量为99的数据集,模型就算无脑地把全部样本预测为负样本也能达到99%的准确度,试想有这么一个分类器,每次我们把数据喂‘给它时,在不调整阈值的情况下,它都倾向于把测试集的样本预测为负样本,你觉得这样的分类器还会是一个好的分类器吗?
在对不平衡数据进行训练时,通常会考虑一下怎么处理不平衡数据能使训练出来的结果较好。
在用xgboost训练二分类模型时,除了直接使用过采样和下采样,xgboost接口还提供一些处理不平衡数据的方法,有scale_pos_weight参数的设置,还有给样本赋予一定的权重。
梯度提升树(GBDT)原理小结
XGBoost算法原理小结
XGBoost算法原理
XGBoost算法原理简介及调参01
XGBoost算法原理简介及调参02-Python代码
XGBoost20题
机器学习算法中 GBDT 和 XGBOOST 的区别有哪些?
xgboost是用二阶泰勒展开的优势在哪?
通俗的将Xgboost的原理讲明白
[机器学习] XGBoost 样本不平衡问题
分类案例:XGB中的样本不均衡问题