
XGBoost(eXtreme Gradient Boosting)其核心是对决策树(Decision Tree)的加强(Boosting)方法,属于集成学习(Ensemble Learning)。
下文分别对决策树、决策树集成以及Xgboost进行介绍。
1、决策树 Decision Tree
决策树是一种常见的监督学习方法,能够用于处理分类和回归问题。
分类问题(离散值):西瓜甜不甜?
回归问题(连续值):房价多少?复制代码
顾名思义就是构建一个树形的决策过程。树中的每一个节点即挑选特定特征对样本进行决策。
特征树的构建主要分为三个步骤:
特征选择、决策树构建、剪枝。
其中根据特征选择的方式不一样,分为ID三、C4.五、CART三类主要算法。
ID3基于信息增益进行特征选择:

分为训练集数据在通过特征A前/后的信息熵。
经过选取特征A最大化信息增益:
可是因为特征的取值不均衡的问题(好比性别只有男女,年龄能够分不少类),ID3算法更倾向于选择取值更多的特征。
CART(Classification And Regression Tree)基于基尼系数进行特征选择,在分类问题中:
经过将计算熵值(对数运算)转变为计算基尼系数(平方运算)大大下降了运算复杂度。
对于回归树而言,CART能够处理回归树问题,其优化目标以下:
即经过特征j和切分点s将数据集切分为两个集合, 并最小化两个集合中各自样本的方差(c1,c2分别是集合样本的均值)。
总的来讲,决策树是基于特征进行if-then的决策过程,高效、易于理解。可是对训练样本严重依赖,容易生成复杂的树结构,形成过拟合(overfitting)等问题。并且若是要学习获得一棵最优的决策树被认为是NP-Complete问题。实际中的决策树是基于启发式的贪心算法创建的,这种算法不能保证创建全局最优的决策树。
2、决策树集成 Tree Ensemble
集成学习(ensemble learning)经过构建并结合多个学习器来完成学习任务,这里主要关注Bagging和Boosting两类。
2.1 Bagging
Bagging (bootstrap aggregating) 是基于Bootstraping重采样方法(有放回的采样),独立训练产生k个模型,并综合k个模型的输出获得最终结果(分类问题能够采用投票方式,回归问题能够采用均值)。Bootstraping一般可以包含63.2%的数据样本

。它能够并行执行,并经过这种方式避免模型对于训练数据过于敏感,减小模型的方差。
Bagging与决策树的结合就获得了随机森林(Random Forest),与Bagging方法惟一的不一样在于Random Forest在特征选择前,使用了特征采样(通常使用
个特征),减小了强分类属性对模型的影响,使得不一样模型更为独立。
2.2 Boosting
Boosting方法中数据集是不变的而在改变样本的权重,使得后续模型更关注与先前模型错分的样本,须要串行执行。具体而言能够分为以下几类:
AdaBoosting(Adaptive Boosting),在Boosting的基础上AdaBoosting为每轮迭代过程当中基于加权后样本的误率赋予每一个模型对应权值。模型权重与迭代过程当中样本权重的更新方法以下:
Gradient Boosting 通常用于回归树,采用上轮模型预测值与真实值之间的残差做为下一轮的输入

表示第i个样本第m轮的残差,

分别为真实值和预测值。
GBDT(Gradient Boosting Decision Tree), 对于下一轮的输入(残差),根据不一样的损失函数提供了不一样的计算方法,

。当损失函数为平方差时,残差与GB中一致。经过加入了学习率的概念,能够调整对于错分数据的关注。
Xgboost本文主角,这里与GBDT先作一个比较,后续作详细说明:
1. 将树模型的复杂度加入到正则项中,来避免过拟合,所以泛化性能会优于GBDT。
2. 损失函数是用泰勒展开式展开的,同时用到了一阶导和二阶导,能够加快优化速度。
3. 和GBDT只支持CART做为基分类器以外,还支持线性分类器,在使用线性分类器的时候可使用L1,L2正则化。
4. 引进了特征子采样,像RandomForest那样,这种方法既能下降过拟合,还能减小计算。
5. 在寻找最佳分割点时,考虑到传统的贪心算法效率较低,实现了一种近似贪心算法,用来加速和减少内存消耗,除此以外还考虑了稀疏数据集和缺失值的处理,对于特征的值有缺失的样本,XGBoost依然能自动找到其要分裂的方向。
6. XGBoost支持并行处理,XGBoost的并行不是在模型上的并行,而是在特征上的并行,将特征列排序后以block的形式存储在内存中,在后面的迭代中重复使用这个结构。这个block也使得并行化成为了可能,其次在进行节点分裂时,计算每一个特征的增益,最终选择增益最大的那个特征去作分割,那么各个特征的增益计算就能够开多线程进行。算法
3、Xgboost
与论文对应,这里分为三个部分对Xgboost进行介绍:Xgboost模型、分割点的查找、算法并行实现。bootstrap
3.1.3 Shrinkage and Column Subsampling
Shrinkage(收缩)即能和GBDT同样设置学习率,削减每棵树的影响,让后面有更大的学习空间。实际应用中,通常把学习率设置得小一点,而后迭代次数设置得大一点。
Column Subsampling(列抽样)即Random Forest中的特征抽样方法,使得每棵树可以尽可能独立。
3.2 分割点的查找 Split Finding
为了找到特征的最优切分点,须要遍历特征全部的取值,并获得全部可能的切分点。而后带入目标函数进行计算,并将最优的目标函数值对应的切分点,做为特征的切分点。可是因为回归问题中,特征是连续的存在不少候选切分点,因此找到最优切分点须要耗费大量的时间和内存。
3.2.1 Approximate Algorithm
Xgboost采用了一种近似作法,对排序后的特征进行分段,只在分段和分段间造成候选的切分点,大大减小了候选分割点的个数。其算法和效果以下图所示

分割点的近似查找方法,分为全局和局部两种api

最优分割和近似分割比较。eps = epsilon,表示按照eps比例对数据集进行划分
这里一个重要的问题是按照何种标准生成候选分割点,Xgboost中并非简单地等分切割,而是根据二阶导数进行加权切割。
。缓存
3.2.2 Sparsity-aware Split Finding
对于数据稀疏的状况,好比:1. 数据缺失值;2. 大量的0值; 3. 进行了One-hot 编码。Xgboost提供了对应的算法给与稀疏的数据一个默认值。其方法核心在于将缺失的样本所有划分在节点的左侧或是右侧,并选择其中增益最大的一种划分方法做为默认划分。bash

3.3 系统设计 System Design
3.3.1 Column Block for Parallel Learning
建树过程当中最大的耗时在于对样本的排序,为了下降排序时间,采用Block结构对排序信息进行存储:
能够看出,只需在建树前排序一次,后面节点分裂时能够直接根据索引获得梯度信息。多线程
在Exact greedy算法中,将整个数据集存放在一个Block中。这样,复杂度从原来的
app
这样,Exact greedy算法就省去了每一步中的排序开销。dom
在近似算法中,使用多个Block,每一个Block对应原来数据的子集。不一样的Block能够在不一样的机器上计算。该方法对Local策略尤为有效,由于Local策略每次分支都从新生成候选切分点。函数
Block结构还有其它好处,数据按列存储,能够同时访问全部的列,很容易实现并行的寻找分裂点算法。此外也能够方便实现以后要讲的out-of score计算。但缺点是空间消耗大了一倍。
3.3.2 Cache-aware Access
使用Block结构的一个缺点是取梯度的时候,是经过索引来获取的,而这些梯度的获取顺序是按照特征的大小顺序的。这将致使非连续的内存访问,可能使得CPU cache缓存命中率低,从而影响算法效率。性能
所以,对于exact greedy算法中, 使用缓存预取。具体来讲,对每一个线程分配一个连续的buffer,读取梯度信息并存入Buffer中(这样就实现了非连续到连续的转化),而后再统计梯度信息。该方式在训练样本数大的时候特别有用,见下图:

在approximate 算法中,对Block的大小进行了合理的设置。定义Block的大小为Block中最多的样本数。设置合适的大小是很重要的,设置过大则容易致使命中率低,太小则容易致使并行化效率不高。通过实验,发现2^16比较好。

3.3.3 Blocks for Out-of-core Computation
当数据量太大不能所有放入主内存的时候,为了使得out-of-core计算称为可能,将数据划分为多个Block并存放在磁盘上。
计算的时候,使用独立的线程预先将Block放入主内存,所以能够在计算的同时读取磁盘
Block压缩,貌似采用的是近些年性能出色的LZ4 压缩算法,按列进行压缩,读取的时候用另外的线程解压。对于行索引,只保存第一个索引值,而后用16位的整数保存与该block第一个索引的差值。
Block Sharding, 将数据划分到不一样硬盘上,提升磁盘吞吐率