决策树与树模型基础:分裂准则、剪枝与可视化

决策树与树模型实战:信息增益/基尼系数分裂准则、CART 树的构建过程、预剪枝与后剪枝、决策边界与可视化、回归树、树模型的优缺点与使用场景、sklearn 端到端实战。

引言

决策树是最直观的机器学习模型——「如果年龄 < 30 且月收入 > 1 万,就批准贷款」。它是一连串 if-else 规则的组合,也是随机森林、梯度提升等一切树集成的地基。本文讲清三件事:树怎么选分裂点(信息增益/基尼)、怎么防止过拟合(预剪枝/后剪枝)、树模型的边界与实战。

前置:/ml-supervised-classification/(分类基础)、/ml-model-evaluation/(交叉验证)。本文是树集成(随机森林/XGBoost/LightGBM)的基模型,后续可衔接 /ml-ensemble-learning/。


目录


1. 决策树是什么:规则即模型

1.1 树 = 分层 if-else

年龄 < 30?
├── 是 → 月收入 > 1万?
│       ├── 是 → 批准贷款
│       └── 否 → 拒绝贷款
└── 否 → 批准贷款

1.2 三个组成部分

部件含义
根节点第一个分裂特征
内部节点一个特征 + 阈值判断
叶节点最终类别/取值

记忆:决策树就是分层 if-else;从根到叶的每条路径都是一条规则,叶节点给出答案。


2. 分裂准则:信息增益与基尼系数

2.1 信息增益(熵视角)

熵衡量混乱程度,分裂后熵下降得越多,说明分类越清晰:

熵(S) = -Σ p_i·log₂(p_i)
信息增益 = 熵(父) - 加权平均熵(子)

2.2 基尼系数(CART 默认)

基尼衡量「随机抽两个样本类别不一致」的概率,越小越纯:

基尼(S) = 1 - Σ p_i²

2.3 一个具体例子

样本 10 个:7 正 3 负
基尼(父) = 1 - (0.7² + 0.3²) = 0.42

按特征 A 分裂 → 子1 [6正0负] 子2 [1正3负]
基尼(子) = (6/10)*0 + (4/10)*[1-(0.25²+0.75²)] = 0.4*0.375 = 0.15
基尼增益 = 0.42 - 0.15 = 0.27  ← 很大,值得分裂

记忆:树选特征 = 选「让分裂后更纯」的那个;分类常用基尼(CART 默认),信息增益等价直觉。


3. 树的构建:递归二分 CART

3.1 CART 是二叉递归

from sklearn.tree import DecisionTreeClassifier

clf = DecisionTreeClassifier(random_state=42)
clf.fit(X_train, y_train)

3.2 构建伪代码

function build(node, data):
  对每个特征、每个候选阈值计算分裂增益
  选增益最大的分裂点
  若达到停止条件:设为叶节点
  否则:左右子节点递归 build

3.3 sklearn 关键参数

参数含义
criteriongini / entropy
max_depth最大深度
min_samples_split分裂最少样本数
min_samples_leaf叶最少样本数

记忆:CART 每次把数据二分,选增益最大的特征+阈值递归分裂;sklearn 默认基尼,深度/叶样本数负责刹车。


4. 连续特征与缺失值处理

4.1 连续特征的候选阈值

对连续特征排序,取相邻值中点作为候选阈值,选增益最大者。

# sklearn 自动处理:对每个连续特征尝试所有切分点
clf = DecisionTreeClassifier(max_depth=4)

4.2 缺失值策略

策略说明
丢弃样本少时不可取
均值/众数填充简单常用
树内建缺失处理XGBoost 自动学缺失值走向,sklearn 无内置

记忆:连续特征取相邻值中点试切分;缺失值建议先填充,想省心用 XGBoost 自动处理缺失。


5. 过拟合问题:为什么树容易学过头

5.1 树是「无限规则库」

深度无限的树能把每个训练样本单独分一类 → 训练集 100% 正确,测试集崩盘。这是高方差模型。

不剪枝的深树:训练 1.0,测试 0.72
适度剪枝的树:训练 0.88,测试 0.85

5.2 防止过拟合的两道闸

  • 预剪枝:建树时就限制(深度、叶样本数)
  • 后剪枝:树建好后剪掉不重要的分支

记忆:深树=高方差,必过拟合;用预剪枝限制深度/叶样本,或用后剪枝删弱分支。


6. 预剪枝:在建树时刹车

6.1 参数刹车

clf = DecisionTreeClassifier(
    max_depth=5,            # 最深 5 层
    min_samples_split=10,   # 少于 10 样本不再分裂
    min_samples_leaf=4,     # 叶至少 4 样本
    max_features='sqrt',    # 每分裂随机取部分特征
    random_state=42)

6.2 用学习曲线找合适深度

import matplotlib.pyplot as plt
train_auc, val_auc = [], []
for d in range(1, 15):
    m = DecisionTreeClassifier(max_depth=d, random_state=42)
    m.fit(X_train, y_train)
    train_auc.append(roc_auc_score(y_train, m.predict_proba(X_train)[:,1]))
    val_auc.append(roc_auc_score(y_val, m.predict_proba(X_val)[:,1]))

plt.plot(range(1,15), train_auc, 'o-', label='训练AUC')
plt.plot(range(1,15), val_auc, 'o-', label='验证AUC')
# 选验证分最高的深度(通常 3-8)

记忆:预剪枝 = 建树前设 max_depth/min_samples_leaf 等闸门;用「训练分 vs 验证分」曲线选合适的深度。


7. 后剪枝:CCP 代价复杂度剪枝

7.1 CCP 原理

给每个子树一个代价:R(T) = 误差 + α·叶节点数。α 越大越倾向剪枝。sklearn 用 ccp_alpha 控制。

7.2 sklearn 用法

# 先建满树,再根据 alpha 剪
tree_full = DecisionTreeClassifier(random_state=42)
tree_full.fit(X_train, y_train)

# 查看候选 alpha 对应的树
path = tree_full.cost_complexity_pruning_path(X_train, y_train)
ccp_alphas = path.ccp_alphas

# 对每个 alpha 剪枝,用交叉验证选最佳
best_alpha, best_score = None, 0
for alpha in ccp_alphas:
    t = DecisionTreeClassifier(random_state=42, ccp_alpha=alpha)
    t.fit(X_train, y_train)
    score = cross_val_score(t, X_train, y_train, cv=5, scoring='roc_auc').mean()
    if score > best_score:
        best_score, best_alpha = score, alpha

final = DecisionTreeClassifier(ccp_alpha=best_alpha, random_state=42)
final.fit(X_train, y_train)

记忆:后剪枝(CCP)在建满树后按代价复杂度剪弱分支;用 ccp_pruning_path 遍历 alpha,交叉验证选最优。


8. 决策边界与可视化

8.1 树的结构可视化

from sklearn.tree import plot_tree

plt.figure(figsize=(20, 10))
plot_tree(clf, filled=True, feature_names=list(X.columns),
          class_names=['0', '1'], rounded=True)
plt.show()

8.2 决策边界可视化(二维示例)

import numpy as np
xx, yy = np.meshgrid(np.linspace(x0_min, x0_max, 100),
                     np.linspace(x1_min, x1_max, 100))
Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.4)   # 边界即树的阶梯状切分

记忆:plot_tree 看结构、contourf 看边界——决策树边界是一族轴对齐的矩形切分。


9. 回归树与树模型选型

9.1 回归树

叶节点取均值,分裂目标是最小化 MSE:

from sklearn.tree import DecisionTreeRegressor

reg = DecisionTreeRegressor(max_depth=4, min_samples_leaf=5, random_state=42)
reg.fit(X_train_reg, y_train_reg)

9.2 单棵树 vs 树集成的选择

场景建议
需要解释每条规则单棵浅树
追求精度随机森林/XGBoost
特征多且有缺失XGBoost/LightGBM
快速原型单棵浅树 + 后接集成

记忆:回归树叶取均值、目标最小化 MSE;要解释用单棵浅树,要精度上树集成。


10. 速查表与一句话记忆

主题关键点
分裂准则基尼(CART 默认)/ 信息增益
建树递归二分,增益最大者分裂
连续特征相邻值中点试切
过拟合深树高方差,必须剪枝
预剪枝max_depth / min_samples_leaf
后剪枝ccp_alpha 代价复杂度
可视化plot_tree / contourf
回归树叶均值 + 最小化 MSE
升级随机森林 / XGBoost

一句话记忆:决策树 = 分层 if-else,每次选「分裂后最纯」的特征+阈值递归二分(基尼/信息增益);深树必过拟合,用预剪枝(限深度/叶样本)或后剪枝(ccp_alpha)刹住;边界是一族轴对齐矩形;树是集成的基模型,要精度就交给随机森林/XGBoost/LightGBM。


延伸阅读

  • /ml-ensemble-learning/ — 基于树的集成学习
  • /ml-model-evaluation/ — 交叉验证与偏差方差
  • /ml-supervised-classification/ — 分类指标与评估
  • /ml-feature-engineering/ — 特征对树模型的影响
  • [[ai-ml]] — 算法原理深度专题
  • scikit-learn 决策树文档

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 集成学习实战:Bagging、随机森林、梯度提升与 Stacking
  2. 迁移学习实战:预训练模型、特征提取与微调全流程
  3. 计算机视觉入门实战:图像处理与 CNN 图像分类