共计 3321 个字符,预计需要花费 9 分钟才能阅读完成。
从决策树到随机森林
机器学习新手常会遇到一个经典问题:模型在训练集上表现完美,但在测试集上却一塌糊涂——这就是过拟合。今天我们要介绍的 随机森林 算法,正是解决这一问题的利器。

基础概念:决策树与集成学习
-
决策树 是最直观的机器学习模型之一,它通过一系列 if-else 规则对数据进行分割。但单个决策树容易 ” 记住 ” 训练数据细节(过拟合),就像学生死记硬背考题却不会举一反三。
-
集成学习 的核心思想是 ” 三个臭皮匠顶个诸葛亮 ”:通过组合多个弱模型(如决策树)来构建强模型。随机森林就是其中最成功的代表。
随机森林防过拟合的三大法宝
1. 样本随机性(Bootstrap 抽样)
每棵决策树训练时:
- 从原始数据集中 有放回地随机抽取 样本(约 63% 的样本会被选中)
- 未被抽中的样本(约 37%)称为 ” 袋外数据 ”(OOB),可用来评估模型性能
数学表示为:对原始数据集 $D$,生成 $B$ 个 bootstrap 样本集 $D_1, D_2,…,D_B$
2. 特征随机性(随机子空间)
在每棵树的每个节点分裂时:
- 不是考虑所有特征,而是 随机选取部分特征子集(通常取特征总数的平方根)
- 这确保了树的多样性,避免所有树关注相同特征
公式表达:分裂节点时从 $m$ 个特征中随机选择 $k$ 个($k \leq \sqrt{m}$)
3. 多数表决 / 平均机制
对于分类任务:
- 每棵树独立投票,最终结果取 多数票
- 相当于 $\hat{y} = \text{mode}{h_1(x), h_2(x),…, h_B(x)}$
对于回归任务:
- 取所有树的 平均值
- 即 $\hat{y} = \frac{1}{B}\sum_{i=1}^B h_i(x)$
Python 实战:从代码看原理
# 导入必要库
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
# 1. 创建模拟数据
X, y = make_moons(n_samples=500, noise=0.3, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
# 2. 训练随机森林
rf = RandomForestClassifier(
n_estimators=100, # 树的数量
max_depth=5, # 树的最大深度
max_features='sqrt', # 特征随机选择
min_samples_split=2, # 节点分裂最小样本数
random_state=42,
oob_score=True # 使用袋外样本评估
)
rf.fit(X_train, y_train)
# 3. 评估性能
print(f"训练集准确率: {rf.score(X_train, y_train):.3f}")
print(f"测试集准确率: {rf.score(X_test, y_test):.3f}")
print(f"OOB 准确率: {rf.oob_score_:.3f}") # 袋外样本评估
# 4. 可视化决策边界
def plot_decision_boundary(clf, X, y):
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k')
plt.title("随机森林决策边界")
plt.figure(figsize=(10, 6))
plot_decision_boundary(rf, X, y)
plt.show()
关键参数解析
- n_estimators:树的数量。通常越大越好,但计算成本会增加
- max_depth:控制树的复杂度。适当限制深度可防止过拟合
- max_features:每棵树使用的特征数。常用 ’sqrt’ 或 ’log2′
- min_samples_split:节点分裂所需最小样本数
- oob_score:是否使用袋外样本评估模型
性能对比:随机森林 vs 单棵决策树
from sklearn.tree import DecisionTreeClassifier
# 单棵决策树
tree = DecisionTreeClassifier(max_depth=5, random_state=42)
tree.fit(X_train, y_train)
print(f"决策树训练集准确率: {tree.score(X_train, y_train):.3f}")
print(f"决策树测试集准确率: {tree.score(X_test, y_test):.3f}")
典型输出结果:
随机森林:
训练集准确率: 0.943
测试集准确率: 0.913
OOB 准确率: 0.903
单棵决策树:
训练集准确率: 0.926
测试集准确率: 0.887
可以看到随机森林在测试集上表现更好,且训练集和测试集差距更小(过拟合程度低)。
特征重要性分析
随机森林可以计算各特征的重要性:
import pandas as pd
# 假设我们有特征名称
features = ['feature1', 'feature2']
importances = rf.feature_importances_
# 创建 DataFrame 展示
feat_importances = pd.DataFrame(importances, index=features, columns=["Importance"])
feat_importances.sort_values(by="Importance", ascending=False).plot(kind='bar')
plt.title("特征重要性排序")
plt.show()
避坑指南
1. 类别不平衡处理
- 使用 class_weight=’balanced’ 参数自动调整类别权重
- 或手动设置 class_weight 参数,如 {0:1, 1:3} 表示类别 1 的重要性是类别 0 的 3 倍
2. 超参数调优策略
推荐使用 网格搜索 + 交叉验证:
from sklearn.model_selection import GridSearchCV
param_grid = {'n_estimators': [50, 100, 200],
'max_depth': [None, 5, 10],
'min_samples_split': [2, 5, 10]
}
grid_search = GridSearchCV(rf, param_grid, cv=5)
grid_search.fit(X_train, y_train)
print("最佳参数:", grid_search.best_params_)
3. 高维稀疏数据注意事项
- 当特征维度很高时(如文本数据),可尝试增大max_features
- 考虑先做特征选择,减少不相关特征
- 注意内存消耗,可能需要减小n_estimators
思考题
- 为什么随机森林中每棵树使用 bootstrap 样本(有放回抽样)而不是简单的数据分割?
- 如果随机森林在训练集上表现很好但在测试集上表现不佳,可能是什么原因?应该如何调整?
- 在极端类别不平衡的数据集上(如欺诈检测),除了调整 class_weight,还可以采用哪些策略来改善随机森林的表现?
结语
随机森林通过 ” 集体决策 ” 机制和双重随机性,在保持较高预测准确率的同时有效控制了过拟合风险。它不需要复杂的特征工程,对参数设置相对鲁棒,是机器学习新手的理想起点。希望本文能帮助你理解其工作原理,并在实践中灵活应用。
正文完
发表至: 未分类
近一天内
