什么是集成学习
集成学习是传统机器学习的基础上运用了一个重要思想:将多个弱分类器按照某种方法组合在一起,形成一个强分类器。(三个臭皮匠赛过诸葛亮)
-
Bagging:把数据集通过有放回的抽样,划分为多个数据集,然后分别训练多个模型。针对分类问题,按照少数服从多数的原则进行投票,针对回归问题,求多个预测结果的平均值。
-
Stacking:通常是不同的模型,而且每个分类器都用了全部的训练数据,得到预测结果 y 1 , y 2 , . . . , y n y_1,y_2,...,y_n y1,y2,...,yn,然后在训练一个分类器Meta Classifier,将这些预测的结果作为输入,得到最终的预测结果。
-
Boosting:与Bagging相同的地方在于同样使用多个弱分类器,不同的地方在于它是通过串行的方式学习这些弱分类器,即每个弱学习器都依赖于前面的模型,并以某种确定性的策略将他们组合起来。
Bagging和Boosting的区别
- 在结构上,Bagging是基分类器并行处理,而Boosting是串行处理
- 训练集上,Bagging的基分类器训练是独立的,而Boosting的训练集是依赖于之前的模型
- 作用,Bagging的作用是减少Variance,而Boosting在于减少Bias
- 对于Bagging,对样本进行重采样,通过重采样得到的子样本集训练模型,最后取平均。因为子样本集的相似性,而且使用相同的弱分类器,因此每个学习器有相似的Bias和Variance,因为每个学习器相互独立,所以可以显著降低Variance,但是无法降低Bias。
- 对于Boosting,采用顺序的方式最小化损失函数,所以Bias是逐步下降的,而子模型之和不能显著的降低Variance。
集成学习模型的时间线:
- XGBoost, LightGBM, CatBoost, NGBoost实际上是对GBDT方法的不同实现,针对同一目标、做了不同的优化处理.
GBDT
- 简单原理:每一棵树学习的是上一颗树留下的残差,最终的结果等于所有树的结果相加。
算法流程
- 初始化 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0
- 每生成一棵树,都会产生残差 r = y i − f i − 1 ( x ) , i = 1 , 2 , 3 , . . . , n r=y_i-f_{i-1}(x),i=1,2,3,...,n r=yi−fi−1(x),i=1,2,3,...,n
- 将上一颗树生成的残差作为下一颗树拟合的目标,即有一颗新的树 h i ( x ) h_i(x) hi(x)
- 每生成一棵树,更新GBDT集成模型 f i ( x ) = f i − 1 ( x ) + h i ( x ) f_i(x) = f_{i-1}(x) + h_i(x) fi(x)=fi−1(x)+hi(x)
- 获得最终的结果 f i ( x ) = ∑ i = 1 n h i ( x ) f_i(x) = \sum^{n}_{i=1}h_i(x) fi(x)=∑i=1nhi(x)
f 0 = 0 初 始 化 模 型 f 1 = f 0 + h 1 = h 1 计 算 首 颗 树 h 1 的 结 果 , 将 其 加 入 的 模 型 中 , 并 获 得 残 差 r 1 f 2 = f 1 + h 2 = h 2 + h 1 根 据 r 1 作 为 新 的 l a b e l 进 行 拟 合 , 计 算 得 出 第 二 棵 树 h 2 , 将 其 加 入 到 模 型 中 , 并 获 得 残 差 r 2 f 3 = f 2 + h 3 = h 3 + h 2 + h 1 将 r 2 作 为 新 的 l a b e l 进 行 拟 合 , 计 算 出 第 三 棵 树 h 3 , 将 其 加 入 到 模 型 中 , 并 获 得 残 差 r 3 . . . f n = f n − 1 + h n = h 1 + h 2 + h 3 + . . . + h n 将 r n − 1 的 结 果 作 为 新 的 l a b e l 进 行 拟 合 , 计 算 出 第 n 棵 树 h n , 并 将 其 加 入 到 模 型 中 , 获 得 残 差 r n \begin{aligned} &f_0 = 0\ 初始化模型\\ &f_1 = f_0+h_1=h_1\ 计算首颗树h_1的结果,将其加入的模型中,并获得残差r_1\\ &f_2 = f_1+h_2=h_2+h_1\ 根据r_1作为新的label进行拟合,计算得出第二棵树h_2,将其加入到模型中,并获得残差r_2\\ &f_3 = f_2+h_3 = h_3+h_2+h_1\ 将r_2作为新的label进行拟合,计算出第三棵树h_3,将其加入到模型中,并获得残差r_3\\ &...\\ &f_n = f_{n-1}+h_n = h_1+h_2+h_3+...+h_n\ 将r_{n-1}的结果作为新的label进行拟合,计算出第n棵树h_n,并将其加入到模型中,获得残差r_n \end{aligned} f0=0 初始化模型f1=f0+h1=h1 计算首颗树h1的结果,将其加入的模型中,并获得残差r1f2=f1+h2=h2+h1 根据r1作为新的label进行拟合,计算得出第二棵树h2,将其加入到模型中,并获得残差r2f3=f2+h3=h3+h2+h1 将r2作为新的label进行拟合,计算出第三棵树h3,将其加入到模型中,并获得残差r3...fn=fn−1+hn=h1+h2+h3+...+hn 将rn−1的结果作为新的label进行拟合,计算出第n棵树hn,并将其加入到模型中,获得残差rn
XGBoost
Xgboost在GBDT的基础上主要做了以下几项优化:
在目标函数上引入了正则项
Ω
(
f
t
)
\Omega\left(f_{t}\right)
Ω(ft),用于控制树的复杂度,防止过拟合
Obj
(
Θ
)
=
L
(
Θ
)
+
Ω
(
Θ
)
L
(
Θ
)
:
损
失
函
数
Ω
(
Θ
)
:
正
则
化
项
Ω
(
f
t
)
=
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
T
:
叶
子
数
量
w
j
:
叶
子
分
数
的
L
2
正
则
项
γ
:
加
入
新
叶
子
节
点
引
入
的
复
杂
度
代
价
\operatorname{Obj}(\Theta)=L(\Theta)+\Omega(\Theta)\\L(\Theta):损失函数\\\Omega(\Theta):正则化项\\ \Omega\left(f_{t}\right)=\gamma T+\frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^{2}\\T:叶子数量\\w_j:叶子分数的L2正则项\\\gamma:加入新叶子节点引入的复杂度代价
Obj(Θ)=L(Θ)+Ω(Θ)L(Θ):损失函数Ω(Θ):正则化项Ω(ft)=γT+21λj=1∑Twj2T:叶子数量wj:叶子分数的L2正则项γ:加入新叶子节点引入的复杂度代价
对目标函数改进,进行二阶泰勒函数展开
- XGBoost的目标函数 :
O b j t = ∑ n l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) + constant O b j^{t}=\sum^{n} l\left(y_{i}, \hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)\right)+\Omega\left(f_{t}\right)+\text { constant }\\ Objt=∑nl(yi,y^i(t−1)+ft(xi))+Ω(ft)+ constant - 对目标函数改进,进行二阶泰勒展开:
f ( x + Δ x ) ≈ f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) Δ x 2 f(x+\Delta x) \approx f(x)+f^{\prime}(x) \Delta x+\frac{1}{2} f^{\prime \prime}(x) \Delta 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 ) ) \begin{aligned} &g_{i}=\partial_{\hat{y}^{(t-1)}} l\left(y_{i}, \hat{y}^{(t-1)}\right) \\ &h_{i}=\partial_{\hat{y}^{(t-1)}}^{2} l\left(y_{i}, \hat{y}^{(t-1)}\right) \end{aligned} 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 2 ( x i ) ] + Ω ( f t ) + constant O b j^{t} \approx \sum_{i=1}^{n}\left[l\left(y_{i}, \hat{y}_{i}^{(t-1)}\right)+g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{t}\right)+\text { constant } Objt≈i=1∑n[l(yi,y^i(t−1))+gift(xi)+21hift2(xi)]+Ω(ft)+ constant
O b j t ≈ ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) = ∑ i = 1 n [ g i w q ( x i ) + 1 2 h i w q ( x i ) 2 ] + γ 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 \begin{aligned} O b j^{t} & \approx \sum_{i=1}^{n}\left[g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{t}\right) \\ &=\sum_{i=1}^{n}\left[g_{i} w_{q\left(x_{i}\right)}+\frac{1}{2} h_{i} w_{q\left(x_{i}\right)}^{2}\right]+\gamma T+\lambda \frac{1}{2} \sum_{j=1}^{T} w_{j}^{2} \\ &=\sum_{j=1}^{T}\left[\left(\sum_{i \in I_{j}} g_{i}\right) w_{j}+\frac{1}{2}\left(\sum_{i \in I_{j}} h_{i}+\lambda\right) w_{j}^{2}\right]+\gamma T \end{aligned} Objt≈i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)=i=1∑n[giwq(xi)+21hiwq(xi)2]+γT+λ21j=1∑Twj2=j=1∑T⎣⎡⎝⎛i∈Ij∑gi⎠⎞wj+21⎝⎛i∈Ij∑hi+λ⎠⎞wj2⎦⎤+γT - T为叶子节点数量
- I j I_j Ij定义为每个叶子节点里面的样本集合 I j = { i ∣ q ( x i ) = j } I_{j}=\left\{i \mid q\left(x_{i}\right)=j\right\} Ij={i∣q(xi)=j}
- f t ( x i ) = w q ( x i ) f_{t}\left(x_{i}\right)=w_{q\left(x_{i}\right)} ft(xi)=wq(xi)即每个样本所在叶子节点索引的分数(叶子权重w)
令 G j , H j G_j,H_j Gj,Hj分别表示为每个叶子节点的一阶梯度的和,与二阶梯度的和
- G j = ∑ i ∈ I j g i G_{j}=\sum_{i \in I_{j}} g_{i} Gj=∑i∈Ijgi
- H j = ∑ i ∈ I j h i H_{j}=\sum_{i \in I_{j}} h_{i} 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
=
∑
j
=
1
T
[
G
j
w
j
+
1
2
(
H
j
+
λ
)
w
j
2
]
+
γ
T
\begin{aligned} O b j^{t} &=\sum_{j=1}^{T}\left[\left(\sum_{i \in I_{j}} g_{i}\right) w_{j}+\frac{1}{2}\left(\sum_{i \in I_{j}} h_{i}+\lambda\right) w_{j}^{2}\right]+\gamma T \\ &=\sum_{j=1}^{T}\left[G_{j} w_{j}+\frac{1}{2}\left(H_{j}+\lambda\right) w_{j}^{2}\right]+\gamma T \end{aligned}
Objt=j=1∑T⎣⎡⎝⎛i∈Ij∑gi⎠⎞wj+21⎝⎛i∈Ij∑hi+λ⎠⎞wj2⎦⎤+γT=j=1∑T[Gjwj+21(Hj+λ)wj2]+γT
求偏导得:
∂
O
b
j
∂
w
j
=
G
j
+
(
H
j
+
λ
)
w
j
=
0
\frac{\partial O b j}{\partial w_{j}}=G_{j}+\left(H_{j}+\lambda\right) w_{j}=0
∂wj∂Obj=Gj+(Hj+λ)wj=0
求解得:
w
j
=
−
G
j
H
j
+
λ
O
b
j
=
−
1
2
∑
j
=
1
T
G
j
2
H
j
+
λ
+
γ
T
w_{j}=-\frac{G_{j}}{H_{j}+\lambda} \quad O b j=-\frac{1}{2} \sum_{j=1}^{T} \frac{G_{j}^{2}}{H_{j}+\lambda}+\gamma T
wj=−Hj+λGjObj=−21j=1∑THj+λGj2+γT
上述中的
O
b
j
Obj
Obj函数代表结构函数(打分函数),代表当指定一个树的结构的时候,我们在目标上最多可以减少多少。
即
O
b
j
=
−
1
2
∑
j
G
j
2
H
j
+
λ
+
3
γ
O b j=-\frac{1}{2} \sum_{j} \frac{G_{j}^{2}}{H_{j}+\lambda}+3 \gamma
Obj=−21∑jHj+λGj2+3γ的分数越小越好,树的结构越稳定。
但是这需要穷举所有的可能,其中的计算量太大。然后提出了贪心法,即利用打分函数计算增益。
以Gain作为是否分割的条件,Gain看作是未分割前的
O
b
j
Obj
Obj减去分割后的左右
O
b
j
Obj
Obj:
Gain
=
1
2
[
G
L
2
H
L
+
λ
+
G
R
2
H
R
+
λ
−
(
G
L
+
G
R
)
2
H
L
+
H
R
+
λ
]
−
γ
\text { Gain }=\frac{1}{2}\left[\frac{G_{L}^{2}}{H_{L}+\lambda}+\frac{G_{R}^{2}}{H_{R}+\lambda}-\frac{\left(G_{L}+G_{R}\right)^{2}}{H_{L}+H_{R}+\lambda}\right]-\gamma
Gain =21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
如果Gain小于,则此节点不做分割,分割方案个数很多,计算量依然很大。
总结一下:
- 贪心方法,获取最优分割节点(split point),将所有样本按照gi从小到大排序,通过遍历,查看每个节点是否需要分割
- 对于特征值的个数为n时,总共有n−1种划分
- Step1,对样本扫描一遍,得出GL,GR
- Step2,根据Gain的分数进行分割
- 通过贪心法,计算效率得到大幅提升,XGBoost重新定义划分属性,即Gain,而Gain的计算是由目标损失函数obj决定的
很多聪明的小伙伴可以看出,如果要使用贪心方法,对于对于连续型特征值,样本数量非常大,该特征取值过多时,遍历所有取值会花费很多时间,且容易过拟合。
于是在后续的发展中,Xgboost提出了分裂节点算法,近似算法-Histogram。
它的思想是在寻找split节点时,不会枚举所有的特征值,而会对特征值做聚合统计,形成若干个bucket(桶),只对bucket边界上的特征值作为split节点的候选,从而获得性能上的提升。
Xgboost算法的特点
- XGBoost将树模型的复杂度加入到正则项中,从而避免过拟合,泛化性能好
损失函数是用泰勒展开式展开的,用到了一阶导和二阶导,可以加快优化速度 - 在寻找最佳分割点时,采用近似贪心算法,用来加速计算
- 不仅支持CART作为基分类器,还支持线性分类器,在使用线性分类器的时候可以使用L1,L2正则化
- 支持并行计算,XGBoost的并行是基于特征计算的并行,将特征列排序后以block的形式存储在内存中,在后面的迭代中重复使用这个结构。在进行节点分裂时,计算每个特征的增益,选择增益最大的特征作为分割节点,各个特征的增益计算可以使用多线程并行
- 优点:速度快、效果好、能处理大规模数据、支持自定义损失函数等
- 缺点:算法参数过多,调参复杂,不适合处理超高维特征数据
Lightgbm
背景:
在常用的机器学习算法中,例如神经网络等算法,都可以以mini-batch的方式训练,训练数据的大小不会受到内存限制。而GBDT在每一次迭代的时候,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。对于工业级海量的数据,普通的GBDT算法是不能满足其需求的。LightGBM的提出是为了解决GBDT在海量数据遇到的问题,让GBDT可以更好更快地用于工业场景。
Lightgbm VS Xgboost
- 模型精度:两个模型相当
- 训练速度:Lightgbm的训练速度更快,约为Xgboost的 1 10 \frac{1}{10} 101
- 内存消耗:Lightgbm占用的内存更小,约为Xgboost的 1 6 \frac{1}{6} 61
- 特征缺失值:两个模型都能自动处理缺失值
- 分类模型:Xgboost不支持类别特征,需要对其进行Onthot编码,而Lightgbm支持分类特征
Lightgbm针对Xgboost做的优化
- Histogram算法:直方图算法,从而减少候选分裂节点的数量
- GOSS算法:基于梯度的单边采样算法,从而减少样本的数量
- EFB算法:互斥特征捆绑算法,从而减少特征的数量
Histogram
在Xgboost中,使用了预排序算法,将样本按照特征取值排序,然后从全部特征取值中找到最优的分裂点位,即有
候
选
分
裂
点
数
量
=
样
本
特
征
值
数
量
−
1
候选分裂点数量 = 样本特征值数量 - 1
候选分裂点数量=样本特征值数量−1
而在Lightgbm中histogram算法的思想是先将连续值离散化成K个整数,同时构造一个宽度为K的直方图,即将连续特征值离散化到K个bin上。当遍历一次数据后,直方图累积了需要统计的量,然后根据直方图的离散值,遍历寻找最优的分割点。
Xgboost需要遍历所有离散化的值,Lightgbm只要遍历K个直方图(bin)的值,既有
候
选
分
裂
点
数
量
=
K
−
1
候选分裂点数量 = K-1
候选分裂点数量=K−1
Goss
- Gradient-based One-Side Sampling,基于梯度的单边采样算法
- Goss思想是通过样本采样,减少目标函数增益Gain的计算复杂度
- 单边采样:只对梯度绝对值较小的样本按照一定比例进行采样,而保留了梯度绝对值较大的样本
- 因为目标函数增益主要来自于梯度绝对值较大的样本 => GOSS算法在性能和精度之间进行了很好的trade off
EFB
- Exclusive Feature Bundling,互斥特征绑定算法
- EFB的思想是特征中包含大量稀疏特征的时候,减少构建直方图的特征数量,从而降低计算复杂度
- 在数据集中通常会有大量的稀疏特征(大部分为0,少量为非0)我们认为这些稀疏特征是互斥的,即不会同时取非零值
- EFB算法可以通过对某些特征的取值重新编码,将多个这样互斥的特征绑定为一个新的特征
- 类别特征可以转换成onehot编码,这些多个特征的onehot编码是互斥的,可以使用EFB将他们绑定为一个特征
- 在LightGBM中,可以直接将每个类别取值和一个bin关联,从而自动地处理它们,也就无需预处理成onehot编码