查看全课大纲(第 30 / 82 节)
  1. 1.函数与数据泛化
  2. 2.向量:从几何直觉到高维空间
  3. 3.矩阵与张量
  4. 4.特征值与特征向量:矩阵变换的不动方向
  5. 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
  6. 6.概率与统计:机器学习的数学基础
  7. 7.独立性与随机变量:贝叶斯公式与二项分布
  8. 8.期望、方差与协方差:随机变量的数字画像
  9. 9.联合概率:当一条轴不够用的时候
  10. 10.概率分布(一):从伯努利到伽马函数
  11. 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
  12. 12.机器学习的模型:找输入与输出的关系
  13. 13.机器学习的应用
  14. 14.过拟合
  15. 15.机器学习的分类
  16. 16.有监督学习:从房价预测说起
  17. 17.损失函数
  18. 18.梯度下降(上):更新规则与学习速率
  19. 19.梯度下降(2)
  20. 20.牛顿方法
  21. 21.KNN简介
  22. 22.距离测算
  23. 23.朴素贝叶斯
  24. 24.朴素贝叶斯的实际应用
  25. 25.信息熵:决策树构建的度量基础
  26. 26.信息增益与决策树的构建
  27. 27.ID3算法
  28. 28.决策树节点的划分
  29. 29.CART树
  30. 30.Bagging
  31. 31.Boosting:自适应提升算法
  32. 32.Gradient Boosting
  33. 33.线性模型:回归与分类的同一副骨架
  34. 34.最小二乘:把最优参数直接解出来
  35. 35.最小二乘法的几何与概率视角
  36. 36.极大似然:给最小二乘一个概率出身
  37. 37.逻辑回归(1):把线性模型接到分类上
  38. 38.逻辑回归(2):损失函数与多分类
  39. 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
  40. 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
  41. 41.优化方法(1):坐标下降与前向选择
  42. 42.优化方法(2):前向策略与最小角回归
  43. 43.拉格朗日乘子法(1):从无约束到等式约束
  44. 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
  45. 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
  46. 46.最大间隔:把分类问题变成优化问题
  47. 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
  48. 48.支持向量:边界上的少数派
  49. 49.SVM 计算过程:一笔到底算完,再走进核方法
  50. 50.贝叶斯公式:概率即信念
  51. 51.隐含变量和概率模型:给看不见的原因建模
  52. 52.EM 算法:期望与最大化的迭代
  53. 53.琴生不等式:EM 的数学钥匙
  54. 54.三个硬币问题:EM 的复盘与衔接
  55. 55.高斯混合模型:用隐变量解释双峰数据
  56. 56.隐马尔可夫模型
  57. 57.隐马尔可夫模型(2):前向算法与动态混合模型
  58. 58.隐马尔可夫模型在 NLP 中的运用
  59. 59.EM 算法:把「为什么收敛」推到底
  60. 60.HMM 在 NLP 中的运用:从分词到机器翻译
  61. 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
  62. 62.高维下的高斯模型
  63. 63.主题模型:从词袋到隐含主题
  64. 64.Dirichlet 分布(1)
  65. 65.Dirichlet 分布(2)
  66. 66.统计学习内容回顾
  67. 67.K-means 算法
  68. 68.PCA 降维
  69. 69.SVD 降维
  70. 70.从聚类到流形
  71. 71.局部线性嵌入
  72. 72.随机邻域嵌入:SNE 与 t-SNE
  73. 73.无参模型:让数据自己说话
  74. 74.高斯过程:函数空间里的正态分布
  75. 75.高斯过程(2):噪声、正则化与核的线性组合
  76. 76.神经网络简介:从感知器到自编码器
  77. 77.反向传播:把误差一层层传回去
  78. 78.卷积神经网络(CNN):让特征自己学出来
  79. 79.循环神经网络(RNN):给网络装上记忆
  80. 80.深度学习回顾:静态一类,动态一类
  81. 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
  82. 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传

Bagging

约 12 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

小象实战讲义 · 机器学习算法精讲

在上一节中,我们学习了决策树 CART 算法。本节课,我们将探讨如何将多个“弱”的决策树(或其它模型)组合起来,形成一个更强大的“强”模型。这种“集成学习”的思想是提升模型性能、降低过拟合风险的关键技术。本节将重点介绍集成学习的第一种经典范式:Bagging,并理解其核心——自助采样法(Bootstrap)与投票/平均机制。

💡 核心导读

  • 集成学习哲学:理解“三个臭皮匠,顶个诸葛亮”的算法实现思路,即如何通过组合多个弱模型来提升整体性能。
  • Bootstrap 自助采样:掌握“有放回随机采样”这一统计学方法,它是 Bagging 生成多样性训练集的基础。
  • Bagging 算法流程:学习从自助采样、训练多个基学习器,到最终通过投票(分类)或平均(回归)进行集成的完整步骤。
  • 随机森林:认识 Bagging 思想在决策树上的直接应用——随机森林,并理解其“随机”的双重含义。
  • 方差与偏差:从降低模型方差的角度,直观理解 Bagging 为何能有效抑制过拟合,提升模型稳定性。

集成学习:从弱到强的智慧

在机器学习中,我们常常发现,一个复杂的模型容易在训练数据上表现完美(过拟合),但在新数据上表现不佳;而一个简单的模型(弱学习器)虽然偏差大,但方差小,更稳定。集成学习(Ensemble Learning)的核心思想就是:将多个相对简单、性能一般的“弱学习器”组合起来,通过某种策略形成一个性能更优、更稳健的“强学习器”。

这就像古语所说的“三个臭皮匠,顶个诸葛亮”。每个弱学习器(臭皮匠)可能只对数据的一部分模式敏感,或者容易受到噪声的影响。但当我们汇集许多弱学习器的意见时,正确的模式会被强化,而随机的噪声则可能被抵消,从而得到更接近“诸葛亮”般智慧的决策。

本节课要讲的 Bagging,以及下节课的 Boosting,是两种最主流的集成学习框架。

Bagging:自助聚合

Bagging 是 Bootstrap AGGregating 的简写。讲师特别提醒别望文生义:它不是一个”袋子”(bag),而是”自助采样 + 聚合”两个词的缩写。

Bootstrap 自助采样

Bootstrap 是一种统计学上的重采样技术。它的操作非常简单:从原始训练集中有放回地随机抽取样本,以构成一个新的训练集。讲师的语境是:拿到一份大数据后,通常 70% 划作训练集、30% 留作测试集(测试数据不能碰,一直放在那儿),而 Bootstrap 就作用在训练集上。

过程像从罐子里抓小球:随机摸一个小球放进新篮子,再把球放回去,接着随机摸下一个……如此往复。每一次 Bootstrap 得到的数据集,都是原始数据的一个”反映与写照”——大部分真实分布被保留,但细节因重复与缺失而各不相同。假设我们有一个包含 nn 个样本的原始训练集 DD,一次采样的完整流程是:

  1. 随机从 DD 中抽取一个样本。
  2. 记录该样本后,将其放回 DD 中。
  3. 重复步骤1和2 nn 次(即采样次数与原始集大小相同),得到一个新的数据集 D1D_1。

由于是有放回采样,D1D_1 中有些样本可能被多次抽到,而有些原始样本可能一次都没被抽中。理论上,当 nn 较大时,一次 Bootstrap 采样中,大约有 1−(1−1n)n≈1−e−1≈63.2%1 - (1 - \frac{1}{n})^n \approx 1 - e^{-1} \approx 63.2\% 的原始样本会被至少抽中一次。这意味着每个新训练集都保留了大部分原始信息,但又通过样本的重复和缺失引入了多样性。

下面我们用一个小例子来模拟 Bootstrap 采样:

import random

# 设定随机种子,保证结果可复现
random.seed(42)

# 原始小型数据集
original_data = ['A', 'B', 'C', 'D', 'E']
n = len(original_data)
print(f"原始数据: {original_data}")

# 进行一次Bootstrap采样(有放回采样n次)
bootstrap_sample_indices = [random.randint(0, n-1) for _ in range(n)]
bootstrap_sample = [original_data[i] for i in bootstrap_sample_indices]

print(f"采样索引: {bootstrap_sample_indices}")
print(f"采样后数据: {bootstrap_sample}")

# 计算独特样本的比例
unique_samples = len(set(bootstrap_sample))
unique_ratio = unique_samples / n * 100
print(f"独特样本比例: {unique_ratio:.1f}% (理论值约63.2%)")
原始数据: ['A', 'B', 'C', 'D', 'E']
采样索引: [0, 0, 2, 1, 1]
采样后数据: ['A', 'A', 'C', 'B', 'B']
独特样本比例: 60.0% (理论值约63.2%)

可以看到,样本 ‘A’ 和 ‘B’ 被重复采样了,而 ‘D’ 和 ‘E’ 没有被采到。新数据集的大小仍是5,但内容已不同于原数据集。

Bagging 算法流程

Bagging 算法正是利用了 Bootstrap 采样来创造多样性:

  1. 生成多样化的训练集:对原始训练集 DD 进行 TT 次独立的 Bootstrap 采样,得到 TT 个新的训练集 D1,D2,...,DTD_1, D_2, ..., D_T。
  2. 并行训练基学习器:用这 TT 个训练集,分别独立地训练 TT 个相同的基学习器(例如决策树)。这些基学习器就是我们的“臭皮匠”。
  3. 聚合预测结果:
    • 分类任务:对于一个新的输入样本 xx,每个基学习器给出自己的类别预测。最终的预测结果是所有基学习器预测的众数(即投票最多的类别)。
    • 回归任务:对于一个新的输入样本 xx,最终的预测结果是所有基学习器预测值的平均值。

这个过程可以形式化地表示为:

  • 分类:H(x)=mode{h1(x),h2(x),...,hT(x)}H(x) = \text{mode}\lbrace h_1(x), h_2(x), ..., h_T(x)\rbrace
  • 回归:H(x)=1T∑t=1Tht(x)H(x) = \frac{1}{T} \sum_{t=1}^{T} h_t(x)

其中,ht(x)h_t(x) 是第 tt 个基学习器的预测,H(x)H(x) 是集成模型的最终预测。

投票的两种等价写法(讲师补充):分类也常写成”求均值”的形式——若类别记为 0/1,把 TT 个预测加起来求均值,得到 0.3 就偏 0 类、0.8 就偏 1 类;更常见的是把类别记为 −1/+1-1/+1,求和后看结果大于 0 还是小于 0——这个写法将贯穿下一节 Boosting 的加权投票。另有一点值得注意:Bagging 默认每个基学习器的权重完全均等——那如果让”学得好的分类器话语权更大”呢?这就引出了下一节的 Boosting。

Bagging 为何有效:降低方差

Bagging 提升模型性能的关键在于降低模型的方差。

回忆一下模型的泛化误差可以分解为偏差(Bias)、方差(Variance)和噪声(Noise)。方差衡量了模型对于训练数据微小变化的敏感度。一个复杂的模型(如深度很深的决策树)方差很高,容易过拟合。

Bagging 通过以下方式降低方差:

  1. 数据多样性:每个基学习器在不同的 Bootstrap 样本集上训练。这些样本集共享主要的数据分布,但在细节(尤其是噪声点和边界点)上有所不同。这迫使每个基学习器学到略有不同的模式。
  2. 平均效应:在聚合时,对于分类任务,一个偶然的错误预测(可能由某个噪声点导致)会被其他多数正确的预测所覆盖;对于回归任务,极端预测值会被平均掉。

一个直观的画面(讲师课上的画图讲解):原始数据里有些点长得很”奇怪”——画了黄圈的那颗、缩在角落离群体很远的那几颗,它们很可能就是噪声。只用全部数据训练一个复杂模型时,分界面会被这些噪声点带得歪歪扭扭,一会儿鼓成”山包”、一会儿绕成”河流”。但 Bootstrap 采样的妙处在于:采 10 次、20 次,大多数采样根本碰不到那些孤零零的噪声点(每个子集里被重复采到的是主体数据)。于是在”干净”子集上训练出的边界往往简单得多——也许数据的本质就是一条简单的直线。多次采样、多个臭皮匠投票之后,噪声的影响被稀释,方差降下来了。

下面的代码模拟了 Bagging 在简单分类场景下的投票过程:

import random
import statistics

random.seed(42)

def weak_learner_predict():
    """模拟一个准确率不高的弱分类器对单个样本的预测。
       假设真实标签应为1,但弱分类器只有60%概率预测正确。"""
    # 模拟60%正确率
    return 1 if random.random() < 0.6 else 0

def bagging_predict(n_estimators):
    """模拟Bagging集成预测:聚合n_estimators个弱分类器的投票结果。"""
    predictions = [weak_learner_predict() for _ in range(n_estimators)]
    # 投票:多数票决
    return 1 if sum(predictions) > n_estimators / 2 else 0

# 模拟对同一个样本进行多次独立预测
n_trials = 10
print("模拟10次独立预测中,单一弱分类器与Bagging集成(5个分类器)的表现:")
single_preds = []
bagging_preds = []

for _ in range(n_trials):
    single_preds.append(weak_learner_predict())
    bagging_preds.append(bagging_predict(5))

print(f"单一弱分类器预测结果: {single_preds}")
print(f"Bagging(5个弱分类器)集成预测结果: {bagging_preds}")
print(f"单一预测中‘1’的比例: {sum(single_preds)/len(single_preds)*100:.1f}%")
print(f"Bagging预测中‘1’的比例: {sum(bagging_preds)/len(bagging_preds)*100:.1f}%")
模拟10次独立预测中,单一弱分类器与Bagging集成(5个分类器)的表现:
单一弱分类器预测结果: [0, 0, 1, 0, 0, 0, 0, 1, 0, 0]
Bagging(5个弱分类器)集成预测结果: [1, 1, 1, 1, 1, 1, 0, 1, 1, 1]
单一预测中‘1’的比例: 20.0%
Bagging预测中‘1’的比例: 90.0%

在这个模拟中,每个弱分类器只有60%的概率做出正确预测(输出1)。单独看,它在10次预测中只对了2次。但当我们用5个这样的弱分类器进行Bagging集成投票时,10次集成预测中有9次给出了正确的预测(1)。这生动地展示了“群体智慧”如何显著提升预测的稳定性与准确性。

随机森林:Bagging的明星代表

随机森林(Random Forest) 是 Bagging 思想与决策树结合的典型成功算法。讲师的一句话定义:用 Bagging 的方式生成很多很多 CART 树,这个算法就叫随机森林——森林(forest),本来就是很多树(many trees)。

标准实现中,随机森林通常还有第二重随机性,进一步拉大基学习器之间的差异、更好地降方差:

  1. 样本随机(Bagging):对训练样本进行 Bootstrap 采样。
  2. 特征随机:在决策树每个节点分裂时,先随机选出一个特征子集(常见大小为 d\sqrt{d} 或 log⁡2(d)\log_2(d),dd 为总特征数),只在这个子集里挑最优分裂特征。

这种双重随机让森林里每棵树从不同的数据子集、不同的特征视角学习,集成的效果通常比普通的 Bagging 决策树更好,过拟合也进一步受到抑制。

下面的代码展示了不同数量基学习器对 Bagging 集成结果的影响:

import random

random.seed(42)

def weak_learner_predict():
    """另一个模拟的弱分类器,正确率设为55%"""
    return 1 if random.random() < 0.55 else 0

def bagging_predict_vote(n_estimators):
    """Bagging投票集成"""
    votes = [weak_learner_predict() for _ in range(n_estimators)]
    return 1 if sum(votes) > n_estimators / 2 else 0

# 测试不同数量基学习器下的集成效果
test_cases = [3, 5, 7]
n_trials = 10

for n_est in test_cases:
    print(f"\n--- Bagging with {n_est} estimators ---")
    predictions = [bagging_predict_vote(n_est) for _ in range(n_trials)]
    print(f"集成预测结果: {predictions}")
    print(f"预测为‘1’的比例: {sum(predictions)/len(predictions)*100:.1f}%")
--- Bagging with 3 estimators ---
集成预测结果: [1, 0, 1, 1, 1, 1, 0, 1, 1, 0]
预测为‘1’的比例: 70.0%

--- Bagging with 5 estimators ---
集成预测结果: [0, 0, 1, 1, 1, 1, 0, 1, 0, 1]
预测为‘1’的比例: 60.0%

--- Bagging with 7 estimators ---
集成预测结果: [0, 1, 1, 1, 1, 0, 1, 1, 1, 1]
预测为‘1’的比例: 80.0%

可以看到,即使弱分类器本身正确率仅略高于50%(55%),通过集成多个分类器,集成预测的正确率(预测为 1 的比例)明显抬升(本例中 7 个学习器时达到 80%)。基学习器数量越多,正确的意见越容易在投票中占据多数,集成也越稳定——当然计算成本随之上升。

📝 动手练一练

  1. 概念辨析:Bagging 算法中,每个基学习器是并行训练的还是串行训练的?这与我们下一节要讲的 Boosting 算法有何根本区别?这种区别导致了它们在降低模型偏差和方差上各有什么侧重?
👉 点击查看参考答案

Bagging 的基学习器是并行训练的。因为每个学习器都是在独立的 Bootstrap 样本集上训练,彼此之间没有依赖关系,所以可以同时进行训练。

而 Boosting 的基学习器是串行训练的。后一个学习器的训练会依赖于前一个学习器的结果,它更关注于被前序学习器错误预测的样本。

这种根本区别导致:

  • Bagging 主要侧重于降低模型的方差。通过平均多个高方差、低偏差的模型(如深度决策树)来获得一个更稳定的模型。它对噪声不太敏感。
  • Boosting 主要侧重于降低模型的偏差。通过串行地纠正前序模型的错误,将多个弱模型(高偏差、低方差)组合成一个强模型。但它对噪声和异常值可能更敏感。
  1. 代码实践:在上面的 Bagging 投票模拟代码中,我们将弱分类器的正确率设置为 55%。请修改代码,将弱分类器的正确率设置为 45%(即比随机猜测还差),然后观察随着基学习器数量(如 3, 5, 7, 9, 11)的增加,集成模型的预测表现(预测为1的比例)有何变化?这个实验说明了 Bagging 对基学习器有什么基本要求?
👉 点击查看参考答案
import random

random.seed(42)

def weak_learner_predict_bad():
    """一个正确率只有45%的'坏'分类器"""
    return 1 if random.random() < 0.45 else 0

def bagging_predict_vote(n_estimators):
    votes = [weak_learner_predict_bad() for _ in range(n_estimators)]
    return 1 if sum(votes) > n_estimators / 2 else 0

# 试验次数加大到 2000,让统计趋势压过单次噪声
for n_est in [3, 5, 7, 9, 11]:
    predictions = [bagging_predict_vote(n_est) for _ in range(2000)]
    correct_rate = sum(predictions) / len(predictions)
    print(f"基学习器数 {n_est:2d}: 集成预测为1的比例 = {correct_rate:.1%}")
基学习器数  3: 集成预测为1的比例 = 40.8%
基学习器数  5: 集成预测为1的比例 = 40.2%
基学习器数  7: 集成预测为1的比例 = 40.1%
基学习器数  9: 集成预测为1的比例 = 38.8%
基学习器数 11: 集成预测为1的比例 = 34.6%

现象与说明: 每个弱分类器只有 45% 的正确率(比瞎猜还差)。集成之后,正确率不升反降——3 个学习器时还有 40.8%,11 个时已跌到 34.6%,且这只是缓降的开端:按大数定律,集成数量继续增大,“多数派”几乎必然是错的那一方,比例将一路逼近 0%。

原因在于 Bagging 的投票机制建立在”多数更可能是对的”这一假设上。基学习器比瞎猜还差时,多数投票只会把系统性的错误放大。这说明了 Bagging 对基学习器的基本要求:至少要略好于随机猜测(>50%)——“臭皮匠”再弱,也得比瞎蒙强一点。如果一个学习器稳定地比 50% 差,理论上可以把它的预测取反变成有效学习器,但这不是 Bagging 的标准用法。

本章小结

本节我们深入探讨了集成学习的第一块基石——Bagging 算法。我们理解了如何通过 Bootstrap 自助采样技术,从原始数据中创造出多个既相似又不同的训练子集,并在此基础上并行训练一系列基学习器。最终,通过简单的投票(分类)或平均(回归)策略,将这些“臭皮匠”的智慧聚合起来,得到一个更强大、更稳定的“诸葛亮”模型。随机森林作为 Bagging 与决策树结合的典范,因其卓越的性能和良好的抗过拟合能力,已成为机器学习中最常用的算法之一。

📋 行动清单

  • 在脑海中复述 Bagging 算法的两个核心步骤:Bootstrap 和 Aggregating。
  • 理解为什么 Bootstrap 采样(有放回)能创造出多样化的训练集,并记住大约 63.2% 的独特样本率。
  • 运行并理解本节中的模拟代码,体会从弱分类器到强集成器的变化过程,特别是基学习器数量与集成稳定性的关系。
  • 思考:如果基学习器本身已经是强学习器(例如一个已经调优好的复杂神经网络),使用 Bagging 还会有显著效果吗?为什么?

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第7章课件:经典机器学习模型 III(CART/集成)
    下载
  • 第7章笔记:CART 树
    下载
  • 第7章笔记:集成学习
    下载
  • 第7章笔记:GBDT
    下载
  • 第7章代码包(RandomForest)
    下载