Bagging与随机森林实战:如何解决机器学习中的过拟合与方差问题

1次阅读
没有评论

共计 2804 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

背景痛点:单一决策树的高方差困境

在机器学习项目中,决策树因其直观易懂、无需特征缩放等优点被广泛使用。但当我们面对复杂数据时,会发现一个尴尬的现象:

Bagging 与随机森林实战:如何解决机器学习中的过拟合与方差问题

  • 训练集上表现完美的决策树,测试集性能可能断崖式下跌
  • 对训练数据微小变化极其敏感(比如删掉 10% 样本后模型结构完全不同)
  • 树深度越大,模型越容易记住噪声而非学习规律

这些现象背后的本质问题是 高方差。从统计角度看,决策树的预测误差可以分解为:

$$ \text{Error} = \text{Bias}^2 + \text{Var} + \varepsilon $$

其中方差项 $\text{Var}$ 在深度决策树中尤为突出。举个具体例子:

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_moons

X, y = make_moons(noise=0.3, random_state=42)

tree = DecisionTreeClassifier(min_samples_leaf=5)
tree.fit(X, y)
print(f"训练准确率: {tree.score(X, y):.3f}")  # 通常 >0.95

# 用相同分布生成新数据测试
X_new, y_new = make_moons(noise=0.3, random_state=99)
print(f"测试准确率: {tree.score(X_new, y_new):.3f}")  # 通常低 0.1~0.2

技术对比:Bagging vs Boosting

解决高方差问题主要有两种思路:

  1. Bagging (Bootstrap Aggregating)
  2. 核心思想:通过并行训练多个基模型,用投票 / 平均降低方差
  3. 数学原理:对于独立同分布的基模型,聚合后的方差满足
    $$ Var\left(\frac{1}{n}\sum_{i=1}^n X_i\right) = \frac{\sigma^2}{n} $$
  4. 典型代表:随机森林

  5. Boosting

  6. 核心思想:串行训练模型,每个新模型专注修正前序模型的错误
  7. 更擅长降低偏差(Bias)
  8. 典型代表:AdaBoost、GBDT

关键区别在于:

  • Bagging 的基模型可以 并行训练,适合大规模数据
  • Bagging 对异常值更鲁棒,因为每个基模型只看到部分数据

随机森林的双重随机性

随机森林在 Bagging 基础上引入了双重随机机制:

  1. 数据随机性
  2. 对训练集进行 Bootstrap 抽样(有放回采样)
  3. 默认采样量为原始训练集大小的 63.2%(数学上 $1-1/e$)

  4. 特征随机性

  5. 每个节点分裂时,只在随机子集的特征中寻找最优分裂
  6. 子集大小通过 max_features 参数控制

这种设计带来三个好处:

  • 进一步降低模型间的相关性
  • 增强对噪声的鲁棒性
  • 天然得到特征重要性评估

Python 实战:关键参数调优

通过 sklearn 实现随机森林时,这些参数需要特别关注:

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

params = {'n_estimators': [50, 100, 200],  # 树的数量
    'max_depth': [None, 5, 10],      # 控制过拟合
    'min_samples_leaf': [1, 3, 5],   # 叶节点最小样本数
    'max_features': ['sqrt', 0.8]    # 特征子集大小
}

rf = RandomForestClassifier(oob_score=True, random_state=42)
grid = GridSearchCV(rf, params, cv=5)
grid.fit(X_train, y_train)

print(f"最优参数: {grid.best_params_}")
print(f"OOB 分数: {grid.best_estimator_.oob_score_:.3f}")

生产环境注意事项

特征重要性评估

除了默认的基尼重要性,更推荐使用排列重要性(Permutation Importance):

from sklearn.inspection import permutation_importance

result = permutation_importance(
    grid.best_estimator_, X_test, y_test,
    n_repeats=10, random_state=42
)

import pandas as pd
importance_df = pd.DataFrame({
    'feature': X.columns,
    'importance': result.importances_mean,
    'std': result.importances_std
}).sort_values('importance', ascending=False)

OOB 分数的局限性

虽然 OOB(Out-of-Bag)分数很方便,但在类别不平衡时可能失真:

  • 默认使用准确率(Accuracy)作为评估指标
  • 对少数类预测偏差不敏感
  • 解决方案:设置 class_weight="balanced" 或改用 F1-OOB

避坑指南

  1. 超高维稀疏数据问题
  2. 当特征维度 >10k 时(如文本 TF-IDF 特征)
  3. 随机特征子集可能包含太多噪声特征
  4. 建议先做特征筛选或改用线性模型

  5. 内存消耗预警

  6. 内存占用与树数量成线性关系
  7. 千棵树 + 深树可能占用数 GB 内存
  8. 解决方案:使用 warm_start 增量训练

性能优化实验

通过控制变量法观察 n_estimators 的影响:

import matplotlib.pyplot as plt

test_errors = []
for n in range(1, 201, 10):
    model = RandomForestClassifier(n_estimators=n, random_state=42)
    model.fit(X_train, y_train)
    test_errors.append(1 - model.score(X_test, y_test))

plt.plot(range(1, 201, 10), test_errors)
plt.xlabel("Number of Trees")
plt.ylabel("Test Error Rate")

典型现象:误差在 50-100 棵树后趋于稳定,继续增加树量只带来计算开销。

开放式思考题

  1. 如何改造随机森林使其适合时间序列数据(考虑时序相关性)?
  2. 在联邦学习场景下,如何设计分布式的随机森林算法?
  3. 当特征之间存在强相关性时,随机森林的特征重要性评估会有什么偏差?

结语

通过本文的实践演示,我们能看到随机森林如何通过集体智慧战胜单一模型的不稳定性。虽然现在有 XGBoost、LightGBM 等新算法,随机森林仍是值得信赖的基线模型——特别是在需要快速验证想法或数据存在较多噪声时。记住:没有免费的午餐,理解算法本质才能用好它。

正文完
 0
评论(没有评论)