共计 1656 个字符,预计需要花费 5 分钟才能阅读完成。
简单介绍一下吧,lightgbm是微软推出的gbdt相关的机器学习库,一开源就受到很多开发者的喜爱吧,主要是运行速度快并且节省内存,同时训练的精度也很高,感觉集中了所有的优势。在此之前用陈天奇的xgboost居多,也是神器。xgboost采用了预排序的方法来处理节点分裂,在计算机领域要么就是空间换时间,或者时间换空间(这个也不是绝对,你可以通过某种特殊的方法两者都可以达到你想要的效果),xgboost为了加速采用了预排序的方式,那么xgboost处理大数据量其实是比较吃内存的,(组里单台机器现在内存250g,基本上不担心内存的问题,手动逃)
现在来看看直方图优化是如何优化的,当然这个优化也是在处理节点分裂的时候。在处理连续特征的时候,如果你想要快速找到最佳的分裂节点要么像之前说到的那样对特征值采用预排序的方式来快速得到最佳的分裂特征值,在这里直方图就是先将特征值先做装箱处理,装箱处理是特征工程中常见的处理方式之一了,下面给个例子说明特征装箱操作。
[0,0.3)—>0
[0.3,0.7)—->1
就是将某个区间的数据映射到离散的数据值。
说完了装箱操作现在看一下微软论文中提到的直方图优化的流程图:
最外面的for循环表示的意思是对当前模型下所有的叶子节点处理,gbdt会训练多个树模型,这个举例可能是其中任何一个模型吧。
第二个for循环开始要对某个叶子分裂处理处理,这一步就开始遍历所有的特征了,你选取的样本可能有50、100或者1000维等特征,为了找出的最佳的特征作为分裂的依据,那么这一个for循环就是干这件事情。
依次往下看,对于当前这个特征新建一个直方图,现在又碰到一个for循环了 ,这个for循环干的事情就是遍历所有的样本来构建直方图,哈哈,此时就用到了之前所描述的装箱操作了,直方图的每个bin中包含了一定的样本,在此计算每个bin中的样本的梯度之和并对bin中的样本记数。
下面就是最后一个for循环了,这个开始遍历所有的bin,找到适合分裂的最佳bin,解释一下这其中涉及到达变量定义
\( S_L\)是当前分裂bin左边所有bin的集合,对比理解\(S_R\),那么\(S_P\)其中的P就是parent的意思,就是父节点,传统的决策树会有分裂前后信息增益的计算,典型的ID3或者C45之类,在这里我们也会计算,但是\(S_R\)中所有bin的梯度之和不需要在额外计算了,直接使用父节点的减去左边的就得到了,是不是觉得很厉害的样子。反正我觉得这点是真的很神奇的地方,虽然看起来很简单,但是也是一个小魔法。
公式的中loss就是来衡量分裂的好坏的,在遍历完所有的特征之后根据loss,理论上loss最小的特征会被选中作为最佳的分裂节点。
这样的化这个这个直方图优化处理的流程应该就说明白的,基本上都是按照论文中的流程来一步一步解释。下面就来看看这种直方图优化的优缺点吧!
优点:
- 首先,最明显就是内存消耗的降低,直方图算法不仅不需要额外存储预排序的结果,而且可以只保存特征离散化后的值,而这个值一般用 8 位整型存储就足够了,内存消耗可以降低为原来的1/8。
- 然后在计算上的代价也大幅降低,预排序算法每遍历一个特征值就需要计算一次分裂的增益,而直方图算法只需要计算k次(k可以认为是常数),时间复杂度从O(#样本数*#特征数)优化到O(k*#特征数)。
缺点:
- 预处理能够忽略零值特征,减少训练代价;而直方图不能对稀疏进行优化,只是计算累加值(累加梯度和样本数)。但是,LightGBM对稀疏进行了优化:只用非零特征构建直方图。
最后一点是比较关键的一点,LightGBM为何使用直方图这种比较粗的分割节点方法,还能达到比较好的效果?
虽然分割的精度变差了,但是对最后结果的影响不是很大,主要由于决策树是弱模型, 分割点是不是精确并不是太重要 ;较粗的分割点也有正则化的效果,可以有效地防止过拟合;即使单棵树的训练误差比精确分割的算法稍大,但在梯度提升(Gradient Boosting)的框架下没有太大的影响。