楼层: 首页/ 软件技术/ Python 机器学习与深度学习/ 决策树与随机森林
5

决策树与随机森林

Decision Tree & Random Forest

决策树就是"二十个问题游戏":每个节点问一个 yes/no 问题,叶子节点给答案。可解释性无敌,但单棵树容易过拟合。随机森林是"三个臭皮匠顶个诸葛亮"——种一堆树投票。

决策树怎么分裂

论信息熵 / 基尼系数

信息熵:度量"混乱程度",全是一类熵为 0,一半一半熵最大。信息增益:分裂前的熵 - 分裂后的加权熵,越大说明这次分裂越有用。基尼系数:随机抽两个样本类别不一样的概率,和熵效果差不多,算得更快(sklearn 默认)。CART 用基尼,ID3 用信息增益,C4.5 用增益率。

基尼:Gini = 1 - Σ pᵢ²

剪枝:防过拟合

方法说明
预剪枝构建树时就限制:max_depth、min_samples_split、min_samples_leaf。快,常用。
后剪枝先长成完全树,再自底向上剪掉不提升验证集精度的分支。更精细但慢。

集成学习三大门派

方法怎么训代表
Bagging并行训多个模型,投票/平均。降方差。随机森林
Boosting串行训,每个新模型纠正前面的错。降偏差。GBDT / XGBoost / AdaBoost
Stacking把多个模型的预测当新特征,训一个元模型。StackingClassifier

随机森林:Bagging + 特征随机

论为什么有效

随机森林做了两层随机:样本随机(Bootstrap 有放回抽样)+ 特征随机(每次分裂只看一部分特征)。这样种出来的树"长得不一样",投票时降低相关性,方差比单棵树小很多。OOB(袋外误差):每棵树没抽到的样本刚好可以当验证集,免费评估。

完整案例:客户流失预测

import numpy as np from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split, cross_val_score from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, roc_auc_score X, y = make_classification( n_samples=5000, n_features=20, n_informative=10, n_redundant=5, random_state=42 ) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 单棵决策树:容易过拟合 tree = DecisionTreeClassifier(max_depth=None, random_state=42) tree.fit(X_train, y_train) print("决策树 训练集:", tree.score(X_train, y_train)) # 决策树 训练集:1.0(背答案了) print("决策树 测试集:", tree.score(X_test, y_test)) # 决策树 测试集:0.91 # 随机森林:100 棵树投票 rf = RandomForestClassifier( n_estimators=200, max_depth=None, min_samples_leaf=2, oob_score=True, random_state=42, n_jobs=-1 ) rf.fit(X_train, y_train) print("随机森林 训练集:", rf.score(X_train, y_train)) # 随机森林 训练集:0.998 print("随机森林 测试集:", rf.score(X_test, y_test)) # 随机森林 测试集:0.95 print("OOB 分数:", rf.oob_score_) # OOB 分数:0.947 # 特征重要性 print("Top 5 重要特征:") for i in np.argsort(rf.feature_importances_)[::-1][:5]: print(f" 特征 {i:2d}: {rf.feature_importances_[i]:.3f}") # 交叉验证 scores = cross_val_score(rf, X, y, cv=5, scoring="roc_auc") print(f"5 折 ROC-AUC:{scores.mean():.3f} ± {scores.std():.3f}") # 5 折 ROC-AUC:0.987 ± 0.006

决策树可视化

from sklearn.tree import plot_tree import matplotlib.pyplot as plt plt.figure(figsize=(20,10)) plot_tree(tree, feature_names=iris.feature_names, class_names=iris.target_names, filled=True) plt.savefig("tree.png", dpi=150, bbox_inches="tight") # 导出为文本,看规则 from sklearn.tree import export_text print(export_text(tree, feature_names=iris.feature_names)) # |--- petal length (cm) <= 2.45 # | |--- class: setosa # |--- petal length (cm) > 2.45 # | |--- petal width (cm) <= 1.75 # | | |--- class: versicolor

决策树预剪枝参数

参数作用
max_depth最大深度,最常用。从 3~10 开始试。
min_samples_split节点至少多少样本才继续分裂,默认 2。
min_samples_leaf叶子至少多少样本,常用 5~20。
max_leaf_nodes最多叶子数。
max_features每次分裂考虑多少特征(随机森林用 sqrt)。

Stacking:用模型预测当新特征

from sklearn.ensemble import StackingClassifier from sklearn.linear_model import LogisticRegression from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier estimators = [ ("rf", RandomForestClassifier(n_estimators=100)), ("svm", SVC(probability=True)), ("lr", LogisticRegression()), ] stack = StackingClassifier( estimators=estimators, final_estimator=LogisticRegression(), cv=5 ) stack.fit(X_train, y_train) # 基模型预测当新特征,元模型(LR)再学怎么组合

本章面试题

面试 · 决策树 / 随机森林

Q1. 决策树为什么容易过拟合?

查看答案

不限制深度的话,每片叶子只放一个样本,把训练数据完全背下来。对策:max_depth、min_samples_leaf、max_leaf_nodes。

Q2. 随机森林为什么比单棵树强?

查看答案

两层随机性(样本 + 特征)让树之间不相关,投票后方差大幅降低。"三个臭皮匠"。

Q3. 信息熵和基尼系数怎么选?

查看答案

效果差不多,基尼算得快(sklearn 默认);熵稍微更"纯"一点。

Q4. Bagging 和 Boosting 区别?

查看答案

Bagging 并行、降方差;Boosting 串行、降偏差。前者适合强学习器(深树),后者适合弱学习器(浅树)。