返回列表

随机森林:从决策树到集成学习的强大工具

发布于 ·

随机森林:从决策树到集成学习的强大工具

引言

在机器学习的广阔领域中,随机森林(Random Forest)无疑是最为成功和广泛应用的算法之一。它以其强大的分类、回归能力,以及对过拟合的良好鲁棒性,成为了数据科学家工具箱中的必备武器。本文将深入探讨随机森林的原理、实现方式以及其在实际应用中的优势与注意事项。

1. 基础概念:决策树

在了解随机森林之前,我们必须先理解其构建的基础——决策树。决策树是一种树形结构的监督学习算法,通过一系列的"if-else"规则对数据进行分类或预测。每个内部节点代表一个特征上的测试,每条分支代表测试的结果,每个叶节点代表一个类别标签(对于分类问题)或一个数值(对于回归问题)。

决策树的优点是模型直观易懂,能够处理数值型和类别型数据,不需要复杂的预处理。然而,单个决策树容易受到训练数据微小变化的影响,导致模型不稳定,并且常常会过拟合。

2. 随机森林的诞生

为了克服决策树的缺点,Breiman等人在1998年提出了随机森林。它是一种基于决策树的集成学习方法。其核心思想是“三个臭皮匠,赛过诸葛亮”——通过组合多个弱学习器(这里是决策树)来形成一个更强大、更稳定的整体学习器。

随机森林的关键在于“随机性”和“投票”。它通过两种随机性来生成一组多样性高的基学习器:

  1. 自助采样法(Bootstrap Sampling)
对于包含 n 个样本的训练集,随机有放回地抽取 n 个样本作为一棵树的训练集。由于是有放回抽样,大约会有 1 - 1/e (约63.2%)的样本被选中,而剩下的未被选中的样本(约占总数的36.8%)则被称为“袋外数据”(Out-of-Bag, OOB),它们可以作为这棵树的验证集。
  1. 随机特征子空间(Feature Randomness)
在构建每棵决策树时,传统的ID3、C4.5或CART算法在每次分裂节点时都会遍历所有特征,从中选择一个最优的特征来进行划分。随机森林则更进一步,它在每次节点分裂时,不是在所有特征中寻找最佳分割点,而是从所有特征中随机选取一个子集(例如 sqrt(p)log2(p) 个特征,其中 p 是总特征数),然后只在这个子集中选择最优特征进行分裂。这进一步增加了树之间的多样性。

3. 随机森林的工作流程

让我们用一个简单的例子来说明随机森林是如何工作的。假设我们要对鸢尾花数据集进行分类。

步骤 1: 创建森林

  • 设置要生成的决策树数量 N = 100

  • 对于每一棵树 i (从1到100):
* 使用自助采样法从原始训练集中抽取一个有放回的样本集 Di,大小与原始集相同。
* 使用 D
i 训练一棵新的决策树 Ti。在训练 Ti 的每一个节点分裂时,只考虑从全部特征中随机抽取的一个子集。

步骤 2: 进行预测 (分类任务)

  • 当一个新的样本 x 需要被分类时,它会被输入到这100棵已经训练好的树中。

  • 每棵树 Ti 都会对 x 做出一个预测结果 yi

  • 最后,采用“投票机制”来决定最终的预测结果 ypred
* 如果是分类问题,ypred 就是得票最多的类别。
* 如果是回归问题,ypred 就是所有树预测值的平均值。

步骤 3: 模型评估 (利用OOB数据)

  • 对于每一棵树 Ti,它的“袋外数据”OOBi 可以用来测试它的性能。

  • 对于每个 OOBi 中的样本,它被输入到除了 Ti 之外的其他99棵树中。

  • 这99棵树的投票结果可以用来判断 Ti 的性能。

  • 最终,我们可以汇总所有 Ti 的OOB误差,得到整个随机森林模型的OOB误差。这个误差可以作为模型性能的一个无偏估计,无需额外的验证集。

4. Python代码示例

下面是一个使用 scikit-learn 库实现随机森林的完整代码示例:

# 导入必要的库
import numpy as np
import pandas as pd
from sklearn.modelselection import traintestsplit
from sklearn.datasets import loadiris
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracyscore, classificationreport, confusionmatrix
import matplotlib.pyplot as plt
import seaborn as sns

--- 1. 加载数据 ---

print("正在加载鸢尾花数据集...") iris = loadiris() X = iris.data # 特征矩阵 y = iris.target # 目标变量 (花的种类)

将数据集划分为训练集和测试集

X
train, Xtest, ytrain, ytest = traintestsplit(X, y, testsize=0.3, randomstate=42, stratify=y)

print(f"训练集大小: {Xtrain.shape[0]}")
print(f"测试集大小: {Xtest.shape[0]}")

--- 2. 创建和训练随机森林模型 ---

print("\n正在训练随机森林模型...")

实例化 RandomForestClassifier

nestimators: 森林中树的数量

randomstate: 用于重现结果

maxfeatures: 节点分裂时考虑的特征数 (默认 'sqrt')

rfclassifier = RandomForestClassifier(nestimators=100, randomstate=42, maxfeatures='sqrt')

训练模型

rfclassifier.fit(Xtrain, ytrain)

--- 3. 模型预测 ---

print("\n正在进行预测...")

在测试集上进行预测

y
pred = rfclassifier.predict(Xtest)

计算准确率

accuracy = accuracyscore(ytest, ypred) print(f"\n模型在测试集上的准确率: {accuracy:.4f}")

--- 4. 模型评估 ---

print("\n--- 详细评估报告 ---") print(classification
report(ytest, ypred, targetnames=iris.targetnames))

print("\n--- 混淆矩阵 ---")
cm = confusionmatrix(ytest, ypred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=iris.target
names,
yticklabels=iris.targetnames)
plt.title('混淆矩阵')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()

--- 5. 探索模型特性 ---

print("\n--- 模型特性探索 ---")

a. 特征重要性 (Feature Importance)

feature
importances = rfclassifier.featureimportances_ featurenames = iris.featurenames

创建DataFrame以便更好地展示

importancedf = pd.DataFrame({'Feature': featurenames, 'Importance': featureimportances}) importancedf = importancedf.sortvalues(by='Importance', ascending=False)

print("\n特征重要性:")
print(importancedf)

可视化特征重要性

plt.figure(figsize=(10, 6)) sns.barplot(x='Importance', y='Feature', data=importance
df, palette='viridis') plt.title('随机森林特征重要性') plt.xlabel('重要性分数') plt.tightlayout() plt.show()

b. 袋外误差 (OOB Score)

注意:只有在设置 oobscore=True 时才能获取

oobscore = rfclassifier.oobscore

print(f"\n袋外准确率 (OOB Score): {oobscore:.4f}")

c. 单棵树示例 (可选)

查看第一棵树的结构

from sklearn.tree import plottree

plt.figure(figsize=(20,10))

plottree(rfclassifier.estimators[0],

featurenames=iris.featurenames,

classnames=iris.targetnames,

filled=True, fontsize=10)

plt.title("随机森林中第一棵决策树的结构")

plt.show()

代码解释

  1. 数据准备: 我们使用经典的鸢尾花数据集 (loadiris)。首先将其分为训练集 (Xtrain, ytrain) 和测试集 (Xtest, ytest)。
  2. 模型定义: 我们创建了一个 RandomForestClassifier 对象,并设置了参数:
* nestimators=100: 森林中有100棵树。 * randomstate=42: 确保每次运行代码时都能得到相同的随机结果,便于复现。 * max_features='sqrt': 每次节点分裂时,只考虑 sqrt(p) 个随机选择的特征。
  1. **