文章目录
看了LightGBM的论文之后,在从头看XGBoost论文,之前虽然看过,现在对比看的时候又有不同。
ABSTRACT
Treeboosting是高效并被广泛应用的机器学习方法。XGBoost是一种适用于大规模数据的端到端的boosting系统。提出了一种新颖的稀疏感知(sparsity-aware)算法和加权分位数快速近似树学习算法。更重要的,我们提供关于缓存访问模式,数据压缩和分片的见解,以构建一个可扩展的树型增强系统。 通过结合这些见解,XGBoost可以使用比现有系统少得多的资源来支撑数十亿个样本的训练。
1. INTRODUCTION
机器学习被广泛应用的关键因素:1.对于复杂数据依赖的建模能力。2.从大数据中学习出有用的模型构建可扩展的系统。
Contributions
- 设计并构建了一个高度可扩展的端到端tree boosting系统。
- 提出了有理论支撑的加权分位图来进行有效的方案计算。
- 引入一种新颖的并行树学习稀疏感知算法。
- 提出了一个有效的缓存感知块结构加速外核树学习。
2. TREE BOOSTING IN A NUTSHELL
2.1 Regularized Learning Objective
正则化的目标:对于给定的大小为n,特征m维的数据集,树的集成模型一般使用K个函数相加最为预测结果。
y
i
^
=
ϕ
(
x
i
)
=
∑
k
=
1
K
f
k
(
x
i
)
,
f
k
∈
F
\hat{y_i} = \phi(x_i) = \sum_{k=1}^{K} f_k(x_i),f_k \in \mathcal{F}
yi^=ϕ(xi)=k=1∑Kfk(xi),fk∈F
其中
F
=
{
f
(
x
)
=
ω
q
(
x
)
}
(
q
:
R
m
→
T
,
w
∈
R
T
)
\mathcal{F} = \{ f(x) = \omega_{q(x)} \}(q : R^m \rightarrow T, w \in R^T)
F={f(x)=ωq(x)}(q:Rm→T,w∈RT)是CART的样本空间,q代表每个数的结构(即将样本映射到节点上),T是树叶子结点数量,w是点点权重,每个函数
f
k
f_k
fk包含独立树q和叶子权重w,
w
q
(
x
)
w_{q(x)}
wq(x)是预测得分,所有的预测得分之和作为最终的预测结果。**和决策树不同的就是回归树每个叶子结点都有权重。**如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qUXNGUX9-1635143234417)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System1.png)]
为了学习到模型中的函数,需要最小化下面正则化的目标函数:
L
(
ϕ
)
=
∑
i
ℓ
(
y
^
i
,
y
i
)
+
∑
k
Ω
(
f
k
)
Ω
(
f
)
=
γ
T
+
1
2
λ
∥
w
∥
2
(2)
\mathcal{L}(\phi)=\sum_i\ell(\hat{y}_i,y_i)+\sum_k\Omega(f_k)\\ \Omega(f)=\gamma T+\frac{1}{2}\lambda\|w\|^2 \tag{2}
L(ϕ)=i∑ℓ(y^i,yi)+k∑Ω(fk)Ω(f)=γT+21λ∥w∥2(2)
这里
ℓ
\ell
ℓ是一个衡量预测值和目标值差异的凸函数。第二项
Ω
\Omega
Ω是控制模型复杂度的正则化项,避免过拟合(即倾向于选择简单的模型)。
2.2 Gradient Tree Boosting
方程2不能再欧式空间上用传统方法优化,而是通过迭代获得最优解。
y
^
i
(
t
)
\hat{y}_i^{(t)}
y^i(t)是第i个实例在第t次迭代的预测值,我们需要加入
f
t
f_t
ft来最小化以下目标
L
(
ϕ
)
=
∑
i
ℓ
(
y
i
,
y
^
i
(
t
−
1
)
+
f
t
(
X
i
)
)
+
∑
k
Ω
(
f
k
)
\mathcal{L}(\phi)=\sum_i\ell(y_i, \hat{y}_i^{(t-1) }+ f_t(X_i))+\sum_k\Omega(f_k)
L(ϕ)=i∑ℓ(yi,y^i(t−1)+ft(Xi))+k∑Ω(fk)
通过泰勒二阶展开近似来快速优化目标函数(泰勒级数
∑
n
=
0
∞
f
(
n
)
(
a
)
n
!
(
x
−
a
)
a
\sum_{n=0}^\infty \frac{f^{(n)} (a)}{n!} (x-a)^a
∑n=0∞n!f(n)(a)(x−a)a)):
L
(
ϕ
)
≃
∑
i
[
ℓ
(
y
i
,
y
^
i
(
t
−
1
)
)
+
g
i
f
t
(
X
i
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
∑
k
Ω
(
f
k
)
\mathcal{L}(\phi) \simeq \sum_i [\ell(y_i, \hat{y}_i^{(t-1) }) + g_if_t(X_i) + \frac1 2 h_i f_t^2(x_i) ]+\sum_k\Omega(f_k)
L(ϕ)≃i∑[ℓ(yi,y^i(t−1))+gift(Xi)+21hift2(xi)]+k∑Ω(fk)
其中
g
i
=
∂
y
t
−
1
l
(
y
i
,
y
^
t
−
1
)
,
h
i
=
∂
y
t
−
1
2
l
(
y
i
,
y
^
t
−
1
)
g_i = \partial_{y^{t-1}} l(y_i, \hat{y}^{t-1}),h_i = \partial^2_{y^{t-1}} l(y_i, \hat{y}^{t-1})
gi=∂yt−1l(yi,y^t−1),hi=∂yt−12l(yi,y^t−1)即l的一阶和二阶导数。移除常数项得到:
L
~
(
ϕ
)
=
∑
i
[
g
i
f
t
(
X
i
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
∑
k
Ω
(
f
k
)
(3)
\tilde{\mathcal{L}}(\phi) = \sum_i [ g_if_t(X_i) + \frac1 2 h_i f_t^2(x_i) ]+\sum_k\Omega(f_k) \tag{3}
L~(ϕ)=i∑[gift(Xi)+21hift2(xi)]+k∑Ω(fk)(3)
定义
I
j
=
{
i
∣
q
(
x
i
)
=
j
}
I_j = \{ i|q(x_i) = j \}
Ij={i∣q(xi)=j}作为叶子结点j的实例集合。将方程3展开为:
L
~
(
ϕ
)
=
∑
i
[
g
i
f
t
(
X
i
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
=
∑
j
=
1
T
[
(
∑
i
∈
I
j
g
i
)
w
j
+
1
2
(
∑
i
∈
I
j
h
i
+
λ
)
w
j
2
]
+
γ
T
(4)
\tilde{\mathcal{L}}(\phi) = \sum_i [ g_if_t(X_i) + \frac1 2 h_i f_t^2(x_i) ]+ \gamma T + \frac 12 \lambda \sum_{j=1}^T w_j^2 \\ = \sum_{j=1}^T [(\sum_{i \in I_j} g_i)w_j + \frac12 (\sum_{i\in I_j}h_i + \lambda)w_j^2] + \gamma T \tag{4}
L~(ϕ)=i∑[gift(Xi)+21hift2(xi)]+γT+21λj=1∑Twj2=j=1∑T[(i∈Ij∑gi)wj+21(i∈Ij∑hi+λ)wj2]+γT(4)
计算权重公式:
w
j
∗
=
−
∑
i
∈
i
j
g
i
∑
i
∈
I
j
h
i
+
λ
(5)
w_j^* = -\frac{\sum_{i\in i_j} g_i}{\sum_{i\in I_j} h_i + \lambda} \tag{5}
wj∗=−∑i∈Ijhi+λ∑i∈ijgi(5)
带入方程4目标函数得(一阶和二阶导数合并成了一项):
L
~
(
t
)
(
q
)
=
−
1
2
∑
j
=
1
T
(
∑
i
∈
I
j
g
i
)
2
∑
i
∈
I
j
h
i
+
λ
+
γ
T
(6)
\tilde{\mathcal{L}}^{(t)} (q)= -\frac12 \sum_{j=1}^T\frac{(\sum_{i \in I_j} g_i)^2}{\sum_{i \in I_j} h_i + \lambda} + \gamma T \tag{6}
L~(t)(q)=−21j=1∑T∑i∈Ijhi+λ(∑i∈Ijgi)2+γT(6)
这一项算出的值就是第t棵树要优化目标函数,使其尽量小。下图展示计算过程(图中Obj少了一个-1/2,我就是这么较真),目标函数越小越好。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XmOpG6Qx-1635143234419)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System2.png)]
枚举所有可能结构的树是不可能的,通过贪心算法从叶节点开始迭代得添加分支,
I
L
,
I
R
I_L,I_R
IL,IR分别是分割点左右分支的实例集,分割的损失下降定义为:
L
=
1
2
[
(
∑
i
∈
I
g
i
)
2
∑
i
∈
I
L
h
i
+
λ
+
(
∑
i
∈
I
g
i
)
2
∑
i
∈
I
R
h
i
+
λ
−
(
∑
i
∈
I
g
i
)
2
∑
i
∈
I
h
i
+
λ
]
−
λ
(7)
\mathcal{L} = \frac12[\frac{(\sum_{i \in I} g_i)^2}{\sum_{i \in I_L} h_i +\lambda} + \frac{(\sum_{i \in I} g_i)^2}{\sum_{i \in I_R} h_i +\lambda} - \frac{(\sum_{i \in I} g_i)^2}{\sum_{i \in I} h_i +\lambda}] - \lambda \tag{7}
L=21[∑i∈ILhi+λ(∑i∈Igi)2+∑i∈IRhi+λ(∑i∈Igi)2−∑i∈Ihi+λ(∑i∈Igi)2]−λ(7)
为什么是这个呢,很简单,就是方程6在分割前后的差值。
2.3 Shrinkage and Column Subsampling
XGBoost加入学习速率收缩和列采样一定程度上也可以防止过拟合。
收缩:在tree boosting过程的每一步添加一个权重 η \eta η,收缩可以减少每棵树的影响并为接下来的树预留了学习空间来提高模型(我理解是收缩使得目前是欠拟合,预留学习空间,如果目前已经拟合完全了,那么接下来的学习就很容易过拟或者震荡)。
列采样(特征)采样,随机森林中的技术,根据用户的反馈防止过拟合效果比行采样还好(XGBoost也支持行采样),同时列采样也能够加速并行算法运算。
3. SPLIT FINDING ALGORITHMS
3.1 Basic Exact Greedy Algorithm
如何找到方程7的合适分割点,精确贪心算法遍历所有特征的可能分割点找出最优的切分,如skleran、r GBM、单机XGBoost都是这样。
下图展示其算法,对于连续特征遍历所有可能的切分点计算上是demanding,为了更高效,必须先将数据以特征值排序,然后计算每个可能划分的梯度统计值(即方程7的值)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sPS82XMp-1635143234420)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System3.png)]
3.2 Approximate Algorithm-近似算法
数据量大的时候精确算法非常消耗时间和内存,本文总结了近似算法如图,算法根据特征分布百分比生成候选切分点,然后以候选切分点将连续特征映射到桶中,汇总然后找到最优的方案。
算法有两种变体:全局划分在建树的初期提出所有候选分裂,然后各级采用相同的拆分方案。本地划分在每次拆分之后提出。全局方案候选方案生成次数更少,同时候选点也跟多,本地划分可能适合建更深的树。XGBoost支持两种划分。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3EcyLGV1-1635143234421)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System4.png)]
3.3 Weighted Quantile Sketch-加权分位
近似算法通过特征百分位点作为划分候选。使集合
D
k
=
{
(
x
1
k
,
h
1
)
,
(
x
2
k
,
h
2
)
,
.
.
.
(
x
n
k
,
h
n
)
}
\mathcal{D}_k = \{(x_{1k}, h_1), (x_{2k}, h_2),...(x_{nk}, h_n) \}
Dk={(x1k,h1),(x2k,h2),...(xnk,hn)}代表样本点的第k个特征值和二阶导数。定义排序函数:
r
k
(
z
)
=
1
∑
(
x
,
h
)
∈
D
k
h
∑
(
x
,
h
)
∈
D
k
,
x
<
z
h
(8)
r_k(z) = \frac 1{\sum_{(x,h) \in D_k} h} \sum_{(x,h) \in D_k, x<z} h \tag{8}
rk(z)=∑(x,h)∈Dkh1(x,h)∈Dk,x<z∑h(8)
方程8表示特征值小于z的样本占整体的比例。目标是找到候选切分点
{
s
k
1
,
s
k
2
,
.
.
.
s
k
l
,
}
\{s_{k1}, s_{k2},... s_{kl}, \}
{sk1,sk2,...skl,}使得:
∣
r
k
(
s
k
,
j
)
−
r
k
(
s
k
,
j
+
1
)
∣
<
ϵ
,
s
k
1
=
min
i
x
i
k
,
s
k
l
=
max
i
x
i
k
(9)
|r_k(s_{k,j)}- r_k(s_{k,j+1)}| < \epsilon, s_{k1} = \min_i x_{ik}, s_{kl} = \max_{i} x_{ik} \tag{9}
∣rk(sk,j)−rk(sk,j+1)∣<ϵ,sk1=iminxik,skl=imaxxik(9)
其中
ϵ
\epsilon
ϵ是近似因子,这意味着有
1
/
ϵ
1/\epsilon
1/ϵ个候选点,每个数据点权重是
h
i
h_i
hi,方程3说明为什么用它做权重。
∑
i
=
1
n
1
2
h
i
(
f
t
(
x
i
)
−
g
i
/
h
i
)
2
+
Ω
(
f
t
)
+
c
o
n
s
t
a
n
t
,
\sum_{i=1}^n \frac12 h_i(f_t(x_i) - g_i/h_i)^2 + \Omega(f_t) + constant,
i=1∑n21hi(ft(xi)−gi/hi)2+Ω(ft)+constant,
其中
h
i
h_i
hi即为平方损失的权重,对于大数据集,找到满足条件的候选分裂是非常重要的。以前的分位算法中没有权重,因为加权数据集没有分位数。
为了解决这个问题,我们提出了新颖的分布式加权的分位数算法,我们理论证明它可以处理加权的数据。总的思路是提出一个支持合并和修剪操作的数据结构,每个操作都被证明保持一定的准确性水平。 证明见xgboost-supp.pdf。
3.4 Sparsity-aware Split Finding
真实数据很多都是稀疏的数据,有很多原因:1.数据中有缺失值。2.统计中频繁出现0条目。3.人工特征工程造成,例如one-hot。为了算法稀疏感知,我们每个树节点加入了默认方向,如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dY3NtZCZ-1635143234423)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System6.png)]
当数据值缺失的时候,样本被划分到默认方向,默认方向是通过学习数据获得的,其算法如下图Alg.3,关键提升在于只看不缺失的实例进入 I k I_k Ik,所提出的算法将不存在作为缺失值处理,并学习处理缺失值的最佳方向。 通过将枚举限制为恒定的解决方案,当不存在对应于用户指定的值时,也可以应用相同的算法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0iwZC1kU-1635143234423)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System10.png)]
大多数现有的树学习算法或者只是针对密集数据进行优化,或者需要特定的程序来处理有限的情况,如分类编码。 XGBoost以统一的方式处理所有的稀疏模式。 更重要的是,我们的方法利用稀疏性使计算复杂度与输入中非缺失条目的数量成线性关系。 图5显示了稀疏感知和对Allstate-10K数据集(的简单实现的比较。 我们发现稀疏感知算法的运行速度比原始版本快50倍。 这证实了稀疏感知算法的重要性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gbm1uCVI-1635143234424)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System8.png)]
4. SYSTEM DESIGN
4.1 Column Block for Parallel Learning
树学习最耗时的就是数据排序,为了减少时间,将数据存储进入划分的内存块单元,块内以压缩列格式(CSC)存储,每个列按相应的特征值排序。输入数据被排序一次,之后都可以复用。
在精确算法中,需要存储整个数据进入一个单块,之后整个数据排序分割,扫描整个块发现候选的划分叶子节点。具体如图,每一列通过相应的特征排序,在列中进行线性扫描。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pq5Ywm64-1635143234425)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System9.png)]
块结构也有助于使用近似算法。 在这种情况下可以使用多个块,每个块对应于数据集中行的子集。 不同的块可以分布在不同的机器上,也可以在磁盘外存储设置中存储。 使用排序结构,分位数查找步骤将成为对已排序列进行线性扫描。 这对于本地提议算法尤其有价值,其中在每个分支频繁地生成候选。 直方图聚合中的二进制搜索也成为线性时间合并样式算法。
收集每列的统计数据可以并行化,为我们提供了一个并行算法,用于分割查找。 重要的是,列块结构也支持列子采样,因为很容易选择块中的一个列的子集。
时间复杂度分析:d表示树的最大深度,k为树的总数量。
在精确贪心算法中,原始自适应稀疏算法复杂度为: O ( K d ∣ ∣ x ∣ ∣ 0 log n ) O(Kd||x||_0 \log n) O(Kd∣∣x∣∣0logn), ∣ ∣ x ∣ ∣ 0 ||x||_0 ∣∣x∣∣0表示非缺失实体,分块树算法中,时间复杂度: O ( K d ∣ ∣ x ∣ ∣ 0 + ∣ ∣ x ∣ ∣ 0 log n ) O(Kd||x||_0+||x||_0 \log n) O(Kd∣∣x∣∣0+∣∣x∣∣0logn),其中第二项是预处理开销,看出分块处理节省了 log n \log n logn开销。
近似计算中,原始算法复杂度 O ( K d ∣ ∣ x ^ ∣ ∣ 0 log q ) O(Kd||\hat x||_0 \log q) O(Kd∣∣x^∣∣0logq),q是候选的划分点数量,分块近似算法复杂度 O ( K d ∣ ∣ x ∣ ∣ 0 + ∣ ∣ x ∣ ∣ 0 log B ) O(Kd||x||_0+||x||_0 \log B) O(Kd∣∣x∣∣0+∣∣x∣∣0logB),其中B是块行数中的最大值。
4.2 Cache-aware Access
尽管所提出的块结构有助于优化拆分计算的计算复杂性,但新算法需要通过行索引间接提取梯度统计,因为这些值是按特征顺序访问的。 这是一个非连续的内存访问。 分裂枚举的一个简单实现引入了累积和非连续内存获取操作之间立即的读/写依赖性(见图8)。 当渐变统计信息不能进入CPU缓存并发生缓存未命中时,这会减慢拆分查找的速度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmpVVLQT-1635143234426)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System11.png)]
对于精确贪婪算法,我们可以通过缓存感知预取算法来缓解这个问题。 具体而言,我们在每个线程中分配一个内部缓冲区,将梯度统计信息提取到其中,然后以小批量方式执行累加。 此预取将直接读/写依赖性更改为更长的依赖关系,并有助于在行数很大时减少运行时开销。 图7给出了Higgs和All-state数据集中缓存感知和非缓存感知算法的比较。 当数据集很大时,我们发现精确贪婪算法的高速缓存感知实现运行的速度是原始版本的两倍。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCEMo4GV-1635143234427)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System7.png)]
对于近似算法,我们通过选择正确的块大小来解决问题。 我们将块大小定义为包含在块中的最大示例数,因为这反映了梯度统计的高速缓存存储成本。 选择过小的块大小会导致每个线程的工作量很小,导致高效的并行化。 另一方面,过大的块会导致缓存未命中,因为梯度统计信息不能进入CPU缓存。 块大小的一个好选择平衡这两个因素。 我们比较了两个数据集上的块大小的各种选择。 结果如图9所示。这个结果验证了我们的讨论,并且显示每块选择216个示例平衡了缓存属性和并行化。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tp43G2q2-1635143234427)(https://raw.githubusercontent.com/InsaneLife/MyPicture/master/论文阅读-XGBoost%20A%20Scalable%20Tree%20Boosting%20System12.png)]
4.3 Blocks for Out-of-core Computation
充分利用磁盘空间也十分重要,特别是内存不能加载完整个数据时。为了实现核外计算,我们将数据分成多个块,并将每个块存储在磁盘上。在计算过程中,使用一个独立的线程将块预取到主存储器中是非常重要的,因此可以在读取磁盘的同时进行计算。然而,这并不能完全解决这个问题,因为磁盘读取占用了大部分的计算时间。减少开销并增加磁盘IO的吞吐量非常重要。我们主要使用两种技术来改善核心外计算。
块压缩: 我们使用的第一种技术是块压缩。该块由列压缩,并在加载到主存储器时由独立线程解压缩。这有助于交换一些解压缩的计算与读取磁盘的成本。我们使用通用压缩算法来压缩特征值。对于行索引,我们将行索引减去块的开始索引,并使用一个16位整数来存储每一个集合。这需要每块216个示例,这被证实是一个好的设置。在我们测试的大部分数据集中,我们实现了大约26%到29%的压缩比。
块分片: 第二种技术是以另一种方式将数据分片到多个磁盘上。预取线程分配给每个磁盘,并将数据提取到内存缓冲区中。训练线程然后交替读取来自每个缓冲区的数据。这有助于在多个磁盘可用时提高磁盘读取的吞吐量
6. END TO END EVALUATIONS
6.1 System Implementation
开源XGBoost主要提供权重分类、排序目标函数,支持python、R、Julia,集成到了本地的数据管道如sklean。在分布式系统中,XGboost也支持Hadoop、MPI、Flink、spark。
具体实验结果见文末论文原文。
7. CONCLUSION
在本文中,我们描述了我们在构建XGBoost时学到的经验,XGBoost是一种可扩展的树型增强系统,被数据科学家广泛使用,并在许多问题上提供了最新的结果。 我们提出了一种处理稀疏数据的新颖的稀疏感知算法和一个理论上合理的加权分位图,用于近似学习。 我们的经验表明,高速缓存访问模式,数据压缩和分片是构建可扩展的树型增强端到端系统的基本要素。 这些经验教训也可以应用于其他机器学习系统。 通过结合这些见解,XGBoost能够使用最少量的资源来解决真实世界的规模问题。
csdn原文:http://blog.csdn.net/shine19930820/article/details/76635267