
本文通过四个递进案例讲解决策树与随机森林的核心思想、代码逻辑和调参方法。案例算法场景重点一决策树分类电信客户流失预测树的生长逻辑、过拟合与剪枝二决策树回归多元回归分类树与回归树的本质区别三随机森林分类垃圾邮件识别集成学习的原理、网格搜索调参四随机森林分类信用卡欺诈检测样本不均衡处理、评估指标选择一、决策树核心思想1.1 本质贪心的特征筛选器决策树要做的事每次选一个特征把数据「切一刀」让切完后的子集尽可能「纯」。然后对每个子集递归重复。衡量「纯不纯」有三个指标指标公式特点信息熵Entropy -∑ pi log2 pi含对数运算略慢基尼系数Gini 1 - ∑ pi2只含乘法和加法scikit-learn 默认信息增益Gain Entropy(划分前) - Entropy(划分后)ID3 算法使用三者实际效果差异极小选 Gini 纯粹是计算效率考虑不需要纠结。1.2 最大的敌人过拟合自由生长的树会把训练集中的噪声也当成规律。一棵在训练集上 99% 准确的树测试集上可能只有 70%。解决方案预剪枝DecisionTreeClassifier( max_depth10, # 树最多 10 层强行截止 min_samples_leaf5, # 每个叶子至少 5 个样本杜绝「微叶子」 min_samples_split10, # 节点至少 10 个样本才允许分裂 )预剪枝靠三个参数控制max_depth10限制决策树最多生长 10 层。深度越大模型越容易记住训练数据的细节过拟合深度太小则学不到足够规律欠拟合。min_samples_leaf5每个叶子节点至少包含 5 个样本。这个参数防止树长出只包含一两个样本的「微叶子」这种叶子往往是噪声造成的假规律。min_samples_split10一个节点至少要有 10 个样本才允许继续分裂。与 min_samples_leaf 配合从「能不能分」和「分完还剩多少」两个角度控制树的复杂度。调参方向训练集高测试集低 → 收紧参数减小深度、增大叶子样本数训练集测试集都低 → 放松参数。1.3 决策树 vs 随机森林维度决策树随机森林方差高数据微变结构大变低多棵树平均消除方差过拟合容易天然抗过拟合可解释性强可完整可视化弱「黑箱」计算量小大N 倍适用场景快速验证、需要解释、数据少追求性能、特征多、数据大二、随机森林核心思想2.1 本质训练一群「各说各话」的树随机森林 Bagging 随机特征选择。Bagging有放回抽样每棵树用不同的数据训练随机特征每个分裂节点只看随机选出 特征分类任务两次随机的目的相同强制每棵树不一样。如果每棵树都一样投 100 次票和投 1 次没区别。2.2 为什么有效单棵树低偏差 高方差能学好但不稳定随机森林低偏差 低方差能学好且稳定多棵树平均极端预测被「平滑」掉。每棵树错误方向不同平均后相互抵消。数学上方差大约降为原来的 1/N。2.3 关键参数RandomForestClassifier( n_estimators100, # 树的数量100~300 够用再多边际收益递减 max_depthNone, # 深度限制过拟合时设为 3~15 min_samples_split2, # 分裂最小样本数过拟合时增大到 5~20 max_featuressqrt, # 每棵树随机选几个特征默认 sqrt 就好 class_weightbalanced, # 样本不均衡时必设自动给少数类加权 n_jobs-1, # 永远设为 -1用全部 CPU 核心 random_state0 # 开发时固定保证结果可复现 )几个关键参数n_estimators100森林中树的数量。100 是经验上的甜点值——再多也能提升但边际收益递减训练时间却线性增长。max_featuressqrt每个分裂节点随机选取的特征数。分类任务默认 sqrt(d)这是让每棵树「看到不同视角」的关键。如果设为 None全部特征每棵树变得相似集成的优势就消失了。class_weightbalanced自动按类别样本数的倒数加权。样本少的类别会获得更高权重让模型不再偏向多数类。样本不均衡场景下必开。n_jobs-1使用全部 CPU 核心并行训练。随机森林的每棵树相互独立天然适合并行。不加这一行100 棵树会串行训练慢一个数量级。random_state0固定随机种子保证每次运行结果完全一致。开发调试时必不可少否则同样的数据和参数可能得出不同的结果调参无从下手。三、通用工具函数后续案例反复使用的混淆矩阵可视化函数import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix def cm_plot(y, yp): 绘制混淆矩阵热力图 y: 真实标签标准答案 yp: 预测标签模型输出 cm confusion_matrix(y, yp) plt.matshow(cm, cmapplt.cm.Blues) plt.colorbar() for x in range(len(cm)): for y in range(len(cm)): plt.annotate(cm[x, y], (y, x), hacenter, vacenter) plt.ylabel(True label) plt.xlabel(Predicted label) return plt函数做了三件事confusion_matrix(y, yp)将真实标签和预测标签对比生成一个 N×N 的矩阵。矩阵的行是真实类别列是预测类别对角线上的数字代表预测正确的样本数。plt.matshow(cm, cmapplt.cm.Blues)用颜色深浅表示数值大小越深代表样本越多。一眼就能看出模型在哪两个类别之间容易混淆。双层 for 循环标注数字在每个格子里写上具体数值。颜色 数字的双重编码让混淆矩阵既能快速扫读看颜色也能精确比较看数字。只看准确率不看混淆矩阵是分类任务中最常见的错误。四、案例一决策树分类——电信客户流失预测场景用历史客户数据预测哪些客户可能流失。宁可多预警误报不可漏掉漏报——重点关注召回率。代码拆解import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier, plot_tree from sklearn import metrics 1. 加载数据 datas pd.read_excel(电信客户流失数据2.xlsx) data datas.iloc[:, :-1] # 除最后一列外所有列 特征矩阵 target datas.iloc[:, -1] # 最后一列 标签0未流失/1流失 2. 划分训练集和测试集 test_size0.2: 80%训练, 20%测试 random_state10: 固定随机种子保证每次运行切分结果一致 data_train, data_test, target_train, target_test train_test_split( data, target, test_size0.2, random_state10) 3. 构建并训练模型 dtr DecisionTreeClassifier( max_depth10, # 限制深度防过拟合 min_samples_leaf5, # 每片叶子至少5个样本 random_state0) dtr.fit(data_train, target_train) fit() 内部逻辑: 1. 根节点: 计算每个特征划分后的 Gini选最优特征 2. 按该特征取值分裂产生子节点 3. 对每个子节点递归重复直到满足停止条件 4. 训练集评估 train_predict dtr.predict(data_train) print(metrics.classification_report(target_train, train_predict)) cm_plot(target_train, train_predict).show() 5. 测试集评估 test_predict dtr.predict(data_test) print(metrics.classification_report(target_test, test_predict)) 训练集 vs 测试集差距 15% → 过拟合收紧参数 两者都低 → 欠拟合放宽参数 6. 可视化 fig, ax plt.subplots(figsize(32, 32)) plot_tree(dtr, filledTrue, axax) # filledTrue 用颜色显示节点纯度 plt.show()关键语句说明pd.read_excel(电信客户流失数据2.xlsx)读取 Excel 格式的客户数据。iloc[:, :-1] 取前 N-1 列作为特征矩阵如通话时长、套餐类型等iloc[:, -1] 取最后一列作为标签是否流失。train_test_split(data, target, test_size0.2, random_state10)将数据切分为 80% 训练集和 20% 测试集。random_state10 固定随机种子确保每次切分结果一致方便复现和对比实验。DecisionTreeClassifier(max_depth10, min_samples_leaf5, random_state0)创建决策树分类器限制深度为 10、叶节点最少 5 个样本防止过拟合。dtr.fit(data_train, target_train)训练模型。内部逻辑从根节点开始遍历每个特征计算按该特征分裂后的 Gini 不纯度选最优特征分裂然后对子节点递归重复直到达到停止条件深度超限、样本不足等。dtr.predict(data_test)对测试集逐样本预测。新样本从根节点出发按训练好的分裂规则一路走到叶子输出叶子中多数样本的类别。metrics.classification_report(target_test, test_predict)输出 Precision、Recall、F1、Accuracy 四项指标的完整报告。训练集和测试集两份报告对比差距超过 15% 说明过拟合。plot_tree(dtr, filledTrue, axax)绘制完整决策树。filledTrue 根据节点纯度自动着色纯度越高颜色越深方便直观检查树的生长逻辑是否合理。评估指标速查指标公式含义PrecisionTP/(TPFP)预测「流失」的人里多少真流失了RecallTP/(TPFN)真流失的人里被揪出了多少F12×(P×R)/(PR)P 和 R 打架时的平衡值Accuracy(TPTN)/总数类别均衡时可用不均衡时是骗子五、案例二决策树回归——多元回归分析分类 vs 回归的本质区别分类树回归树分裂标准Gini / Entropy纯度MSE数值接近程度叶子输出多数样本的类别所有样本 y 的均值评估指标Accuracy, F1, RecallR², MSE, MAE过拟合风险高更高回归追求精确值更容易死记硬背代码拆解import pandas as pd from sklearn.tree import DecisionTreeRegressor data pd.read_csv(多元回归.csv, encodinggbk) x data.iloc[:, :-1] y data.iloc[:, -1] reg DecisionTreeRegressor( max_depth2 # 回归树更容易过拟合深度从小开始试 ) reg.fit(x, y) y_pr reg.predict(x) predict() 逻辑: 新样本走到叶子 → 输出该叶子所有训练样本 y 的均值 所以回归树每个叶子输出一个常数不同叶子代表不同数值区间 评估回归效果 from sklearn.metrics import r2_score, mean_squared_error print(R²:, r2_score(y, y_pr)) print(MSE:, mean_squared_error(y, y_pr))若深度设为 2树的结构只有两层可以画出来检查每个节点的分裂条件和叶子的均值。实践中回归树的 max_depth 建议从 2~5 开始用交叉验证选择最佳深度。六、案例三随机森林分类——垃圾邮件识别场景邮件文本特征维度很高单棵决策树容易过拟合到某个关键词上。随机森林的多棵树投票可以有效提升泛化能力。代码拆解import pandas as pd from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report 1. 加载邮件数据 假设数据已通过词袋模型或 TF‑IDF 转换为数值特征矩阵 df pd.read_csv(spam_emails.csv) # 最后一列为 label1垃圾邮件0正常 X df.iloc[:, :-1] y df.iloc[:, -1] 2. 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42) 3. 随机森林基线模型 rf RandomForestClassifier( n_estimators100, max_featuressqrt, random_state0, n_jobs-1 ) rf.fit(X_train, y_train) pred rf.predict(X_test) print(基线模型报告) print(classification_report(y_test, pred)) 4. 网格搜索调参 param_grid { n_estimators: [100, 200], max_depth: [10, 20, None], min_samples_split: [2, 5], min_samples_leaf: [1, 2] } grid GridSearchCV( RandomForestClassifier(random_state0, n_jobs-1), param_grid, cv3, # 3折交叉验证快速评估 scoringf1, # 垃圾邮件检测关注 F1 verbose1 ) grid.fit(X_train, y_train) print(最佳参数, grid.best_params_) print(最佳 CV 分数, grid.best_score_) 用最优参数重新评估测试集 best_rf grid.best_estimator_ test_pred best_rf.predict(X_test) print(最优模型测试报告) print(classification_report(y_test, test_pred))调参经验网格搜索并非暴力遍历所有组合实际中可以先粗调再细调。例如先用大步长确定 n_estimators 范围再微调 max_depth 和 min_samples_split。如果特征数远大于样本数适当增大 min_samples_leaf 可以避免叶子过于零碎。七、案例四随机森林分类——信用卡欺诈检测场景信用卡交易数据中欺诈样本占比通常不到 1%极度不均衡。不能只看准确率必须同时关注 Recall、F1 和 ROC AUC。代码拆解import pandas as pd from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import (classification_report, roc_auc_score, confusion_matrix) 1. 加载数据 df pd.read_csv(creditcard.csv) X df.drop(Class, axis1) # Class 列为标签0正常1欺诈 y df[Class] 2. 查看样本分布 print(正常样本数, (y 0).sum()) print(欺诈样本数, (y 1).sum()) print(欺诈比例{:.4f}%.format(100 * y.mean())) 3. 划分训练/测试 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy # stratify 保持分布一致 ) 4. 模型训练加入类别权重 rf RandomForestClassifier( n_estimators100, class_weightbalanced, # 自动为少数类欺诈加权 max_depth15, random_state0, n_jobs-1 ) rf.fit(X_train, y_train) 5. 评估 pred rf.predict(X_test) prob rf.predict_proba(X_test)[:, 1] # 取正类欺诈的概率 print(分类报告) print(classification_report(y_test, pred)) print(AUC, roc_auc_score(y_test, prob)) 混淆矩阵 cm confusion_matrix(y_test, pred) print(混淆矩阵) print(cm) TP右下: 真实欺诈且被模型识别 FN左下: 真实欺诈但被漏判 → 最要命的错误不均衡处理技巧总结class_weightbalanced最简单有效的方式让模型更看重少数类。评估指标选择不均衡场景 Accuracy 会虚高必须看 Recall能否抓出欺诈和 AUC整体排序能力。采样方法若 class_weight 效果不理想还可尝试 SMOTE 过采样或随机欠采样但要避免引入数据泄露。八、总结决策树是入手机器学习很好的切入点。搞懂了它怎么生长、怎么分裂、怎么剪枝再学 Bagging、Boosting 这些集成方法会轻松不少。随机森林本质就是 Bagging 加随机特征选择——一群各说各话的树投票比单棵树稳得多。几个要点单棵树容易过拟合预剪枝参数max_depth、min_samples_leaf是调参的重头戏随机森林的 n_estimators 到 100~200 后收益就不明显了优先调分裂相关的参数分类任务不看混淆矩阵就像开车不看后视镜样本不均衡时class_weightbalanced 搭配合适的评估指标Recall、F1、AUC比死磕超参更重要。四个案例跑通了分类、回归、调参和不均衡处理这几种典型场景。建议先把代码跑起来再换自己的数据试试——很多东西跑一遍就有感觉了。