GBDT(Gradient boosting decision tree)
boosting集成算法思想,多个基学习器之间是串行逻辑。
下一个基学习器学习的是上一个基学习器的负梯度(当损失函数是平方损失时,等价于学习残差),最后预测结果是所有基学习器预测结果相加。
由于残差只有回归场景下存在,所以GBDT基学习器都是回归树,但能通过预测类别概率来实现分类场景。
F
t
(
x
)
=
∑
i
=
1
t
f
i
(
x
)
F_{t}(x)=\sum_{i=1}^{t}f_i(x)
Ft(x)=∑i=1tfi(x)
l
(
y
,
F
t
(
x
)
)
=
l
(
y
,
F
t
−
1
(
x
)
+
(
F
t
(
x
)
−
F
t
−
1
(
x
)
)
)
l(y,F_{t}(x))=l(y,F_{t-1}(x)+(F_{t}(x)-F_{t-1}(x)))
l(y,Ft(x))=l(y,Ft−1(x)+(Ft(x)−Ft−1(x))) ,
根据泰勒一阶展开公式,
f
(
x
0
+
△
x
)
≃
f
(
x
0
)
+
f
′
(
x
0
)
△
x
f(x_0+\triangle x)\simeq f(x_0)+f^{'}(x_0)\triangle x
f(x0+△x)≃f(x0)+f′(x0)△x,
l
(
y
,
F
t
(
x
)
)
=
l
(
y
,
F
t
−
1
(
x
)
)
+
∂
l
(
y
,
F
t
−
1
)
∂
F
t
−
1
(
x
)
(
F
t
(
x
)
−
F
t
−
1
(
x
)
)
l(y,F_{t}(x))=l(y,F_{t-1}(x))+ \frac{\partial l(y,F_{t-1})}{\partial F_{t-1}(x)}(F_{t}(x)-F_{t-1}(x))
l(y,Ft(x))=l(y,Ft−1(x))+∂Ft−1(x)∂l(y,Ft−1)(Ft(x)−Ft−1(x))
如果想要
l
(
y
,
F
t
(
x
)
)
l(y,F_{t}(x))
l(y,Ft(x))尽可能的小,则
F
t
(
x
)
−
F
t
−
1
(
x
)
=
−
γ
∂
l
(
y
,
F
t
−
1
)
∂
F
t
−
1
(
x
)
F_{t}(x)-F_{t-1}(x)=-\gamma \frac{\partial l(y,F_{t-1})}{\partial F_{t-1}(x)}
Ft(x)−Ft−1(x)=−γ∂Ft−1(x)∂l(y,Ft−1)
f
t
(
x
)
=
−
γ
∂
l
(
y
,
F
t
−
1
)
∂
F
t
−
1
(
x
)
f_t(x)=-\gamma \frac{\partial l(y,F_{t-1})}{\partial F_{t-1}(x)}
ft(x)=−γ∂Ft−1(x)∂l(y,Ft−1)
所以每个基学习器学习的都是损失函数的负梯度
当损失函数是平方损失时,负梯度刚好是残差
l
(
y
,
F
t
(
x
)
)
=
(
y
−
F
t
(
x
)
)
2
l(y,F_{t}(x))=(y-F_{t}(x))^2
l(y,Ft(x))=(y−Ft(x))2时,
f
t
(
x
)
=
2
γ
(
y
−
F
t
−
1
(
x
)
)
=
2
γ
(
y
−
y
^
t
−
1
)
f_t(x)=2\gamma(y-F_{t-1}(x))=2\gamma (y-\hat{y}_{t-1})
ft(x)=2γ(y−Ft−1(x))=2γ(y−y^t−1)
XGBoost(Extreme Gradient Boosting)
基本原理
作为GBDT扩展版。
第
t
t
t个基学习器预测结果
y
^
i
(
t
)
=
y
^
i
(
t
−
1
)
+
f
(
t
)
(
x
i
)
\hat{y}^{(t)}_{i}=\hat{y}^{(t-1)}_{i}+f^{(t)}(x_i)
y^i(t)=y^i(t−1)+f(t)(xi),
i
=
1
,
.
.
.
,
n
i=1,...,n
i=1,...,n
损失函数
l
(
y
i
,
y
^
i
(
t
)
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
(
t
−
1
)
+
f
(
t
)
(
x
i
)
)
l(y_{i},\hat{y}^{(t)}_{i})=\sum_{i=1}^{n}l(y_{i},\hat{y}^{(t-1)}_{i}+f^{(t)}(x_i))
l(yi,y^i(t))=∑i=1nl(yi,y^i(t−1)+f(t)(xi))
目标函数
o
b
j
(
t
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
(
t
)
)
+
∑
k
=
1
t
Ω
(
f
(
k
)
)
obj^{(t)}=\sum_{i=1}^{n}l(y_{i},\hat{y}^{(t)}_{i})+\sum_{k=1}^{t}\Omega (f^{(k)})
obj(t)=∑i=1nl(yi,y^i(t))+∑k=1tΩ(f(k)),正则项
Ω
(
f
(
k
)
)
=
1
2
λ
∑
j
=
1
T
(
k
)
w
j
2
+
γ
T
(
k
)
\Omega(f^{(k)})=\frac{1}{2}\lambda \sum_{j=1}^{ T^{(k)}} w^2_j+ \gamma T^{(k)}
Ω(f(k))=21λ∑j=1T(k)wj2+γT(k),
w
j
w_j
wj代表第
j
j
j个叶子节点的权重(?),
T
(
k
)
T^{(k)}
T(k)表示叶子节点数。
根据泰勒二阶展开公式,
f
(
x
0
+
△
x
)
≃
f
(
x
0
)
+
f
′
(
x
0
)
△
x
+
1
2
f
′
′
(
x
0
)
△
x
2
f(x_0+\triangle x)\simeq f(x_0)+f^{'}(x_0)\triangle x+\frac{1}{2}f^{''}(x_0){\triangle x}^2
f(x0+△x)≃f(x0)+f′(x0)△x+21f′′(x0)△x2,
所以xgboost可以自定义损失函数,只要损失函数一阶与二阶可导,
o
b
j
(
t
)
=
∑
i
=
1
n
(
l
(
y
i
,
y
^
i
(
t
−
1
)
)
+
l
′
(
y
i
,
y
^
i
(
t
−
1
)
)
f
(
t
)
(
x
i
)
+
1
2
l
′
′
(
y
i
,
y
^
i
(
t
−
1
)
)
f
(
t
)
2
(
x
i
)
)
+
∑
k
=
1
t
Ω
(
f
(
k
)
)
obj^{(t)}=\sum_{i=1}^{n}(l(y_{i},\hat{y}^{(t-1)}_{i})+l^{'}(y_{i},\hat{y}^{(t-1)}_{i})f^{(t)}(x_{i})+\frac{1}{2}l^{''}(y_{i},\hat{y}^{(t-1)}_{i}){f^{(t)}}^2(x_{i}))+\sum_{k=1}^{t}\Omega (f^{(k)})
obj(t)=∑i=1n(l(yi,y^i(t−1))+l′(yi,y^i(t−1))f(t)(xi)+21l′′(yi,y^i(t−1))f(t)2(xi))+∑k=1tΩ(f(k))
由于构建第
t
t
t个基学习器时,第
t
−
1
t-1
t−1个基学习器已构建完,
l
(
y
i
,
y
^
i
(
t
)
)
l(y_{i},\hat{y}^{(t)}_{i})
l(yi,y^i(t))、
g
i
=
l
′
(
y
i
,
y
^
i
(
t
−
1
)
)
g_i=l^{'}(y_{i},\hat{y}^{(t-1)}_{i})
gi=l′(yi,y^i(t−1))、
h
i
=
l
′
′
(
y
i
,
y
^
i
(
t
−
1
)
)
h_i=l^{''}(y_{i},\hat{y}^{(t-1)}_{i})
hi=l′′(yi,y^i(t−1))为常数项,待优化的目标函数可简化为
o
b
j
=
∑
i
=
1
n
(
g
i
f
(
x
i
)
+
1
2
h
i
f
2
(
x
i
)
)
+
Ω
(
f
)
obj= \sum_{i=1}^{n}(g_if(x_{i})+\frac{1}{2}h_i{f}^2(x_{i}))+\Omega (f)
obj=∑i=1n(gif(xi)+21hif2(xi))+Ω(f)
Xgboost不仅支持决策树作为基学习器,还支持线性模型,这里以基学习器是决策树为例继续介绍:
接下来可将样本下标
i
i
i转换成样本所属的叶子节点下标
j
j
j,从而简化式子(因为节点数比样本数少,且同一个节点数上的预测结果相同,相当于聚合了一层)。
设
j
=
q
(
x
i
)
j=q(x_i)
j=q(xi),
q
q
q代表的是决策树结构、说明样本x最后会被划分到第几个叶子节点上,则
f
(
x
i
)
=
w
q
(
x
i
)
=
w
j
f(x_i)=w_{q(x_i)}=w_j
f(xi)=wq(xi)=wj
o
b
j
=
∑
i
=
1
n
(
g
i
w
q
(
x
i
)
+
1
2
h
i
w
q
(
x
i
)
2
)
+
1
2
λ
∑
j
=
1
T
w
j
2
+
γ
T
=
∑
j
=
1
∣
T
∣
(
∑
q
(
x
i
)
∈
I
j
(
g
i
w
j
+
1
2
h
i
w
j
2
)
)
+
1
2
λ
∑
j
=
1
T
w
j
2
+
γ
T
obj= \sum_{i=1}^{n}(g_iw_{q(x_i)}+\frac{1}{2}h_iw^2_{q(x_i)})+\frac{1}{2}\lambda \sum_{j=1}^{ T} w^2_j+ \gamma T=\sum_{j=1}^{|T|}(\sum_{q(x_i) \in I_j}(g_iw_{j}+\frac{1}{2}h_iw^2_{j}))+\frac{1}{2}\lambda \sum_{j=1}^{ T} w^2_j+ \gamma T
obj=∑i=1n(giwq(xi)+21hiwq(xi)2)+21λ∑j=1Twj2+γT=∑j=1∣T∣(∑q(xi)∈Ij(giwj+21hiwj2))+21λ∑j=1Twj2+γT,
设
G
j
=
∑
q
(
x
i
)
∈
I
j
g
i
G_j=\sum_{q(x_i) \in I_j}g_i
Gj=∑q(xi)∈Ijgi,
H
j
=
∑
q
(
x
i
)
∈
I
j
h
i
H_j=\sum_{q(x_i) \in I_j}h_i
Hj=∑q(xi)∈Ijhi,
o
b
j
=
∑
j
=
1
T
(
G
j
w
j
+
1
2
H
j
w
j
2
)
+
1
2
λ
∑
j
=
1
T
w
j
2
+
γ
T
=
∑
j
=
1
T
(
G
j
w
j
+
1
2
(
H
j
+
λ
)
w
j
2
)
+
γ
T
obj=\sum_{j=1}^{T}(G_jw_{j}+\frac{1}{2}H_jw^2_{j})+\frac{1}{2}\lambda \sum_{j=1}^{T} w^2_j+ \gamma T=\sum_{j=1}^{T}(G_jw_{j}+\frac{1}{2}(H_j+\lambda)w^2_{j}) + \gamma T
obj=∑j=1T(Gjwj+21Hjwj2)+21λ∑j=1Twj2+γT=∑j=1T(Gjwj+21(Hj+λ)wj2)+γT
∂
o
b
j
∂
w
j
=
G
j
+
(
H
j
+
λ
)
w
j
=
0
\frac{\partial{obj}}{\partial{w_j}}=G_j+(H_j+\lambda)w_{j}=0
∂wj∂obj=Gj+(Hj+λ)wj=0,
w
j
=
−
G
j
H
j
+
λ
w_j=-\frac{G_j}{H_j+\lambda}
wj=−Hj+λGj,
o
b
j
m
i
n
=
∑
j
=
1
T
(
−
G
j
2
2
(
H
j
+
λ
)
)
+
γ
T
obj_{min}=\sum_{j=1}^{T}(-\frac{{G_j}^2}{2(H_j+\lambda)}) + \gamma T
objmin=∑j=1T(−2(Hj+λ)Gj2)+γT
-
分裂节点的特征选择标准: G a i n = o b j − o b j n e w Gain=obj-obj_{new} Gain=obj−objnew,某个叶子节点 j j j分裂后 o b j n e w = o b j l e f t + o b j r i g h t obj_{new}=obj_{left}+obj_{right} objnew=objleft+objright,此时其他叶子节点不变化,则有
G a i n = o b j − o b j n e w = 1 2 ( G j l e f t 2 H j l e f t + λ + G j r i g h t 2 H j r i g h t + λ − G j 2 H j + λ ) − γ Gain=obj-obj_{new}=\frac{1}{2}(\frac{{G_{j_{left}}}^2}{H_{j_{left}}+\lambda}+\frac{{G_{j_{right}}}^2}{H_{j_{right}}+\lambda}-\frac{G_j^2}{H_j+\lambda})- \gamma Gain=obj−objnew=21(Hjleft+λGjleft2+Hjright+λGjright2−Hj+λGj2)−γ
G j = G j l e f t + G j r i g h t G_j=G_{j_{left}}+G_{j_{right}} Gj=Gjleft+Gjright, H j = H j l e f t + H j r i g h t H_j=H_{j_{left}}+H_{j_{right}} Hj=Hjleft+Hjright -
根据分裂粒度分为两种方式
- 精确算法:遍历所有样本
- 近似算法:类似直方图
-
特征重要性计算方式:
- 比较特征分裂带来的增益
- 比较袋外误差,拿出一部分训练数据,作为测试集计算误差,再对测试集单独特征进行打乱再计算误差,利用两次误差的差值作为特征重要性
对比GBDT的优化之处
- 引入二阶泰勒展开,提高拟合精确率,支持自定义损失函数,支持线性分类器
- 加入正则项,提高泛化性
- 预排序,实现特征并行,针对特征分裂进行优化
- Block(?到底是啥呢)存储一个或多个特征,不同block可以在不同的机器上在构造基学习器前进行排序(缺失值除外),并利用分位数作为候选分裂阈值
- 以稀疏格式(Compressed Sparse Column Format,CSC)存储
- 存储特征值对应的样本索引,从而获取梯度,注意这里获取顺序是按特征值大小顺序进行会造成访问非连续的内存空间,使得CPU cache缓存命中率低从而影响算法效率,所以需要缓存优化
- 缓存优化
- 精确算法采用缓存预取,对每个线程分配一个连续的buffer存储梯度,再进行统计
- 近似算法设置合理block大小,大小定义为block中最多的样本数,设置合适的大小是很重要的,设置过大则容易导致命中率低,过小则容易导致并行化效率不高。
- Block先放在磁盘用时再读入内存,计算的时候,使用独立的线程预先将Block放入主内存,因此可以在计算的同时读取磁盘。但是由于磁盘IO速度太慢,通常更不上计算的速度。因此,需要提升磁盘IO的销量。Xgboost采用了2个策略:
- 列压缩
- 不同的磁盘
- 特征随机采样(类似随机森林)
- 缺失值处理:特征值缺失的样本不参与分裂,缺失样本归为子节点后能够最大化增益的子节点作为缺失样本的默认方向
- shrinkage:每棵树学习的是上一个树梯度乘以某个小于等于1的系数
LightGBM(Light gradient boosting machine)
LightGBM 由微软提出,主要用于解决 GDBT 在海量数据中遇到的问题,以便其可以更好更快地用于工业实践中。
对比XGB的优化之处
-
树的生长策略
- xgb是level-wise,level-wise指在树分裂的过程中,同一层的非叶子节点,只要继续分裂能够产生正的增益就继续分裂下去,容易并行,但不利于达到较优预测效果
- lgb是leaf-wise,同一层的非叶子节点仅仅选择分裂增益最大的叶子节点进行分裂,一般要用最大深度防止树过深导致的过拟合
-
节点分裂规则
- 基于直方图
- 对每个特征构建直方图(构建规则?),并统计每个bin的一阶/二阶梯度之和和样本数量,遍历所有bin,取增益最大的bin分割,这里对特征值离散化也自带了降低过拟合的效果
- 计算左边、再用父节点-左边=右边得到梯度之和和样本总量,从而计算出增益(这里好像和XGB不一样)
- 单边梯度采样
- 对梯度小的样本采样后参与分裂时增益计算,计算值会除以采样比例用于近似得到原本不采样的结果
- 基于直方图
-
类别特征
- 组合同一特征的不同值进行分裂,降低类别特征样本分布稀疏导致的增益变化偏小(?)
- 捆绑互斥特征进行分裂,降低特征数量、提高泛化性(?)
-
降低通信代价
- 特征并行(当数据量相对较小,特征相对较大)
- 每个worker留有一份完整的数据集,但是每个worker仅在特征子集上进行最佳切分点的寻找;worker之间需要相互通信,通过比对损失来确定最佳切分点;然后将这个最佳切分点的位置进行全局广播,每个worker进行切分即可。
- xgb的特征并行与lgbm的最大不同在于xgb每个worker节点中仅有部分的列数据,也就是垂直切分,每个worker寻找局部最佳切分点,worker之间相互通信,然后在具有最佳切分点的worker上进行节点分裂,再由这个节点广播一下被切分到左右节点的样本索引号,其他worker才能开始分裂。二者的区别就导致了lgbm中worker间通信成本明显降低,只需通信一个特征分裂点即可,而xgb中要广播样本索引。
- 数据并行(当数据量很大,特征相对较少)
- 先对数据水平切分,每个worker上的数据先建立起局部的直方图,然后合并成全局的直方图,采用直方图相减的方式,先计算样本量少的节点的样本索引,然后直接相减得到另一子节点的样本索引,这个直方图算法使得worker间的通信成本降低一倍,因为只用通信样本量少的子节点的样本索引。xgb中的数据并行也是水平切分,然后单个worker建立局部直方图,再合并为全局,不同在于根据全局直方图进行各个worker上的节点分裂时会单独计算子节点的样本索引,因此效率贼慢,每个worker间的通信量也就变得很大。
- 投票并行(当数据量和维度都很大)
- 数据并行中的合并直方图的代价相对较大,尤其是当特征维度很大时。大致思想是:每个worker首先会找到本地的一些优秀的特征,然后进行全局投票,根据投票结果,选择top的特征进行直方图的合并,再寻求全局的最优分割点。
- 特征并行(当数据量相对较小,特征相对较大)
-
缓存优化
- 预排序中有2个操作频繁的地方会造成cache miss,一是对梯度的访问,在计算gain的时候需要利用梯度,不同特征访问梯度的顺序都是不一样的,且是随机的,因此这部分会造成严重的cache-miss。二是对于索引表的访问,预排序使用了一个行号到叶子节点号的索引表(row_idx_to_tree_node_idx ),来防止数据切分时对所有的数据进行切分,即只对该叶子节点上的样本切分。在与level-wise进行结合的时候, 每一个叶子节点都要切分数据,这也是随机的访问。这样会带来严重的系统性能下降。而直方图算法则是天然的cache friendly。在直方图算法的第3个for循环的时候,就已经统计好了每个bin的梯度,因此,在计算gain的时候,只需要对bin进行访问,造成的cache-miss问题会小很多。
随机森林和GBDT的异同点
1、 组成随机森林的树可以是回归树, 也可以是分类树; 而 GBDT 只由回归树组成
2、 组成随机森林的树可以并行生成; 而 GBDT 只能是串行生成
3、 对于最终的输出结果而言, 随机森林采用多数投票等; 而 GBDT 则是将所有结果累加起来, 或者加权累加起来
4、 随机森林对异常值不敏感, GBDT 对异常值非常敏感
5、 随机森林对训练集一视同仁, GBDT 是基于权值的弱分类器的集成
6、 随机森林是通过减少模型方差提高性能, GBDT 是通过减少模型偏差提高性能
参考资料
★【机器学习】决策树(下)——XGBoost、LightGBM(非常详细)
最详细的XGBoost推导过程–关于XGBoost我所知道的一切
稀疏矩阵的存储格式CSC理解
通俗、有逻辑的写一篇说下Xgboost的原理,供讨论参考
梯度下降与一阶泰勒展开
gbdt的残差为什么用负梯度代替?
gbdt、xgb、lgb、cat面经整理
XGBoost的优化方法
Lightgbm 直方图优化算法深入理解
Xgboost系统设计:分块并行、缓存优化和Blocks for Out-of-core Computation
其他
不严谨地说,cache的内容是ram的一个子集,而物理上cache访问速度高于ram,当读取某地址a,若a在cache中,则直接从cache快速读取,称为cache命中,否则需要从ram中读取并更新到cache中,称为cache miss。当跑一个benchmark,使用系统perf工具等方式统计运行过程中cache 命中占cache总访问次数之比,为命中率。一般从两个角度优化,soc设计时用于调整cache大小和层级方案,优化软件算法时用于调整算法中mem步长,mem块大小等,提高速度
https://www.zhihu.com/question/388423021/answer/1159310071