XGBoost 极限提升树 (Extreme Gradient Boosting)

Posted Wing以一

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XGBoost 极限提升树 (Extreme Gradient Boosting)相关的知识,希望对你有一定的参考价值。

XGBoost是一个以提升树为核心的算法系统,它覆盖了至少3+建树流程、10+损失函数,可以实现各种类型的梯度提升树。同时,XGBoost天生被设计成支持巨量数据,因此可以自由接入GPU/分布式/数据库等系统、还创新了众多工程上对传统提升算法进行加速的新方法。

【本文给对于XGBoost零基础de人,跟着完成所有实验,必有收获。后续会用实际算法竞赛题目来巩固(尽量不拖更)】

GBDT ? 如果你一无所知,请先看如下链接再开始XBGBoost。GBDT 梯度提升树(Gradient Boosting Decision Tree)(万字全解)_Wing以一的博客-CSDN博客

目录

一、XGBoost基本实现

1、基础思想

(1)经验风险和结构分析的平衡

(2)降低复杂度,提升模型运行速度,便于处理大体量的数据

2、程序实现(回归)

(1)读取数据

(2)设置参数

(3)训练模型

(4)预测结果

(5)使用交叉验证进行训练

 3、程序实现(分类)

(1)`objective`参数的选择

(2)读取数据

(3)设置参数

(4)训练模型

(5)预测结果与转换

(6)交叉验证

二、XGBoost参数详解

1、用于迭代过程的参数

 2、结构风险相关参数

3、弱评估器及相关参数

(1)  dart

(2)gbtree

(3)剪枝

(4)抽样

4、训练相关参数


一、XGBoost基本实现

1、基础思想

Boosting算法基本建模流程:

依据上一个弱评估器的结果,计算损失函数

使用自适应地影响下一个弱评估器的构建。

集成模型输出的结果,受到整体所有弱评估器~ 的影响。

(1)经验风险和结构分析的平衡

树模型的学习能力与过拟合风险之间的平衡,就是预测精确性与模型复杂度之间的平衡,也是经验风险与结构风险之间的平衡。XGBoost中,每一次建树都会对精确性与复杂度之间进行衡量。

  • 新的不纯度衡量指标,考虑复杂度

在上文的GBDT中,使用了弗里德曼均方误差friedman_mse来衡量叶子的不纯度,有丰富的剪枝参数可以选择,获得较高的精度。可是我们也在这个过程中建立了190棵树,深度为29,它的复杂度太高了。

XGBoost重新设定了分枝指标结构分数Structure Score,也被称为质量分数Quality Score),以及基于结构分数的结构分数增益Gain of structure score),结构分数增益可以使得决策树向整体结构更简单的方向生长。

  • 目标函数中增加结构风险项

XGBoost为损失函数加入结构风险项,构成目标函数

GBDT当中,我们的目标是找到损失函数的最小值,也就是让预测结果与真实结果差异最小,这一流程只关心精确性、不关心复杂度和过拟合情况。如果出现了过拟合,我们通过调节树结构相关的参数(例如`max_depth`,`min_impurity_decrease`)来限制过拟合。

XGBoost的目标是找到目标函数的最小值,会优先利用结构风险中的参数来控制过拟合。

(2)降低复杂度,提升模型运行速度,便于处理大体量的数据

使用估计贪婪算法、平行学习、分位数草图算法等方法构建了适用于大数据的全新建树流程。

使用感知缓存访问技术与核外计算技术,提升算法在硬件上的运算性能。

引入Dropout技术,为整体建树流程增加更多随机性、让算法适应更大数据。

2、程序实现(回归)



Python API Reference — xgboost 1.7.1 documentation文档里有所有你需要的。(以下截图均来自官网,图中已标注在官网的具体位置,可自己对应查看)

这里以先回归模型为例。

(1)读取数据

 如果我们想要更快的运行速度,并后续在GPU说进行相关运行,我们就必须使用XGBoost自定义的数据结构DMatrix。DMatrix的结构一旦生成无法查看无法改动!数据预处理需要在转换为DMatrix之前完成。如果我们有划分训练集和测试集,则需要分别将训练集和测试集转换为DMatrix。

#一、读取数据
data = pd.read_csv(r"E:\\House Price\\train_encode.csv",index_col=0)
#回归数据
X = data.iloc[:,:-1]
y = data.iloc[:,-1]
#转换数据格式
data_xgb = xgb.DMatrix(X,y)
#如果有分割训练集和测试集
from sklearn.model_selection import train_test_split
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,y,test_size=0.2,random_state=1409)
dtrain = xgb.DMatrix(Xtrain,Ytrain)
dtest = xgb.DMatrix(Xtest,Ytest)

(2)设置参数

#二、设置参数
params = "max_depth":5,"seed":1409    #通常param字典中包含较多参数

 除了建树棵树、提前停止这两个关键元素,其他参数基本都被设置在params当中。

(3)训练模型

#三、训练模型
reg = xgb.train(params, data_xgb, num_boost_round=100)

(4)预测结果

#四、预测结果
y_pred = reg.predict(data_xgb)
print(y_pred)
#用RMSE进行评估
print(MSE(y,y_pred,squared=False))#RMSE

(5)使用交叉验证进行训练

xgb.cv()进行训练并不会返回模型!!

#用交叉验证进行训练
params = "max_depth":5,"seed":1409
result = xgb.cv(params,data_xgb,num_boost_round=100
                ,nfold=5 #补充交叉验证中所需的参数,nfold=5表示5折交叉验证
                ,seed=1409 #交叉验证的随机数种子,params中的是管理boosting过程的随机数种子
               )
print(result)

每一行代表了每次迭代后进行交叉验证的结果的均值。 这样的数据集十分适合绘图展示。

 3、程序实现(分类)

XGBoost默认实现回归算法,如果要执行分类需要主动声明。在params中填写objective参数,xgboost会根据所使用的损失函数来判断任务类型。

(1)`objective`参数的选择

  • 用于回归

reg:squarederror:平方损失,即,其中1/2是为了计算简便

reg:squaredlogerror:平方对数损失,即,其中1/2是为了计算简便

  • 用于分类

binary:logistic:二分类交叉熵损失,使用该损失时`predict`接口输出概率。

binary:logitraw:二分类交叉熵损失,使用该损失时`predict`接输出执行sigmoid变化之前的值。

multi:softmax:多分类交叉熵损失,使用该损失时`predict`接口输出具体的类别。

multi:softprob:多分类交叉熵,适用该损失时`predict`接口输出每个样本每个类别下的概率。

  • 用于排序

rank:pairwise:采用评分机制进行训练

……

(2)读取数据

#一、读取数据
#导入2个分类数据集:乳腺癌数据集与手写数字数据集
from sklearn.datasets import load_breast_cancer, load_digits

#二分类数据
X_binary = load_breast_cancer().data
y_binary = load_breast_cancer().target
data_binary = xgb.DMatrix(X_binary,y_binary)

#多分类数据
X_multi = load_digits().data
y_multi = load_digits().target
data_multi = xgb.DMatrix(X_multi, y_multi)

(3)设置参数

#二、设置参数
params1 = "seed":1409, "objective":"binary:logistic"
           ,"eval_metric":"logloss" #二分类交叉熵损失
          
#二分类直接返回概率,不返回类别
params2 = "seed":1409, "objective":"multi:softmax"
           ,"eval_metric":"mlogloss" #多分类交叉熵损失
           ,"num_class":10
#多分类,选择`multi:softmax`时返回具体类别,也可以选择`multi:softprob`返回概率。

(4)训练模型

#三、训练模型
clf_binary = xgb.train(params1, data_binary, num_boost_round=100)
clf_multi = xgb.train(params2, data_multi, num_boost_round=100)

(5)预测结果与转换

#四、预测结果
y_pred_binary = clf_binary.predict(data_binary)
#print(y_pred_binary)
y_pred_multi = clf_multi.predict(data_multi)
print(y_pred_multi )
#预测返回概率后的进一步处理
#二分类转换为类别
y_pred_binary_exchange = (y_pred_binary > 0.5).astype("int")
print(y_pred_binary_exchange)
#二分类计算准确率
ACC_binary= ACC(y_binary,(y_pred_binary > 0.5).astype(int))
print(ACC_binary)
ACC_multi = ACC(y_multi, y_pred_multi)
print(ACC_multi)

(6)交叉验证

params2 = "seed":1409
           , "objective":"multi:softmax" 
           , "num_class":10
result = xgb.cv(params2,data_multi,num_boost_round=100
                ,metrics = ("mlogloss") #交叉验证的评估指标
                ,nfold=5
                ,seed=1409
               )
print(result)

二、XGBoost参数详解

XGBoost原始论文:https://arxiv.org/pdf/1603.02754v1.pdf

1、用于迭代过程的参数

对于样本集成算法当中一共有棵树,则参数`num_boost_round`的取值为K。假设现在正在建立第个弱评估器,则第个弱评估器上的结果可以表示为。假设整个Boosting算法对样本输出的结果为,则该结果一般可以被表示为k=1~k=K过程当中,所有弱评估器结果的加权求和(XGBoost算法不计算树权重,故去除权重项):

对于第k次迭代来说,则有:

将本轮建好的决策树加入之前的建树结果时,可以增加参数,表示为第k棵树加入整体集成算法时的学习率(参数eta)

在XGBoost当中,`num_boost_round`的默认值为10,`eta`的默认值为0.3。我们需要在这两者之间作出调整权衡。eta较大则控制的增长速度较快,可能在更少的num_boost_round就停止了训练。

建立第一个弱评估器时有:

没有第0棵树的,因此的值在数学过程及算法具体实现过程中都需要进行单独的确定,而这个值就由`base_score`确定。`base_score`可以为任何数值,但不同于之前的GBDT,无法将随机森林这样的评估器作为(在迭代次数和数据量都充足的情况下,一般不调整此参数)。

`max_delta_step`参数代表了每次迭代时被允许的最大。被设置为0,则说明不对每次迭代的大小做限制,如果该参数被设置为正数C,则代表,否则执行:。(样本极度不均衡可以试着调一调,通常不改动)

 2、结构风险相关参数

xgboost向着令目标函数最小化的方向运行。在XGBoost的定义中,目标函数是针对每一棵树的,而不是针对一个样本或整个算法

对任意树来说,目标函数有两个组成部分,一部分是任意可微的损失函数,它控制模型的经验风险。从数值上它等于现在树上所有样本上损失函数之和,其中单一样本的损失为。另一部分是控制模型复杂度的,它控制当前树的结构风险

表示现在这棵树上一共使用了M个样本,表示单一样本的损失函数。当模型迭代完毕之后,最后一棵树上的目标函数就是整个XGBoost算法的目标函数。

 结构风险又由两部分组成,一部分是控制树结构的,另一部分则是正则项:

可以自由设置的这三个系数都与结构风险有关,这三个系数也正对应着xgboost中的三个参数:`gamma`,`alpha`与`lambda`。

这三个参数如果调大的话,均可以达到控制过拟合的效果。

`gamma`:乘在一棵树的叶子总量之前,依照叶子总量对目标函数施加惩罚的系数,默认值为0,可填写任何[0, ∞]之间的数字。

`alpha`与`lambda`:乘在正则项之前,依照叶子权重的大小对目标函数施加惩罚的系数,也就是正则项系数。`lambda`的默认值为1,`alpha`的默认值为0,在原论文中是使用了L2正则化。

在实际调参过程中,这三个没有上限的参数,必须关注它们的敏感度问题。(若轻微的参数变化对模型产生较大的影响,则称这个参数敏感)。

当树的结构十分复杂的情况下,叶子总量T是一个很大的值(1000000),对应的参数gamma轻微的变化就会对模型产生较大的影响。这个时候如果原始标签的数值很小(0-1),`lambda`和`alpha`就不敏感了,因为这个参数要到以上,才能对模型产生可见的影响。

反之,`lambda`和`alpha`就会敏感。

这三个参数敏感度的实验代码在文末链接下载,此处不作展示。

3、弱评估器及相关参数

参数`booster`:使用哪种弱评估器。 可以输入"gbtree"、"gblinear"或者"dart"。每一种弱评估器都有自己的params列表。

  • "gbtree":使用遵循XGBoost规则的CART树,又被称为“XGBoost独有树”,XGBoost Unique Tree。
  • "dart":使用抛弃提升树,DART(Dropout Multiple Additive Regression Tree)在建树过程中会随机抛弃一些树的结果,可以更好地防止过拟合。在数据量巨大、过拟合容易产生时经常被使用,但可能会伤害模型的学习能力,同时可能会需要更长的迭代时间。
  • "gblinear":使用线性模型,当弱评估器类型是"gblinear"而损失函数是MSE时,表示使用xgboost方法来集成线性回归。当弱评估器类型是"gblinear"而损失函数是交叉熵损失时,则代表使用xgboost来集成逻辑回归。

(1)  dart

原始论文:https://arxiv.org/abs/1505.01866

DART树在

每一次迭代前都会随机地抛弃部份树,即不让这些树参与的计算,这种随机放弃的方式被叫做“Dropout”。因为是随机抛弃的,它就可以削弱前端树(刚开始迭代,效果明显的树)的影响力,大幅提升抗过拟合的能力。

 举个栗子:假如我们已经建立了四棵树如下

k = 1k = 2k = 3k =4
10.90.80.6

建立第5棵树的时候 = 1+0.9+0.8+0.6

采用dart的方法随机抛弃一棵的话可能= 1+0.9+0.6

通过对的影响,影响了迭代的目标函数,影响下一次迭代结果。

 rate_dropone_drop同时设定时,实际执行时取较大的。

sample_type中"uniform":表示均匀不放回抽样;"weighted":表示按照每棵树的权重(整棵树上所有叶子权重之和)进行有权重的不放回抽样。

注意,该不放回是指在一次迭代中不放回。每一次迭代中的抛弃是相互独立的,因此每一次抛弃都是从所有树中进行抛弃。上一轮迭代中被抛弃的树在下一轮迭代中可能被包括。

normalize_type中"tree"(默认),表示新生成的树的权重等于所有被抛弃的树的权重的均值;"forest",表示新生成的树的权重等于所有被抛弃的树的权重之和。

 dart实验的相关代码,见文末链接。py文件已清楚注释,此处不作展示。

dart在对抗过拟合方面有较好的效果,但这是以降低模型学习能力为代价的。

(2)gbtree

CART树:向着叶子质量提升/不纯度下降的方向分枝、并且每一层都是二叉树。

XGBoost只有一个自定的分枝指标:结构分数(Structure Score)与结构分数增益(Gain of Structure Score)(也被叫做结构分数之差),更大程度地保证了CART树向减小目标函数的方向生长。

 以上是原论文中的一些标注。

结构分数增益Gain = 左节点的结构分数 + 右节点的结构分数 - 父节点的结构分数

(不同于之前的GBDT,无论不纯度衡量指标是基尼系数还是信息熵,随着树的逐步建立,不纯度是逐渐降低的,我们按照不纯度变化最大来进行分支)

在树生长的过程中,选择Gain最大的点进行分枝。(结构分数增益越大,分支结果越好)

(信息熵、基尼系数只评估某一个特定节点如果左右节点一个为1一个为5,我们可以说左节点更好;但结构分数只能够评估结构本身的优劣,无论左右节点结构分数哪个更大都不能评估左右节点的优劣。)

原论文中上图公式6利用一棵树上所有叶子的结构分数之和来评估整棵树的结构的优劣。

(3)剪枝

min_child_weight:是任意节点上所允许的最小的值。如果一个节点上的小于该参数中设置的值,该节点就会被剪枝。min_child_weight越大越不容易过拟合,但学习能力越弱。

看看在原始论文中标注的结构分数的分母:

 如果损失函数为则求二阶导后,任意样本的都是1,求和后就就是该叶子节点上的样本总量。

gamma:在目标函数中是叶子数量T前的系数,放大gamma可以将目标函数的重点转移至结构风险,从而控制过拟合。

结构分数增益:

XGBoost中任意结构的分数增益不能为负,任意增益为负的节点都会被剪枝.

gamma越大越不容易过拟合,但学习能力越弱。

lambdaalpha:正则化系数,同时也位于结构分数中不同位置,间接影响树的生长和分枝。

使用L2正则化时,结构分数为:

当lambda越大,结构分数会越小,参数gamma的重要性会增加,模型整体的剪枝会变得更加严格。同时,它也可以通过目标函数将模型学习的重点引向结构风险。

使用L1正则化时,结构分数为:

当alpha越大时,结构分数会越大,参`gamma的重要性会被缩小,模型整体的剪枝会变得更宽松。同时,它也可以通过目标函数将模型学习的重点引向结构风险。

通常正则化我们只选其一进行使用,如果L2正则化本身不足以抵抗过拟合的时候,可以尝试L1正则化和L2正则化一起使用。但对于模型的影响太复杂了,不好调参。

(4)抽样

对样本和特征进行抽样来增加弱评估器多样性、从而控制过拟合。

subsample:对样本进行抽样的比例,默认为1,可输入(0,1]之间的任何浮点数。输入0.6,则表示随机抽样60%的样本进行建树。

XGBoost中的样本抽样是不放回抽样,不会产生袋外数据,样本量较少的情况下保持为1,不进行抽样。

sampling_method`:对样本进行抽样时所使用的抽样方法,默认均匀抽样。输入"uniform":表示使用均匀抽样,此时subsample最好大于0.5。("gradient_based":表示使用有权重的抽样,暂时没用过)

对XGBoost来说,特征的抽样可以发生在建树之前(用`colsample_bytree`设置)、生长出新的一层树之前(用`colsample_bylevel`设置)、或者每个节点分枝之前(用`colsample_bynode`设置)。

三者之间会互相影响,全特征集 >= 建树所用的特征子集 >= 建立每一层所用的特征子集 >= 每个节点分枝时所使用的特征子集

(总计16个特征,colsample_bytree=0.5,从中选了8个特征,colsample_bylevel=0.5从8个里面再选4个,colsample_bynode=0.5从4个里面再选2个)不建议三个同时使用哦。、

大部分时候我们都会使用贪婪树来运行XGBoost算法,但在XGBoost当中还有其他几种不同的建树模式,包括基于直方图的估计贪婪树(approx greedy tree)、快速直方图贪婪树(Fast Histogram Approximate Greedy Tree)、以及基于GPU运行的快速直方图贪婪树等。

4、训练相关参数

early_stopping_rounds:位于`xgb.train`方法当中。如果规定的评估指标(evals中进行设置)不能连续`early_stopping_rounds`次迭代提升,那就触发提前停止。

evals:位于`xgb.train`方法当中,用于规定训练当中所使用的评估指标,一般都与损失函数保持一致,也可选择与损失函数不同的指标。

verbosity:用于打印训练流程和训练结果的参数。verbosity中设置数字[0,1,2,3],参数默认值为1。0:不打印任何内容;1:表示如果有警告,打印警告;2:打印建树的全部信息;3:正在debug,打印更多的信息。

scale_pos_weight:负正样本比值,用来调节样本不均衡。

nthread:允许并行的最大线程数,xgboost在默认运行时会占用大量资源。

以上是关于XGBoost 极限提升树 (Extreme Gradient Boosting)的主要内容,如果未能解决你的问题,请参考以下文章

XP的极限编程(eXtreme Programming)

极限编程XP(Extreme Programming)

从提升树到 XGBoost, 原理简介

机器学习之路: python 实践 提升树 XGBoost 分类器

深度 | 对比TensorFlow提升树与XGBoost:我们该使用怎样的梯度提升方法

机器学习 gbdt-xgboost 决策树提升