前言
关于与集成学习相关的基础决策树 CART 的相关原理这里不再介绍,请参考 决策树 ★★。
一、概念
集成学习的思想在于运用集体智慧,将多个弱学习器集成为一个强学习器,常见的集成类型有 Bagging 和 Boosting 两种,而对弱学习器的选择有三种:1)不同类型的弱学习器;2)相同类型但参数不同的弱学习器;3)相同类型但训练集不同的弱学习器。
Bagging
Bagging 运用的即是以上第三种思路。简单而言,分为三个步骤:
- 使用 Bootstrap 生成若干个数据集;
- 将这若干个数据集分别喂给若干个弱学习器训练;
- 预测时,将这若干个弱学习器的输出结果简单合成(分类时投票表决/回归时取平均)。
Bootstrap 有放回地从原样本集中随机抽取平均 63.2%个样本的组合,具体原理在此不赘述。其随机性使得基于 Bagging 原理的模型能够有效避免过拟合。但万物都有两面,这样的做法同时也对样本的分布提出更高的要求。
Boosting
Bagging 对弱学习器的训练是并行的,加入了更多的随机过程,而串行处理的 Boosting 则赋予了弱学习器之间更大的依赖性,每一个弱学习器的变现都会对整体模型的表现有相当大的影响,因此需要对异常数据特别小心。分为以下几个步骤:
- 训练一个弱学习器;
- 根据弱学习器的表现调整样本数据;
- 将调整过后的样本喂给一个新的弱学习器训练,重复以上步骤直到满足停止条件。
二、算法
随机森林
随机森林是 Bagging 思想下最为知名的算法应用,该算法不仅对样本进行 Bootstrap 采样,还新增了两项特殊处理以增强随机性:1)对每个新数据集随机挑选特征组成新的特征空间;2)决策树生成后不进行后剪枝,保持原有形式。在基础决策树的代码已完备的基础上,随机森林的实现其实相当简单,在此直接跳过。
AdaBoost
Abbr. Adaptive Boosting,最为经典的 Boosting 分类模型,通过指数损失函数赋予分类错误的样本更高的权重;每个弱学习器也会依据其分类精度获取相应的最终投票权重系数。AdaBoost 一般用于分类任务,其具有普适性的思想使其也能应用于回归任务,只需要加入一定的判别条件 —— 回归值是否落在标签值一定范围之内。AdaBoost 对弱学习器的选择没有强制性要求,原则上弱学习器不应该太强,否则 AdaBoost 很难起到提升的效果。
Algorithm AdaBoost( D D D, f f f, m m m):
Input: Sample data D = { ( X i , y i ) ∣ i = 1 , . . . , n } D=\{(X_i,y_i)|i=1,...,n\} D={(Xi,yi)∣i=1,...,n}, basic classifier/regressor f f f, maximum iteration times m m m.
Output: Prediction result y ^ \hat{y} y^.
initialize weight array w w w of sample data
for each epoch t t t in m m m epoches do
construct a new sample data D ′ D' D′ by applying w w w on original data D D D
feed D ′ D' D′ to a new weak classifier/regressor f t ′ f'_t ft′
calculate error rate e t e_t et ∈ [ 0 , 1 ] \in[0,1] ∈[0,1]
calculate voting weight of f t ′ f'_t ft′ as α t = 1 2 ⋅ ln 1 − e t e t \alpha_t=\frac{1}{2}\cdot \ln \frac{1-e_{t}}{e_t} αt=21⋅lnet1−et
update w w w based on the prediction performance, w i = w i ⋅ exp [ − α t I ] w_i=w_i\cdot \exp[-\alpha_t\mathbb{I}] wi=wi⋅exp[−αtI], indicator function I ∈ { 1 , − 1 } \mathbb{I}\in\{1,-1\} I∈{1,−1}
normalize w w w
normalize α \alpha α
aggregate weak classifiers/regressors into a strong classifier/regressor F = ∑ t = 1 m α t f t ′ F=\sum_{t=1}^m \alpha_tf'_t F=∑t=1mαtft′
return F ( X ) F(X) F(X)
GBDT
Abbr. Gradient Boosting Decision Tree,梯度提升树。与 AdaBoost 不同,GBDT 不调整样本权重,而是在每次迭代时使用上一轮学习器损失函数的负梯度替代原有的数据标签,训练新的学习器。模型的预测结果将顺着梯度方向逐步向标签值靠近。在回归任务中,损失函数一般为残差的平方: L ( y i , f ( X i ) ) = ( y i − f ( X i ) ) 2 L(y_i,f(X_i))=\big(y_i-f(X_i)\big)^2 L(yi,f(Xi))=(yi−f(Xi))2 g i = − ∂ L ( y i , f ( X i ) ) ∂ f ( X i ) = 2 [ y i − f ( X i ) ] g_i=-\frac{\partial L(y_i,f(X_i))}{\partial f(X_i)}=2\big[y_i-f(X_i)\big] gi=−∂f(Xi)∂L(yi,f(Xi))=2[yi−f(Xi)]
二元分类任务中,当
y
i
∈
{
1
,
−
1
}
y_i\in\{1,-1\}
yi∈{1,−1}:
L
(
y
i
,
f
(
X
i
)
)
=
log
[
1
+
exp
(
−
y
i
⋅
f
(
X
i
)
)
]
L(y_i,f(X_i))=\log[1+\exp(-y_i\cdot f(X_i))]
L(yi,f(Xi))=log[1+exp(−yi⋅f(Xi))]
g
i
=
−
∂
L
(
y
i
,
f
(
X
i
)
)
∂
f
(
X
i
)
=
y
i
1
+
exp
(
y
i
f
(
X
i
)
)
g_i=-\frac{\partial L(y_i,f(X_i))}{\partial f(X_i)}=\frac{y_i}{1+\exp(y_if(X_i))}
gi=−∂f(Xi)∂L(yi,f(Xi))=1+exp(yif(Xi))yi
多元分类任务相较而言更为复杂,在每一轮迭代中,GBDT 会为每一种分类生成一次决策树,各个分类下决策树的生成结果通过归一化即为样本属于各个分类的概率。假设一共有
K
K
K 种分类,归属于
k
k
k 分类的样本标签值
y
i
,
k
=
1
y_{i,k}=1
yi,k=1,
y
i
,
s
=
0
(
s
≠
k
)
y_{i,s}=0(s\ne k)
yi,s=0(s=k):
L
(
y
i
,
f
1
(
X
i
)
,
.
.
.
,
f
K
(
X
i
)
)
=
∑
k
=
1
K
y
i
,
k
log
p
k
(
X
i
)
L\big(y_i,f_1(X_i),...,f_K(X_i)\big)=\sum_{k=1}^Ky_{i,k}\log p_k(X_i)
L(yi,f1(Xi),...,fK(Xi))=k=1∑Kyi,klogpk(Xi)
p
k
(
X
i
)
=
exp
(
f
k
(
X
i
)
)
∑
k
exp
(
f
k
(
X
i
)
)
p_k(X_i)=\frac{\exp(f_k(X_i))}{\sum_k\exp(f_k(X_i))}
pk(Xi)=∑kexp(fk(Xi))exp(fk(Xi))
g
i
=
−
∂
L
(
y
i
,
f
1
(
X
i
)
,
.
.
.
,
f
K
(
X
i
)
)
∂
f
(
X
i
)
=
y
i
,
k
−
p
k
(
X
i
)
g_i=-\frac{\partial L\big(y_i,f_1(X_i),...,f_K(X_i)\big)}{\partial f(X_i)}=y_{i,k}-p_k(X_i)
gi=−∂f(Xi)∂L(yi,f1(Xi),...,fK(Xi))=yi,k−pk(Xi)
不同于回归任务,由于分类模型最终生成的结果为连续值,需要通过近似处理将连续值转换为离散值,以输出分类结果。以下伪代码及相关公式进行了适当的简化处理,如有疑问请提出。
Algorithm GBDT( D D D, T T T, m m m):
Input: Sample data D = { ( X i , y i ) ∣ i = 1 , . . . , n } D=\{(X_i,y_i)|i=1,...,n\} D={(Xi,yi)∣i=1,...,n}, basic classifier/regressor T T T, maximum iteration times m m m.
Output: Prediction result y ^ \hat{y} y^.
initialize prediction result f 0 ( X i ) = arg min c ∑ i = 1 n L ( y i , c ) f_0(X_i)=\arg\min_c\sum_{i=1}^nL(y_i,c) f0(Xi)=argminc∑i=1nL(yi,c)
for each epoch t t t in m m m epoches do
calculate gradient for each sample ( X i , y i ) (X_i,y_i) (Xi,yi), g i = − ∂ L ( y i , f t − 1 ( X i ) ) ∂ f t − 1 ( X i ) g_{i}=-\frac{\partial L(y_i,f_{t-1}(X_i))}{\partial f_{t-1}(X_i)}_{} gi=−∂ft−1(Xi)∂L(yi,ft−1(Xi))
use { ( X i , g i ) } \{(X_i,g_i)\} {(Xi,gi)} to train a new classifier/regression T t T_t Tt
# at each node j j j with subsample data d d d, c j ∗ = arg min c ∑ X i ∈ d L ( y i , f t − 1 ( X i ) + c ) c_j^*=\arg\min_c\sum_{X_i\in d}L(y_i,f_{t-1}(X_i)+c) cj∗=argminc∑Xi∈dL(yi,ft−1(Xi)+c)
update f t ( X i ) ← f t − 1 ( X i ) + T t ( X i ) f_t(X_i)\leftarrow f_{t-1}(X_i)+T_t(X_i) ft(Xi)←ft−1(Xi)+Tt(Xi)
return f m ( X ) f_m(X) fm(X)
AdaBoost 中的 α \alpha α 权重同样也可应用于 GBDT 中,这样一来通过迭代靠近标签值的速度将减慢,意味着我们需要更多的迭代次数完成训练。更多关于 GBDT 的介绍请见 相关论文 或 相关博客。
XGBoost
Abbr. eXtreme Gradient Boosting,由华盛顿大学的陈天奇带领团队开发,一经问世便大放异彩,至今仍然广为使用。该算法在 GBDT 的基础上,进行了诸多优化和调整,最核心的几项:1)在损失函数中引入正则项;2)使用拟牛顿法,引入泰勒二阶展开式更新目标函数;3)对特征的分布进行分桶,取边界作为切割候选点;4)对分类型特征自动进行 one-hot 特征编码。正则化后的损失函数表达式为: L r ( y i , f ( X i ) ) = L ( y i , f ( X i ) ) + Ω ( f ) L_r(y_i,f(X_i))=L(y_i,f(X_i))+\Omega(f) Lr(yi,f(Xi))=L(yi,f(Xi))+Ω(f) Ω ( f ) = γ T l e a f + 1 2 λ ∣ ∣ w ∣ ∣ 2 \Omega(f)=\gamma T_{leaf}+\frac{1}{2}\lambda ||w||^2 Ω(f)=γTleaf+21λ∣∣w∣∣2其中, Ω ( f ) \Omega(f) Ω(f) 即为代表树复杂度的正则项, T l e a f T_{leaf} Tleaf 代表叶节点数目, ∣ ∣ w ∣ ∣ ||w|| ∣∣w∣∣ 为叶节点固定输出值的 L2 范数, γ \gamma γ 和 λ \lambda λ 为相应的惩罚系数。引入正则项后算法的泛化性能得到一定提升。在梯度更新方面,GBDT 通过计算负梯度 (一阶求导) 拟合新的决策树,而 XGBoost 采用拟牛顿法应用二阶导数信息,加快优化速率。抛开数学理论层面,Python 中的 XGBoost 库还通过以下方面实现优化:1)支持更多类型的基础学习器;2)如同随机森林,支持特征子采样及样本子采样,作为过拟合的解决方案;3)添加了对稀疏数据集和缺失值的处理,将是否缺失认定为一种划分方式;4)通过特殊的内存处理方式,使得最优特征的选取能够并行化处理。更详细的原理,推荐博文:XGBoost 的前世今生。
LightGBM
XGBoost 的表现已非常优异,但在面对大批量数据时常常耗时过久。为应对此问题,微软在 2017 年初发布了 LightGBM,在不大幅度降低 XGBoost 准确度的基础上对其运行速度与空间效率进行了大大提升。LightGBM 最核心的优化称为 直方图优化,相对于 XGBoost,不仅仅在遍历寻求最优切割点时应用直方图,在节点分裂时通过将父节点与数据量较小的子节点进行直方图做差,从而直接获取另一个子节点的分布,大幅度提高效率。此外,LightGBM 还修改了枝叶生长的方式 (level-wise to leaf-wise),将分类型特征的处理从 XGBoost 的 one-hot 编码改为二分类条件判断。更详细的介绍,推荐博文:LightGBM——提升机器算法。
CatBoost
Abbr. Category Boosting,由 Yandex 公司开发,基于 XGBoost 和 LightGBM 对 GBDT 算法做进一步调整,旨在解决各类因训练数据和测试数据分布不同而产生的数据偏移问题,在应对分类型变量时表现更为优异。CatBoost 的两大特性包括:1)针对分类型特征进行独特的数值化处理, V k = ∣ D x i = k ∣ + p r i o r ∣ D ∣ + 1 V_k=\frac{|D_{x_i=k}|+prior}{|D|+1} Vk=∣D∣+1∣Dxi=k∣+prior (XGBoost 进行 one-hot 等方式处理,LightGBM 采用二分类条件判断);2)对不同特征进行组合。经过反复测试,XGBoost 准确度最高但运行速度最慢,LightGBM 准确度稍次但速度快数倍以上,CatBoost 属于均衡型选手,表现相对不太稳定。网络上针对三种算法的实验和对比并不少,读者可自行查阅。