Boosting
Boosting 是一种提升算法,可以将弱的学习算法提升(boost)为强的学习算法。其基本思路如下:
- 利用初始训练样本集训练得到一个基学习器。
- 提高被基学习器误分的样本的权重,使得那些被错误分类的样本在下一轮训练中可以得到更大的关注,利用调整后的样本训练得到下一个基学习器。
- 重复上述步骤,直到得出 \(M\) 个学习器。
- 对于分类问题,采用有权重的投票方式;对于回归问题,采用加权平均得到预测值。
Adaboost
Adaboost 1 是 Boosting 算法中最具代表性的一个。原始的 Adaboost 算法用于解决二分类问题,因此对于一个训练集
\[
T = \{\left(x_1, y_1\right), \left(x_2, y_2\right), ..., \left(x_n, y_n\right)\}
\]
其中 \(x_i \in \mathcal{X} \subseteq \mathbb{R}^n\),\(y_i \in \mathcal{Y} = \{-1, +1\}\),首先初始化训练集的权重
\[
\begin{split}
D_1 =& \left(w_{11}, w_{12}, ..., w_{1n}\right) \\
w_{1i} =& \dfrac{1}{n}, i = 1, 2, ..., n
\end{split}
\]
根据每一轮训练集的权重 \(D_m\),对训练集数据进行抽样得到 \(T_m\),再根据 \(T_m\) 可以得到每一轮的基学习器 \(h_m\)。
Adaboost
通过计算可以得出基学习器 \(h_m\) 的误差为 \(\epsilon_m\),根据基学习器的误差计算得出该基学习器在最终学习器中的系数
\[
\alpha_m = \dfrac{1}{2} \ln \dfrac{1 - \epsilon_m}{\epsilon_m}
\]
更新训练集的权重
\[
\begin{split}
D_{m+1} =& \left(w_{m+1, 1}, w_{m+1, 2}, ..., w_{m+1, n}\right) \\
w_{m+1, i} =& \dfrac{w_{m, i}}{Z_m} \exp \left(-\alpha_m y_i h_m\left(x_i\right)\right) \\
Z_m =& \sum_{i = 1}^{n} w_{m, i} \exp \left(-\alpha_m y_i h_m \left(x_i\right)\right)
\end{split}
\]
其中 \(Z_m\) 为规范化因子,从而保证 \(D_{m+1}\) 为一个概率分布。
Adaboost
最终根据构建的 \(M\) 个基学习器得到最终的学习器:
\[
h_f\left(x\right) = \text{sign}\left(\sum_{m=1}^{M} \alpha_i h_m\left(x\right)\right)
\]
AdaBoost 算法过程如下所示:
\begin{algorithm} \caption{Adaboost 算法} \begin{algorithmic} \Require 学习算法 $L$,子模型个数 $M$,训练数据集 $T$ \Ensure AdaBoost 算法 $h_f\left(x\right)$ \Procedure{AdaBoost}{$L, M, T$} \State $D_1\left(x\right) \gets 1 / n$ \For{$m$ = $1$ to $M$} \State $T_{sub} \gets $ sample from training set $T$ with weights \State $h_m \gets L\left(T_{sub}\right)$ \State $\epsilon_m \gets Error\left(h_m\right)$ \If{$\epsilon_m > 0.5$} \State \textbf{break} \EndIf \State $\alpha_m \gets \dfrac{1}{2} \ln \dfrac{1 - \epsilon_m}{\epsilon_m}$ \State $D_{m+1} \gets \dfrac{D_m \exp \left(-\alpha_m y h_m\left(x\right)\right)}{Z_m}$ \EndFor \State $h_f\left(x\right) \gets \mathbf{sign}\left(\sum_{m=1}^{M} \alpha_i h_m\left(x\right)\right)$ \Return $h_f\left(x\right)$ \EndProcedure \end{algorithmic} \end{algorithm}
GBM
GBM(Gradient Boosting Machine)是另一种基于 Boosting 思想的集成算法,GBM 还有很多其他的叫法,例如:GBDT,GBRT,MART等等。GBM 算法由 3 个主要概念构成:Gradient Boosting(GB),Regression Decision Tree(DT 或 RT)和 Shrinkage。
从 GBM 的众多别名中可以看出,GBM 中使用的决策树并非我们最常用的分类树,而是回归树。分类树主要用于处理响应变量为因子型的数据,例如天气(可以为晴,阴或下雨等)。回归树主要用于处理响应变量为数值型的数据,例如商品的价格。当然回归树也可以用于二分类问题,对于回归树预测出的数值结果,通过设置一个阈值即可以将数值型的预测结果映射到二分类问题标签上,即 \(\mathcal{Y} = \{-1, +1\}\)。
对于 Gradient Boosting 而言,首先,Boosting 并不是 Adaboost 中的 Boost 的概念,也不是 Random Forest 中的重抽样。在 Adaboost 中,Boost 是指在生成每个新的基学习器,跟根据上一轮基学习器分类对错对训练集设置不同的权重,使得在上一轮中分类错误的样本在生成新的基学习器时更被重视。GBM 中在应用 Boost 概念时,每一轮所使用的数据集没有经过重抽样,也没有更新样本的权重,而是每一轮选择了不用的回归的目标值,即上一轮计算得出的残差(Residual)。其次,Gradient 是指在新一轮中在残差减少的梯度(Gradient)上建立新的基学习器。
GBM
下面通过一个年龄预测的示例介绍 GBM 的工作流程。存在 4 个人 \(P = \{p_1, p_2, p_3, p_4\}\),他们对应的年龄为 \(14, 16, 24, 26\)。其中 \(p_1, p_2\) 分别是高一和高三学生,\(p_3, p_4\) 分别是应届毕业生和工作两年的员工。利用决策树模型进行训练可以得到如图所示的结果:
GBM
利用 GBM 训练得到模型,由于数据量少,在此限定每个基学习器中的叶子节点最多为 2 个,即树的深度最大为 1 层。训练得到的结果如图所示:
在训练第一棵树过程中,利用年龄作为预测值,根据计算可得由于 \(p_1, p_2\) 年龄相近,\(p_3, p_4\) 年龄相近被划分为两组。通过计算两组中真实年龄和预测的年龄的差值,可以得到第一棵树的残差 \(R = \{-1, 1, -1, 1\}\)。因此在训练第二棵树的过程中,利用第一棵树的残差作为预测值,最终所有人的年龄均被正确预测,即最终所有的残差均为 \(0\)。
GBM
则对于训练集中的 4 个人利用训练得到的 GBM 模型预测的结果如下:
- \(p_1\):14岁高一学生。购物较少,经常问学长问题,预测年龄 \(Age = 15 - 1 = 14\)。
- \(p_2\):16岁高三学生。购物较少,经常被学弟问问题,预测年龄 \(Age = 15 + 1 = 16\)。
- \(p_3\):24岁应届毕业生。购物较多,经常问师兄问题,预测年龄 \(Age = 25 - 1 = 24\)。
- \(p_4\):26岁2年工作经验员工。购物较多,经常被师兄问问题,预测年龄 \(Age = 25 + 1 = 26\)。
\begin{algorithm} \caption{GBM 算法} \begin{algorithmic} \Require 子模型个数 $M$,训练数据集 $T$ \Ensure GBM 算法 $h_f\left(x\right)$ \Procedure{GBM}{$M, T$} \State $F_1\left(x\right) \gets \sum_{i = 1}^{N} y_i / N$ \For {$m$ = $1$ to $M$} \State $r_m \gets y - F_m \left(x\right)$ \State $T_m \gets \left(x, r_m\right)$ \State $h_m \gets RegressionTree \left(T_m\right)$ \State $\alpha_m \gets \dfrac{\sum_{i = 1}^{N} r_{im} h_m \left(x_i\right)}{\sum_{i = 1}^{N} h_m \left(x_i\right)^2}$ \State $F_m \left(x\right) = F_{m-1} \left(x\right) + \alpha_m h_m \left(x\right)$ \EndFor \State $h_f\left(x\right) = F_M \left(x\right)$ \Return $h_f\left(x\right)$ \EndProcedure \end{algorithmic} \end{algorithm}
GBM
在 GBM 中也应用到了 Shrinkage 的思想,其基本思想可以理解为在每一轮利用残差学习得到的回归树仅学习到了一部分知识,即无法完全信任一棵树的结果。因此,Shrinkage 思想认为在新的一轮学习中,不利用全部残差训练模型,而只利用其中一部分,即:
\[
r_m = y - s F_m \left(x\right), 0 \leq s \leq 1
\]
注意,这里的 Shrinkage 和学习算法中 Gradient 的步长是两个不相关的概念。Shrinkage 设置小一些可以避免发生过拟合现象;而 Gradient 中的步长如果设置太小则会陷入局部最优,如果设置过大又容易结果不收敛。
XGBoost
XGBoost 是由 Chen 等人 1 提出的一种梯度提升树模型框架。XGBoost 的基本思想同 GBDT 一样,对于一个包含 \(n\) 个样本和 \(m\) 个特征的数据集 \(\mathcal{D} = \left\{\left(\mathbf{x}_i, y_i\right)\right\}\),其中 \(\left|\mathcal{D}\right| = n, \mathbf{x}_i \in \mathbb{R}^m, y_i \in \mathbb{R}\),一个集成树模型可以用 \(K\) 个加法函数预测输出:
\[
\hat{y}_i = \phi \left(\mathbf{x}_i\right) = \sum_{k=1}^{K}{f_k \left(\mathbf{x}_i\right)}, f_k \in \mathcal{F}
\]
其中,\(\mathcal{F} = \left\{f \left(\mathbf{x}\right) = w_{q \left(\mathbf{x}\right)}\right\} \left(q: \mathbb{R}^m \to T, w \in \mathbb{R}^T\right)\) 为回归树 (CART),\(q\) 表示每棵树的结构,其将一个样本映射到最终的叶子节点,\(T\) 为叶子节点的数量,每个 \(f_w\) 单独的对应一棵结构为 \(q\) 和权重为 \(w\) 的树。不同于决策树,每棵回归树的每个叶子节点上包含了一个连续的分值,我们用 \(w_i\) 表示第 \(i\) 个叶子节点上的分值。
XGBoost
library(mlr3)
library(mlr3learners)
library(xgboost)
task_wine <- mlr_tasks$get("wine")
train_set_ids <- sample(
task_wine$nrow, 0.8 * task_wine$nrow)
test_set_ids <- setdiff(
seq_len(task_wine$nrow), train_set_ids)
learner <- lrn("classif.xgboost")
learner$train(task_wine, row_ids = train_set_ids)
pred <- learner$predict(
task_wine, row_ids = test_set_ids)
truth
response 1 2 3
1 12 1 0
2 0 14 0
3 0 0 9
pred$score(msr("classif.acc"))
Stacking
Stacking 本身是一种集成学习方法,同时也是一种模型组合策略,我们首先介绍一些相对简单的模型组合策略:平均法和投票法。
对于数值型的输出 \(h_i \left(\mathbf{x}\right) \in \mathbb{R}\),
\[
H \left(\mathbf{x}\right) = \dfrac{1}{M} \sum_{i=1}^{M}{h_i \left(\mathbf{x}\right)}
\]
- 加权平均法 (Weighted Averaging)
\[
H \left(\mathbf{x}\right) = \sum_{i=1}^{M}{w_i h_i \left(\mathbf{x}\right)}
\]
其中,\(w_i\) 为学习器 \(h_i\) 的权重,且 \(w_i \geq 0, \sum_{i=1}^{T}{w_i} = 1\)。
Stacking
对于 分类型的任务,学习器 \(h_i\) 从类别集合 \(\left\{c_1, c_2, \dotsc, c_N\right\}\) 中预测一个标签。我们将 \(h_i\) 在样本 \(\mathbf{x}\) 上的预测输出表示为一个 \(N\) 维向量 \(\left(h_i^1 \left(\mathbf{x}\right); h_i^2 \left(\mathbf{x}\right); \dotsc, h_i^N \left(\mathbf{x}\right)\right)\),其中 \(h_i^j \left(\mathbf{x}\right)\) 为 \(h_i\) 在类型标签 \(c_j\) 上的输出。
- 绝对多数投票法 (Majority Voting)
\[
H \left(\mathbf{x}\right) = \left\{
\begin{array}{c}
c_j, & \displaystyle\sum_{i=1}^{M}{h_i^j \left(\mathbf{x}\right) > 0.5 \displaystyle\sum_{k=1}^{N}{\displaystyle\sum_{i=1}^{M}{h_i^k \left(\mathbf{x}\right)}}} \\
\text{拒绝}, & \text{其他情况}
\end{array}\right.
\]
即如果一个类型的标记得票数过半,则预测为该类型,否则拒绝预测。
- 相对多数投票法 (Plurality Voting)
\[
H \left(\mathbf{x}\right) = c_{\operatorname{argmax}_j \sum_{i=1}^{M}{h_i^j \left(\mathbf{x}\right)}}
\]
即预测为得票数最多的类型,如果同时有多个类型获得相同最高票数,则从中随机选取一个。
Stacking
\[
H \left(\mathbf{x}\right) = c_{\operatorname{argmax}_j \sum_{i=1}^{M}{w_i h_i^j \left(\mathbf{x}\right)}}
\]
其中,\(w_i\) 为学习器 \(h_i\) 的权重,且 \(w_i \geq 0, \sum_{i=1}^{M}{w_i} = 1\)。
绝对多数投票提供了“拒绝预测”,这为可靠性要求较高的学习任务提供了一个很好的机制,但如果学习任务要求必须有预测结果时则只能选择相对多数投票法和加权投票法。在实际任务中,不同类型的学习器可能产生不同类型的 \(h_i^j \left(\boldsymbol{x}\right)\) 值,常见的有:
- 类标记,\(h_i^j \left(\mathbf{x}\right) \in \left\{0, 1\right\}\),若 \(h_i\) 将样本 \(\mathbf{x}\) 预测为类型 \(c_j\) 则取值为 1,否则取值为 0。使用类型标记的投票称之为 “硬投票” (Hard Voting)。
- 类概率,\(h_i^j \left(\mathbf{x}\right) \in \left[0, 1\right]\),相当于对后验概率 \(P \left(c_j \ | \ \mathbf{x}\right)\) 的一个估计。使用类型概率的投票称之为 “软投票” (Soft Voting)。
Stacking
Stacking 1 2 方法又称为 Stacked Generalization,是一种基于分层模型组合的集成算法。Stacking 算法的基本思想如下:
- 利用初级学习算法对原始数据集进行学习,同时生成一个新的数据集。
- 根据从初级学习算法生成的新数据集,利用次级学习算法学习并得到最终输出。
对于初级学习器,可以是相同类型也可以是不同类型,同时在生成新的数据集的过程中,其相应变量仍为原始数据集中的相应变量。Stacking 算法流程如图所示:
Stacking
\begin{algorithm} \caption{Stacking 算法} \begin{algorithmic} \Require \\ 初级学习算法 $L = \{L_1, L_2, ... L_M\}$ \\ 次级学习算法 $L'$ \\ 训练数据集 $T = \{(\mathbf{x}_1, y_1), (\mathbf{x}_2, y_2), ..., (\mathbf{x}_N, y_N)\}$ \Ensure Stacking 算法 $h_f\left(x\right)$ \Procedure{Stacking}{$L, L', T$} \For{$m$ = $1$ to $M$} \State $h_t \gets L_m \left(T\right)$ \EndFor \State $T' \gets \varnothing$ \State \Comment{接下文} \EndProcedure \end{algorithmic} \end{algorithm}
\begin{algorithm} \begin{algorithmic} \Procedure{Stacking}{$L, L', T$} \State \Comment{接上文} \For{$i$ = $1$ to $N$} \For{$m$ = $1$ to $M$} \State $z_{im} \gets h_m(\mathbf{x}_i)$ \EndFor \State $T' \gets T' \cup \left(\left(z_{i1}, z_{i2}, ..., z_{iM}\right), y_i\right)$ \EndFor \State $h' \gets L' \left(T'\right)$ \State $h_f\left(\mathbf{x}\right) \gets h' \left(h_1\left(\mathbf{x}\right), h_2\left(\mathbf{x}\right), ..., h_M\left(\mathbf{x}\right)\right)$ \Return $h_f\left(\mathbf{x}\right)$ \EndProcedure \end{algorithmic} \end{algorithm}
Stacking
次级学习器的训练集是由初级学习器产生的,如果直接利用初级学习器的训练集生成次级学习器的训练集,则过拟合风险会比较大 1。因此,一般利用在训练初始学习器中未使用过的样本来产生次级学习器的训练样本。
以 \(k\) 折交叉检验为例:初始的训练集 \(T\) 被随机划分为 \(k\) 个大小相近的集合 \(T_1, T_2, ..., T_k\)。令 \(T_j\) 和 \(\bar{T_j}\) 表示第 \(j\) 折的测试集和训练集。则对于 \(M\) 个初级学习算法,其学习器 \(h_m^{\left(j\right)}\) 是根据训练集 \(\bar{T_j}\) 生成的,对于测试集 \(T_j\) 中的每个样本 \(x_i\),得到 \(z_{im} = h_m^{\left(j\right)} \left(x_i\right)\)。则根据 \(x_i\) 所产生的次级学习器的训练样本为 \(\left(\left(z_{i1}, z_{i2}, ..., z_{iM}\right), y_i\right)\)。最终利用 \(M\) 个初级学习器产生的训练集 \(T' = \{\left(z_i, y_i\right)\}_{i=1}^N\) 训练次级学习器。
Stacking
library(mlr3)
library(mlr3pipelines)
dst <- lrn("classif.rpart", predict_type = "prob")
svm <- lrn("classif.svm", predict_type = "prob")
glm <- lrn("classif.glmnet", predict_type = "prob")
dst_po <- po("learner_cv", id = "DecisionTree", learner = dst)
svm_po <- po("learner_cv", id = "SVM", learner = svm)
union_po <- po("featureunion", id = "FeatureUnion", innum = 2)
glm_po <- po("learner", id = "GLM", learner = glm)
stack <- gunion(list(dst_po, svm_po)) %>>% union_po %>>% glm_po
stack_lrn <- GraphLearner$new(stack)
stack_lrn$train(task_wine, row_ids = train_set_ids)
pred <- learner$predict(
task_wine, row_ids = test_set_ids)
pred$confusion
truth
response 1 2 3
1 12 1 0
2 0 14 0
3 0 0 9
pred$score(msr('classif.acc'))