当前位置: 首页 > news >正文

随机森林的 “Bootstrap 采样” 与 “特征随机选择”:如何避免过拟合?(附分类 / 回归任务实战)

随机森林的 “Bootstrap 采样” 与 “特征随机选择”:如何避免过拟合?(附分类 / 回归任务实战)

第一部分:揭开随机森林的神秘面纱

1.1 告别“过拟合”,拥抱更强大的模型

在机器学习的旅程中,我们常常会遇到一个“敌人”——过拟合(Overfitting)。想象一个学生,他只会死记硬背老师划定的考试范围和标准答案。在模拟考试(训练数据)中,他总能考满分,因为题目一模一样。可一旦到了正式考场(测试数据),题目稍微变换一下形式,他就束手无策,成绩一落千丈。

这种“在训练数据上表现完美,但在新数据上表现糟糕”的现象,就是“过拟合”。我们精心构建的模型,就像那个只会死记硬背的学生,失去了泛化到新环境的能力。

决策树(Decision Tree)模型,作为一种简单直观的算法,就很容易陷入过拟合的陷阱。它会不断地划分数据,直到把训练集中的每个样本都“安排”得明明白白,但这往往导致它学习到了过多的噪音和细节。

那么,如何让我们的模型变得更“聪明”,而不是一个只会“死记硬背”的书呆子呢?答案是:集成学习(Ensemble Learning)。古话说得好:“三个臭皮匠,顶个诸葛亮”。如果我们能汇集多个模型的智慧,是不是就能得到一个更全面、更稳健的决策?

今天,我们将要学习的主角——随机森林(Random Forest),正是集成学习中的佼佼者。它通过构建一片由决策树组成的“森林”,让每棵树独立学习、独立思考,最后通过集体投票的方式,做出远比单棵树更准确、更可靠的判断。

在本篇博客中,我将牵着你的手,一步步带你:

  1. 理解随机森林为何能有效避免过拟合。
  2. 掌握其背后的两大核心技术:Bootstrap 采样与特征随机选择。
  3. 从零开始,搭建环境,并亲手完成一个分类任务和一个回归任务的实战。

1.2 什么是随机森林?—— 一片由决策树构成的“智慧森林”

随机森林,顾名思义,它不是单一的模型,而是一个包含了多棵决策树的“森林”。当有新的数据需要预测时,森林中的每一棵决策树都会独立地给出一个自己的判断。最后,模型会综合所有树的“意见”,通过“投票”(分类任务)或“取平均值”(回归任务)的方式,得出最终的结论。

这就像一个专家委员会在进行会诊。每一位专家(决策树)都有自己的知识背景和判断依据。面对同一个病例(输入数据),他们分别给出诊断。最后,委员会采纳最多专家支持的诊断结果(分类),或者综合所有专家的数值评估得出一个平均指标(回归)。这种集体决策的方式,远比依赖单一专家的判断要可靠得多。

我们可以用下面的图来直观地理解这个结构:

输入数据
决策树 1
决策-树 2
决策树 ...
决策树 N
预测结果 N
最终结果: 投票/平均

你可能会问,如果森林里的每一棵树都长得一模一样,那和只有一棵树又有什么区别呢?问得好!这正是随机森林“随机”二字的精髓所在。为了让每一棵树都具备自己独特的“视角”,随机森林在两个关键环节引入了随机性:

  1. 训练样本的随机性:每棵树的训练数据,都是从原始数据集中随机抽样得来的。
  2. 特征选择的随机性:每棵树在构建时,其内部的每个节点都只能从一部分随机选择的特征中进行分裂。

正是这两个“随机”的引入,才保证了森林中树木的多样性,从而铸就了模型的强大。接下来,让我们深入探索这两大“法宝”。

第二部分:随机森林避免过拟合的两大“法宝”

2.1 法宝一:Bootstrap 采样 —— “我的训练数据,我做主”

是什么?

Bootstrap 采样,又称“自助法采样”或“有放回抽样”。它的过程非常简单:

假设我们有一个包含 N 个样本的原始数据集。现在,我们想为森林中的第一棵树准备训练数据。我们从原始数据集中随机抽取一个样本,记录下来,然后把它放回去。接着,我们再重复这个过程,总共重复 N 次。

这样,我们就得到了一个同样包含 N 个样本的新数据集。由于是“有放回”抽样,这个新的数据集中,有些原始样本可能一次都没被抽到,而有些则可能被抽到了一次、两次甚至更多次。

随机森林会为每一棵将要构建的决策树,都重复进行一次这样的 Bootstrap 采样,为它们各自生成一套“专属”的训练数据。

为什么?

这样做最大的好处,就是保证了每棵树学习的“起点”不同

如果所有的树都使用完全相同的训练数据,它们很可能会学习到相同的模式,犯相同的错误,最终长成一模一样的“克隆树”,这就失去了集成的意义。

通过 Bootstrap 采样,每棵树接触到的数据都有细微的差别。有的树可能对某类样本“见多识广”,有的则可能从未见过某些样本。这种差异性,使得每棵树都成了某一方面的“偏科生”,但当我们将这些“偏科生”的意见汇集起来时,就能得到一个非常全面的“全才”模型。

可视化

下面的流程图清晰地展示了 Bootstrap 采样的过程:

graph TDsubgraph 原始数据集 (N个样本)D1[样本1]D2[样本2]D3[样本3]D4[...]D5[样本N]endsubgraph 为决策树1采样 (得到N个样本)direction LRS1_1(样本3)S1_2(样本1)S1_3(样本3)S1_4(...)S1_5(样本N)endsubgraph 为决策树2采样 (得到N个样本)direction LRS2_1(样本N)S2_2(样本2)S2_3(样本1)S2_4(...)S2_5(样本2)endsubgraph 为决策树N采样 (得到N个样本)direction LRSN_1(...)SN_2(...)endD1 & D2 & D3 & D4 & D5 -- "有放回抽样" --> S1_1 & S1_2 & S1_3 & S1_4 & S1_5;D1 & D2 & D3 & D4 & D5 -- "有放回抽样" --> S2_1 & S2_2 & S2_3 & S2_4 & S2_5;D1 & D2 & D3 & D4 & D5 -- "有放回抽样" --> SN_1 & SN_2;

2.2 法宝二:特征随机选择 —— “别让‘学霸’特征带偏节奏”

是什么?

如果说 Bootstrap 采样保证了“输入”的多样性,那么特征随机选择则保证了“学习过程”的多样性。

决策树的构建过程,是在每个节点上,选择一个“最优”的特征来进行数据划分。但在随机森林中,这个规则被修改了。

当某棵决策树需要在某个节点进行分裂时,它不会考察当前所有的特征。而是,先从所有 M 个特征中,随机挑选出 m 个特征(通常 m 远小于 M),然后再从这 m 个特征中,挑选出最优的一个用于分裂

这个过程,在树的每一个分裂节点上,都会重复进行。

为什么?

想象一下,在你的数据集中,有一个“学霸”特征。这个特征的预测能力非常强,如果让决策树自由选择,它很可能在每一个节点都优先使用这个特征,导致森林中大部分树的结构都变得非常相似,因为它们都严重依赖这同一个“学霸”。

这就削弱了我们想要的多样性。

通过随机选择特征,我们“强迫”决策树在分裂时,给那些不那么强势的“普通”特征一些机会。也许在某个特定的数据子集中,一个“普通”特征反而能发挥出意想不到的好效果。

这极大地增强了森林中树木之间的差异性,降低了它们之间的相关性。当所有树都从不同角度、依据不同特征来进行判断时,整个森林的鲁棒性(Robustness)和泛化能力就得到了质的飞跃。

可视化

下图展示了在一个决策节点上,特征是如何被随机选择的:

graph TDA[所有特征: M个<br/>(Feature 1, Feature 2, ... Feature M)] --> B{在节点分裂时};B -- "随机选择m个特征" --> C[候选特征子集<br/>(Feature 3, Feature 8, ...)];C -- "从中选择最优特征" --> D[使用Feature 8进行分裂];

2.3 总结:两大“法宝”如何协同工作

现在,我们将这两个强大的工具结合起来,看看一个完整的随机森林是如何被构建并避免过拟合的:

  1. 样本随机:通过 Bootstrap 采样,为每棵树生成一份独特的数据“教材”,保证了树与树之间的“宏观”差异性。
  2. 特征随机:在每棵树的生长过程中,通过限制节点分裂时的候选特征,保证了树内部结构的“微观”差异性。

这两个机制共同作用,确保了森林中的每一棵树都是独一无二的。它们从不同的数据、不同的角度学习,最终汇集成的集体智慧,自然就拥有了强大的泛化能力,能够很好地抵抗过拟合。

第三部分:万事俱备,只欠“实战”

理论讲完了,是时候动手实践了!一个好的程序员,不仅要懂理论,更要能动手。接下来,我们一起搭建一个简单的机器学习环境。

3.1 环境准备:搭建你的机器学习实验室

第一步:拥有 Python

首先,你需要一个 Python 环境。现代的操作系统通常都内置了 Python。你可以在终端(Terminal 或命令提示符)中输入以下命令来检查:

python --version
# 或者
python3 --version

如果能看到版本号(如 Python 3.9.7),说明你已经安装好了。如果没有,请前往 Python 官方网站 下载并安装最新版本。

第二步:创建虚拟环境(强烈推荐)

为什么需要虚拟环境? 想象一下,你同时在做两个项目,项目 A 需要 1.0 版本的工具库,而项目 B 需要 2.0 版本。如果你把它们都装在系统里,就会产生冲突。虚拟环境就像是为每个项目创建了一个独立的、干净的“实验室”,项目 A 的工具放在 A 实验室,项目 B 的工具放在 B 实验室,它们互不干扰。

让我们为本次实战创建一个名为 rf_env 的虚拟环境:

# 1. 创建虚拟环境
python3 -m venv rf_env# 2. 激活虚拟环境
# 在 macOS / Linux 上:
source rf_env/bin/activate
# 在 Windows 上:
.\\rf_env\\Scripts\\activate

激活成功后,你会看到终端提示符前面多了 (rf_env) 的字样,这表示你已经进入了这个独立的“实验室”。

第三步:安装必备工具库

在这个虚拟环境中,我们需要安装几个核心的 Python 库:

  • scikit-learn:这是我们今天的主角,一个强大的机器学习库,随机森林模型就在其中。
  • pandas:用于数据处理和分析,可以方便地读取和操作数据。
  • numpy:Python 科学计算的基础库,提供了高效的数组操作。

使用 pip(Python 的包管理器)来安装它们:

pip install scikit-learn pandas numpy

等待安装完成,我们的实验环境就准备就绪了!

第四部分:实战演练:从零开始构建你的随机森林模型

现在,让我们用刚刚学到的知识,来解决两个真实的机器学习问题。

4.1 任务一:随机森林分类实战(以“鸢尾花”分类为例)

鸢尾花数据集是一个非常经典的分类任务数据集,它包含了三种不同鸢尾花(Setosa, Versicolour, Virginica)的四个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)。我们的任务是构建一个模型,根据这些特征来判断鸢尾花属于哪个种类。

第一步:创建代码文件

在你喜欢的位置,创建一个名为 classification_test.py 的 Python 文件,然后我们开始编写代码。

第二步:编写代码

# classification_test.py# 1. 导入我们需要的工具库
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score# 2. 加载数据并了解它
# scikit-learn 内置了鸢尾花数据集,我们可以直接加载
iris = load_iris()
# 为了方便观察,我们把它转换成 pandas 的 DataFrame 格式
# 特征数据
X = pd.DataFrame(iris.data, columns=iris.feature_names)
# 目标类别 (我们要预测的值)
y = pd.Series(iris.target)# 打印前5行数据看看
print("--- 特征数据 (前5行) ---")
print(X.head())
print("\\n--- 目标数据 (前5行) ---")
print(y.head())# 3. 划分数据集
# 将数据划分为 训练集 和 测试集,这是评估模型性能的标准流程
# test_size=0.3 表示我们将30%的数据用作测试,剩下的70%用作训练
# random_state 是一个随机种子,保证每次划分的结果都一样,便于复现
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
print(f"\\n训练集大小: {X_train.shape[0]} a, 测试集大小: {X_test.shape[0]}")# 4. “反面教材”:训练一个单独的决策树
print("\\n--- 训练单个决策树 ---")
# 创建一个决策树分类器实例
single_tree = DecisionTreeClassifier(random_state=42)
# 使用训练数据来训练模型
single_tree.fit(X_train, y_train)# 使用训练好的模型在 测试集 和 训练集 上进行预测
pred_tree_test = single_tree.predict(X_test)
pred_tree_train = single_tree.predict(X_train)# 评估模型性能
acc_tree_test = accuracy_score(y_test, pred_tree_test)
acc_tree_train = accuracy_score(y_train, pred_tree_train)
print(f"单个决策树 - 训练集准确率: {acc_tree_train:.4f}")
print(f"单个决策树 - 测试集准确率: {acc_tree_test:.4f}")
# 你可能会发现,训练集准确率是1.0,这正是过拟合的迹象!# 5. “正面典型”:训练一个随机森林
print("\\n--- 训练随机森林 ---")
# 创建一个随机森林分类器实例
# n_estimators=100 表示这个森林里有100棵决策树
# random_state 同样是为了结果可复现
random_forest = RandomForestClassifier(n_estimators=100, random_state=42)
# 使用训练数据来训练模型
random_forest.fit(X_train, y_train)# 使用训练好的模型在 测试集 和 训练集 上进行预测
pred_rf_test = random_forest.predict(X_test)
pred_rf_train = random_forest.predict(X_train)# 评估模型性能
acc_rf_test = accuracy_score(y_test, pred_rf_test)
acc_rf_train = accuracy_score(y_train, pred_rf_train)
print(f"随机森林 - 训练集准确率: {acc_rf_train:.4f}")
print(f"随机森林 - 测试集准确率: {acc_rf_test:.4f}")

第三步:运行与结果分析

在你的终端中(确保你仍处于 rf_env 虚拟环境中),运行这个文件:

python classification_test.py

你会看到类似下面的输出:

--- 特征数据 (前5行) ---sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0                5.1               3.5                1.4               0.2
1                4.9               3.0                1.4               0.2
2                4.7               3.2                1.3               0.2
3                4.6               3.1                1.5               0.2
4                5.0               3.6                1.4               0.2--- 目标数据 (前5行) ---
0    0
1    0
2    0
3    0
4    0
dtype: int64训练集大小: 105, 测试集大小: 45--- 训练单个决策树 ---
单个决策树 - 训练集准确率: 1.0000
单个决策树 - 测试集准确率: 1.0000--- 训练随机森林 ---
随机森林 - 训练集准确率: 1.0000
随机森林 - 测试集准确率: 1.0000

结果分析

在这个特定的例子中,因为鸢尾花数据集非常“干净”且易于区分,你会发现单个决策树和随机森林在测试集上的表现都达到了 100% 的准确率。但请注意关键的一点:单个决策树在训练集上的准确率是 1.0000,这说明它完美地“记住”了所有训练数据,这是一个典型的过拟合信号。虽然在这个简单任务上它的测试表现也很好,但在更复杂、噪音更多的数据集上,这种过拟合会严重影响其泛化能力。

而随机森林,同样在训练集上表现完美,但它的完美是建立在“集体智慧”上的,其模型本质上比单个决策树要稳健得多。让我们在下一个回归任务中,更清晰地看到这种差异。

4.2 任务二:随机森林回归实战(以“加州房价”预测为例)

现在,我们来挑战一个更复杂的任务:根据加州各个街区的人口、收入等特征,来预测房价的中位数。这是一个回归问题。

第一步:创建代码文件 regression_test.py

第二步:编写代码

# regression_test.py# 1. 导入工具库
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
import numpy as np# 2. 加载数据
housing = fetch_california_housing()
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = pd.Series(housing.target)# 3. 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)# 4. 训练单个决策树回归模型
print("--- 训练单个决策树回归模型 ---")
tree_reg = DecisionTreeRegressor(random_state=42)
tree_reg.fit(X_train, y_train)# 在测试集上进行预测
pred_tree = tree_reg.predict(X_test)
# 使用“均方根误差 (RMSE)”来评估模型
# RMSE 越小,说明模型的预测值与真实值的差异越小,模型越好
tree_rmse = np.sqrt(mean_squared_error(y_test, pred_tree))
print(f"单个决策树 - 测试集 RMSE: {tree_rmse:.4f}")# 5. 训练随机森林回归模型
print("\\n--- 训练随机森林回归模型 ---")
forest_reg = RandomForestRegressor(n_estimators=100, random_state=42)
forest_reg.fit(X_train, y_train)# 在测试集上进行预测
pred_forest = forest_reg.predict(X_test)
# 同样使用 RMSE 进行评估
forest_rmse = np.sqrt(mean_squared_error(y_test, pred_forest))
print(f"随机森林 - 测试集 RMSE: {forest_rmse:.4f}")# 6. 对比结果
print(f"\\n随机森林相比单个决策树,误差降低了: {((tree_rmse - forest_rmse) / tree_rmse) * 100:.2f}%")

第三步:运行与结果分析

在终端中运行:

python regression_test.py

你将看到如下输出:

--- 训练单个决策树回归模型 ---
单个决策树 - 测试集 RMSE: 0.7107--- 训练随机森林回归模型 ---
随机森林 - 测试集 RMSE: 0.5034随机森林相比单个决策树,误差降低了: 29.17%

结果分析

这次的结果对比就非常明显了!

  • 单个决策树模型的均方根误差(RMSE)是 0.7107
  • 随机森林模型的均方根误差(RMSE)是 0.5034

随机森林的预测误差降低了近 30%!这清晰地证明,在处理更复杂的回归问题时,由多棵树组成的森林通过集成学习,显著地减少了由单个决策树过拟合带来的误差,得到了一个预测能力更强、更可靠的模型。

http://www.xdnf.cn/news/1386991.html

相关文章:

  • 华为云CCE的Request和Limit
  • Ethercat主从站移植时的问题记录(二)—无法进入OP状态且从站PDI中断不触发
  • 什么是Jmeter? Jmeter工作原理是什么?
  • linux上安装methylkit -- 安全下车版 (正经版: Linux环境下安装methylKit的实践与避坑指南)
  • springboot java开发的rocketmq 顺序消息保证
  • CAN总线(Controller Area Network Bus)控制器局域网总线(二)
  • 无人机图传模块原理及作用——开启飞行视野的关键技术
  • 第二阶段WinForm-9:委托复习
  • 应用转生APP:无需Root权限的应用双开和Xposed模块加载工具
  • 计算机是如何运行的
  • [AI人脸替换] docs | 环境部署指南 | 用户界面解析
  • c++ template
  • OpenCSG月度更新2025.8
  • 电影交流|基于SprinBoot+vue的电影交流平台小程序系统(源码+数据库+文档)
  • C++基础(④链表反转(链表 + 迭代 / 递归))
  • 公司内网部署离线deepseek+docker+ragflow本地模型实战
  • SpringBoot整合Spring WebFlux弃用自带的logback,使用log4j2,并启动异步日志处理
  • Python计算点云的均值、方差、标准差、凸点(顶点)、质心和去中心化
  • Docker03-知识点整理
  • Go Vendor 和 Go Modules:管理和扩展依赖的最佳实践
  • 项目一系列-第9章 集成AI千帆大模型
  • C/C++---预定义常量
  • iCloud 备份与 iTunes 备份:有何不同
  • Jenkins Pipeline(二)-设置Docker Agent
  • Python中的匿名函数详解(lambda)
  • 无人机固件升级与技术要点解析
  • 命令行操作:逻辑运算符、重定向与管道
  • Cesium 入门教程(十二):时间动画实例
  • AI共链·智存未来 | 绿算技术受邀出席华为AI SSD发布会
  • 预测模型及超参数:3.集成学习:[1]LightGBM