Educoder 机器学习之随机森林算法 第2关:从零构建RandomForestClassifier

📅 2026/7/14 1:38:15
Educoder 机器学习之随机森林算法 第2关:从零构建RandomForestClassifier
1. 理解随机森林的核心思想随机森林这个名字听起来有点神秘但其实它的核心思想非常直观。想象你正在参加一场知识竞赛面对一个复杂的问题你会怎么做聪明的做法不是依赖一个人的判断而是询问多位专家然后综合他们的意见。这就是随机森林的基本理念——通过集体智慧做出更准确的决策。在实际算法实现中这个集体由多棵决策树组成。每棵决策树就像一位专家它们各自从不同角度分析数据。当需要做预测时所有树都会给出自己的判断最终结果由投票决定。这种集体决策的方式比单棵决策树更加稳健不容易被个别异常数据误导。为什么这种方法有效关键在于两点数据随机性和特征随机性。每棵树训练时不仅使用的数据样本是随机抽取的有放回抽样而且考虑的特征也是随机选取的。这种双重随机性确保了各棵树之间的差异性使得整体模型具有更强的泛化能力。2. 搭建随机森林的骨架代码让我们从最基础的代码结构开始。在Python中我们需要先定义一个RandomForestClassifier类。这个类需要包含三个核心方法初始化方法__init__、训练方法fit和预测方法predict。import numpy as np from collections import Counter from sklearn.tree import DecisionTreeClassifier class RandomForestClassifier(): def __init__(self, n_model10): self.n_model n_model # 森林中树的数量 self.models [] # 存储所有决策树的列表 self.col_indexs [] # 存储每棵树使用的特征索引 def fit(self, feature, label): pass # 训练逻辑将在这里实现 def predict(self, feature): pass # 预测逻辑将在这里实现初始化方法设置了三个重要属性n_model控制森林中树的数量默认10棵models列表用于保存训练好的决策树col_indexs记录每棵树使用的特征子集这个骨架虽然简单但已经包含了随机森林最核心的结构。接下来我们需要填充fit和predict方法的实现细节。3. 实现数据的有放回采样随机森林的第一重随机性体现在数据采样上。在fit方法中我们需要为每棵树生成一个独特的训练数据集。这个过程称为自助采样(Bootstrap Sampling)是Bagging算法的核心。具体实现时我们需要确定采样数量通常与原始数据集相同随机选择样本索引允许重复选择根据索引提取对应的数据和标签def fit(self, feature, label): for i in range(self.n_model): m len(feature) # 样本数量 # 有放回随机采样生成索引数组 index np.random.choice(m, m) sample_data feature[index] sample_label label[index]这段代码中np.random.choice实现了有放回抽样。假设原始数据有1000个样本这个方法会随机选择1000个索引可能重复从而创建一个新的训练集。大约有63.2%的原始样本会被选中剩下的36.8%成为袋外(OOB)样本可用于评估模型性能。4. 实现特征的随机选择随机森林的第二重随机性体现在特征选择上。传统决策树会考虑所有特征而随机森林中的每棵树只使用特征的一个随机子集。这种做法增加了树之间的差异性提高了模型的泛化能力。特征子集的大小通常取特征总数的对数k int(np.log2(len(feature[0])))然后我们需要随机选择k个特征col_index np.random.permutation(len(feature[0]))[:k] sample_data sample_data[:, col_index]np.random.permutation生成一个随机排列我们取前k个作为选中的特征索引。这样每棵树都基于不同的特征组合进行训练确保了多样性。5. 构建并保存决策树有了随机采样的数据和特征接下来就可以构建决策树了。我们使用sklearn的DecisionTreeClassifier因为它已经实现了高效的决策树算法。model DecisionTreeClassifier() model.fit(sample_data, sample_label) self.models.append(model) self.col_indexs.append(col_index)这里需要注意两点我们使用的是默认参数的决策树实际应用中可以根据需求调整参数必须保存每棵树使用的特征索引(col_index)因为在预测时需要相同的特征子集6. 实现预测投票机制预测阶段是随机森林展现集体智慧的关键。每棵树都会对测试样本给出预测最终结果由多数投票决定。实现步骤遍历所有树用各自的特征子集进行预测收集所有树的预测结果对每个测试样本统计各分类的得票数选择得票最多的分类作为最终预测def predict(self, feature): result [] vote [] for i, model in enumerate(self.models): f feature[:, self.col_indexs[i]] # 使用相同的特征子集 r model.predict(f) vote.append(r) vote np.array(vote) for i in range(len(feature)): v sorted(Counter(vote[:, i]).items(), keylambda x: x[1], reverseTrue) result.append(v[0][0]) return np.array(result)Counter用于统计各分类的得票数sorted按得票数降序排列v[0][0]取票数最多的分类。这种投票机制使得随机森林对个别树的错误预测具有鲁棒性。7. 关键参数的影响与调优虽然我们的实现使用了默认参数但了解关键参数对性能的影响非常重要n_model树的数量增加树的数量通常会提高性能但边际效益递减一般设置在100-500之间超过后提升有限但计算成本增加特征子集大小(k)通常取特征总数的对数或平方根较小的k增加树之间的差异性但可能影响单棵树的性能可以尝试log2(n_features)或sqrt(n_features)决策树深度在我们的实现中使用的是完全生长的树实际应用中可以通过max_depth控制树深防止过拟合调优这些参数需要通过交叉验证来找到最佳组合。虽然我们的简单实现没有包含这些高级功能但在实际项目中使用sklearn的RandomForestClassifier时这些参数调优是必不可少的步骤。8. 随机森林的优缺点分析经过上面的实现我们现在可以更深入地理解随机森林的特点优点抗过拟合能力强得益于双重随机性和投票机制处理高维数据效果好自动选择特征子集对缺失值不敏感可以保持较好的准确性并行化容易各棵树独立训练缺点模型解释性较差相比单棵决策树更难解释计算资源消耗大特别是树数量多时对噪声较大的数据可能过拟合需要调整树深度等参数在实际应用中随机森林通常能提供还不错的基准性能是很多数据科学项目的首选算法。当需要更好性能时可以考虑梯度提升树(如XGBoost)但后者通常需要更多的调参工作。9. 从零实现的完整代码示例将上述所有部分组合起来我们就得到了完整的RandomForestClassifier实现import numpy as np from collections import Counter from sklearn.tree import DecisionTreeClassifier class RandomForestClassifier(): def __init__(self, n_model10): self.n_model n_model self.models [] self.col_indexs [] def fit(self, feature, label): for i in range(self.n_model): m len(feature) index np.random.choice(m, m) col_index np.random.permutation(len(feature[0]))[:int(np.log2(len(feature[0])))] sample_data feature[index] sample_data sample_data[:, col_index] sample_lable label[index] model DecisionTreeClassifier() model.fit(sample_data, sample_lable) self.models.append(model) self.col_indexs.append(col_index) def predict(self, feature): result [] vote [] for i, model in enumerate(self.models): f feature[:, self.col_indexs[i]] r model.predict(f) vote.append(r) vote np.array(vote) for i in range(len(feature)): v sorted(Counter(vote[:, i]).items(), keylambda x: x[1], reverseTrue) result.append(v[0][0]) return np.array(result)这段代码虽然简洁但完整实现了随机森林的核心逻辑。你可以直接用它来完成Educoder平台的第2关任务或者作为理解随机森林工作原理的教学示例。