基于Python的自定义随机森林(Random Forest)分类器

      本文介绍了一个基于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))

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容