机器学习之随机森林详解
摘要随机森林Random Forest是一种基于Bagging集成学习思想的 ensemble method通过构建多棵决策树并综合其预测结果来实现分类和回归任务。本文详细介绍了随机森林的核心原理、关键超参数、OOB误差估计机制以及其在特征重要性分析、异常检测等场景中的应用。文中提供了完整的Python代码示例涵盖鸢尾花分类、OOB误差估计、特征重要性可视化和超参数调优等实战内容。所有代码均基于scikit-learn库可直接运行。关键词随机森林Bagging集成学习决策树OOB误差特征重要性scikit-learn1. 引言在机器学习领域没有一种算法能够适用于所有场景。每种模型都有其优势和局限性而集成学习Ensemble Learning正是通过组合多个模型来弥补单一模型的不足。随机森林作为集成学习中最具代表性的算法之一凭借其优异的性能、较强的抗过拟合能力和易用性在学术界和工业界都得到了广泛应用。从Kaggle竞赛的历史来看随机森林及其变体在众多表格数据分类和回归任务中表现出色常常作为强有力的基准模型Baseline。本文将系统性地介绍随机森林的原理、实现细节和使用技巧帮助读者全面掌握这一重要算法。2. 集成学习基础2.1 为什么需要集成学习在讨论随机森林之前我们需要理解集成学习背后的核心思想。想象一下这样一个场景你要决定是否投资某只股票你会怎么做你可能会咨询多位朋友的意见然后综合他们的判断做出最终决策。如果其中一位朋友给出了过于极端的建议而其他几位都给出了相反的建议你自然会倾向于相信多数人的判断。这就是集成学习的基本哲学——群体的智慧往往优于个体。在机器学习中单个模型无论是决策树、SVM还是神经网络都可能存在偏差或方差问题。通过构建多个模型并综合它们的预测结果我们可以获得更稳定、更准确的预测。2.2 BaggingBootstrap Aggregating原理Bagging是随机森林的基石其核心思想可以通过以下步骤理解步骤一有放回随机采样Bootstrap Sampling假设我们有一个包含N个样本的训练数据集。从中随机抽取一个样本复制到新的采样集中然后将原样本放回数据集。重复这个过程N次我们就得到了一个与原数据集大小相同的Bootstrap样本。由于是有放回采样Bootstrap样本中大约会包含原数据集63.2%的不同样本这是统计学上的经典结论。步骤二训练基学习器使用每个Bootstrap样本分别训练一个基学习器如决策树。步骤三聚合预测结果对于分类任务采用多数投票Majority Voting——让所有基学习器分别预测然后选择获得票数最多的类别作为最终预测。对于回归任务则采用简单平均Simple Averaging——将所有基学习器的预测值取平均。下面我们用代码演示Bagging的基本流程import numpy as np import matplotlib.pyplot as plt from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import BaggingClassifier from sklearn.datasets import make_moons from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 生成非线性分类数据集双月牙形 X, y make_moons(n_samples500, noise0.3, random_state42) # 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 对比单棵决策树与Bagging10棵树 tree_clf DecisionTreeClassifier(random_state42) tree_clf.fit(X_train, y_train) tree_pred tree_clf.predict(X_test) print(f单棵决策树准确率: {accuracy_score(y_test, tree_pred):.4f}) bagging_clf BaggingClassifier( estimatorDecisionTreeClassifier(), n_estimators10, bootstrapTrue, # 使用Bootstrap采样 oob_scoreTrue, # 计算OOB分数 random_state42 ) bagging_clf.fit(X_train, y_train) bagging_pred bagging_clf.predict(X_test) print(fBagging10棵树准确率: {accuracy_score(y_test, bagging_pred):.4f}) print(fBagging OOB分数: {bagging_clf.oob_score_:.4f}) # 可视化对比 fig, axes plt.subplots(1, 2, figsize(14, 5)) # 绘制决策边界 def plot_decision_boundary(clf, X, y, ax, title): h 0.02 x_min, x_max X[:, 0].min() - 0.5, X[:, 0].max() 0.5 y_min, y_max X[:, 1].min() - 0.5, X[:, 1].max() 0.5 xx, yy np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) Z clf.predict(np.c_[xx.ravel(), yy.ravel()]) Z Z.reshape(xx.shape) ax.contourf(xx, yy, Z, alpha0.4) ax.scatter(X[:, 0], X[:, 1], cy, alpha0.8, edgecolorsk) ax.set_title(title) ax.set_xlabel(Feature 1) ax.set_ylabel(Feature 2) plot_decision_boundary(tree_clf, X_test, y_test, axes[0], 单棵决策树) plot_decision_boundary(bagging_clf, X_test, y_test, axes[1], Bagging10棵树) plt.tight_layout() plt.savefig(bagging_comparison.png, dpi150) plt.show()运行结果单棵决策树准确率: 0.7900 Bagging10棵树准确率: 0.9000 Bagging OOB分数: 0.8875从结果可以看到Bagging通过集成多棵树显著提升了分类准确率。2.3 偏差与方差的权衡理解偏差-方差分解对于深入理解随机森林至关重要。模型的泛化误差可以分解为泛化误差 偏差² 方差 不可约误差偏差Bias模型预测值的期望与真实值之间的差异。高偏差意味着模型欠拟合无法捕捉数据的基本模式。方差Variance同一模型在不同训练集上的预测变化程度。高方差意味着模型过拟合对训练数据的微小变化过于敏感。单棵决策树通常具有低偏差但高方差的特性——它们可以很好地拟合训练数据但容易过拟合导致在不同数据集上表现差异很大。Bagging通过以下方式降低方差每个Bootstrap样本训练一棵树产生N棵略有不同的树预测时取平均/投票这些不同的预测值会相互抵消极端错误最终结果是偏差保持较低因为每棵树都足够深但方差大幅降低这就是Bagging的核心优势——在不显著增加偏差的情况下大幅降低方差。3. 随机森林原理详解3.1 随机森林 决策树 Bagging 随机特征选择随机森林在Bagging的基础上更进一步引入了随机特征选择机制。标准Bagging中每棵树都使用全部特征来寻找最优分裂点。而随机森林在每个节点分裂时只考虑特征的一个随机子集。这个看似简单的改动带来了巨大的好处进一步增加树之间的多样性如果每棵树都使用相同的特征即使使用不同的Bootstrap样本树的结构可能仍然相似。随机特征选择确保每棵树都有独特的视角。更强的抗过拟合能力限制每个节点可用的特征数量防止树变得过于复杂。更好的泛化性能多样性增加使整体预测更加稳健。3.2 随机森林算法流程以下是随机森林的完整训练流程输入训练数据集 D包含 N 个样本和 M 个特征 树的数量 T 特征子集大小 m通常 m √M 或 log₂(M) 输出T 棵决策树的集合 {tree₁, tree₂, ..., treeₜ} 对于 t 1 到 T 1. Bootstrap采样从 D 中有放回地抽取 N 个样本得到 Bootstrap 数据集 Dₜ 2. 训练决策树 treeₜ - 从根节点开始 - 对每个节点 a. 随机选择 m 个特征不放回 b. 在这 m 个特征中找到最优分裂 c. 按照最优分裂将节点分为两个子节点 - 持续分裂直到满足停止条件如最大深度、最小样本数等 - 不进行剪枝 3. 返回 treeₜ 预测 - 分类T 棵树投票取票数最多的类别 - 回归T 棵树预测值取平均3.3 随机森林的Python实现from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor from sklearn.datasets import load_breast_cancer, fetch_california_housing from sklearn.model_selection import cross_val_score from sklearn.metrics import classification_report, mean_squared_error # 分类示例乳腺癌数据集 print( * 50) print(乳腺癌数据集分类示例) print( * 50) # 加载数据 cancer load_breast_cancer() X, y cancer.data, cancer.target # 训练随机森林分类器 rf_clf RandomForestClassifier( n_estimators100, max_depthNone, # 不限制深度让树完全生长 min_samples_split2, # 节点分裂所需最小样本数 min_samples_leaf1, # 叶节点最小样本数 max_featuressqrt, # 每次分裂考虑的特征数sqrt为开平方 bootstrapTrue, # 使用Bootstrap采样 oob_scoreTrue, # 计算OOB分数 random_state42, n_jobs-1 # 使用所有CPU核心 ) # 交叉验证评估 cv_scores cross_val_score(rf_clf, X, y, cv5, scoringaccuracy) print(f5折交叉验证准确率: {cv_scores.mean():.4f} (/- {cv_scores.std() * 2:.4f})) # 训练模型 rf_clf.fit(X, y) print(fOOB分数: {rf_clf.oob_score_:.4f}) print(f测试集准确率: {rf_clf.score(X, y):.4f}) # 打印特征重要性Top 10 feature_importance rf_clf.feature_importances_ feature_names cancer.feature_names indices np.argsort(feature_importance)[::-1] print(\n特征重要性排名Top 10:) for i in range(min(10, len(feature_names))): print(f {i1}. {feature_names[indices[i]]}: {feature_importance[indices[i]]:.4f}) # 回归示例加州房价数据集 print(\n * 50) print(加州房价数据集回归示例) print( * 50) # 加载数据 housing fetch_california_housing() X_h, y_h housing.data, housing.target # 训练随机森林回归器 rf_reg RandomForestRegressor( n_estimators100, max_depth15, # 限制最大深度 min_samples_split5, # 内部节点分裂所需最小样本数 min_samples_leaf2, # 叶节点最小样本数 random_state42, n_jobs-1 ) # 交叉验证评估 cv_mse -cross_val_score(rf_reg, X_h, y_h, cv5, scoringneg_mean_squared_error) print(f5折交叉验证 MSE: {cv_mse.mean():.4f} (/- {cv_mse.std() * 2:.4f})) print(f5折交叉验证 RMSE: {np.sqrt(cv_mse.mean()):.4f}) # 训练和预测 rf_reg.fit(X_h, y_h) predictions rf_reg.predict(X_h[:5]) print(f\n前5个样本的预测值: {predictions}) print(f对应真实值: {y_h[:5]})运行结果 乳腺癌数据集分类示例 5折交叉验证准确率: 0.9632 (/- 0.0249) OOB分数: 0.9596 测试集准确率: 1.0000 特征重要性排名Top 10: 1. worst radius: 0.0715 2. worst perimeter: 0.0698 3. mean concave points: 0.0584 4. mean radius: 0.0553 5. worst area: 0.0508 ... 加州房价数据集回归示例 5折交叉验证 MSE: 0.2639 (/- 0.0192) 5折交叉验证 RMSE: 0.51374. 关键超参数详解随机森林有多个重要超参数理解它们的作用对于调优模型至关重要。4.1 n_estimators - 树的数量n_estimators控制随机森林中树的数量。理论上树越多模型越稳定泛化能力越强。但边际效益会递减——增加到一定程度后增加更多的树只会增加计算成本而性能提升很小。经验法则分类任务100-500棵树通常足够回归任务可能需要更多200-1000棵使用OOB分数或验证集监控找到最优数量# 探究树的数量与性能的关系 from sklearn.datasets import load_iris iris load_iris() X, y iris.data, iris.target # 测试不同数量的树 n_trees_list [10, 50, 100, 200, 500] train_scores [] oob_scores [] for n_trees in n_trees_list: rf RandomForestClassifier( n_estimatorsn_trees, bootstrapTrue, oob_scoreTrue, random_state42 ) rf.fit(X, y) train_scores.append(rf.score(X, y)) oob_scores.append(rf.oob_score_) print(f树数量: {n_trees:3d} | 训练准确率: {rf.score(X, y):.4f} | OOB准确率: {rf.oob_score_:.4f}) # 可视化 plt.figure(figsize(10, 5)) plt.plot(n_trees_list, train_scores, b-o, label训练准确率) plt.plot(n_trees_list, oob_scores, r-o, labelOOB准确率) plt.xlabel(树的数量 (n_estimators)) plt.ylabel(准确率) plt.title(随机森林树的数量 vs 准确率) plt.legend() plt.grid(True, alpha0.3) plt.savefig(n_estimators_analysis.png, dpi150) plt.show()4.2 max_depth - 最大深度max_depth限制每棵树的最大深度。深度越大树越复杂越容易过拟合深度越小树越简单可能欠拟合。关键点max_depthNone树完全生长直到所有叶节点纯或样本数少于min_samples_split限制深度可以显著减少训练时间结合min_samples_split和min_samples_leaf使用效果更好# 对比不同max_depth的效果 max_depths [3, 5, 10, 15, None] depth_train_scores [] depth_test_scores [] X_train, X_test, y_train, y_test train_test_split(iris.data, iris.target, test_size0.3, random_state42) for depth in max_depths: rf RandomForestClassifier(n_estimators100, max_depthdepth, random_state42) rf.fit(X_train, y_train) depth_train_scores.append(rf.score(X_train, y_train)) depth_test_scores.append(rf.score(X_test, y_test)) print(fmax_depth: {str(depth):5s} | 训练准确率: {rf.score(X_train, y_train):.4f} | 测试准确率: {rf.score(X_test, y_test):.4f})4.3 min_samples_split 和 min_samples_leafmin_samples_split节点分裂所需的最小样本数。如果节点样本数少于这个值则不再分裂。min_samples_leaf叶节点所需最小样本数。如果分裂会导致叶节点样本数少于这个值则不进行分裂。这两个参数共同作用防止树变得过于复杂# 演示min_samples_split和min_samples_leaf的效果 params_combos [ (2, 1), # 默认值不过滤 (10, 5), # 较严格 (20, 10), # 非常严格 (50, 20) # 极度严格 ] print(min_samples_split | min_samples_leaf | 训练准确率 | 测试准确率 | 叶节点数) print(- * 75) for min_split, min_leaf in params_combos: rf RandomForestClassifier( n_estimators100, min_samples_splitmin_split, min_samples_leafmin_leaf, random_state42 ) rf.fit(X_train, y_train) # 计算总叶节点数 total_leaves sum(tree.get_n_leaves() for tree in rf.estimators_) print(f{min_split:17d} | {min_leaf:16d} | {rf.score(X_train, y_train):.4f} | {rf.score(X_test, y_test):.4f} | {total_leaves:5d})4.4 max_features - 特征采样比例max_features控制每个节点分裂时考虑的特征数量。这是随机森林引入的关键随机性来源。常用设置sqrt分类时推荐等于√MM为特征总数log2分类时推荐等于log₂(M)0.3-0.7可以尝试的范围None使用所有特征等同于Bagging# 对比不同max_features设置 max_features_options [sqrt, log2, None, 0.5, 0.7] print(max_features | 训练准确率 | 测试准确率) print(- * 45) for mf in max_features_options: rf RandomForestClassifier(n_estimators100, max_featuresmf, random_state42) rf.fit(X_train, y_train) print(f{str(mf):12s} | {rf.score(X_train, y_train):.4f} | {rf.score(X_test, y_test):.4f})5. OOBOut-of-Bag误差详解5.1 OOB误差的概念在Bagging过程中每个Bootstrap样本大约包含原数据集63.2%的不同样本。这意味着对于每棵树大约有36.8%的样本从未被用于该树的训练。这些未被使用的样本被称为Out-of-BagOOB样本。OOB误差的巧妙之处在于我们可以利用这些未见过的样本来评估模型性能而无需单独的验证集或交叉验证5.2 OOB误差计算方法对于每个样本xᵢ我们可以找到所有未使用xᵢ的树即xᵢ在那些树的OOB集中让这些树对xᵢ进行预测然后综合这些预测得到该样本的OOB预测。最后比较所有样本的OOB预测与真实标签计算整体OOB误差。OOB误差的优势无需交叉验证可以快速获得无偏的性能估计节省数据所有数据都可用于训练与验证集误差高度相关可以作为模型选择和超参数调优的可靠指标5.3 OOB误差的Python实现from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import load_breast_cancer import numpy as np # 加载数据 cancer load_breast_cancer() X, y cancer.data, cancer.target # 演示OOB误差估计 print(随机森林 OOB 误差估计演示) print( * 50) # 不使用OOB rf_no_oob RandomForestClassifier(n_estimators100, oob_scoreFalse, random_state42) rf_no_oob.fit(X, y) print(f无OOB - 训练准确率: {rf_no_oob.score(X, y):.4f}) # 使用OOB rf_oob RandomForestClassifier(n_estimators100, oob_scoreTrue, random_state42) rf_oob.fit(X, y) print(f有OOB - 训练准确率: {rf_oob.score(X, y):.4f}) print(f有OOB - OOB分数: {rf_oob.oob_score_:.4f}) # OOB分数的解读 print(f\nOOB分数与训练准确率的差异: {rf_oob.score(X, y) - rf_oob.oob_score_:.4f}) print(差异越大说明模型过拟合越严重) # 验证OOB与交叉验证的一致性 from sklearn.model_selection import cross_val_score cv_scores cross_val_score(rf_oob, X, y, cv5) print(f\n5折交叉验证准确率: {cv_scores.mean():.4f} (/- {cv_scores.std() * 2:.4f})) print(fOOB分数: {rf_oob.oob_score_:.4f}) print(可以看到OOB分数与CV分数非常接近说明OOB估计是可靠的)6. 随机森林使用场景6.1 表格数据分类与回归随机森林在结构化表格数据上的表现通常非常出色是Kaggle竞赛中的常胜模型。它特别适合特征类型混合数值型类别型特征维度不是特别高几百个特征以内数据量中等几千到几百万样本6.2 特征重要性分析随机森林提供了天然的特征重要性Feature Importance度量这是其最受欢迎的应用之一。通过分析每个特征在所有树中对分裂和信息增益的贡献我们可以识别最关键的特征。import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.ensemble import RandomForestClassifier import numpy as np # 加载鸢尾花数据集 iris load_iris() X, y iris.data, iris.target # 训练随机森林 rf RandomForestClassifier(n_estimators200, random_state42) rf.fit(X, y) # 获取特征重要性 feature_importance rf.feature_importances_ feature_names iris.feature_names indices np.argsort(feature_importance)[::-1] # 可视化特征重要性 plt.figure(figsize(10, 6)) colors plt.cm.viridis(np.linspace(0, 0.8, len(feature_names))) plt.bar(range(len(feature_names)), feature_importance[indices], colorcolors) plt.xticks(range(len(feature_names)), [feature_names[i] for i in indices], rotation45, haright) plt.xlabel(特征) plt.ylabel(重要性) plt.title(随机森林特征重要性分析 - 鸢尾花数据集) plt.tight_layout() plt.savefig(feature_importance.png, dpi150) plt.show() # 打印排名 print(特征重要性排名:) for i, idx in enumerate(indices): print(f {i1}. {feature_names[idx]}: {feature_importance[idx]:.4f})6.3 异常检测Anomaly Detection随机森林可用于异常检测方法是对于每个样本计算它穿过所有树的平均深度或平均非叶节点数。异常点通常位于树的较浅层因为它们很快就被分离出去了或者在特征空间中远离训练数据。from sklearn.ensemble import IsolationForest from sklearn.datasets import make_blobs import matplotlib.pyplot as plt # 生成包含异常点的数据 X, y make_blobs(n_samples300, centers1, cluster_std0.5, random_state42) # 添加异常点 outliers np.random.uniform(-6, 6, (20, 2)) X_outliers np.vstack([X, outliers]) y_outliers np.hstack([y, [-1]*20]) # -1表示异常 # 使用Isolation Forest进行异常检测 iso_forest IsolationForest(n_estimators100, contamination0.05, random_state42) predictions iso_forest.fit_predict(X_outliers) # 可视化 plt.figure(figsize(10, 6)) inliers predictions 1 outliers_mask predictions -1 plt.scatter(X_outliers[inliers, 0], X_outliers[inliers, 1], cblue, label正常点, alpha0.6) plt.scatter(X_outliers[outliers_mask, 0], X_outliers[outliers_mask, 1], cred, label异常点, alpha0.8, edgecolorsk) plt.title(Isolation Forest 异常检测结果) plt.legend() plt.savefig(anomaly_detection.png, dpi150) plt.show() # 输出检测统计 print(f检测出的异常点数量: {(predictions -1).sum()}) print(f异常点比例: {(predictions -1).sum() / len(predictions):.2%})6.4 缺失值填补随机森林可以用于迭代式填补缺失值方法如下用均值/中位数初步填补将填补后的数据训练随机森林用训练好的随机森林重新预测缺失值重复步骤2-3直到收敛或达到最大迭代次数from sklearn.datasets import load_iris from sklearn.ensemble import RandomForestRegressor import numpy as np import pandas as pd # 加载数据并人为添加缺失值 iris load_iris() X, y iris.data, iris.target feature_names iris.feature_names # 创建DataFrame方便操作 df pd.DataFrame(X, columnsfeature_names) df[target] y # 人为添加缺失值约10%的数据 np.random.seed(42) missing_mask np.random.random(df.shape) 0.1 df_with_missing df.copy() df_with_missing[missing_mask] np.nan print(f缺失值数量: {df_with_missing.isna().sum().sum()}) print(f缺失值比例: {df_with_missing.isna().mean().mean():.2%}) # 使用随机森林迭代填补缺失值 def rf_impute(df, target_col, n_iterations10, n_estimators100): 使用随机森林迭代填补缺失值 df_imputed df.copy() for iteration in range(n_iterations): df_temp df_imputed.copy() # 对每一列进行填补 for col in df_temp.columns: if col target_col: continue # 找出缺失值的位置 missing_mask df_temp[col].isna() if missing_mask.sum() 0: continue # 准备训练数据 train_data df_temp[~missing_mask] predict_data df_temp[missing_mask] # 特征是除了目标列和当前列外的所有列 feature_cols [c for c in df_temp.columns if c ! col and c ! target_col] X_train train_data[feature_cols] y_train train_data[col] X_predict predict_data[feature_cols] # 训练随机森林 rf RandomForestRegressor(n_estimatorsn_estimators, random_state42) rf.fit(X_train, y_train) # 预测并填补 if len(X_predict) 0: predicted_values rf.predict(X_predict) df_imputed.loc[missing_mask, col] predicted_values if iteration 0 or iteration n_iterations - 1: print(f迭代 {iteration 1}/{n_iterations} 完成) return df_imputed # 执行填补 df_imputed rf_impute(df_with_missing, target, n_iterations5) # 评估填补效果 print(\n填补后的数据统计:) print(df_imputed.describe())7. 实战鸢尾花分类完整示例下面我们将完成一个完整的鸢尾花分类实战对比单棵决策树和随机森林的性能差异并展示OOB误差估计和特征重要性分析。import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.tree import DecisionTreeClassifier, plot_tree from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import ( accuracy_score, classification_report, confusion_matrix, ConfusionMatrixDisplay ) # 1. 数据加载与探索 print( * 60) print(鸢尾花分类实战 - 单棵决策树 vs 随机森林) print( * 60) iris load_iris() X, y iris.data, iris.target feature_names iris.feature_names target_names iris.target_names print(f\n数据集信息:) print(f 样本数: {X.shape[0]}) print(f 特征数: {X.shape[1]}) print(f 类别: {list(target_names)}) # 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) print(f 训练集: {X_train.shape[0]} 样本) print(f 测试集: {X_test.shape[0]} 样本) # 2. 训练单棵决策树 print(\n - * 40) print(训练单棵决策树) print(- * 40) tree_clf DecisionTreeClassifier(random_state42) tree_clf.fit(X_train, y_train) tree_train_pred tree_clf.predict(X_train) tree_test_pred tree_clf.predict(X_test) print(f\n单棵决策树结果:) print(f 训练集准确率: {accuracy_score(y_train, tree_train_pred):.4f}) print(f 测试集准确率: {accuracy_score(y_test, tree_test_pred):.4f}) print(\n分类报告:) print(classification_report(y_test, tree_test_pred, target_namestarget_names)) # 3. 训练随机森林 print(- * 40) print(训练随机森林100棵树) print(- * 40) rf_clf RandomForestClassifier( n_estimators100, bootstrapTrue, oob_scoreTrue, random_state42 ) rf_clf.fit(X_train, y_train) rf_train_pred rf_clf.predict(X_train) rf_test_pred rf_clf.predict(X_test) print(f\n随机森林结果:) print(f 训练集准确率: {accuracy_score(y_train, rf_train_pred):.4f}) print(f 测试集准确率: {accuracy_score(y_test, rf_test_pred):.4f}) print(f OOB分数: {rf_clf.oob_score_:.4f}) print(\n分类报告:) print(classification_report(y_test, rf_test_pred, target_namestarget_names)) # 4. 混淆矩阵可视化 fig, axes plt.subplots(1, 2, figsize(14, 5)) # 决策树混淆矩阵 ConfusionMatrixDisplay.from_estimator( tree_clf, X_test, y_test, display_labelstarget_names, cmapBlues, axaxes[0] ) axes[0].set_title(单棵决策树 - 混淆矩阵) # 随机森林混淆矩阵 ConfusionMatrixDisplay.from_estimator( rf_clf, X_test, y_test, display_labelstarget_names, cmapGreens, axaxes[1] ) axes[1].set_title(随机森林 - 混淆矩阵) plt.tight_layout() plt.savefig(confusion_matrices.png, dpi150) plt.show() # 5. 特征重要性分析 print(\n - * 40) print(特征重要性分析) print(- * 40) importances rf_clf.feature_importances_ indices np.argsort(importances)[::-1] print(\n随机森林特征重要性:) for i, idx in enumerate(indices): print(f {i1}. {feature_names[idx]}: {importances[idx]:.4f}) # 可视化 fig, axes plt.subplots(1, 2, figsize(14, 5)) # 条形图 colors plt.cm.RdYlGn(np.linspace(0.2, 0.8, len(feature_names))) axes[0].barh(range(len(feature_names)), importances[indices], colorcolors) axes[0].set_yticks(range(len(feature_names))) axes[0].set_yticklabels([feature_names[i] for i in indices]) axes[0].set_xlabel(重要性) axes[0].set_title(随机森林特征重要性) axes[0].invert_yaxis() # 箱线图每棵树的特征重要性分布 tree_importances np.array([tree.feature_importances_ for tree in rf_clf.estimators_]) axes[1].boxplot(tree_importances, labelsfeature_names, vertFalse) axes[1].set_xlabel(重要性) axes[1].set_title(各特征重要性分布100棵树的箱线图) plt.tight_layout() plt.savefig(feature_importance_detailed.png, dpi150) plt.show() # 6. 超参数调优 print(\n - * 40) print(超参数调优使用GridSearchCV) print(- * 40) param_grid { n_estimators: [50, 100, 200], max_depth: [3, 5, 10, None], min_samples_split: [2, 5, 10], min_samples_leaf: [1, 2, 4] } rf_grid GridSearchCV( RandomForestClassifier(random_state42), param_grid, cv5, scoringaccuracy, n_jobs-1, verbose1 ) rf_grid.fit(X_train, y_train) print(f\n最优参数: {rf_grid.best_params_}) print(f最优交叉验证分数: {rf_grid.best_score_:.4f}) # 使用最优模型在测试集上评估 best_rf rf_grid.best_estimator_ best_pred best_rf.predict(X_test) print(f最优模型测试集准确率: {accuracy_score(y_test, best_pred):.4f})运行结果 鸢尾花分类实战 - 单棵决策树 vs 随机森林 数据集信息: 样本数: 150 特征数: 4 类别: [setosa, versicolor, virginica] 训练集: 105 样本 测试集: 45 样本 -------------------------------------------------- 单棵决策树结果: 训练集准确率: 1.0000 测试集准确率: 0.9778 随机森林结果: 训练集准确率: 1.0000 测试集准确率: 1.0000 OOB分数: 0.9429 -------------------------------------------------- 特征重要性分析 随机森林特征重要性: 1. petal width (cm): 0.4441 2. petal length (cm): 0.4169 3. sepal width (cm): 0.0928 4. sepal length (cm): 0.0462 -------------------------------------------------- 超参数调优使用GridSearchCV 最优参数: {max_depth: None, min_samples_leaf: 1, min_samples_split: 2, n_estimators: 50} 最优交叉验证分数: 0.9619 最优模型测试集准确率: 1.00008. 随机森林的优缺点总结8.1 优点性能优异在大多数分类和回归任务中表现出色抗过拟合通过集成和随机特征选择降低过拟合风险并行化友好每棵树相互独立可并行训练特征重要性内置特征重要性分析OOB估计无需交叉验证即可评估模型处理缺失值可以处理缺失值和类别型特征鲁棒性对异常值和噪声相对不敏感易用性超参数通常使用默认值就能获得不错的效果8.2 缺点解释性差数百棵树组成的模型难以解释相比单棵决策树计算成本需要训练多棵树比单棵树慢内存消耗需要存储所有树内存占用较大边缘化优势在某些任务中可能不如梯度提升如XGBoost、LightGBM表现好时间复杂度随着树的数量增加预测时间线性增长8.3 适用场景总结场景推荐程度说明表格数据分类/回归⭐⭐⭐⭐⭐随机森林的最佳应用场景特征重要性分析⭐⭐⭐⭐⭐内置支持非常方便异常检测⭐⭐⭐⭐Isolation Forest是专门为此设计的变体高维稀疏数据如文本⭐⭐效果通常不如线性模型或深度学习实时预测⭐⭐⭐需要预训练好模型单次预测速度可以接受9. 结语随机森林是机器学习工具箱中不可或缺的利器。它完美地体现了群体智慧的思想——通过集成多棵决策树并引入随机性既保留了决策树强大的表达能力又大幅提升了泛化能力。在实际应用中随机森林通常可以作为首选模型来快速建立基准性能然后再尝试更复杂的算法如XGBoost、LightGBM等梯度提升方法。特别是在特征重要性分析、缺失值处理等场景中随机森林提供了开箱即用的解决方案。掌握随机森林的原理和使用技巧对于每一位机器学习从业者都是必备的基本功。希望本文能够帮助读者全面理解随机森林并在实际项目中灵活运用。