1.集成算法思想
看上面一个图例左边:有5个样本,现在想看下这5个人愿不愿意去玩游戏,这5个人现在都分到了叶子结点里面,对不同的叶子结点分配不同的权重项,正数代表这个人愿意去玩游戏,负数代表这个人不愿意去玩游戏。所以我们可以通过叶子结点和权值的结合,来综合的评判当前这个人到底是愿意还是不愿意去玩游戏。上面「tree1」那个小男孩它所处的叶子结点的权值是+2(可以理解为得分)。
用单个决策树好像效果一般来说不是太好,或者说可能会太绝对。通常我们会用一种集成的方法,就是一棵树效果可能不太好,用两棵树呢?
看图例右边的「tree2」,它和左边的不同在于它使用了另外的指标,出了年龄和性别,还可以考虑使用电脑频率这个划分属性。通过这两棵树共同帮我们决策当前这个人愿不愿意玩游戏,小男孩在「tree1」的权值是+2,在「tree2」的权值是+0.9, 所以小男孩最终的权值是+2.9(可以理解为得分是+2.9)。老爷爷最终的权值也是通过一样的过程得到的。
所以说,我们通常在做分类或者回归任务的时候,需要想一想一旦选择用一个分类器可能表达效果并不是很好,那么就要考虑用这样一个集成的思想。上面的图例只是举了两个分类器,其实还可以有更多更复杂的弱分类器,一起组合成一个强分类器。
2. XGBoost原理推导
预测值:
y
i
^
=
∑
j
w
j
x
i
j
\widehat{{{y}_{i}}}=\sum\limits_{j}{{{w}_{j}}{{x}_{ij}}}
yi
=j∑wjxij
目标函数:
l
(
y
i
,
y
i
^
)
=
(
y
i
−
y
i
^
)
2
l\left( {{y}_{i}},\widehat{{{y}_{i}}} \right)={{\left( {{y}_{i}}-\widehat{{{y}_{i}}} \right)}^{2}}
l(yi,yi
)=(yi−yi
)2
如何最优函数解?
集成算法的表示:
y
i
^
=
∑
k
=
1
K
f
k
(
x
i
)
,
f
k
∈
F
\widehat{{{y}_{i}}}=\sum\limits_{k=1}^{K}{{{f}_{k}}\left( {{x}_{i}} \right)},{{f}_{k}}\in \mathcal{F}
yi
=k=1∑Kfk(xi),fk∈F
在XGBoost里,每棵树是一个一个往里面加的,每加一个都是希望效果能够提升,下图就是XGBoost这个集成的表示(核心)。这里的新函数实际上是第t棵树。
现在还剩下一个问题,我们如何选择每一轮加入什么呢?答:选取一个f来使得我们的目标函数尽量最大化地降低。
目标函数:
O
b
j
(
t
)
=
∑
i
=
1
n
l
(
y
i
,
y
i
^
(
t
)
)
+
∑
i
=
1
t
Ω
(
f
i
)
Ob{{j}^{\left( t \right)}}=\sum\nolimits_{i=1}^{n}{l\left( {{y}_{i}},{{\widehat{{{y}_{i}}}}^{\left( t \right)}} \right)}+\sum\nolimits_{i=1}^{t}{\Omega \left( {{f}_{i}} \right)}
Obj(t)=∑i=1nl(yi,yi
(t))+∑i=1tΩ(fi)
=
∑
i
=
1
n
l
(
y
i
,
y
i
^
(
t
−
1
)
+
f
t
(
x
i
)
)
+
∑
i
=
1
t
Ω
(
f
i
)
+
c
o
n
s
t
a
n
t
\text{ =}\sum\nolimits_{i=1}^{n}{l\left( {{y}_{i}},{{\widehat{{{y}_{i}}}}^{\left( t-1 \right)}}+{{f}_{t}}\left( {{x}_{i}} \right) \right)}+\sum\nolimits_{i=1}^{t}{\Omega \left( {{f}_{i}} \right)}+constant
=∑i=1nl(yi,yi
(t−1)+ft(xi))+∑i=1tΩ(fi)+constant
Ω
(
f
i
)
\Omega \left( {{f}_{i}} \right)
Ω(fi)为正则惩罚项。
这里我们考虑平方损失函数,并把
∑
i
=
1
t
−
1
Ω
(
f
i
)
\sum\nolimits_{i=1}^{t-1}{\Omega \left( {{f}_{i}} \right)}
∑i=1t−1Ω(fi)归到常数项中:
目标函数:
O
b
j
(
t
)
=
∑
i
=
1
n
(
y
i
−
(
y
i
^
(
t
−
1
)
+
f
t
(
x
i
)
)
)
2
+
Ω
(
f
t
)
+
c
o
n
s
t
Ob{{j}^{\left( t \right)}}={{\sum\nolimits_{i=1}^{n}{\left( {{y}_{i}}-\left( {{\widehat{{{y}_{i}}}}^{\left( t-1 \right)}}+{{f}_{t}}\left( {{x}_{i}} \right) \right) \right)}}^{2}}+\Omega \left( {{f}_{t}} \right)+const
Obj(t)=∑i=1n(yi−(yi
(t−1)+ft(xi)))2+Ω(ft)+const
由于在第t轮中
y
i
{{y}_{i}}
yi和
y
i
^
(
t
−
1
)
{{\widehat{{{y}_{i}}}}^{\left( t-1 \right)}}
yi
(t−1)都是已知的,便可以在化简时将他们归到常数项中。
目标函数:
O
b
j
(
t
)
=
∑
i
=
1
n
[
2
(
y
i
^
(
t
−
1
)
−
y
i
)
f
t
(
x
i
)
+
f
t
(
x
i
)
2
]
+
Ω
(
f
t
)
+
c
o
n
s
t
Ob{{j}^{\left( t \right)}}=\sum\nolimits_{i=1}^{n}{\left[ 2\left( {{\widehat{{{y}_{i}}}}^{\left( t-1 \right)}}-{{y}_{i}} \right){{f}_{t}}\left( {{x}_{i}} \right)+{{f}_{t}}{{\left( {{x}_{i}} \right)}^{2}} \right]}+\Omega \left( {{f}_{t}} \right)+const
Obj(t)=∑i=1n[2(yi
(t−1)−yi)ft(xi)+ft(xi)2]+Ω(ft)+const
2
(
y
i
^
(
t
−
1
)
−
y
i
)
2\left( {{\widehat{{{y}_{i}}}}^{\left( t-1 \right)}}-{{y}_{i}} \right)
2(yi
(t−1)−yi)我们一般称之为残差。
除了平方损失的情况外,其他损失函数似乎还是很复杂,于是想到用泰勒展开式对目标函数进行分解:
目标函数:
O
b
j
(
t
)
=
∑
i
=
1
n
l
(
y
i
,
y
i
^
(
t
−
1
)
+
f
t
(
x
i
)
)
+
∑
i
=
1
t
Ω
(
f
i
)
+
c
o
n
s
t
a
n
t
Ob{{j}^{\left( t \right)}}\text{=}\sum\nolimits_{i=1}^{n}{l\left( {{y}_{i}},{{\widehat{{{y}_{i}}}}^{\left( t-1 \right)}}+{{f}_{t}}\left( {{x}_{i}} \right) \right)}+\sum\nolimits_{i=1}^{t}{\Omega \left( {{f}_{i}} \right)}+constant
Obj(t)=∑i=1nl(yi,yi
(t−1)+ft(xi))+∑i=1tΩ(fi)+constant
泰勒展开:
f
(
x
+
△
x
)
≃
f
(
x
)
+
f
′
(
x
)
△
x
+
1
2
f
′
′
(
x
)
△
x
2
f\left( x+\vartriangle x \right)\simeq f\left( x \right)+{{f}^{'}}\left( x \right)\vartriangle x+\frac{1}{2}{{f}^{''}}\left( x \right)\vartriangle {{x}^{2}}
f(x+△x)≃f(x)+f′(x)△x+21f′′(x)△x2
定义:
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 }_{{{\widehat{y}}^{\left( t-1 \right)}}}}l\left( {{y}_{i}},{{\widehat{y}}^{\left( t-1 \right)}} \right),{{h}_{i}}=\partial _{{{\widehat{y}}^{\left( t-1 \right)}}}^{2}l\left( {{y}_{i}},{{\widehat{y}}^{\left( t-1 \right)}} \right)
gi=∂y
(t−1)l(yi,y
(t−1)),hi=∂y
(t−1)2l(yi,y
(t−1))
目标函数:
O
b
j
(
t
)
=
∑
i
=
1
n
[
l
(
y
i
,
y
i
^
(
t
−
1
)
)
+
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
(
x
i
)
2
]
+
Ω
(
f
t
)
+
c
o
n
s
t
a
n
t
Ob{{j}^{\left( t \right)}}=\sum\nolimits_{i=1}^{n}{\left[ l\left( {{y}_{i}},{{\widehat{{{y}_{i}}}}^{\left( t-1 \right)}} \right)+{{g}_{i}}{{f}_{t}}\left( {{x}_{i}} \right)+\frac{1}{2}{{h}_{i}}{{f}_{t}}{{\left( {{x}_{i}} \right)}^{2}} \right]}+\Omega \left( {{f}_{t}} \right)+constant
Obj(t)=∑i=1n[l(yi,yi
(t−1))+gift(xi)+21hift(xi)2]+Ω(ft)+constant
利用泰勒展开三项,做一个近似,我们可以很清晰地看到,最终的目标函数只依赖于每个数据点在误差函数上的一阶导数和二阶导数。
将常数移除掉,则目标函数进一步化简:
目标函数:
O
b
j
(
t
)
=
∑
i
=
1
n
[
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
(
x
i
)
2
]
+
Ω
(
f
t
)
Ob{{j}^{\left( t \right)}}=\sum\nolimits_{i=1}^{n}{\left[ {{g}_{i}}{{f}_{t}}\left( {{x}_{i}} \right)+\frac{1}{2}{{h}_{i}}{{f}_{t}}{{\left( {{x}_{i}} \right)}^{2}} \right]}+\Omega \left( {{f}_{t}} \right)
Obj(t)=∑i=1n[gift(xi)+21hift(xi)2]+Ω(ft)
接下来我们正则惩罚项进行定义。对于f的定义做一下细化,把树拆分成结构部分q和叶子权重部分w。下图是一个具体的例子。结构函数q把输入映射到叶子的索引号上面去,而w给定了每个索引号对应的叶子分数是什么。
定义这个复杂度包含了一棵树里面节点的个数,以及每个树叶子节点上面输出分数的L2模平方。当然这不是唯一的一种定义方式,不过这一定义方式学习出的树效果一般都比较不错。下图还给出了复杂度计算的一个例子。
在这种新的定义下,我们可以把目标函数进行如下改写
定义
G
j
=
∑
i
∈
I
j
g
i
,
H
j
=
∑
i
∈
I
j
h
i
{{G}_{j}}\text{=}\sum\nolimits_{i\in {{I}_{j}}}{{{g}_{i}}},{{H}_{j}}=\sum\nolimits_{i\in {{I}_{j}}}{{{h}_{i}}}
Gj=∑i∈Ijgi,Hj=∑i∈Ijhi,再次将目标函数进行化简:
目标函数:
O
b
j
(
t
)
=
∑
j
=
1
T
[
(
∑
i
∈
I
j
g
i
)
w
j
+
1
2
(
∑
i
∈
I
j
h
i
+
λ
)
w
j
2
]
+
γ
T
Ob{{j}^{\left( t \right)}}=\sum\nolimits_{j=1}^{T}{\left[ \left( \sum\nolimits_{i\in {{I}_{j}}}{{{g}_{i}}} \right){{w}_{j}}+\frac{1}{2}\left( \sum\nolimits_{i\in {{I}_{j}}}{{{h}_{i}}}+\lambda \right){{w}_{j}}^{2} \right]}+\gamma T
Obj(t)=∑j=1T[(∑i∈Ijgi)wj+21(∑i∈Ijhi+λ)wj2]+γT
=
∑
j
=
1
T
[
G
j
w
j
+
1
2
(
H
j
+
λ
)
w
j
2
]
+
γ
T
\text{ =}\sum\nolimits_{j=1}^{T}{\left[ {{G}_{j}}{{w}_{j}}+\frac{1}{2}\left( {{H}_{j}}+\lambda \right){{w}_{j}}^{2} \right]}+\gamma T
=∑j=1T[Gjwj+21(Hj+λ)wj2]+γT
我们对
w
j
{{w}_{j}}
wj求导得:
w
j
∗
=
G
j
H
j
+
λ
w_{j}^{*}=\frac{{{G}_{j}}}{{{H}_{j}}+\lambda }
wj∗=Hj+λGj
把
w
j
∗
w_{j}^{*}
wj∗代入得:
O
b
j
=
−
1
2
∑
j
=
1
T
G
j
2
H
j
+
λ
+
γ
T
Obj=-\frac{1}{2}\sum\nolimits_{j=1}^{T}{\frac{G_{j}^{2}}{{{H}_{j}}+\lambda }}+\gamma T
Obj=−21∑j=1THj+λGj2+γT
Obj代表了我们指定一棵树的结构的时候,我们在目标上最多减少多少。我们可以把它叫作结构分数。你可以认为这个分数就是类似于基尼系数一样更加一般的对于树结构进行打分的函数。下面是一个具体的打分函数计算的例子:
贪心法:每一次尝试去对已有的叶子加入一个分割
对于每次扩展,我们还是要枚举所有可能的分割方案,如何高效地枚举所有的分割呢?我假设我们要枚举所有x < a 这样的条件,对于某个特定的分割a我们要计算a左边和右边的导数和。
我们可以发现对于所有的a,我们只要做一遍从左到右的扫描就可以枚举出所有分割的梯度和GL和GR。然后用上面的公式计算每个分割方案的分数就可以了。
观察这个目标函数,大家会发现第二个值得注意的事情就是引入分割不一定会使得情况变好,因为我们有一个引入新叶子的惩罚项。优化这个目标对应了树的剪枝,当引入的分割带来的增益小于一个阀值的时候,我们可以剪掉这个分割。大家可以发现,当我们正式地推导目标的时候,像计算分数和剪枝这样的策略都会自然地出现,而不再是一种heuristic(启发式)操作了。(启发式可以理解为拍脑袋想出来的,没有完整的理论证明)
3.总结
这里我们对GBDT和XGBoost进行比较。
3.1GBDT
GB算法中最典型的基学习器是决策树,尤其是CART,正如名字的含义,GBDT是GB和DT的结合。要注意的是这里的决策树是回归树,GBDT中的决策树是个弱模型,深度较小一般不会超过5,叶子节点的数量也不会超过10,对于生成的每棵决策树乘上比较小的缩减系数(学习率<0.1),有些GBDT的实现加入了随机抽样(subsample 0.5<=f <=0.8)提高模型的泛化能力。通过交叉验证的方法选择最优的参数。
CART分类树在很多书籍和资料中介绍比较多,但是再次强调GDBT中使用的是回归树。作为对比,先说分类树,我们知道CART是二叉树,CART分类树在每次分枝时,是穷举每一个feature的每一个阈值,根据GINI系数找到使不纯性降低最大的的feature以及其阀值,然后按照feature<=阈值,和feature>阈值分成的两个分枝,每个分支包含符合分支条件的样本。用同样方法继续分枝直到该分支下的所有样本都属于统一类别,或达到预设的终止条件,若最终叶子节点中的类别不唯一,则以多数人的类别作为该叶子节点的性别。回归树总体流程也是类似,不过在每个节点(不一定是叶子节点)都会得一个预测值,以年龄为例,该预测值等于属于这个节点的所有人年龄的平均值。分枝时穷举每一个feature的每个阈值找最好的分割点,但衡量最好的标准不再是GINI系数,而是最小化均方差–即(每个人的年龄-预测年龄)^2 的总和 / N,或者说是每个人的预测误差平方和 除以 N。这很好理解,被预测出错的人数越多,错的越离谱,均方差就越大,通过最小化均方差能够找到最靠谱的分枝依据。分枝直到每个叶子节点上人的年龄都唯一(这太难了)或者达到预设的终止条件(如叶子个数上限),若最终叶子节点上人的年龄不唯一,则以该节点上所有人的平均年龄作为该叶子节点的预测年龄。
3.2XGBoost
XGBoost是GB算法的高效实现,XGBoost中的基学习器除了可以是CART(gbtree)也可以是线性分类器(gblinear)。XGBoost算法的步骤和GB基本相同,都是首先初始化为一个常数,GBDT是根据一阶导数ri,XGBoost是根据一阶导数gi和二阶导数hi,迭代生成基学习器,相加更新学习器。
XGBoost在实现时还做了许多优化:
(1)在寻找最佳分割点时,考虑传统的枚举每个特征的所有可能分割点的贪心法效率太低,xgboost实现了一种近似的算法。大致的思想是根据百分位法列举几个可能成为分割点的候选者,然后从候选者中根据上面求分割点的公式计算找出最佳的分割点。
(2)XGBoost考虑了训练数据为稀疏值的情况,可以为缺失值或者指定的值指定分支的默认方向,这能大大提升算法的效率,paper提到50倍。
(3)特征列排序后以块的形式存储在内存中,在迭代中可以重复使用;虽然boosting算法迭代必须串行,但是在处理每个特征列时可以做到并行。
(4)按照特征列方式存储能优化寻找最佳的分割点,但是当以行计算梯度数据时会导致内存的不连续访问,严重时会导致cache miss,降低算法效率。paper中提到,可先将数据收集到线程内部的buffer,然后再计算,提高算法的效率。
(5)XGBoost还考虑了当数据量比较大,内存不够时怎么有效的使用磁盘,主要是结合多线程、数据压缩、分片的方法,尽可能的提高算法的效率。
参考文章:
https://blog.csdn.net/huacha__/article/details/81029680
https://www.cnblogs.com/zhouxiaohui888/p/6008368.html
https://www.cnblogs.com/wxquare/p/5541414.html
https://homes.cs.washington.edu/~tqchen/pdf/BoostedTree.pdf