算法强化 —— XGBoost(三)

缺失值处理

真实场景中,有很多可能导致产生稀疏。如:数据缺失、某个特征上出现很多0项、人工进行one-hot编码导致大量的0
理论上,数据缺失和数值0的含义是不同的,数值0是有效的
实际上,数值0的处理方式类似缺失值的处理方式,都视为稀疏特征
在xgboost中,数值0的处理方式和缺失值的处理方式是同一的。这只是一个计算上的优化,用于加速稀疏特征的处理速度
对于稀疏特征,只需要对有效值进行处理,无效值则采用默认的分裂方向。
注意每个结点的默认分裂方向可能不同。

XGBoost中实现的方式

在xgboost算法的实现中,允许对数值0进行不同的处理。可以将数值0视作缺失值,也可以将其视作有效值。
如果数值0是有真实意义的,则建议将其视作有效值。

缺失值处理算法

输入:数据集 D = ( x 1 , y 1 ~ ) , ( x 2 , y 2 ~ ) , . . . , ( x N , y N ~ ) D = {(\overrightarrow{x_1},\tilde{y_1}),(\overrightarrow{x_2},\tilde{y_2}),...,(\overrightarrow{x_N},\tilde{y_N})} ,其中样本 x i = ( x i , 1 , x i , 2 , . . . , x i , n ) T \overrightarrow{x_i} = (x_{i,1},x_{i,2},...,x_{i,n})^T
属于当前叶节点的样本的下标集合 I \mathbb{I}
属于当前叶节点,且第k维特征有效的样本的下标集合 I k = i I x k , i m i s s i n g \mathbb{I}_k = {i \in \mathbb{I} | x_{k,i} \not= missing}
输出:当前叶节点最佳分裂点

算法

初始化: s c o r e 0 , G i I g i , H i I h i score \leftarrow 0, G \leftarrow \sum_{i \in \mathbb{I}}g_i,H \leftarrow \sum_{i \in \mathbb{I}}h_i
遍历各维度:k = 1,…,n

先从左边开始遍历
初始化 G L 0 , H L 0 G_L \leftarrow 0,H_L \leftarrow 0
遍历各拆分点:沿着第k维,将当前有效的叶节点的样本从小到大排序,相当于所有无效特征值的样本放在最右侧,因此可以保证无效的特征值都在右子树。
然后用j顺序遍历排序后的样本下标:
G L G L + g j , H L H L + h j G R G G L , H R H H L \begin{aligned} &\mathbf{G}_{L} \leftarrow \mathbf{G}_{L}+g_{j}, \quad \mathbf{H}_{L} \leftarrow \mathbf{H}_{L}+h_{j}\\ &\mathbf{G}_{R} \leftarrow \mathbf{G}-\mathbf{G}_{L}, \quad \mathbf{H}_{R} \leftarrow \mathbf{H}-\mathbf{H}_{L} \end{aligned}
score max ( score , G L 2 H L + λ + G R 2 H R + λ G 2 H + λ ) \text {score} \leftarrow \max \left(\text {score}, \frac{\mathbf{G}_{L}^{2}}{\mathbf{H}_{L}+\lambda}+\frac{\mathbf{G}_{R}^{2}}{\mathbf{H}_{R}+\lambda}-\frac{\mathbf{G}^{2}}{\mathbf{H}+\lambda}\right)

再从右边开始遍历
初始化 G R 0 , H R 0 G_R \leftarrow 0,H_R \leftarrow 0
遍历各拆分点:沿着第k维,将当前有效的叶节点的样本从大到小排序,相当于所有无效特征值的样本放在最左侧,因此可以保证无效的特征值都在左子树。
然后用j顺序遍历排序后的样本下标:
G R G R + g j , H R H R + h j G L G G L , H L H H R \begin{aligned} &\mathbf{G}_{R} \leftarrow \mathbf{G}_{R}+g_{j}, \quad \mathbf{H}_{R} \leftarrow \mathbf{H}_{R}+h_{j}\\ &\mathbf{G}_{L} \leftarrow \mathbf{G}-\mathbf{G}_{L}, \quad \mathbf{H}_{L} \leftarrow \mathbf{H}-\mathbf{H}_{R} \end{aligned}
score max ( score , G L 2 H L + λ + G R 2 H R + λ G 2 H + λ ) \text {score} \leftarrow \max \left(\text {score}, \frac{\mathbf{G}_{L}^{2}}{\mathbf{H}_{L}+\lambda}+\frac{\mathbf{G}_{R}^{2}}{\mathbf{H}_{R}+\lambda}-\frac{\mathbf{G}^{2}}{\mathbf{H}+\lambda}\right)
选取最大的score对应的维度和拆分点作为最优拆分点

其他优化

预排序

xgboost提出column block 数据结构来降低排序时间
每一个block代表一个属性,样本在该block中按照它在该属性的值排好序
这些block只需要在程序开始的时候计算一次,后续排序只需要线性扫描这些block即可
由于属性之间是独立的,因此在每个维度寻找划分点可以并行计算
block可以仅存放样本的索引,而不是样本本身,这样节省了大量的存储空间

缓存命中率的优化

Cache-aware预取

由于在column block中,样本的顺序会被打乱,这会使得从导数数组中获取 g i g_i 时的缓存命中率较低。
因此xgboost提出了cache-aware预取算法,用于提升缓存命中率
xgboost会以minibatch的方式累加数据,然后在后台开启一个线程来加载需要用到的导数 g i g_i
这里有个这种,minibatch太大,则会引起cache miss;太小,则并行程度较低

out of score 大数据集

xgboost利用硬盘来处理超过内存容量的大数据。其中使用了下列技术
使用block 压缩技术来环节内存和硬盘的数据交换IO:数据按列压缩,并且在硬盘到内存的传输过程中被自动解压缩
数据随机分片到多个硬盘,每个硬盘对应一个预取线程,从而加大"内存-硬盘"交换数据的吞吐量

发布了110 篇原创文章 · 获赞 3 · 访问量 4081

猜你喜欢

转载自blog.csdn.net/qq_33357094/article/details/105088759