07 集成学习

三个臭皮匠,赛过诸葛亮

三个臭皮匠,赛过诸葛亮

集成学习概念图 三个简单的“弱学习器”(圆形、方形、三角形)通过聚合过程,组合成一个复杂的“强学习器”。 弱学习器 A 弱学习器 B 弱学习器 C + + 强学习器

在金融与经济中,预测无处不在

我们依赖各种模型进行关键决策:

金融与经济中的预测应用 四个图标分别代表银行、对冲基金、政府和保险公司,并配有它们各自的典型预测任务。 银行 预测客户是否会 信用卡违约 对冲基金 预测股票明天是 上涨还是下跌 政府 预测下一季度的 GDP 增长率 保险公司 预测客户提出 索赔的风险

核心问题:单一模型 vs. 群体智慧

任何一个单一模型都可能存在缺陷、偏差或错误。

本章要探讨的问题是:

如果我们将许多“还不错”的模型结合起来,能否得到一个“非常强大”的超级模型?

剧透一下:答案是肯定的。这个方法就叫做集成学习 (Ensemble Learning)

本章学习目标

  1. 理解理论:通过霍夫丁不等式,从数学上理解为什么“人多力量大”。
  2. 掌握机制:区分并掌握三种核心集成机制:Bagging, Boosting, 和 Stacking
  3. 构建基石:深入学习最常用的基础模块:决策树
  4. 应用实践:亲手用 Python 对真实的信用卡违约数据进行建模,见证集成的威力。

学习路线图

本章学习路线图 一个分为四个阶段的水平路线图:理论依据,三大机制,决策树,以及Python实战。 1 理论依据 (Why it works) 2 三大机制 (How it works) 3 决策树 (The Building Block) 4 Python 实战 (Real-world Impact)

Part 1: 理论依据

为什么“群体智慧”是可靠的?

霍夫丁不等式 (Hoeffding’s Inequality)

霍夫丁不等式从概率论上给出了一个保证:

大量独立随机变量的均值,会以极高的概率收敛于其真实期望值

换句话说,只要样本够多,样本均值就非常接近总体均值。

这听起来很抽象,我们用一个经典的抛硬币实验来理解。

直观理解:抛硬币实验

  • 问题:有一枚可能不均匀的硬币,正面朝上的真实概率是 p (未知)。我们如何估计 p
  • 方法:抛 n 次,计算正面朝上的频率 h (样本均值)。
  • 直觉n 越大,h 应该越接近真实的 p

霍夫丁不等式精确地量化了这种“接近”的程度。

霍夫丁不等式的数学表达

它告诉我们,样本均值 h 与真实期望 p 的差距大于任意小量 ε 的概率,是随着样本数 n 的增加而指数级下降的

\[ \large{P(|h - p| > \epsilon) \le 2e^{-2n\epsilon^2}} \]

这个 \(e\)负指数项是关键。这意味着我们每增加一次观测(抛一次硬币),犯大错误的概率就会急剧减小。

从抛硬币到集成学习的飞跃

抛硬币实验 一枚硬币 抛一次的结果 (正面/反面) 抛 T 次硬币 最终的正面频率 集成学习 (Ensemble) 一个“弱”学习器 一次预测的结果 (正确/错误) 训练 T 个独立的学习器 投票后的最终预测

集成学习的错误率呈指数级下降

基于这个类比,霍夫丁不等式的一个推论告诉我们:

如果我们有 T独立的、错误率为 ε 的二元分类器(其中 ε < 0.5,即比随机猜测好),通过简单投票组成的集成模型 H(x),其犯错的概率会随着 T 的增加而指数级下降:

\[ \large{P(H(x) \ne y) \le \exp(-2T(0.5 - \epsilon)^2)} \]

理论的核心结论

集成学习之所以有效,依赖于两个关键假设:

  1. 独立性 (Independence):每个“臭皮匠”的思考方式需要有差异。如果所有人都犯同样的错误,集成是没有意义的。
  2. 优于随机 (Better than Random):每个“臭皮匠”的能力都必须比瞎猜要好一点 (对于二分类问题,准确率 > 50%)。

只要满足这两个条件,我们几乎总能通过集成得到一个强大的学习器。

Part 2: 三大核心机制

实践中,我们如何创造出满足那两个条件的“一群臭皮匠”呢?

集成学习的三大流派

集成学习方法对比 (Ensemble Methods) Bagging 关系:独立并行 解决:降低方差 (Variance) 好比:专家独立分析后投票 Boosting 关系:顺序依赖 解决:降低偏差 (Bias) 好比:学生反复专攻错题 Stacking 关系:分层结构 解决:提升预测精度 好比:委员会主席总结陈词

机制一:Bagging (Bootstrap Aggregating)

Bagging 的工作流程非常直观:自助采样 (Bootstrap) + 聚合 (Aggregating)

  1. 采样 (Bootstrap): 从原始训练集 D 中,进行有放回地随机抽样,生成 T 个大小相同的新训练集 D_1, D_2, ..., D_T
  2. 训练 (Train): 在每个新训练集 D_t 上,独立地、并行地训练一个基学习器 h_t
  3. 聚合 (Aggregate):
    • 分类: 简单投票
    • 回归: 简单平均

Bagging 工作流程图

Bagging 流程 (Bootstrap Aggregating) 原始训练集 D 1. 自助采样 (Bootstrap) 训练集 D₁ 训练集 D₂ 训练集 Dₙ 2. 并行训练 (Parallel Training) 学习器 h₁ 学习器 h₂ 学习器 hₙ 3. 聚合 (Aggregation) 最终模型 H

Bagging 的魔力:降低方差

  • 方差 (Variance):模型在不同训练集上预测结果的波动程度。高方差意味着模型对训练数据过于敏感,容易过拟合 (Overfitting)
  • Bagging 为何有效:每个基学习器只看到了部分数据,它们各自的过拟合方向可能不同。通过对这些不同方向的错误进行平均或投票,总体的波动性被有效平滑,从而降低了方差。
  • 最成功的应用随机森林 (Random Forest)

机制二:Boosting (提升法)

Boosting 是一族将弱学习器“提升”为强学习器的算法,它采用的是一种串行的、迭代的方式。

  1. 初始化: 赋予所有训练样本相同的权重。
  2. 迭代训练 (t=1 to T):
    1. 在当前带权重的样本集上,训练一个弱学习器 h_t
    2. 增大h_t 预测错误的样本的权重。
    3. 减小h_t 预测正确的样本的权重。
  3. 最终组合: 最终的强学习器是所有弱学习器的加权组合

Boosting 工作流程图

Boosting 算法核心流程 初始数据 (等权重 w₁) 弱学习器 h₁ 弱学习器 h₂ ... 弱学习器 hₙ 根据 h₁ 的误差,更新数据权重为 w₂ 最终强学习器 H(x) H(x) = sign(Σ αᵢhᵢ(x)) 加权组合 (Weighted Combination)

Boosting 的核心:降低偏差

  • 偏差 (Bias):模型的预测值与真实值之间的系统性差距。高偏差意味着模型欠拟合 (Underfitting),没有学到数据的基本规律。
  • Boosting 为何有效:每一轮新的学习器都被迫去关注上一轮学习器“搞不定”的那些困难样本。这个过程不断地修正模型的系统性错误,逐步减小偏差。
  • 著名算法AdaBoost, Gradient Boosting Machines (GBM), XGBoost

机制三:Stacking (堆叠法)

Stacking 是一种更精巧的组合策略,它试图学习如何“聪明地”组合基学习器的预测,而不是简单地投票或平均。

  1. 第一层 (Level 0): 训练多个不同的基学习器。将它们的预测结果作为新的特征。
  2. 第二层 (Level 1): 训练一个“元学习器 (Meta-Learner)”,它的输入是第一层模型的预测,输出是最终的预测。

Stacking 工作流程图

Stacking (堆叠法) 流程图 原始训练集 Level 0: 基学习器 (Base Learners) 模型 A (SVM) 模型 B (KNN) 模型 C (RF) 新特征集 (New Features from Predictions) Level 1: 元学习器 (Meta Learner) 元模型 (Logistic Regression)

Stacking 的优势:模型融合

  • 核心思想: Stacking 不是简单地组合预测,而是训练一个元模型来学习在什么情况下应该更相信哪个基模型
  • 例如: 元模型可能会学到:“如果模型A和模型B的预测很接近,但模型C的预测差很远,那么最终结果应该更倾向于A和B的平均值”。
  • 应用场景: 在数据科学竞赛(如 Kaggle)中非常流行,能够将多个高性能模型的优势融合在一起,榨取最后的性能提升。

Part 3: 最受欢迎的“积木”——决策树

为什么我们花这么多时间讨论决策树?因为它是迄今为止最常用、最成功的集成学习基学习器

决策树:天生的集成学习“好材料”

  • 优点:
    • 非线性,能捕捉复杂关系。
    • 模型可解释性强(单个树)。
    • 训练速度相对较快。
  • 缺点:
    • 非常容易过拟合,单个决策树的性能往往不稳定(高方差)。

绝配:决策树的高方差特性,恰好能被 Bagging (如随机森林) 通过平均来有效抑制!决策树的“弱小”(通过限制深度)又使其成为 Boosting 的完美“原料”。

决策树如何做出决策?

决策树通过一系列“是/否”问题,将复杂的数据集不断划分,直到每个子集都足够“纯净”。

  • 根节点 (Root Node): 代表整个数据集。
  • 内部节点 (Internal Node): 代表一个特征上的判断(一个问题)。
  • 分支 (Branch): 代表这个判断的输出(问题的答案)。
  • 叶节点 (Leaf Node): 代表最终的决策类别预测值

决策树剖析图

决策树结构 (Decision Tree Structure) 场景是教室吗? 有老师吗? 自习 上课 自习 是 (Yes) 否 (No) 是 (Yes) 否 (No) 根节点 (Root Node) 内部节点 (Internal Node) 叶节点 (Leaf Node) 分支 (Branch)

核心问题:如何选择“最好的”分裂?

决策树生长的关键,在于每一步都要选择一个最优的特征来分裂数据。

“最优”的标准是:分裂之后,各个子集的“纯度”最高

“纯度”越高,意味着不确定性越小,分类越明确。

纯度 (Purity) 的直观理解

数据集纯度示意图 三个容器代表数据集。第一个容器混合了两种颜色的球,代表低纯度。第二个大部分是同一种颜色,代表中等纯度。第三个全是同一种颜色,代表高纯度。 低纯度 (最混乱) 中等纯度 高纯度 (最纯净)

如何量化“纯度”?

我们用两个主要指标来衡量“不纯度”或“混乱程度”:

  1. 信息熵 (Information Entropy) -> 用于 ID3, C4.5 算法
  2. 基尼不纯度 (Gini Impurity) -> 用于 CART 算法

目标都是一样的:选择一个分裂,使得分裂后子节点的不纯度加权和最小

度量纯度之一:信息熵

  • 信息熵 H(D) 是度量一个数据集 D 不确定性混乱程度的指标。
  • 熵越大,表示数据集越混乱(混合的类别越多)。
  • 熵越小,表示数据集越纯净(大部分样本属于同一类别)。

对于一个有 K 个类别的数据集 D,其信息熵定义为: \[ \large{H(D) = - \sum_{k=1}^{K} p_k \log_2(p_k)} \] 其中 p_k 是第 k 类样本所占的比例。

熵的数值特性

假设一个数据集中只有两类:正例 (+)反例 (-)

场景 p_+ p_- H(D) 纯度
完全纯净 1.0 0.0 -1*log2(1) - 0 = 0 最高
混合 0.8 0.2 -0.8*log2(0.8) - 0.2*log2(0.2) ≈ 0.72 较低
最混乱 0.5 0.5 -0.5*log2(0.5) - 0.5*log2(0.5) = 1 最低

当正反例各占一半时,不确定性最大,熵达到最大值1。

分裂标准1:信息增益 (Information Gain)

ID3 算法使用的分裂标准是信息增益

  • 思想: 计算用某个属性 A 分裂数据集 D 后,系统不确定性(熵)下降了多少。下降得越多,说明 A 这个属性用来分类的效果越好。
  • 公式: \[ \large{\text{Gain}(D, A) = H(D) - H(D|A)} \] 其中 H(D) 是分裂前的熵,H(D|A) 是分裂后各子集熵的加权平均,称为条件熵
  • 决策: 选择使 Gain(D, A) 最大的属性 A 作为分裂节点。

案例:计算“场景”属性的信息增益 (1/3)

让我们用一个例子,手动计算属性“场景”的信息增益。

数据集D: 15个样本, 9个“上课”(正例), 6个“自习”(反例)。

1. 计算分裂前的总熵 H(D):

\[ \large{H(D) = - \frac{9}{15} \log_2\left(\frac{9}{15}\right) - \frac{6}{15} \log_2\left(\frac{6}{15}\right) \approx 0.971} \]

案例:计算“场景”属性的信息增益 (2/3)

2. 计算按“场景”分裂后的条件熵 H(D|场景):

属性“场景”有3个取值:教室(7个)、宿舍(3个)、户外(5个)。

  • 教室 (D1): 5正, 2反. \(\large{H(D_1) = - \frac{5}{7}\log_2(\frac{5}{7}) - \frac{2}{7}\log_2(\frac{2}{7}) \approx 0.863}\)
  • 宿舍 (D2): 1正, 2反. \(\large{H(D_2) = - \frac{1}{3}\log_2(\frac{1}{3}) - \frac{2}{3}\log_2(\frac{2}{3}) \approx 0.918}\)
  • 户外 (D3): 3正, 2反. \(\large{H(D_3) = - \frac{3}{5}\log_2(\frac{3}{5}) - \frac{2}{5}\log_2(\frac{2}{5}) \approx 0.971}\)

条件熵是加权平均: \[ \large{H(D|\text{场景}) = \frac{7}{15}H(D_1) + \frac{3}{15}H(D_2) + \frac{5}{15}H(D_3) \approx 0.910} \]

案例:计算“场景”属性的信息增益 (3/3)

3. 计算信息增益 Gain(D, 场景):

\[ \large{\text{Gain}(D, \text{场景}) = H(D) - H(D|\text{场景})} \] \[ \large{\approx 0.971 - 0.910 = 0.061} \]

ID3 算法会为所有属性(场景、老师、学生等)都计算信息增益,然后选择那个增益最大的属性作为第一个分裂节点。

信息增益的缺陷与改进

  • 问题: 信息增益准则对可取值数目较多的属性有所偏好。例如,如果用“学号”作为属性,每个学生一个分支,则每个叶子节点都无比纯净,信息增益会非常大,但这显然是过拟合。
  • C4.5 算法的改进: 使用信息增益率 (Gain Ratio) 来校正这种偏好。
  • 公式: \[ \large{\text{GainRatio}(D, A) = \frac{\text{Gain}(D, A)}{IV(A)}} \] 其中 IV(A) 是属性A的固有值 (Intrinsic Value),属性A的取值越多,IV(A) 通常越大,起到了惩罚作用。

度量纯度之二:基尼不纯度

CART (Classification and Regression Tree) 算法使用基尼指数来选择分裂属性。

  • 基尼不纯度 Gini(D): 从数据集 D 中随机抽取两个样本,其类别标记不一致的概率。
  • 基尼指数越小,数据集的纯度越高。

\[ \large{\text{Gini}(D) = \sum_{k=1}^{K} p_k (1 - p_k) = 1 - \sum_{k=1}^{K} p_k^2} \]

分裂标准2:基尼指数增益

  • 思想: 选择一个属性 A 进行分裂,使得分裂后的基尼指数加权和最小
  • 公式: 对于属性 A 的某个划分 a,分裂后的基尼指数为: \[ \large{\text{GiniIndex}(D|A=a) = \sum_{v=1}^{V} \frac{|D^v|}{|D|} \text{Gini}(D^v)} \]
  • 决策: 选择使 GiniIndex(D|A=a) 最小的属性 A 和划分 a
  • 优势: 相比于熵的计算,基尼指数不涉及对数运算,计算上更高效。

案例:计算基尼指数 (1/2)

我们用同样的数据计算属性“场景=教室”这个二分划分的基尼指数。

数据集D: 15个样本。

划分:

  • D1 (“场景=教室”): 7个样本 (5正, 2反)
  • D2 (“场景!=教室”): 8个样本 (4正, 4反)

1. 计算 D1D2 的基尼指数: \[ \large{\text{Gini}(D_1) = 1 - \left(\frac{5}{7}\right)^2 - \left(\frac{2}{7}\right)^2 \approx 0.408} \] \[ \large{\text{Gini}(D_2) = 1 - \left(\frac{4}{8}\right)^2 - \left(\frac{4}{8}\right)^2 = 0.5} \]

案例:计算基尼指数 (2/2)

2. 计算该划分的加权基尼指数:

\[ \large{\text{GiniIndex}(D|\text{场景=教室}) = \frac{7}{15}\text{Gini}(D_1) + \frac{8}{15}\text{Gini}(D_2)} \] \[ \large{\approx \frac{7}{15}(0.408) + \frac{8}{15}(0.5) \approx 0.190 + 0.267 = 0.457} \]

CART 算法会遍历所有属性的所有可能二分点,计算它们的加权基尼指数,并选择最小的那个作为最优分裂点。

决策树的现实问题:过拟合

如果不对决策树的生长加以限制,它会一直分裂,直到每个叶子节点只包含一个样本。这时训练集上的错误率为0,但模型会变得极其复杂,对新数据的泛化能力很差。

决策树过拟合过程示意 欠拟合 X1 A B 高偏差 模型过于简单 良好拟合 X1 X2 X3 A B C D 泛化能力好 平衡 过拟合 X1 高方差 学习到噪声 模型复杂度提升 过拟合: 模型在训练集上表现极佳,但泛化能力差 (Overfitting: Excellent on training data, poor generalization)

解决方案:剪枝 (Pruning)

为了防止过拟合,我们需要对决策树进行“剪枝”。

  • 预剪枝 (Pre-pruning): 在树的生长过程中,如果一个分裂不能带来泛化性能的提升(例如,在验证集上性能下降),就提前停止分裂
    • 优点: 速度快,生成的树更小。
    • 缺点: 可能过于“短视”,错过一些好的分裂组合。
  • 后剪枝 (Post-pruning): 先生成一棵完整的决策树,然后自底向上地考察节点。如果剪掉一个子树能提升泛化性能,就把它剪掉。
    • 优点: 通常效果更好,更不容易错过好的结构。
    • 缺点: 计算开销更大。

Part 4: 强大的集成模型

现在,我们把决策树这个“积木”和 Bagging/Boosting 这两种“搭建方法”结合起来。

Bagging + 决策树 = 随机森林

随机森林 (Random Forest) 是 Bagging 的一个非常成功的扩展和改进,它在 Bagging 的基础上,额外引入了特征随机性

构建过程:

  1. 进行 T自助采样 (Bootstrap),得到 T 个训练子集。
  2. 对每个子集训练一棵决策树。在训练这棵树的每个节点进行分裂时:
    • 不再是从所有 d 个特征中选择最优特征。
    • 而是随机选择 l 个特征 (l < d),然后从这 l 个特征中选择最优的。
  3. 最终将 T 棵树通过投票或平均进行预测。

随机森林的双重随机性

随机森林的双重随机性示意图 展示了随机森林的两个随机来源:行采样(自助采样)和列采样(特征随机选择)。 原始数据集 F1F2...Fk... 1. 行采样 训练子集 (用于一棵树) F1F2...Fk... 2. 列采样 (特征随机)

随机森林为何更强大?“求同存异”

  • 特征随机性的作用: 降低了森林中树与树之间的相关性
  • 为何降低相关性很重要: 如果不随机选择特征,森林中的每棵树在顶层节点分裂时,都很可能会选择同一个最强的特征,导致所有树的结构都非常相似。这样的集成,效果就大打折扣。
  • 通过强制每棵树在分裂时只能考虑一部分特征,随机森林让每棵树都从不同的“视角”去学习,变得“各有所长”。当它们组合在一起时,就能形成更强大的互补效应,进一步降低整体的方差。

Boosting + 决策树 = AdaBoost

AdaBoost (Adaptive Boosting) 是 Boosting 家族最经典的算法。

核心思想的迭代循环:

  1. 训练一个弱学习器 h_t
  2. 评估 h_t 的表现,赋予它一个权重 α_t (表现越好,权重越高)。
  3. 根据 h_t 的预测结果,更新训练样本的权重 w (预测错误的样本权重变大)。
  4. 重复此过程。

最终模型是所有弱学习器的加权组合。

AdaBoost 算法流程详解 (1/4): 初始化

目标: 训练一个强分类器 \(\large{H(x) = sign(\sum \alpha_t h_t(x))}\)

1. 初始化: 所有 N 个训练样本的权重被初始化为相等:

\[ \large{w_{1,n} = 1/N} \quad \text{for } n=1, \dots, N \]

AdaBoost 算法流程详解 (2/4): 训练与评估

循环 for t = 1 to T:

  1. 训练弱学习器: 使用当前带权重的训练集训练一个弱学习器 h_t(x),使其最小化加权错误。

  2. 计算加权错误率 ε_t: h_t 分类错误的样本的权重之和。 \[ \large{\epsilon_t = \sum_{n=1}^{N} w_{t,n} I(h_t(x_n) \ne y_n)} \]

  3. 计算该学习器的权重 α_t: \[ \large{\alpha_t = \frac{1}{2} \ln\left(\frac{1 - \epsilon_t}{\epsilon_t}\right)} \] 错误率 ε_t 越低,α_t 越大,话语权越大。

AdaBoost 算法流程详解 (3/4): 更新权重

  1. 更新样本权重 w: 这是算法的核心步骤。 \[ \large{w_{t+1, n} = \frac{w_{t,n} \exp(-\alpha_t y_n h_t(x_n))}{Z_t}} \] (\(Z_t\) 是归一化因子,确保新权重和为1)

    直观解释:

    • 如果样本 n 被分类正确 (\(y_n h_t(x_n) = 1\)),指数项为负,w 变小。
    • 如果样本 n 被分类错误 (\(y_n h_t(x_n) = -1\)),指数项为正,w 变大。

AdaBoost 算法流程详解 (4/4): 最终组合

3. 最终输出: 将所有 T 个弱学习器按照其权重 α_t 进行加权投票,得到最终的强分类器:

\[ \large{H(x) = \text{sign}\left(\sum_{t=1}^{T} \alpha_t h_t(x)\right)} \]

Part 5: 实战 - 预测信用卡违约

理论已经足够,让我们进入最激动人心的部分:用真实数据来检验这些模型。

  • 任务: 预测客户下个月是否会信用卡违约。
  • 数据: “Default of Credit Card Clients” 数据集 (UCI)。
  • 工具: Python, pandas, scikit-learn
  • 模型对比:
    1. 单个决策树 (基准)
    2. 随机森林 (Bagging)
    3. AdaBoost (Boosting)

实战第1步:数据加载与准备

我们将使用 ucimlrepo 库直接获取数据,并进行训练集和测试集的划分。

# 导入必要的库
import pandas as pd
from sklearn.model_selection import train_test_split
from ucimlrepo import fetch_ucirepo 

# --- 从UCI库获取数据 ---
# 这是一个标准化的数据加载方式,保证了数据的可复现性
credit_default = fetch_ucirepo(id=350) 
X = credit_default.data.features 
y = credit_default.data.targets.squeeze() # 转换成Pandas Series

# --- 数据分割 ---
# 将数据分为训练集(70%)和测试集(30%)
# random_state=42 保证每次分割结果一致
# stratify=y 保证训练集和测试集中违约比例与原始数据一致
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

print(f'训练集维度: {X_train.shape}')
print(f'测试集维度: {X_test.shape}')
训练集维度: (21000, 23)
测试集维度: (9000, 23)

实战第2步:训练基准模型 - 单个决策树

我们先训练一个决策树作为性能基准。为了防止过拟合,我们限制其最大深度为5。

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, roc_auc_score

# --- 训练模型 ---
# max_depth=5 限制树的深度,防止过拟合
dt_clf = DecisionTreeClassifier(max_depth=5, random_state=42)
dt_clf.fit(X_train, y_train)

# --- 评估模型 ---
y_pred_dt = dt_clf.predict(X_test)
y_prob_dt = dt_clf.predict_proba(X_test)[:, 1] # 获取正类的预测概率

acc_dt = accuracy_score(y_test, y_pred_dt)
auc_dt = roc_auc_score(y_test, y_prob_dt)

print(f'单个决策树 (max_depth=5):')
print(f'  准确率 (Accuracy): {acc_dt:.4f}')
print(f'  AUC: {auc_dt:.4f}')
单个决策树 (max_depth=5):
  准确率 (Accuracy): 0.8164
  AUC: 0.7427

实战第3步:训练 Bagging 模型 - 随机森林

现在,让我们看看由100棵决策树组成的“森林”表现如何。n_estimators 就是基学习器的数量 T

from sklearn.ensemble import RandomForestClassifier

# --- 训练模型 ---
# n_estimators=100: 构建100棵树
# n_jobs=-1: 使用所有CPU核心并行计算,加快速度
rf_clf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42, n_jobs=-1)
rf_clf.fit(X_train, y_train)

# --- 评估模型 ---
y_pred_rf = rf_clf.predict(X_test)
y_prob_rf = rf_clf.predict_proba(X_test)[:, 1]

acc_rf = accuracy_score(y_test, y_pred_rf)
auc_rf = roc_auc_score(y_test, y_prob_rf)

print(f'随机森林 (100棵树, max_depth=5):')
print(f'  准确率 (Accuracy): {acc_rf:.4f}')
print(f'  AUC: {auc_rf:.4f}')
随机森林 (100棵树, max_depth=5):
  准确率 (Accuracy): 0.8118
  AUC: 0.7675

观察: 随机森林的准确率和AUC都比单个决策树有所提升。

实战第4步:训练 Boosting 模型 - AdaBoost

最后,我们来试试 AdaBoost。它也是基于决策树,但采用的是串行的、关注错误样本的提升策略。

from sklearn.ensemble import AdaBoostClassifier

# --- 训练模型 ---
# AdaBoost 通常使用比较浅的树(“树桩”),这里用max_depth=1
base_estimator = DecisionTreeClassifier(max_depth=1)

ada_clf = AdaBoostClassifier(
    estimator=base_estimator,
    n_estimators=100,
    random_state=42
)
ada_clf.fit(X_train, y_train)

# --- 评估模型 ---
y_pred_ada = ada_clf.predict(X_test)
y_prob_ada = ada_clf.predict_proba(X_test)[:, 1]

acc_ada = accuracy_score(y_test, y_pred_ada)
auc_ada = roc_auc_score(y_test, y_prob_ada)

print(f'AdaBoost (100个树桩):')
print(f'  准确率 (Accuracy): {acc_ada:.4f}')
print(f'  AUC: {auc_ada:.4f}')
AdaBoost (100个树桩):
  准确率 (Accuracy): 0.8160
  AUC: 0.7704

观察: AdaBoost 的表现也明显优于单个决策树。

实战第5步:结果对比与可视化

语言是苍白的,让我们把结果画出来。AUC (Area Under the Curve) 是一个比准确率更稳健的分类模型评估指标,它衡量了模型区分正负样本的综合能力。

import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import seaborn as sns
import pandas as pd

# Fallback font for Chinese characters if needed
# try:
#     plt.rcParams['font.sans-serif'] = ['Heiti TC']
# except:
#     pass

# 设置绘图风格
sns.set_theme(style="whitegrid", context="talk")
fig, ax = plt.subplots(figsize=(10, 6), dpi=100)

# 数据
results = pd.DataFrame({
    'Model': ['单个决策树', '随机森林', 'AdaBoost'],
    'AUC': [auc_dt, auc_rf, auc_ada]
}).sort_values('AUC', ascending=True)

colors = ['#86BBD8', '#F26419', '#33658A']

# 绘图
bars = ax.barh(results['Model'], results['AUC'], color=colors, height=0.6)
ax.set_xlim(0.76, 0.785)
ax.set_xlabel('ROC AUC Score', fontsize=14, labelpad=10)
ax.set_title('模型性能对比:信用卡违约预测', fontsize=18, pad=20, weight='bold')

# 在柱状图上显示数值
for bar in bars:
    width = bar.get_width()
    ax.text(width + 0.0005, bar.get_y() + bar.get_height()/2, f'{width:.4f}', 
            ha='left', va='center', fontsize=14, weight='bold')

# 美化图表
ax.spines[['top', 'right', 'bottom']].set_visible(False)
ax.xaxis.grid(True, linestyle='--', which='major', color='grey', alpha=0.5)
ax.yaxis.grid(False)
ax.tick_params(axis='y', labelsize=14, length=0)
ax.tick_params(axis='x', labelsize=12)

plt.tight_layout()
plt.show()
Figure 1: 三种模型在信用卡违约预测任务上的AUC表现对比

结果分析:群体智慧的胜利

Figure 1 可以清晰地看到:

  • 集成学习模型(随机森林和AdaBoost)的性能显著优于单个决策树。
  • 这有力地证明了我们从理论开始的推导:将多个“还不错”的学习器(决策树)通过系统性的方法(Bagging, Boosting)组合起来,确实可以得到一个更强大的模型。
  • 在这个特定任务上,随机森林和 AdaBoost 的表现相近,都取得了很好的效果。

集成学习的另一大优势:可解释性

集成模型,特别是基于树的模型,还有一个巨大的优点:它们可以告诉我们,哪些输入特征对于做出最终决策最重要

这在经济和金融应用中至关重要。我们不仅想预测,更想理解预测背后的驱动因素。

可视化特征重要性

让我们看看随机森林模型认为哪些因素是预测信用卡违约的最重要指标。

import numpy as np

# 获取特征重要性
importances = rf_clf.feature_importances_
feature_names = X.columns
df_importance = pd.DataFrame({'feature': feature_names, 'importance': importances})
df_importance = df_importance.sort_values('importance', ascending=False).head(15)


# 设置绘图风格
fig, ax = plt.subplots(figsize=(10, 7), dpi=100)

# 绘图
sns.barplot(
    x='importance', 
    y='feature',
    data=df_importance,
    palette='viridis',
    ax=ax
)

# 美化图表
ax.set_title('特征重要性分析 (来自随机森林)', fontsize=18, pad=20, weight='bold')
ax.set_xlabel('相对重要性 (Mean Decrease in Impurity)', fontsize=14, labelpad=10)
ax.set_ylabel('')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.tick_params(axis='both', which='major', labelsize=12)
plt.tight_layout()
plt.show()
Figure 2: 随机森林模型得出的特征重要性排序

商业洞察:什么驱动了违约风险?

Figure 2 中,我们可以得到非常直观的商业洞察:

  1. 历史还款状态 (PAY_0, PAY_2…) 是最重要的预测因子。 这完全符合金融常识:一个客户最近是否按时还款,是他未来信誉的最强信号。
  2. 账单金额 (BILL_AMT1) 和信用额度 (LIMIT_BAL) 也非常重要。
  3. 人口统计学信息(如年龄 AGE 也有一定作用,但远不如客户的近期行为数据重要。

这种分析对于银行制定信贷政策和风险管理策略具有极高的价值。

本章总结

  1. 理论基础: “群体智慧”有坚实的数学基础(霍夫丁不等式)。组合多个比随机猜测好的独立学习器,可以指数级降低错误率。
  2. 两大路径:
    • Bagging (以随机森林为代表): 并行训练,通过投票/平均来降低方差,使模型更稳定。
    • Boosting (以AdaBoost为代表): 串行训练,迭代地关注错误样本,通过加权组合来降低偏差,使模型更准确。
  3. 核心组件: 决策树是集成学习最理想的“积木”,其自身的高方差或可控的“弱”性使其能与 Bagging 和 Boosting 完美结合。
  4. 实践价值: 集成学习不仅能带来预测性能的显著提升,还能提供如特征重要性等深刻的业务洞察。

Q & A

谢谢!