XGBoost是boosting算法的其中一种。Boosting算法的思想是将许多弱分类器集成在一起形成一个强分类器。因为XGBoost是一种提升树模型,该算法思想就是不断地添加树,不断地进行特征分裂来生长一棵树,每次添加一个树,其实是学习一个新函数,去拟合上次预测的残差。
当我们训练完成得到k棵树,我们要预测一个样本的分数,其实就是根据这个样本的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点就对应一个分数,最后只需要将每棵树对应的分数加起来就是该样本的预测值。所以它是将许多树模型集成在一起,形成一个很强的分类器。而所用到的树模型则是CART回归树模型。讲解其原理前,先讲解一下CART回归树。
XGBoost支持的基分类器包括决策树和线性模型,我们这里只讨论更常见的基于树的情况。为防止过拟合,XGBoost设置了基于树的复杂度作为正则项:
XGBoost的子模型树和决策树模型一样,要依赖节点递归分裂的贪心准则来实现树的生成。除此外,XGBoost还支持近似算法,解决数据量过大超过内存、或有并行计算需求的情况。
基本思路和CART一样,对特征值排序后遍历划分点,将其中最优的分裂收益作为该特征的分裂收益,选取具有最优分裂收益的特征作为当前节点的划分特征,按其最优划分点进行二叉划分,得到左右子树。
上图是一次节点分裂过程,很自然地,分裂收益是树A的评分减去树B的评分。由(4),虚线框外的叶节点,即非分裂节点的评分均被抵消,只留下分裂后的LR节点和分裂前的S节点进行比较,因此分裂收益的表达式为:
XGBoost还提供了上述贪心准则的近似版本,简言之,将特征分位数作为划分候选点。 这样将划分候选点集合由全样本间的遍历缩减到了几个分位数之间的遍历。
具体而言,特征分位数的选取有global和local两种可选策略:global在全体样本上的特征值中选取,在根节点分裂之前进行一次即可;local则是在待分裂节点包含的样本特征值上选取,每个节点分裂前都要进行。通常,global由于只能划分一次,其划分粒度需要更细。
在XGB原始论文中,作者在Higgs Boson数据集上比较了精确贪心准则、global近似和local近似三类配置的测试集AUC,用eps代表取分位点的粒度,如eps=0.25代表将数据集划分为1/0.25=4个buckets,发现global(eps=0.05)和local(eps=0.3)均能达到和精确贪心准则几乎相同的性能。
这三类配置在XGBoost包均有支持。
XGBoost还引入了两项特性:列采样和学习率。
列采样,即随机森林中的做法,每次节点分裂的待选特征集合不是剩下的全部特征,而是剩下特征的一个子集。是为了更好地对抗过拟合(我不是很清楚GBDT中列采样降低过拟合的理论依据。原文这里提到的动机是某GBDT的软件用户反馈列采样比行采样更能对抗过拟合),还能减少计算开销。
学习率,或者叫步长、shrinkage,是在每个子模型前(即在每个叶节点的回归值上)乘上该系数,削弱每颗树的影响,使得迭代更稳定。可以类比梯度下降中的学习率。XGBoost默认设定为0.3。
缺失值应对策略是算法需要考虑的。特征稀疏问题也同样需要考虑,如部分特征中出现大量的0或干脆是one-hot encoding这种情况。XGBoost用稀疏感知策略来同时处理这两个问题:概括地说,将缺失值和稀疏0值等同视作缺失值,再将这些缺失值“绑定”在一起,分裂节点的遍历会跳过缺失值的整体。这样大大提高了运算效率。
0值在XGB中被处理为数值意义上的0还是NA,不同平台上的默认设置不同,可参考本处。总的来说需要结合具体平台的设置,预处理区分开作为数值的0(不应该被处理为NA)和作为稀疏值的0(应该被处理为NA)。
分裂节点依然通过遍历得到,NA的方向有两种情况,在此基础上对非缺失值进行切分遍历。或者可以理解NA被分到一个固定方向,非缺失值在升序和降序两种情况下进行切分遍历。
如上图所示,若某个特征值取值为1,2,5和大量的NA,XGBoost会遍历以上6种情况(3个非缺失值的切分点 × 缺失值的两个方向),最大的分裂收益就是本特征上的分裂收益,同时,NA将被分到右节点。
XGBoost将每一列特征提前进行排序,以块(Block)的形式储存在缓存中,并以索引将特征值和梯度统计量
对应起来,每次节点分裂时会重复调用排好序的块。而且不同特征会分布在独立的块中,因此可以进行分布式或多线程的计算。
特征值排序后通过索引来取梯度
会导致访问的内存空间不一致,进而降低缓存的命中率,影响算法效率。为解决这个问题,XGBoost为每个线程分配一个单独的连续缓存区,用来存放梯度信息。
数据量过大时,不能同时全部载入内存。XGBoost将数据分为多个blocks并储存在硬盘中,使用一个独立的线程专门从磁盘中读取数据到内存中,实现计算和读取数据的同时进行。为了进一步提高磁盘读取数据性能,XGBoost还使用了两种方法:一是通过压缩block,用解压缩的开销换取磁盘读取的开销;二是将block分散储存在多个磁盘中,有助于提高磁盘吞吐量。
1.使用许多策略去防止过拟合,如:正则化项、Shrinkage and Column Subsampling等。
正则化:XGBoost 在目标函数中加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、叶子节点权重的 L2 范式。正则项降低了模型的方差,使学习出来的模型更加简单,有助于防止过拟合;
Shrinkage(缩减):相当于学习速率。XGBoost 在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间;
列抽样:XGBoost 借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算;
2. 精度更高:GBDT 只用到一阶泰勒展开,而 XGBoost 对损失函数进行了二阶泰勒展开。XGBoost 引入二阶导一方面是为了增加精度,另一方面也是为了能够自定义损失函数,二阶泰勒展开可以近似大量损失函数;
3.支持并行化,这是XGBoost的闪光点,虽然树与树之间是串行关系,但是同层级节点可并行。具体的对于某个节点,节点内选择最佳分裂点,候选分裂点计算增益用多线程并行。训练速度快。
4.添加了对稀疏数据的处理。
5.交叉验证,early stop,当预测结果已经很好的时候可以提前停止建树,加快训练速度。
6.支持设置样本权重,该权重体现在一阶导数g和二阶导数h,通过调整权重可以去更加关注一些样本。
缺失值处理:XGBoost 采用的稀疏感知算法极大的加快了节点分裂的速度;
灵活性更强:GBDT 以 CART 作为基分类器,XGBoost 不仅支持 CART 还支持线性分类器,(使用线性分类器的 XGBoost 相当于带 L1 和 L2 正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题))。此外,XGBoost 工具支持自定义损失函数,只需函数支持一阶和二阶求导;
可以并行化操作:块结构可以很好的支持并行计算。
虽然利用预排序和近似算法可以降低寻找最佳分裂点的计算量,但在节点分裂过程中仍需要遍历数据集;
预排序过程的空间复杂度过高,不仅需要存储特征值,还需要存储特征对应样本的梯度统计值的索引,相当于消耗了两倍的内存。
首先,对所有特征都按照特征的数值进行预排序。其次,在遍历分割点的时候用O(#data)的代价找到一个特征上的最好分割点。最后,在找到一个特征的最好分割点后,将数据分裂成左右子节点。
这样的预排序算法的优点是能精确地找到分割点。但是缺点也很明显:首先,空间消耗大。这样的算法需要保存数据的特征值,还保存了特征排序的结果(例如,为了后续快速的计算分割点,保存了排序后的索引),这就需要消耗训练数据两倍的内存。其次,时间上也有较大的开销,在遍历每一个分割点的时候,都需要进行分裂增益的计算,消耗的代价大。最后,对cache优化不友好。在预排序后,特征对梯度的访问是一种随机访问,并且不同的特征访问的顺序不一样,无法对cache进行优化。同时,在每一层长树的时候,需要随机访问一个行索引到叶子索引的数组,并且不同特征访问的顺序也不一样,也会造成较大的cache miss。
1.GBDT在优化时只用到一阶导数信息,XGBoost同时用到了一阶和二阶导数,还支持自定义损失 函数,前提损失函数可一阶和二阶求导;XGBoost为什么用二阶导数?
2.正则化:XGBoost显式地加入了正则项来控制模型的复杂度,能有效防止过拟合。
3.列采样:XGBoost采用了随机森林中的做法,每次节点分裂前进行列随机采样。
缺失值处理:XGBoost运用稀疏感知策略处理缺失值,而GBDT没有设计缺失策略。
4.寻找最佳分割点时,实现了一种近似法,还考虑了稀疏数据集、缺失值的处理,大大提升算法的效率;
5.并行高效:XGBoost的列块设计能有效支持并行运算,提高效率。
6.近似直方图算法,用于高效地生成候选的分割点;
7.在算法实现时做了很多优化,大大提升了算法的效率,内存空间不够时,利用了分块、预取、压缩、多线程协作的思想。
参考地址:https://juejin.im/post/5b7669c4f265da281c1fbf96
XGBoost的参数一共分为三类:
1.通用参数:宏观函数控制。
参数:控制每一步的booster(tree/regression)。booster参数一般可以调控模型的效果和计算代价。我们所说的调参,很这是大程度上都是在调整booster参数。
学习目标参数:控制训练目标的表现。我们对于问题的划分主要体现在学习目标参数上。比如我们要做分类还是回归,做二分类还是多分类,这都是目标参数所提供的。
Note: 我下面介绍的参数都是我觉得比较重要的, 完整参数请戳官方文档
通用参数
booster:我们有两种参数选择,gbtree和gblinear。gbtree是采用树的结构来运行数据,而gblinear是基于线性模型。
silent:静默模式,为1时模型运行不输出。
nthread: 使用线程数,一般我们设置成-1,使用所有线程。如果有需要,我们设置成多少就是用多少线程。
2、Booster参数
n_estimator: 也作num_boosting_rounds
这是生成的最大树的数目,也是最大的迭代次数。
learning_rate: 有时也叫作eta,系统默认值为0.3,。
每一步迭代的步长,很重要。太大了运行准确率不高,太小了运行速度慢。我们一般使用比默认值小一点,0.1左右就很好。
gamma:系统默认为0,我们也常用0。
在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。gamma指定了节点分裂所需的最小损失函数下降值。 这个参数的值越大,算法越保守。因为gamma值越大的时候,损失函数下降更多才可以分裂节点。所以树生成的时候更不容易分裂节点。范围: [0,∞]
subsample:系统默认为1。
这个参数控制对于每棵树,随机采样的比例。减小这个参数的值,算法会更加保守,避免过拟合。但是,如果这个值设置得过小,它可能会导致欠拟合。 典型值:0.5-1,0.5代表平均采样,防止过拟合. 范围: (0,1],注意不可取0
colsample_bytree:系统默认值为1。我们一般设置成0.8左右。
用来控制每棵随机采样的列数的占比(每一列是一个特征)。 典型值:0.5-1范围: (0,1]
colsample_bylevel:默认为1,我们也设置为1.
这个就相比于前一个更加细致了,它指的是每棵树每次节点分裂的时候列采样的比例
max_depth: 系统默认值为6
我们常用3-10之间的数字。这个值为树的最大深度。这个值是用来控制过拟合的。max_depth越大,模型学习的更加具体。设置为0代表没有限制,范围: [0,∞]
max_delta_step:默认0,我们常用0.
这个参数限制了每棵树权重改变的最大步长,如果这个参数的值为0,则意味着没有约束。如果他被赋予了某一个正值,则是这个算法更加保守。通常,这个参数我们不需要设置,但是当个类别的样本极不平衡的时候,这个参数对逻辑回归优化器是很有帮助的。
lambda:也称reg_lambda,默认值为0。
权重的L2正则化项。(和Ridge regression类似)。这个参数是用来控制XGBoost的正则化部分的。这个参数在减少过拟合上很有帮助。
alpha:也称reg_alpha默认为0,
权重的L1正则化项。(和Lasso regression类似)。 可以应用在很高维度的情况下,使得算法的速度更快。
scale_pos_weight:默认为1
在各类别样本十分不平衡时,把这个参数设定为一个正值,可以使算法更快收敛。通常可以将其设置为负样本的数目与正样本数目的比值。
3、学习目标参数
objective [缺省值=reg:linear]
reg:linear– 线性回归
reg:logistic – 逻辑回归
binary:logistic – 二分类逻辑回归,输出为概率
binary:logitraw – 二分类逻辑回归,输出的结果为wTx
count:poisson – 计数问题的poisson回归,输出结果为poisson分布。在poisson回归中,max_delta_step的缺省值为0.7 (used to safeguard optimization)
multi:softmax – 设置 XGBoost 使用softmax目标函数做多分类,需要设置参数num_class(类别个数)
multi:softprob – 如同softmax,但是输出结果为ndata*nclass的向量,其中的值是每个数据分为每个类的概率。
eval_metric [缺省值=通过目标函数选择]
rmse: 均方根误差
mae: 平均绝对值误差
logloss: negative log-likelihood
error: 二分类错误率。其值通过错误分类数目与全部分类数目比值得到。对于预测,预测值大于0.5被认为是正类,其它归为负类。 error@t: 不同的划分阈值可以通过 ‘t’进行设置
merror: 多分类错误率,计算公式为(wrong cases)/(all cases)
mlogloss: 多分类log损失
auc: 曲线下的面积
ndcg: Normalized Discounted Cumulative Gain
map: 平均正确率
一般来说,我们都会使用xgboost.train(params, dtrain)函数来训练我们的模型。这里的params指的是booster参数。