本文介绍了一个基于Python的自定义随机森林(Random Forest)分类器,并将其应用于MNIST手写数字识别任务。以下从算法原理、核心代码和核心用途三个方面详细说明:
1.算法原理概述
随机森林是一种基于集成学习(Ensemble Learning)的监督学习算法,核心思想是通过构建多棵独立的决策树,并综合它们的预测结果(分类任务采用多数投票,回归任务采用均值)来提升模型的泛化能力和准确性。其关键机制包括:
(1)自助采样(Bootstrap Sampling):从原始数据集中有放回地随机抽取样本,生成多个不同的训练子集(每棵树使用不同的子集)。
(2)特征随机选择:每棵树训练时仅使用随机选择的特征子集(而非全部特征),降低树之间的相关性。
(3)多数投票(Majority Voting):分类任务中,多棵树的预测结果通过投票决定最终类别;回归任务中取均值。
2.数据加载与预处理
本节准备MNIST数据集并划分为训练集和测试集。
MNIST数据集:包含6万张训练图和1万张测试图,每张图是28×28像素的手写数字,展平为784维特征向量,标签为对应的数字(0-9)。
# 数据加载与预处理
data = pd.read_csv('mnist_data.csv')
X = data.drop(columns=['label'])
y = data['label']
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
3.自定义随机森林类(CustomRandomForest类)
本节介绍了CustomRandomForest类,该类封装了随机森林的核心逻辑,包括数据采样、特征选择、决策树训练和预测。
(1) 初始化方法(__init__函数)
__init__函数主要设置随机森林的超参数,并初始化存储结构。
def __init__(self, n_estimators=100, max_depth=5, max_features='sqrt', bootstrap=True):
self.n_estimators = n_estimators # 决策树数量(森林规模)
self.max_depth = max_depth # 每棵树的最大深度(限制复杂度,防过拟合)
self.max_features = max_features # 每棵树随机选择的特征数(如√(总特征数))
self.bootstrap = bootstrap # 是否使用自助采样(默认开启)
self.trees = [] # 存储训练好的决策树及对应特征子集
self.feature_indices = None # 特征名称(用于后续特征对齐)
关键参数
n_estimators:森林中决策树的数量(越大模型越稳定,但计算成本越高);
max_depth:每棵树的最大深度(限制树的复杂度,避免过拟合);
max_features:每棵树分裂时随机选择的特征数(如sqrt表示选择√(总特征数)个特征);
bootstrap:是否通过自助采样生成不同的训练子集(开启时每棵树使用不同的数据)。
(2) 自助采样方法(_bootstrap_sample函数)
_bootstrap_sample函数的作用是从原始数据集中有放回地随机抽取n_samples个样本(允许重复),生成新的训练子集。
def _bootstrap_sample(self, X, y):
"""自助采样法生成训练子集"""
n_samples = X.shape[0]
indices = np.random.choice(n_samples, n_samples, replace=True) # 有放回抽样
return X.iloc[indices], y.iloc[indices] # 返回采样后的数据和标签
意义:每棵树使用不同的训练子集,增加模型的多样性,避免单棵树过拟合原始数据的噪声。
(3) 特征随机选择方法(_select_features函数)
_select_features函数的作用是从所有特征中随机选择max_features数量的特征子集,用于当前决策树的训练。
def _select_features(self, X):
"""随机选择特征子集"""
if self.max_features == 'sqrt':
# 选择√(总特征数)个特征(如总特征784时,选28个)
return X.columns[np.random.choice(X.shape[1], int(np.sqrt(X.shape[1])), replace=False)]
elif self.max_features == 'log2':
# 选择log₂(总特征数)个特征(如总特征784时,选约9.8→9个)
return X.columns[np.random.choice(X.shape[1], int(np.log2(X.shape[1])), replace=False)]
else:
return X.columns # 不随机选择(使用全部特征)
意义:限制每棵树使用的特征数量,降低树之间的相关性(若所有树都使用相同特征,模型退化为单棵树),提升整体泛化能力。
(4) 训练方法(fit函数)
fit函数是通过循环训练多棵决策树,构建随机森林。
关键步骤
自助采样:生成不同的训练子集,确保每棵树“看到”的数据略有不同;
特征随机选择:每棵树仅使用部分特征,降低树间相关性;
决策树训练:使用DecisionTreeClassifier训练单棵树,通过max_depth限制复杂度,splitter='random'进一步增加分裂的随机性(区别于默认的“最优分裂”)。
def fit(self, X, y):
"""训练随机森林模型"""
self.feature_indices = X.columns # 记录原始特征名称(用于对齐)
X = X.copy() # 避免修改原始数据
y = y.copy()
for _ in range(self.n_estimators):
# 步骤1:自助采样生成训练子集
X_sample, y_sample = self._bootstrap_sample(X, y)
# 步骤2:随机选择特征子集
selected_features = self._select_features(X_sample)
X_sample = X_sample[selected_features] # 仅保留选中的特征
# 步骤3:训练决策树(限制最大深度,增加分裂随机性)
tree = DecisionTreeClassifier(
max_depth=self.max_depth,
splitter='random' # 分裂时随机选择特征(而非最优特征)
)
tree.fit(X_sample, y_sample) # 用采样后的数据训练树
self.trees.append((tree, selected_features)) # 保存树和对应的特征子集
(5) 预测方法(predict函数)
predict函数是对测试数据进行预测,综合多棵树的预测结果。
关键步骤
单树预测:每棵树仅使用训练时选中的特征子集对测试数据进行预测,得到各树的预测结果矩阵;
多数投票:对每个样本的所有树预测结果统计频率,选择出现次数最多的类别作为最终预测结果。
def predict(self, X):
"""预测方法(多数投票)"""
# 步骤1:用每棵树对测试数据进行预测(仅使用训练时的特征子集)
tree_preds = np.array([
tree.predict(X[feat]) for tree, feat in self.trees # 每棵树用自己选中的特征预测
])
# 步骤2:对每个样本的所有树预测结果进行多数投票
return np.apply_along_axis(
lambda x: Counter(x).most_common(1)[0][0], # 统计最常见类别
axis=0, # 按列(样本)处理
arr=tree_preds # 所有树的预测结果矩阵(形状:[n_trees, n_samples])
)
4.模型训练与评估
本节训练自定义随机森林模型,并在测试集上评估性能。
# 初始化自定义随机森林
custom_rf = CustomRandomForest(
n_estimators=100,
max_depth=10,
max_features='sqrt',
bootstrap=True
)
# 训练模型
custom_rf.fit(X_train, y_train)
# 预测与评估
y_pred = custom_rf.predict(X_test)
print("Custom RF Accuracy:", accuracy_score(y_test, y_pred))
print("Classification Report:
", classification_report(y_test, y_pred))
评估指标
accuracy_score:整体分类准确率(正确预测数/总样本数);
classification_report:按类别统计的精确率(Precision,预测为某类的样本中实际正确的比例)、召回率(Recall,实际某类样本中被正确预测的比例)、F1值(两者的调和平均)。
总结
本文通过自定义实现随机森林的核心机制(自助采样、特征随机选择、多树集成),完成了MNIST手写数字识别任务。具体应用场景和价值包括:
(1)手写数字识别任务
场景:MNIST是计算机视觉领域的经典基准数据集,常用于验证分类模型的性能。此代码演示了如何用自定义随机森林模型处理高维图像数据(784维像素特征),完成0-9数字的分类任务。
优势:相比单棵决策树,随机森林通过集成多棵树的预测结果,显著提升了模型的泛化能力,能有效处理MNIST中的噪声和变体(如旋转、倾斜的数字)。
(2)集成学习方法的实践教学
教学价值:代码完整展示了随机森林的核心实现逻辑(自助采样、特征随机选择、多树训练、多数投票),帮助学习者深入理解集成学习的原理(通过多样性提升模型鲁棒性)。
对比验证:可与scikit-learn的RandomForestClassifier对比(后者优化了特征重要性计算、OOB分数等细节),理解工业级实现与自定义实现的差异。
(3)高维数据处理示例
技术展示:MNIST的784维特征属于典型的高维数据,随机森林无需特征缩放(如标准化),可直接处理,适用于图像、文本等高维场景。此代码验证了随机森林在高维数据上的有效性。
完整代码如下:
"""
文件名: RandomForest.py
作者: 温州慧橙博海科技有限公司 <huichengbohai>
创建日期: 2025-07-15
描述: 将自定义的随机森林(Random Forest)分类器,应用于MNIST手写数字识别任务。
"""
import numpy as np
import pandas as pd
from collections import Counter
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score
class CustomRandomForest:
def __init__(self, n_estimators=100, max_depth=5, max_features='sqrt', bootstrap=True):
self.n_estimators = n_estimators
self.max_depth = max_depth
self.max_features = max_features
self.bootstrap = bootstrap
self.trees = []
self.feature_indices = None
def _bootstrap_sample(self, X, y):
"""自助采样法生成训练子集"""
n_samples = X.shape[0]
indices = np.random.choice(n_samples, n_samples, replace=True)
return X.iloc[indices], y.iloc[indices]
def _select_features(self, X):
"""随机选择特征子集"""
if self.max_features == 'sqrt':
return X.columns[np.random.choice(X.shape[1], int(np.sqrt(X.shape[1])), replace=False)]
elif self.max_features == 'log2':
return X.columns[np.random.choice(X.shape[1], int(np.log2(X.shape[1])), replace=False)]
else:
return X.columns
def fit(self, X, y):
"""训练随机森林模型"""
self.feature_indices = X.columns
X = X.copy()
y = y.copy()
for _ in range(self.n_estimators):
# 自助采样
X_sample, y_sample = self._bootstrap_sample(X, y)
# 随机选择特征
selected_features = self._select_features(X_sample)
X_sample = X_sample[selected_features]
# 训练决策树
tree = DecisionTreeClassifier(
max_depth=self.max_depth,
splitter='random' # 增加随机性
)
tree.fit(X_sample, y_sample)
self.trees.append((tree, selected_features))
def predict(self, X):
"""预测方法(多数投票)"""
tree_preds = np.array([
tree.predict(X[feat]) for tree, feat in self.trees
])
return np.apply_along_axis(lambda x: Counter(x).most_common(1)[0][0], axis=0, arr=tree_preds)
# 数据加载与预处理
data = pd.read_csv('mnist_data.csv')
X = data.drop(columns=['label'])
y = data['label']
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 初始化自定义随机森林
custom_rf = CustomRandomForest(
n_estimators=100,
max_depth=10,
max_features='sqrt',
bootstrap=True
)
# 训练模型
custom_rf.fit(X_train, y_train)
# 预测与评估
y_pred = custom_rf.predict(X_test)
print("Custom RF Accuracy:", accuracy_score(y_test, y_pred))
print("Classification Report:
", classification_report(y_test, y_pred))


















暂无评论内容