《一篇文章搞定GBDTXgboost和LightGBM的面试》
Posted cx2016
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《一篇文章搞定GBDTXgboost和LightGBM的面试》相关的知识,希望对你有一定的参考价值。
一篇文章搞定GBDT、Xgboost和LightGBM的面试
一枚永远找不到路的 路痴
GBDT和XGB基本上是机器学习面试里面的必考题。最近面试了五十场面试,基本三分之二的面试官都问了关于GBDT和XGB的问题。
下面把里面常用的知识点、常见的面试题整理出来
首先来说集成学习
集成学习
boosting
- 串行的方式训练基分类器,各分类器之间有依赖。每次训练时,对前一层基分类器分错的样本给与更高的权重
bagging
- bagging是Bootstrap aggregating的意思,各分类器之间无强依赖,可以并行。
方差&偏差
- 偏差:
- 【背下来】偏差是指由有所采样得到的大小为m的训练数据集,训练出的所有模型的输出的平均值和真实模型输出之间的偏差。
- 通常是由对学习算法做了错误的假设导致的
- 描述模型输出结果的期望与样本真实结果的差距。分类器表达能力有限导致的系统性错误,表现在训练误差不收敛
方差:
- 【背下来】是指有所有采样得到的大小为m的训练数据集,训练出的所有模型的输出的方差
- 描述模型对于给定值的输出稳定性。分类器对样本分布过于敏感,到指在训练样本较少的时候,出现过拟合
- 基分类器的错误,是偏差和方差之和
- boosting方法通过逐步聚焦分类器分错的样本,减少集成分类器的偏差
- Bagging采用分而治之的策略,通过对样本多次采样,分别训练多个模型,减少方差
- 为什么决策树是常用的基分类器
- 可以方便地将样本权重正和岛训练过程中,不需要使用过采样的方法来调整样本券种
- 决策树的表达能力和繁华能力,可以通过调节树的层数来做折中
- 数据样本扰动对决策树影响较大,因此不同子样本集生成的基分类器随机性就较大。这样的不稳定学习器更适合作为基分类器。
- 插一句,神经网络也适合做基分类器
Adaboost
- boosting的思想:
- 对分类正确的样本降低权重
- 对错误分类的样本升高或者保持全中不变
- 在模型融合过程中,根据错误率对基分类器器进行加权融合,错误率低的分类器拥有更大的“话语权”
GBDT
- GBDT也称MART。基本的树:CART
- 拟合函数:负梯度
- 损失函数:
- 回归:直接用连续的值计算负梯度
- 均方差
- 绝对损失
- huber损失
- 分位数损失
- 分类:指数(此时退化成为Adaboost)、对数
- 正则化:
- 步长,v就是步长,空驶学习速度 ( )= −1( )+ ? ( )
- 自采样比例(subsample)无放回的抽样
- 正则化剪枝
GBDT主要的优点有:
- 1) 可以灵活处理各种类型的数据,包括连续值和离散值。
- 2) 在相对少的调参时间情况下,预测的准确率也可以比较高。这个是相对SVM来说的。
- 3)使用一些健壮的损失函数,对异常值的鲁棒性非常强。比如 Huber损失函数和Quantile损失函数。
- from百面机器学习:
- 预测阶段计算速度较快,树与树之间可以并行化计算
- 在分布稠密的数据机上,泛化能力和表达能力都比较好
- 具有较好的解释性和鲁棒性
- 能够自动发现特征质检的高阶关系
- 不需要做特殊预处理(比如归一化)
GBDT的主要缺点有:
- 由于弱学习器之间存在依赖关系,难以并行训练数据。不过可以通过自采样的SGBT来达到部分并行。
- 在高维稀疏数据上,表现不如SVM或神经网络
- 在处理文本分类特征问题上,相对其他模型优势不如在处理数值特征时明显
- 训练过程需要串行,只能在决策树内部采用一些局部并行手段提高训练速度
Xgboost
- 思路:对损失函数进行了二阶泰勒展开,同时使用一阶二阶导数
- 正则化:
- 损失函数:
- 二阶泰勒展开:
- 第i个样本在第t个托学习期的一阶、二阶导数分别记为
- 最终的损失函数:
- 决策树的分裂标准不是MSE,而是下式:
- 分裂方式:
- 分裂前后损失函数的差值为:
- XGB通过最大化这个差值作为准则来进行决策树的构建。通过遍历所有特征的取值,寻找时的损失函数前后相差最大时的分裂方式
- 一次求解出决策树最优的所有J个子节点区域、和每个子节点区域的最优解w
- 效率优化:
- 并行:Boosting算法的弱学习器是没法并行迭代的,但是单个弱学习器里面最耗时的是决策树的分裂过程,XGBoost针对这个分裂做了比较大的并行优化。对于不同的特征的特征划分点,XGBoost分别在不同的线程中并行选择分裂的最大增益。
- 内存优化:对训练的每个特征排序并且以块的的结构存储在内存中,方便后面迭代重复使用,减少计算量。计算量的减少参见上面第4节的算法流程,首先默认所有的样本都在右子树,然后从小到大迭代,依次放入左子树,并寻找最优的分裂点。这样做可以减少很多不必要的比较。
- 存储+IO:通过设置合理的分块的大小,充分利用了CPU缓存进行读取加速(cache-aware access)。使得数据读取的速度更快。另外,通过将分块进行压缩(block compressoin)并存储到硬盘上,并且通过将分块分区到多个硬盘上实现了更大的IO。
- 效果优化:
- 缺失值:XGBoost没有假设缺失值一定进入左子树还是右子树,则是尝试通过枚举所有缺失值在当前节点是进入左子树,还是进入右子树更优来决定一个处理缺失值默认的方向,这样处理起来更加的灵活和合理。
- 引入了适用于树模型的正则项用于控制模型复杂度。
- 包含了输的叶子节点个数、每个叶子节点输出分数的L2平方和
- 正则化项γ起到了一定的预剪枝的效果
- xgboost采用预剪枝策略,只有分裂后的增益大于0才会进行分裂。
- 参数
- XGB架构参数
- booster:CART、或者线性模型、或者DART
- n_estimator:
- objective:
- 分类:MSE
- 分类:二分类用logistic、多分类用softma
- 弱学习器参数
- max_depth:树的深度
- min_child_weight:最小子节点的权重。如果某个子节点权重小于这个阈值,则不会在分裂。使用的是该节点所有二阶导数的和
- gamma:分裂所带来的损失最小阈值,大于此值,才能继续分裂
- subsample:子采样参数,无放回抽样
- colsample_bytree 整棵树的特征采样比例
- colsample_bylevel 某层的特征采样比例
- colsample_bynode 某一个树节点的特征采样比例
- reg_alpha:L1正则化参数
- reg_lambda: L2正则化参数
- 其他
- n_jobs控制算法的并发线程数
- scale_pos_weight用于类别不平衡的时候,负例和正例的比例。类似于sklearn中的class_weight
- importance_type则可以查询各个特征的重要性程度。最后可以通过调用booster的get_score方法获取对应的特征权重。
- “weight”通过特征被选中作为分裂特征的计数来计算重要性
- “gain”和“total_gain”则通过分别计算特征被选中做分裂特征时带来的平均增益和总增益来计算重要性
- “cover”和 “total_cover”通过计算特征被选中做分裂时的平均样本覆盖度和总体样本覆盖度来来计算重要性。
- 总体:
- 另一个梯度提升树的实现框架
- 引入了基于直方图的统计量来表示特征离散化之后的值
- 构造叶子节点直方图的时候,可以直接用父节点的直方图与兄弟节点的直方图进行作差得到
- LGB不需要通过所有样本计算信息增益了,而且内置特征降维技术,所以更快
- 优化:
- 如果一个样本的梯度较小,证明这个样本训练的误差已经很小了,所以不需要计算了。我们在XGB的那篇文章中说过,GBDT的梯度算出来实际上就是残差,梯度小残差就小,所以该样本拟合较好,不需要去拟合他们了。
- 这听起来仿佛很有道理,但问题是丢掉他们会改变数据的分布,还是无法避免信息损失,进而导致精度下降,所以LGB提出了一个很朴实无华且枯燥的方法进行优化。
- LGB的优化方法是,在保留大梯度样本的同时,随机地保留一些小梯度样本,同时放大了小梯度样本带来的信息增益。
- 优点:
- 支持高效并行训练、训练速度快内存消耗低、准确率高、分布式支持
对比
- 作为GBDT的高效实现,XGBoost是一个上限特别高的算法,因此在算法竞赛中比较受欢迎。简单来说,对比原算法GBDT,XGBoost主要从下面三个方面做了优化:
- 一是算法本身的优化:在算法的弱学习器模型选择上,对比GBDT只支持决策树,还可以直接很多其他的弱学习器。在算法的损失函数上,除了本身的损失,还加上了正则化部分。在算法的优化方式上,GBDT的损失函数只对误差部分做负梯度(一阶泰勒)展开,而XGBoost损失函数对误差部分做二阶泰勒展开,更加准确。算法本身的优化是我们后面讨论的重点。
- 二是算法运行效率的优化:对每个弱学习器,比如决策树建立的过程做并行选择,找到合适的子树分裂特征和特征值。在并行选择之前,先对所有的特征的值进行排序分组,方便前面说的并行选择。对分组的特征,选择合适的分组大小,使用CPU缓存进行读取加速。将各个分组保存到多个硬盘以提高IO速度。
- 三是算法健壮性的优化:对于缺失值的特征,通过枚举所有缺失值在当前节点是进入左子树还是右子树来决定缺失值的处理方式。算法本身加入了L1和L2正则化项,可以防止过拟合,泛化能力更强。
- 联系&区别(百面机器学习+知乎):
- GBDT是机器学习算法;XGB是算法的工程实现
- XGB使用CART做基分类器的时候,显式地加入了正则项来控制模型的复杂度,有利于防止过拟合,提高泛化能力
- 正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。
- GBDT在模型训练的时候只使用了一阶导数信息,XGB对代价函数进行了二姐泰勒展开,同时使用一阶、二阶导数信息。并且可以自定义代价函数,只要是二阶可导数的
- 传统GBDT使用CART作为基分类器;XGB支持多种类型的基分类器,如线性分类器
- 缺失值:
- 传统GBDT没有专门针对缺失值进行处理;XGB能够自动学习出缺失值的处理策略:
- 指定缺失值的分隔方向:可以为缺失值或者指定的值指定分支的默认方向,为了保证完备性,会分别处理将missing该特征值的样本分配到左叶子结点和右叶子结点的两种情形,分到那个子节点带来的增益大,默认的方向就是哪个子节点,这能大大提升算法的效率。
- 忽略缺失值:在寻找splitpoint的时候,不会对该特征为missing的样本进行遍历统计,只对该列特征值为non-missing的样本上对应的特征值进行遍历,通过这个工程技巧来减少了为稀疏离散特征寻找splitpoint的时间开销
- Shrinkage(缩减),相当于学习速率(xgboost中的eta)。每次迭代,增加新的模型,在前面成上一个小于1的系数,降低优化的速度,每次走一小步逐步逼近最优模型比每次走一大步逼近更加容易避免过拟合现象;
- 抽样
- 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样(即每次的输入特征不是全部特征),不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
- 行抽样:传统GBDT在每轮迭代时使用全部的数据;XGB则采用了类似RF的策略,支持对数据进行采样
- 并行化处理:
- 在训练之前,预先对每个特征内部进行了排序找出候选切割点,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。
- 在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行,即在不同的特征属性上采用多线程并行方式寻找最佳分割点。
- 与其他模型对比
- VS 线性模型:
- 提升树可以较好地处理不在同一个区间的特征(年龄vs星期几vs比率);
- 可以更好地应对异常点;
- 更好处理特征质检的相关性;
- 处理非线性决策边界问题
- VS RF:
- RF可以并行训练,比较不容易过拟合;提升树可以学习到更复杂的决策边界,效果更好
- VS NN:
- NN vc维较多,训练比较困难;提升树在小数据集上效果会很好
- from知乎
- 传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
- 传统GBDT在优化时只用到一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。顺便提一下,xgboost工具支持自定义代价函数,只要函数可一阶和二阶求导。
- xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff角度来讲,正则项降低了模型的variance,使学习出来的模型更加简单,防止过拟合,这也是xgboost优于传统GBDT的一个特性。
- Shrinkage(缩减),相当于学习速率(xgboost中的eta)。每次迭代,增加新的模型,在前面成上一个小于1的系数,降低优化的速度,每次走一小步逐步逼近最优模型比每次走一大步逼近更加容易避免过拟合现象;
- 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样(即每次的输入特征不是全部特征),不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
- 忽略缺失值:在寻找splitpoint的时候,不会对该特征为missing的样本进行遍历统计,只对该列特征值为non-missing的样本上对应的特征值进行遍历,通过这个工程技巧来减少了为稀疏离散特征寻找splitpoint的时间开销
- 指定缺失值的分隔方向:可以为缺失值或者指定的值指定分支的默认方向,为了保证完备性,会分别处理将missing该特征值的样本分配到左叶子结点和右叶子结点的两种情形,分到那个子节点带来的增益大,默认的方向就是哪个子节点,这能大大提升算法的效率。
- 并行化处理:在训练之前,预先对每个特征内部进行了排序找出候选切割点,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行,即在不同的特征属性上采用多线程并行方式寻找最佳分割点。
编辑于 06-20
!--
.boom-extension-active{cursor:>以上是关于《一篇文章搞定GBDTXgboost和LightGBM的面试》的主要内容,如果未能解决你的问题,请参考以下文章