基于梯度提升算法的学习器叫做GBM(Gradient Boosting Machine)。
理论上,GBM可以选择各种不同的学习算法作为基学习器。
现实中,用得最多的基学习器是决策树。
为什么梯度提升方法倾向于选择决策树(通常是CART树)作为基学习器呢?
这与决策树算法自身的优点有很大的关系。决策树可以认为是if-then规则的集合,易于理解,可解释性强,预测速度快。同时,决策树算法相比于其他的算法需要更少的特征工程,比如可以不用做特征标准化,可以很好的处理字段缺失的数据,也可以不用关心特征间是否相互依赖等。决策树能够自动组合多个特征,它可以毫无压力地处理特征间的交互关系并且是非参数化的,因此你不必担心异常值或者数据是否线性可分(举个例子,决策树能轻松处理好类别A在某个特征维度x的末端,类别B在中间,然后类别A又出现在特征维度x前端的情况)。不过,单独使用决策树算法时,有容易过拟合缺点。所幸的是,通过各种方法,抑制决策树的复杂性,降低单颗决策树的拟合能力,再通过梯度提升的方法集成多个决策树,最终能够很好的解决过拟合的问题。
由此可见,梯度提升方法和决策树学习算法可以互相取长补短,是一对完美的搭档。至于抑制单颗决策树的复杂度的方法有很多,比如限制树的最大深度、限制叶子节点的最少样本数量、限制节点分裂时的最少样本数量、吸收bagging的思想对训练样本采样(subsample),在学习单颗决策树时只使用一部分训练样本、借鉴随机森林的思路在学习单颗决策树时只采样一部分特征、在目标函数中添加正则项惩罚复杂的树结构等。
GBDT不擅长处理高基数类别特征,如果基数低,问题也不大。
原因可见:lightgbm和catboost内部对类别特征有自己的特征工程方案,而xgboost这类无法直接处理类别特征的库则一般来说:1、单值离散用编码的方法;2、多值离散用embedding。
损失函数不同(废话),预测存在差异。
回归则直接加权求和输出全部基学习器的预测结果,分类还要将预测结果加权求和的结果放到sigmoid或者softmax里面转化为概率值。
变成adaboost。
优点:
(1)预测结点的计算速度,树与树之间可并行计算。
(2)在分布稠密的数据上,泛化能力和表达能力都很好,这使得GBDT在kaggle的众多竞赛上,经常名列榜首。
(3)采用决策树作为弱分类器使得GBDT模型具有较好的解释性和鲁棒性,能够自动发现特征间的高阶关系,并且也不需要对数据进行特殊的预处理,如归一化。
局限性:
(1)GBDT在高维稀疏的数据集上,表现不如支持向量机或者神经网络。
(2)GBDT在处理文本分类特征问题上,相对其他模型的优势不如它在处理数值特征时明显。
(3)训练过程需要串行训练,只能在决策树内部采用一些局部并行的手段提高训练速度。
高维稀疏的数据集,gbdt对维度超高的稀疏数据集,其正则项约束基本没用,并且决策空间会变成太多零散的决策小空间,具体可见上gbdt为何不好处理高基数类别特征的问题。
而lr的l1正则项可以很好的约束没啥用 的稀疏特征,直接w置0即可。
1、算法层面:
(1)损失函数的二阶泰勒展开;(GBDT只是泰勒公式的一阶展开)
(2)树的正则化概念的引入,XGB对叶节点数量和叶子节点输出进行了约束,方式是将二者形成的约束项加入损失函数中;
(3)XGB二阶泰勒展开与树正则化推出了新的叶子节点输出的计算公式而不是原始gbdt那样的简单平均;
(4)a、对于基础学习器的改进,
分裂的时候自动根据是否产生正增益指导是否进行分裂,因为引入了正则项的概念,分裂的时候这个预剪枝更加严苛;
b、对于缺失值的处理,xgboost根据左右子节点的增益大小将缺失值分到增益大的节点中,而sklearn中的gbdt是无法处理缺失值的,因为sklearn中的gbdt是以sklearn中的cart为基学习器的,而sklearn中的cart也并没有实现对缺失值的处理功能。
(5)学习率,Shrinkage,对每一颗树都乘以小于1的学习率,来削弱每一颗树的影响,这样的结果就是会引入更多的树来处理使得基学习器得数量变多,从而降低过拟合,不过其实sklearn中的gbdt也实现了。。。不知道为什么这么多人把这一点也列为不同;
(6)引入了随机森林使用的列采样功能,便于降低过拟合;
(7)引入了许多近似直方图之类的优化算法来进一步提高树的训练速度与抗过拟合的能力,这个比较复杂,因为实现了很多种算法,后面单独写一篇来总结;
2.工程层面
(1)对每个特征进行分块(block)并排序(pre_sort),将排序后的结构保存在内存中,这样后续分裂的时候就不需要重复对特征进行排序然后计算最佳分裂点了,并且能够进行并行化计算.这个结构加速了split finding的过程,只需要在建树前排序一次,后面节点分裂时直接根据索引得到梯度信息
(2) https://zhuanlan.zhihu.com/p/75217528 其它更复杂的工程优化处理见这里。。。。
第一种解释: RF深。随机森林的思路是用大量低偏差高方差的基学习器进行集成,简单平均(不过lightgbm中的rf貌似不太一样,没有细致研究过),降低方差,所以希望每一个基学习器的精度尽量高,如果随机森林的基学习器偏差大,对于100个或者10000个精度为0.6的学习器,很难通过随机森林的集成方式来达到好的效果;而gbdt本身就是对误差的不断拟合,本身就是一个偏差很低的集成框架,那么为了同时也使得方差缩小,需要基学习器的泛化性能好一些,避免整个框架的偏差很低但方差很大的窘境;
第二种解释:随机森林每一颗树都是独立的,每一颗树都是以原始标签进行训练的,在不进行任何限制的情况下会生长的比较深,而gbdt不一样,每一轮都是以上一轮的负梯度为新标签进行训练,训练到一定程度的时候我们去观察负梯度就可以发现,因为很多样本已经得到很好的拟合,所以负梯度会比较小,比如可能是这样的[0.000000001,0.000000001,0.000000001,0.0000000015…],这样树在分裂的时候实际上再进行分裂的增益并不大,甚至分裂之后的增益反而减少,这就导致了基树训练的时候很早就停止了,从而导致树的深度降低。
分类树无法处理连续值,负梯度一般都是连续值。
1、形式上的统一:
下面来自xgb的官网叙述:
可以看到,损失函数为mse的时候,注意,此时我们没有进行二阶泰勒展开
对比可以看到,其它损失函数泰勒展开之后去掉常数最终的形式和mse的不泰勒展开的形式是完全一致的(mse的二阶梯为常数1,一阶梯度是y_pred-y_True),这么做的好处是,这样的话,1、 xgboost在对mse的损失函数设计完求解器之后,这一套代码可以直接复用给别的损失函数来使用,因为我们如果不做二阶泰勒展开的话,比如新的损失函数是二元交叉熵,在工程设计上,我们还要将损失函数的求导,然后把求导之后的式子写出来:
而进行了这样的设计之后,后续如果还有一些什么别的损失函数,底层的求解mse的代码可以直接使用,使用者只需要自行去求解新的损失函数的一阶梯度和二阶梯度的表达式,然后通过xgboost的自定义损失函数的功能就可以实现使用完备的xgboost的框架来求解自己的损失函数的最优值了。
2、关于速度的问题,gbdt的前向分布的求解思路可以说就和我们常见的逻辑回归求解的梯度下降是类似的,线性回归的梯度下降每一轮通过更新参数的方式接近损失函数的最优值,而gbdt则是用基学习器去拟合,相对而言,xgboost类似于使用牛顿法来求解线性回归,所以下面从牛顿和梯度下降的角度来阐述,实际上我们常说的牛顿法比梯度下降法快是不准确的,应该是牛顿法的收敛速度要比梯度下降法快,也就是说牛顿法使用的迭代次数相对于梯度下降法要更少,但是由于涉及到计算二阶导的信息,牛顿法不一定在算法训练的时间上总比梯度下降法快,只是相对于梯度下降法而言,更少的迭代达到最优,这一点来看,并不算是优势。
(个人的思路):一般来说采用贝叶斯优化或者遗传算法等启发式的优化算法确定相对最佳参数(如果不熟悉的话用随机搜索也是可以的,或者网格搜索但是参数得到步长设置的很大,一步一步确定相对最优参数的区间),然后再根据实际的模型在验证集上的表现做一些微调,对于过拟合优先调整max_depth和树的数量,在实际使用过程中这两个参数对于模型的整体效果影响很大很明显。对于欠拟合,反着来就行了。
1.xgb怎么梯度下降的:
和gbdt是一样的,t-1轮的所有的子数的总预测值和真实值进入损失函数的负梯度的表达式计算得到负梯度作为第t轮要拟合的标签值。严格来说,这是前向分布算法,虽然他和梯度下降法的思路非常相似,但是梯度下降法对于每一轮的负梯度的使用方法是作为上一轮参数的参数的更新量,而xgb是直接将其作为标签值用新的基学习器去拟合。
2.xgb的正则化
叶子节点个数的正则化约束,参数为gamma,
叶子节点输出值的正则化约束,参数是lambda。
3.XGB特征重要性程度是怎么判断的?
官网给出的方案,total_gain就是特征带来的总的分裂增益,也就是我们常规意义上的分裂总增益,weight,被用来作为分裂节点的次数,也就是我们常规意义上的分裂总次数,gain=total_gain/weight,计算的是每一次分裂带来的平均增益,total_cover表示特征分裂的样本数,举个例子,假设初始样本有10000个,第一次分裂的时候使用了特征A,也就是特征A在这10000个样本上分裂,则此时的cover值为10000,假设根据特征A分裂出左枝的样本有1000个,右边有9000个,而在左枝特征B是最优特征根据这1000个样本进行分裂,则B当前的cover是1000,依次类推最后求和。
将原始特征进行排序之后以块的形式保存到内存中,在块里面保存排序后的特征值及对应样本的引用,以便于获取样本的一阶、二阶导数值,但意味着除了保存原始特征之外还要保存原始特征的排序结果,耗内存。
1)特征并行
前提是每个worker留有一份完整的数据集,但是每个worker仅在特征子集上进行最佳切分点的寻找;worker之间需要相互通信,通过比对损失来确定最佳切分点;然后将这个最佳切分点的位置进行全局广播,每个worker进行切分即可。
xgb的特征并行与lgbm的最大不同在于xgb每个worker节点中仅有部分的列数据,也就是垂直切分,每个worker寻找局部最佳切分点,worker之间相互通信,然后在具有最佳切分点的worker上进行节点分裂,再由这个节点广播一下被切分到左右节点的样本索引号,其他worker才能开始分裂。
二者的区别就导致了lgbm中worker间通信成本明显降低,只需通信一个特征分裂点即可,而xgb中要广播样本索引。
2)数据并行
当数据量很大,特征相对较少时,可采用数据并行策略。
lgbm中先对数据水平切分,每个worker上的数据先建立起局部的直方图,然后合并成全局的直方图,采用直方图相减的方式,先计算样本量少的节点的样本索引,然后直接相减得到另一子节点的样本索引,这个直方图算法使得worker间的通信成本降低一倍,因为只用通信以此样本量少的节点。
xgb中的数据并行也是水平切分,然后单个worker建立局部直方图,再合并为全局,不同在于根据全局直方图进行各个worker上的节点分裂时会单独计算子节点的样本索引,因此效率贼慢,每个worker间的通信量也就变得很大。
3)投票并行(lgbm)
当数据量和维度都很大时,选用投票并行,该方法是数据并行的一个改进。数据并行中的合并直方图的代价相对较大,尤其是当特征维度很大时。
大致思想是:每个worker首先会找到本地的一些优秀的特征,然后进行全局投票,根据投票结果,选择top的特征进行直方图的合并,再寻求全局的最优分割点。这个方法我没有找到很好的解释,因此,面试过程中答出前面两种我觉得就ok了吧。
后剪枝计算代价太高了,合并一次叶节点就要计算一次测试集的表现,数据量大的情况下非常消耗时间,而且也并不是特别必要,因为这样很容易过拟合测试集。
xgboost原理中的稀疏感知是关于缺失值的,
就是在非缺失的样本上做分裂然后缺失值根据分别进入左右节点带来的增益来决定要划分到哪个节点。如果是常规意义上的高基数类别特征进行onehot之后的0-1矩阵,xgb没有什么特别的处理方案。
Histgram算法 将浮点型数值离散为K个,统计离散值的累积量,遍历直方图找最优特征分裂点
直方图加速:叶子结点的直方图可由父亲结点的直方图与兄弟结点的直方图做差得到
leave wise选取信息增益最大的叶子结点继续分裂(容易过拟合,利用max_depth参数控制)