缺失值处理
真实场景中,有很多可能导致产生稀疏。如:数据缺失、某个特征上出现很多0项、人工进行one-hot编码导致大量的0
理论上,数据缺失和数值0的含义是不同的,数值0是有效的
实际上,数值0的处理方式类似缺失值的处理方式,都视为稀疏特征
在xgboost中,数值0的处理方式和缺失值的处理方式是同一的。这只是一个计算上的优化,用于加速稀疏特征的处理速度
对于稀疏特征,只需要对有效值进行处理,无效值则采用默认的分裂方向。
注意每个结点的默认分裂方向可能不同。
XGBoost中实现的方式
在xgboost算法的实现中,允许对数值0进行不同的处理。可以将数值0视作缺失值,也可以将其视作有效值。
如果数值0是有真实意义的,则建议将其视作有效值。
缺失值处理算法
输入:数据集
D=(x1
,y1~),(x2
,y2~),...,(xN
,yN~),其中样本
xi
=(xi,1,xi,2,...,xi,n)T
属于当前叶节点的样本的下标集合
I
属于当前叶节点,且第k维特征有效的样本的下标集合
Ik=i∈I∣xk,i=missing
输出:当前叶节点最佳分裂点
算法
初始化:
score←0,G←∑i∈Igi,H←∑i∈Ihi
遍历各维度:k = 1,…,n
先从左边开始遍历
初始化
GL←0,HL←0
遍历各拆分点:沿着第k维,将当前有效的叶节点的样本从小到大排序,相当于所有无效特征值的样本放在最右侧,因此可以保证无效的特征值都在右子树。
然后用j顺序遍历排序后的样本下标:
GL←GL+gj,HL←HL+hjGR←G−GL,HR←H−HL
score←max(score,HL+λGL2+HR+λGR2−H+λG2)
再从右边开始遍历
初始化
GR←0,HR←0
遍历各拆分点:沿着第k维,将当前有效的叶节点的样本从大到小排序,相当于所有无效特征值的样本放在最左侧,因此可以保证无效的特征值都在左子树。
然后用j顺序遍历排序后的样本下标:
GR←GR+gj,HR←HR+hjGL←G−GL,HL←H−HR
score←max(score,HL+λGL2+HR+λGR2−H+λG2)
选取最大的score对应的维度和拆分点作为最优拆分点
其他优化
预排序
xgboost提出column block 数据结构来降低排序时间
每一个block代表一个属性,样本在该block中按照它在该属性的值排好序
这些block只需要在程序开始的时候计算一次,后续排序只需要线性扫描这些block即可
由于属性之间是独立的,因此在每个维度寻找划分点可以并行计算
block可以仅存放样本的索引,而不是样本本身,这样节省了大量的存储空间
缓存命中率的优化
Cache-aware预取
由于在column block中,样本的顺序会被打乱,这会使得从导数数组中获取
gi时的缓存命中率较低。
因此xgboost提出了cache-aware预取算法,用于提升缓存命中率
xgboost会以minibatch的方式累加数据,然后在后台开启一个线程来加载需要用到的导数
gi
这里有个这种,minibatch太大,则会引起cache miss;太小,则并行程度较低
out of score 大数据集
xgboost利用硬盘来处理超过内存容量的大数据。其中使用了下列技术
使用block 压缩技术来环节内存和硬盘的数据交换IO:数据按列压缩,并且在硬盘到内存的传输过程中被自动解压缩
数据随机分片到多个硬盘,每个硬盘对应一个预取线程,从而加大"内存-硬盘"交换数据的吞吐量