集成学习概述
集成学习的核心思想是:**"三个臭皮匠,顶个诸葛亮"**。通过组合多个基学习器(弱学习器)的预测结果,获得一个比任何单个学习器都更强大的最终模型。 **为什么集成有效?** 假设有 $M$ 个独立的分类器,每个的错误率为 $\epsilon$,且错误相互独立。则 $M$ 个分类器同时犯错的概率为: $$ P(\text{全部错误}) = \epsilon^M $$ 通过多数投票,集成后的错误率会显著降低。即使基学习器只是略优于随机猜测($\epsilon < 0.5$),足够多的基学习器组合后也能达到极低的错误率。
根据基学习器的组合方式,集成学习主要分为两大类: | 类型 | 代表算法 | 核心思想 | 降低的误差 | |------|---------|---------|-----------| | **Bagging(并行)** | 随机森林 | 投票/平均,少数服从多数 | **方差(Variance)** | | **Boosting(串行)** | AdaBoost、GBDT、XGBoost、LightGBM | 串行训练,后一个模型修正前一个模型的错误 | **偏差(Bias)** | > **偏差-方差分解视角**: > - Bagging 通过并行训练多模型取平均,**降低方差**,适合高方差低偏差的模型(如深度决策树) > - Boosting 通过串行修正残差,**降低偏差**,适合低方差高偏差的模型(如决策树桩)
Bagging与随机森林
**Bagging(Bootstrap Aggregating)** 的步骤: 1. **自助采样(Bootstrap)**:从原始数据集(含 $N$ 个样本)中有放回地随机抽取 $N$ 个样本,构成一个新的训练集。 2. **并行训练**:用每个采样得到的数据集独立训练一个基学习器(通常是决策树)。 3. **聚合预测**: - **分类任务**:投票(Voting),少数服从多数 - **回归任务**:取平均值 **Bootstrap 采样的数学性质**: 每次采样时,每个样本被抽中的概率为 $1/N$,不被抽中的概率为 $(1 - 1/N)$。$N$ 次采样后,某个样本**始终未被抽中**的概率为: $$ P(\text{未抽中}) = \left(1 - \frac{1}{N}\right)^N \approx \frac{1}{e} \approx 36.8\% $$ 这意味着每次 Bootstrap 采样大约会遗漏 $36.8\%$ 的样本,这些样本称为 **Out-of-Bag(OOB)样本**,可用于无交叉验证的模型评估。
在分类问题中,投票有两种方式: **硬投票(Hard Voting)**:每个分类器投一票(类别标签),取得票最多的类别。 $$ \hat{y} = \text{mode}\{h_1(x), h_2(x), \ldots, h_M(x)\} $$ **软投票(Soft Voting)**:每个分类器输出各类别的概率,取概率平均值最大的类别。 $$ \hat{y} = \arg\max_k \frac{1}{M} \sum_{m=1}^{M} P_m(y=k \mid x) $$ > **课程中的经典例子**: > > "最终为B的三个中,属于B的概率并不高;而A的两票中,A的概率有绝对优势" > > 这说明:硬投票时,3个分类器预测为B、2个预测为A,则B胜出。但如果那3个B的概率都只有0.51,而2个A的概率都是0.99,软投票会发现A的平均概率更高,从而做出更合理的判断。**软投票通常更可靠。**
随机森林是Bagging的扩展,在**样本随机**的基础上增加了**特征随机**: - **样本随机**:Bootstrap采样 - **特征随机**:每棵树分裂时,只从全部特征中随机选取 $k$ 个来寻找最佳分裂点 - 分类问题:$k = \sqrt{d}$($d$ 为总特征数) - 回归问题:$k = d/3$ **随机森林的构建示例**(吃鸡游戏数据集): | 参数 | 设置 | |------|------| | 树的数量 | 40棵 | | 每棵树的最大特征数 | $\sqrt{\text{总特征数}}$ | | 数据预处理 | 合并相似变量、删除异常值(如外挂数据)、类别编码(pd.get_dummies) | **特征选择技巧**: - 第1轮训练后,根据特征重要性筛选出相对重要的特征 - 第2轮训练,只使用这些重要特征重新建模
假设每棵树的预测为 $T_m(x)$,方差为 $\sigma^2$,两棵树之间的相关性为 $\rho$。则 $M$ 棵树的平均预测的方差为: $$ \text{Var}\left(\frac{1}{M}\sum_{m=1}^{M} T_m(x)\right) = \rho \sigma^2 + \frac{1 - \rho}{M}\sigma^2 $$ - 第一项 $\rho\sigma^2$:由于树之间的相关性无法消除的方差 - 第二项:通过增加树的数量 $M$ 可以降低的方差 随机森林通过**特征随机**降低了树之间的相关性 $\rho$,从而进一步降低了整体方差。
AdaBoost详解
Boosting 是**串行训练**的策略: 1. 先训练第一个弱学习器 2. 找出第一个学习器预测错误的数据 3. 增加这些错误数据的权重,降低正确数据的权重 4. 训练第二个弱学习器,使其更关注之前预测错误的数据 5. 重复上述过程,直到达到指定的学习器数量 6. 将所有弱学习器加权组合,得到最终分类器 > **"经常计算,错误数据的权重被加大,原来各个特征数据的权重是一样的"** > > 这正是 AdaBoost 的核心机制。
AdaBoost(Adaptive Boosting,自适应提升)是最经典的Boosting算法。下面以**二分类问题**(标签为 $y_i \in \{+1, -1\}$)为例,给出完整的计算流程。 | 符号 | 含义 | |------|------| | $N$ | 样本总数 | | $M$ | 弱学习器数量(迭代轮数) | | $w_i^{(m)}$ | 第 $m$ 轮第 $i$ 个样本的权重 | | $G_m(x)$ | 第 $m$ 个弱分类器,输出 $+1$ 或 $-1$ | | $\epsilon_m$ | 第 $m$ 轮加权错误率 | | $\alpha_m$ | 第 $m$ 个弱分类器的权重系数 | | $G(x)$ | 最终分类器 | **Step 1:初始化样本权重** $$ w_i^{(1)} = \frac{1}{N}, \quad i = 1, 2, \ldots, N $$ **Step 2:迭代训练(第 $m$ 轮)** - 训练弱分类器 $G_m(x)$ - 计算加权错误率:$\epsilon_m = \sum_{i=1}^{N} w_i^{(m)} \cdot \mathbb{I}(G_m(x_i) \neq y_i)$ - 计算分类器权重:$\alpha_m = \frac{1}{2} \ln\left(\frac{1 - \epsilon_m}{\epsilon_m}\right)$ - 更新样本权重:$w_i^{(m+1)} = w_i^{(m)} \cdot \exp(-\alpha_m \cdot y_i \cdot G_m(x_i))$ - 归一化权重 **Step 3:构建最终分类器** $$ f(x) = \sum_{m=1}^{M} \alpha_m \cdot G_m(x) $$ $$ G(x) = \text{sign}\left(\sum_{m=1}^{M} \alpha_m \cdot G_m(x)\right) $$
假设有10个样本,标签为 $y \in \{+1, -1\}$。 **第1轮** - 初始化:$w_i^{(1)} = 0.1$ - 训练得到 $G_1(x)$,假设分类错误的样本为第3、7、9号 - 加权错误率:$\epsilon_1 = 0.1 + 0.1 + 0.1 = 0.3$ - 分类器权重:$\alpha_1 = \frac{1}{2}\ln\left(\frac{1-0.3}{0.3}\right) \approx 0.424$ - 更新权重: - 正确样本(7个):$w = 0.1 \times e^{-0.424} \approx 0.0655$ - 错误样本(3个):$w = 0.1 \times e^{0.424} \approx 0.1528$ - 归一化: - 正确样本:$w_i^{(2)} \approx 0.0714$ - 错误样本:$w_i^{(2)} \approx 0.1667$ 验证:$7 \times 0.0714 + 3 \times 0.1667 \approx 1$ ✓ **最终分类器** 假设训练了 $M=3$ 轮,得到: | 轮次 $m$ | $G_m(x)$ 输出 | $\alpha_m$ | |---------|-------------|-----------| | 1 | $+1$ | 0.424 | | 2 | $-1$ | 0.693 | | 3 | $+1$ | 0.549 | 对于某个样本 $x$: $$ f(x) = 0.424 \times (+1) + 0.693 \times (-1) + 0.549 \times (+1) = 0.28 $$ $$ G(x) = \text{sign}(0.28) = +1 $$
> **常见问题**:"最终的分类器 $G(x)$ 的结果是类似1或-1相差分类的列表吗?" **需要区分两个概念:** **1. $f(x)$ —— 加权组合值(实数)** $$f(x) = \sum_{m=1}^{M} \alpha_m \cdot G_m(x)$$ 这是一个**实数值**,可正可负。它的绝对值大小还反映了分类的"置信度": - $|f(x)|$ 越大,说明各弱分类器对这个样本的预测越一致,置信度越高 - $|f(x)|$ 越小(接近0),说明弱分类器之间存在分歧,置信度较低 **2. $G(x)$ —— 最终分类结果** $$G(x) = \text{sign}(f(x))$$ 这才是最终的分类输出,确实只有 **$+1$ 或 $-1$**(二分类情况下)。 | 函数 | 取值 | 含义 | |------|------|------| | $G_m(x)$ | $\{+1, -1\}$ | 每个弱分类器的输出 | | $f(x)$ | 实数 | 加权累加值,含置信度信息 | | $G(x)$ | $\{+1, -1\}$ | 最终分类结果 |
梯度提升与GBDT家族
- **AdaBoost**:通过调整样本权重来关注难分类样本 - **Gradient Boosting**:通过拟合**残差(负梯度)**来逐步修正模型 **核心思想**: 将模型 $F(x)$ 看作一个函数,通过迭代的方式逐步逼近最优解: $$ F_m(x) = F_{m-1}(x) + \eta \cdot h_m(x) $$ 其中: - $F_{m-1}(x)$ 是前 $m-1$ 轮累加得到的模型 - $h_m(x)$ 是第 $m$ 棵新树,拟合残差 $r_i^{(m)} = y_i - F_{m-1}(x_i)$ - $\eta$ 是学习率(通常 0.01~0.3) **损失函数视角**: 对于回归问题,使用平方损失 $L(y, F) = \frac{1}{2}(y - F)^2$,负梯度为: $$ -\frac{\partial L}{\partial F} = y - F $$ 这正是残差!所以拟合残差等价于拟合负梯度。 **为什么需要多棵树?** 1. **残差是动态更新的**:每棵树只拟合**当前模型**的残差 2. **防止过拟合**:一棵无限深的树会完美记住训练数据(包括噪声) 3. **学习率与步长的配合**:$n_\text{estimators}$ 是步数,$\eta$ 是步长 **类比**:就像用积木拼一条复杂的曲线。一块巨型积木虽然能贴合,但稍有变动就废了;用很多小块积木,每块只贡献一点点,拼出的曲线更平滑、更稳健。
XGBoost(eXtreme Gradient Boosting)在 GBDT 基础上做了多项优化: **1. 加入正则化项** 目标函数 = 损失函数 + 正则化项: $$ \text{Obj} = \sum_{i=1}^{N} L(y_i, \hat{y}_i) + \sum_{m=1}^{M} \Omega(h_m) $$ 其中 $\Omega(h_m) = \gamma T + \frac{1}{2}\lambda \sum_{j=1}^{T} w_j^2$,$T$ 是叶子节点数,$w_j$ 是叶子权重。 **2. 二阶泰勒展开** XGBoost 对损失函数进行二阶泰勒展开,同时利用一阶梯度 $g_i$ 和二阶梯度 $h_i$: $$ \text{Obj}^{(m)} \approx \sum_{i=1}^{N} \left[g_i h_m(x_i) + \frac{1}{2} h_i h_m(x_i)^2\right] + \Omega(h_m) $$ 这比 GBDT 只使用一阶梯度的方式收敛更快、更精确。 **3. 并行处理** 虽然 Boosting 是串行算法,但 XGBoost 在**特征层面**做了并行:在寻找最佳分裂点时,可以并行扫描所有特征。
LightGBM 在 XGBoost 基础上进一步优化速度: **1. 基于直方图的分裂算法(Histogram-based)** - 将连续特征离散化为 $k$ 个区间(默认 255 个) - 构建直方图后,寻找最佳分裂点只需遍历 $k$ 个区间,而非所有样本 - 时间复杂度从 $O(N \cdot d)$ 降低到 $O(k \cdot d)$ **2. Leaf-wise 树生长策略** - XGBoost 使用 Level-wise(按层生长),同一层的所有叶子一起分裂 - LightGBM 使用 Leaf-wise(按叶子生长),每次选择**分裂增益最大**的叶子进行分裂 - Leaf-wise 能更快降低损失,但需要控制 max_depth 防止过拟合 **3. 直接支持类别特征** LightGBM 可以自动处理类别特征,无需 One-Hot 编码。
| 参数 | 建议 | |------|------| | $n_\text{estimators}$ | 设一个较大的值(如 10000),配合 early stopping | | learning_rate | 较小的值(0.01~0.1),与树的个数成反比 | | max_depth | 通常 3~8,控制每棵树的复杂度 | | num_leaves (LightGBM) | $2^{\mathrm{max\_depth}}$ 左右,控制模型复杂度 | | early_stopping_rounds | 验证集不再提升时提前停止,自动找到最佳树的数量 |
算法对比与总结
除了 Bagging 和 Boosting,还有一种更通用的集成方法——**Stacking**: 1. 用多个不同的基学习器(如SVM、决策树、神经网络)分别训练 2. 将这些基学习器的预测结果作为新的特征 3. 用一个"元学习器"(Meta-learner)来组合这些预测 **与 Bagging/Boosting 的区别**: - Bagging/Boosting 的基学习器通常是同质的(都是决策树) - Stacking 的基学习器可以是异质的(SVM、树、神经网络混用)
| 算法 | 类型 | 基学习器 | 核心机制 | 特点 | |------|------|---------|---------|------| | Random Forest | Bagging | 决策树 | 样本+特征随机,投票 | 并行、抗过拟合、能输出特征重要性 | | AdaBoost | Boosting | 决策树桩 | 调整样本权重 | 关注错分样本、对噪声敏感 | | GBDT | Boosting | 决策树 | 拟合残差 | 效果稳定、训练较慢 | | XGBoost | Boosting | 决策树 | 二阶导数+正则化 | GBDT的优化版,加入并行处理 | | LightGBM | Boosting | 决策树 | 直方图+Leaf-wise | 速度极快、内存占用低 | | CatBoost | Boosting | 决策树 | 自动处理类别特征 | 减少预测偏移、对类别特征友好 |
当数据中存在类别变量时,常见处理方式: - **One-Hot 编码**:`pd.get_dummies(train, columns=['category_col'])` - 适合低基数类别 - 高基数时会导致特征维度爆炸 - **目标编码(Target Encoding)**:用目标变量的均值来编码类别 - 适合高基数类别 - 容易过拟合,需要配合平滑处理 - **CatBoost 内置处理**:自动、高效地处理类别特征,使用 Ordered Target Statistics 减少预测偏移
**核心公式速查**: 1. **Bootstrap 未抽中概率**: $$P = \left(1 - \frac{1}{N}\right)^N \approx 36.8\%$$ 2. **AdaBoost 分类器权重**: $$\alpha_m = \frac{1}{2}\ln\left(\frac{1-\epsilon_m}{\epsilon_m}\right)$$ 3. **AdaBoost 权重更新**: $$w_i^{(m+1)} = w_i^{(m)} \cdot \exp(-\alpha_m y_i G_m(x_i))$$ 4. **最终分类器**: $$G(x) = \text{sign}\left(\sum_{m=1}^{M}\alpha_m G_m(x)\right)$$ 5. **GBDT 迭代公式**: $$F_m(x) = F_{m-1}(x) + \eta \cdot h_m(x)$$
参考