SL Scikit-Learn Learn机器学习教程

Lesson 08

决策树:用 if-else 做预测

决策树是最直观可解释的模型。本章讲解树的构建过程(信息增益/基尼系数)、剪枝控制过拟合、以及 sklearn 的 DecisionTreeClassifier/Regressor 实战。

  • 预计时长45 分钟
  • 难度中等
  • 前置章节第 06 章

本章细分学习地图

模块拆细学习单元完成后应产出掌握标准
构建过程递归分裂、叶节点、预测路径。手画一棵简单决策树。能说清分裂的停止条件。
分裂标准熵、信息增益、基尼不纯度。手算一个分裂的信息增益。能区分熵和基尼的差异。
剪枝max_depth、min_samples_leaf、ccp_alpha。不同深度对比实验。能解释过拟合和剪枝的关系。
实战训练、预测、可视化、特征重要性。完整分类代码。能解读 feature_importances_。

学习目标

决策树构建流程

1选最佳分裂特征
2递归分裂子集
3达到停止条件
4叶节点做预测

本章完成度

已完成 0/4

决策树的构建过程

决策树通过贪心递归分裂构建:在每个节点选择一个特征和阈值,将数据分成两部分,使子节点的"不纯度"最小。

01
选择最佳分裂

遍历所有特征和所有阈值,找到让子节点不纯度下降最多的分裂方式。

02
递归分裂

对每个子节点重复步骤 1,直到满足停止条件。

03
叶节点预测

分类:叶中多数类;回归:叶中目标均值。

贪心 = 局部最优

决策树每一步只考虑当前节点的最优分裂,不回溯。这意味着它不一定能找到全局最优树,但速度很快。

分裂标准

Gini(t) = 1 − Σc pc² 基尼不纯度:pc 是节点 t 中类别 c 的比例
H(t) = − Σc pc · log₂(pc) 信息熵:不确定性的度量
标准特点sklearn 参数
Gini计算更快,sklearn 默认。偏好较大的纯子集。criterion="gini"
Entropy信息论基础。在某些数据上可能产生更平衡的树。criterion="entropy"
实践中差别不大

Gini 和 Entropy 在大多数数据集上产生几乎相同的树。默认 Gini 即可,除非有特殊理由。

剪枝:控制过拟合

不加限制的决策树会长到完美拟合训练集(每个叶只有一个样本),严重过拟合。常用剪枝参数:

参数作用建议
max_depth限制树的最大深度。从 3-10 开始,用 CV 选择。
min_samples_split节点分裂所需的最少样本数。默认 2,增大可减少过拟合。
min_samples_leaf叶节点最少样本数。增大可防止叶太小(噪声主导)。
ccp_alpha代价复杂度剪枝(后剪枝)。cost_complexity_pruning_path 找最佳值。
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score

X, y = load_iris(return_X_y=True)

for depth in [1, 2, 3, 5, None]:
    dt = DecisionTreeClassifier(max_depth=depth, random_state=42)
    score = cross_val_score(dt, X, y, cv=5).mean()
    print(f"max_depth={str(depth):>4s}  准确率={score:.3f}")

互动实验:树深度如何导致过拟合

下面用一份带少量噪声样本的二分类数据(●训练样本 / ▲测试样本)实时构建一棵决策树。拖动 max_depth 滑块,观察决策边界如何从简单矩形变成紧贴噪声点的锯齿形状,以及训练集准确率和测试集准确率如何分道而行。

训练准确率
0
测试准确率
0
叶子节点数
0
观察重点

max_depth 较小时边界简单、训练和测试准确率都不高(欠拟合)。随着深度增加,训练准确率会一路逼近 100%——树在努力"记住"包括噪声点在内的每一个训练样本;但测试准确率往往在某个深度后不再提升甚至下降,因为树学到的是训练集里的噪声而非真实规律。这正是决策树需要剪枝(限制 max_depth)的原因。

sklearn 实战

from sklearn.datasets import load_wine
from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.model_selection import train_test_split

X, y = load_wine(return_X_y=True)
feature_names = load_wine().feature_names

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(X_train, y_train)

print(f"训练准确率: {dt.score(X_train, y_train):.3f}")
print(f"测试准确率: {dt.score(X_test, y_test):.3f}")

# 文本可视化
print("\n树结构:")
print(export_text(dt, feature_names=list(feature_names), max_depth=3))

# 特征重要性
print("\n特征重要性:")
for name, imp in sorted(zip(feature_names, dt.feature_importances_),
                         key=lambda x: -x[1])[:5]:
    print(f"  {name}: {imp:.3f}")
决策树 vs 线性模型

决策树不需要特征缩放,能自动捕捉非线性关系和特征交互。但单棵树方差大,容易过拟合。下一章的集成方法(随机森林)通过组合多棵树解决这个问题。

练习题

第 1 题:一个节点有 10 个正样本和 10 个负样本,基尼不纯度是多少?

参考思路:Gini = 1 − (0.5² + 0.5²) = 1 − 0.5 = 0.5(最大不纯度)。

第 2 题:max_depth=1 的决策树叫什么?它有什么特点?

参考思路:叫决策桩(Decision Stump)。只做一次分裂,模型极简单(高偏差),常用作弱学习器组合(如 AdaBoost)。

检查点

离开本章前,请确认:

  • 能解释决策树的贪心递归分裂过程。
  • 能区分基尼不纯度和信息熵,知道 sklearn 默认用 Gini。
  • 能用 max_depth、min_samples_leaf 等参数控制过拟合。
  • 能用 export_text 或 plot_tree 可视化树结构,并解读 feature_importances_。