1. 随机森林(random forest)和 GBDT 都是属于集成学习(ensemble learning)的范畴,有什么不同?
集成学习下有两个重要的策略 Bagging 和 Boosting,Bagging算法是这样,每个分类器都随机从原样本中做有放回的采样,然后分别在这些采样后的样本上训练分类器,然后再把这些分类器组合起来,简单的多数投票一般就可以,其代表算法是随机森林。Boosting 的算法是这样,它通过迭代地训练一系列的分类器,每个分类器采用的样本分布都和上一轮的学习结果有关。其代表算法是 AdaBoost,GBDT。
2. 为什么随机森林的树深度往往大于 GBDT 的树深度?
其实就机器学习算法来说,其泛化误差可以分解为两部分,偏差(bias)和方差(variance)。偏差指的是算法的期望预测与真实预测之间的偏差程度,反应了模型本身的拟合能力;方差度量了同等大小的训练集的变动导致学习性能的变化,刻画了数据扰动所导致的影响。如下图所示,当模型越复杂时,拟合的程度就越高,模型的训练偏差就越小。但此时如果换一组数据可能模型的变化就会很大,即模型的方差很大。所以模型过于复杂的时候会导致过拟合。当模型越简单时,即使我们再换一组数据,最后得出的学习器和之前的学习器的差别就不那么大,模型的方差很小。还是因为模型简单,所以偏差会很大。也就是说,当我们训练一个模型时,偏差和方差都得照顾到,漏掉一个都不行。

对于 Bagging 算法来说,由于我们会并行地训练很多不同的分类器的目的就是降低这个方差(variance),因为采用了相互独立的基分类器多了以后,h 的值自然就会靠近。所以对于每个基分类器来说,目标就是如何降低这个偏差(bias),所以我们会采用深度很深甚至不剪枝的决策树。对于 Boosting 来说,每一步我们都会在上一轮的基础上更加拟合原数据,所以可以保证偏差(bias),所以对于每个基分类器来说,问题就在于如何选择 variance 更小的分类器,即更简单的分类器,所以我们选择了深度很浅的决策树。
3. GBDT 如何用于分类?
GBDT 无论用于分类还是回归一直都是使用的 CART 回归树。不会因为我们所选择的任务是分类任务就选用分类树,这里面的核心是因为 GBDT 每轮的训练是在上一轮的训练的残差基础之上进行训练的。这里的残差就是当前模型的负梯度值 。这个要求每轮迭代的时候,弱分类器的输出的结果相减是有意义的,残差相减是有意义的。在分类训练的时候,是针对样本 XXX 每个可能的类都训练一个分类回归树。针对样本有三类的情况,我们实质上是在每轮的训练的时候是同时训练三颗树。第一棵树针对样本xxx 的第一类,输入为(x,0)(x, 0)(x,0)。第二棵树输入针对样本 xxx 的第二类,假设 xxx 属于第二类,输入为(x,1)(x, 1)(x,1)。第三棵树针对样本 x 的第三类,输入为(x,0)(x, 0)(x,0)。在这里每棵树的训练过程其实就是就是我们之前已经提到过的 CART 的生成过程。在此处我们参照之前的生成树的程序即可以就解出三棵树,以及三棵树对xxx 类别的预测值 f1(x),f2(x),f3(x)f_1(x), f_2(x), f_3(x)f1(x),f2(x),f3(x)。那么在此类训练中,我们仿照多分类的逻辑回归,使用 softmax 来产生概率。并且我们我们可以针对类别 1 求出残差 f11(x)=0−f1(x)f_{11}(x) = 0 − f_1(x)f11(x)=0−f1(x);类别 2 求出残差 f22(x)=1−f2(x)f_{22}(x) = 1 − f_2(x)f22(x)=1−f2(x);类别 3 求出残差 f33(x)=0−f3(x)f_{33}(x) = 0 − f_3(x)f33(x)=0−f3(x)。然后开始第二轮训练,针对第一类输入为(x,f11(x))(x, f_{11}(x))(x,f11(x)),针对第二类输入为(x,f22(x))(x, f_{22}(x))(x,f22(x)),针对第三类输入为(x,f33(x))(x, f_{33}(x))(x,f33(x))。继续训练出三棵树,一直迭代 M 轮,每轮构建 3 棵树。当训练完毕以后,新来一个样本 x1x_1x1,我们需要预测该样本的类别的时候,便可使用 softmax 计算每个类别的概率。
4. xgboost和gbdt的区别:
- 传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
- 传统GBDT在优化时只用到一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。而且,xgboost工具支持自定义代价函数,只要函数可一阶和二阶求导。
- xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff角度来讲,正则项降低了模型的variance,使学习出来的模型更加简单,防止过拟合,这也是xgboost优于传统GBDT的一个特性。
- Shrinkage(缩减),相当于学习速率(xgboost中的eta)。xgboost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。实际应用中,一般把eta设置得小一点,然后迭代次数设置得大一点。(补充:传统GBDT的实现也有学习速率)
- 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
- 对缺失值的处理。对于特征的值有缺失的样本,xgboost可以自动学习出它的分裂方向。
- xgboost工具支持并行。boosting不是一种串行的结构吗?怎么并行的?注意xgboost的并行不是tree粒度的并行,xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。xgboost的并行是在特征粒度上的。决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),xgboost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行(通过Pre-sorted的方法实现特征并行)。
- 可并行的近似直方图算法。树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以xgboost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点。
5. lightgbm和xgboost的区别:
- xgboost采用Level-wise的叶子节点生长策略,Level-wise遍历一次数据可以同时分裂同一层的叶子,但是它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂;而lightgbm采用的是leaf-wise叶子生长策略,每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后进行分裂,如此循环。同Level-wise相比,Leaf-wise可以降低更多的误差,得到更好的精度,其缺点是可能会长出较深的决策树,产生过拟合。因此,lightgbm在Leaf-wise之上增加了max_depth的限制,在保证高效率的同时防止过拟合。
- lightgbm采用的是基于histogram的决策树算法,而xgboost是基于pre-sorted的决策树算法。和pre-sorted算法相比,histogram算法在内存和计算代价方面都有着显著的提升:
- 内存:直方图算法的内存消耗为(#data* #features * 1Bytes)(因为对特征分桶后只需保存特征离散化之后的值),而xgboost的exact算法内存消耗为:(2 * #data * #features* 4Bytes),因为xgboost既要保存原始feature的值,也要保存这个值的顺序索引,这些值需要32位的浮点数来保存。
- 计算代价:预排序算法在选择好分裂特征计算分裂收益时需要遍历所有样本的特征值,时间为(#data*#features),而直方图算法只需要遍历桶就行了,时间为(#bin*#features)
- 直方图做差加速 :一个子节点的直方图可以通过父节点的直方图减去兄弟节点的直方图得到,从而加速计算。
- lightgbm支持直接支持类别特征:在决策树算法上增加了类别特征的决策规则,对离散特征分裂时,每个取值都当作一个桶,分裂时的增益算的是”是否属于某个category“的gain。类似于one-hot编码。
- Cache命中率优化
-
Pre-sorted算法导致cache miss的因素有以下两种:
- 随机访问梯度O(data×feature) O(data\times feature)O(data×feature):当计算增益Gain GainGain时,需要用到数据的梯度信息来进行计算。但同一特征对梯度访问的顺序是随机的,而且,不同的特征访问梯度的顺序也是不同的,并且是随机的。
- 随机访问row_idx_to_node_idx索引表O(data×feature) O(data\times feature)O(data×feature):Pre-sorted算法需要访问row_idx_to_node_idx索引表,防止数据切分时对所有的特征进行切分。和访问梯度方式相同,所有的特征都需要访问这个索引表来进行索引。
-
Histogram算法提高了cache命中率
- 连续访问梯度:对梯度的访问,由于没有对特征进行排序,因此,所有的特征都用相同的方式来访问。而且,只需要对梯度访问的顺序进行一次重新排序,所有的特征都能连续的访问梯度。
- 直方图算法不需要把数据id到叶子节点的索引表(不需要索引表,不存在缓存消失问题)
-
- 并行学习的优化
- 特征并行
- 传统特征并行:feature parallel通常垂直划分数据(不同的worker有不同的特征集),然后在本地特征集寻找最佳划分点{特征,阈值},最后将各个本地的最佳划分点通信整合,得到最终的最优划分并以最佳划分方法对数据进行划分,将数据划分结果广播传递给其他worker其他worker对接收到的数据做进一步的划分。
- lightgbm特征并行:每个worker都有全部数据,各个worker在本地数据集上寻找最佳划分点{特征,阈值},然后将各个本地的划分结果通信整合得到最优划分,最后在每个worker上执行最优划分。
- 数据并行
- 传统数据并行:对全部数据进行水平划分,每个worker以本地数据构建本地直方图,然后通信整合成全局直方图,最终在全局直方图中寻找最优划分,执行此划分
- lightgbm数据并行:lightgbm使用分散规约(Reduce scatter)的方式对不同worker的不同特征(不重叠)进行整合,把直方图合并的任务分摊到不同的worker,降低了通讯和计算,然后worker从本地整合的直方图中寻找最佳划分并同步到全局的最佳划分。
- 特征并行