引言
决策树是最直观的机器学习模型——「如果年龄 < 30 且月收入 > 1 万,就批准贷款」。它是一连串 if-else 规则的组合,也是随机森林、梯度提升等一切树集成的地基。本文讲清三件事:树怎么选分裂点(信息增益/基尼)、怎么防止过拟合(预剪枝/后剪枝)、树模型的边界与实战。
前置:/ml-supervised-classification/(分类基础)、/ml-model-evaluation/(交叉验证)。本文是树集成(随机森林/XGBoost/LightGBM)的基模型,后续可衔接 /ml-ensemble-learning/。
目录
- 1. 决策树是什么:规则即模型
- 2. 分裂准则:信息增益与基尼系数
- 3. 树的构建:递归二分 CART
- 4. 连续特征与缺失值处理
- 5. 过拟合问题:为什么树容易学过头
- 6. 预剪枝:在建树时刹车
- 7. 后剪枝:CCP 代价复杂度剪枝
- 8. 决策边界与可视化
- 9. 回归树与树模型选型
- 10. 速查表与一句话记忆
- 延伸阅读
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 关键参数
| 参数 | 含义 |
|---|---|
| criterion | gini / 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 决策树文档
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。