1. 问题导入:人类是如何做决策的?
前置知识指引:在学习决策树算法之前,建议先完成:
- (理解概率分布与期望值);
- (对比线性/非线性决策边界形态)。
假设你是一家商业银行的风险控制专家。当一位新客户提交贷款申请时,你会怎么决定是否批准?
你可能会在脑海中快速做出一连串判断问答:
- 信用记录好不好? → 如果是“不良信用”,直接“拒绝贷款”;如果是“良好信用”,继续问:
- 年收入高不高? → 如果“年收入大于 15 万”,直接“批准贷款”;如果“年收入偏低”,决定“拒绝贷款”。
这一连串形如 “如果……那么……” (If-Then) 的树状结构,就是决策树算法 (Decision Tree) 的直觉!
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
与 Logistic 回归或 SVM 不同,决策树不需要计算复杂的向量点积,它通过建立一棵清晰的“判断树”,不仅预测准确,而且具备极强的可解释性!
2. 学习目标
完成本章学习后,你将能够:
- 掌握决策树构建流程:理解自顶向下的递归划分 (Recursive Partitioning) 贪心策略;
- 熟练手算不纯度指标:掌握信息熵 (Entropy)、信息增益 (ID3)、增益率 (C4.5) 与基尼指数 (CART) 的计算公式;
- 透彻理解真实手算案例:通过具体的 8 样本贷款数据集,完成根节点特征选择的分步手算推导;
- 掌握过拟合控制与剪枝:对比预剪枝 (Pre-pruning) 与后剪枝 (Post-pruning) 的调参策略;
- 熟练进行 Python 代码实战:使用
sklearn.tree.DecisionTreeClassifier 训练模型、提取特征重要性与可视化决策树。
3. 互动实验室:决策树正交特征切分体验
请通过下方交互实验室调节树的最大深度(Max Depth),直观观察决策树如何在二维特征空间中通过轴对齐切分线分割数据,并降低节点不纯度:
拖动深度滑块,观察决策树如何通过轴对齐直线分割平面并降低不纯度
青色点 = 批准贷款 ($y=1$), 橙色点 = 拒绝贷款 ($y=0$)
决策树不纯度 (Impurity) 动态度量
Gini Index当前树的划分层级拓扑:
[层级 1] 收入 x1 ≥ 3.0?增益: +0.357
左叶(收入<3.0): 5点 [不纯度 0.000]右分支(收入≥3.0): 7点 [不纯度 0.245]
[层级 2] 年龄 x2 ≥ 2.0?进一步划分
上节点: 2点 [不纯度 0.500]下叶(纯洁): 5点 [不纯度 0.000]
随着 Depth 增加,决策树不断在平面上画出轴对齐的分界线。Depth=3 时全局不纯度降至 0;增加到 Depth=4 则会引发**过拟合**。
4. 不纯度度量与切分准则
决策树的核心问题是:在当前节点,应该优先选择哪一个特征进行切分?
理想的切分应当使划分后的子节点尽可能“纯”(即同一子节点内的样本尽量属于同一个类别)。
信息熵 (Entropy) 用于度量数据集的混乱程度/不确定性:
H(D)=−k=1∑Kpklog2(pk)
其中 pk 代表类别 k 在数据集 D 中的占比。
- 纯度极高:当样本全为同一类(p1=1)时,H(D)=0;
- 混乱极高:当正负样本各占一半(p1=0.5,p0=0.5)时,H(D)=1.0。
使用特征 a 切分数据集 D 产生的信息增益为切分前后熵的差值:
Gain(D,a)=H(D)−v=1∑V∣D∣∣Dv∣H(Dv)
ID3 算法选择信息增益最大的特征进行切分。但 ID3 偏向于选择取值较多的特征(例如“用户身份证号”,切分后每个子节点只有 1 个人,纯度极大但毫无泛化价值)。
4.3 增益率 (Gain Ratio - C4.5 算法)
为了纠正 ID3 偏向多值特征的缺陷,C4.5 算法引入了增益率 (Gain Ratio),利用固有值 IV(a) 对多值特征进行惩罚:
Gain_ratio(D,a)=IV(a)Gain(D,a),其中 IV(a)=−v=1∑V∣D∣∣Dv∣log2(∣D∣∣Dv∣)
4.4 基尼指数 (Gini Impurity - CART 算法)
CART (Classification and Regression Trees) 算法使用基尼指数 (Gini Impurity):
Gini(D)=1−k=1∑Kpk2
对于二分类问题,若正类占比为 p,则 Gini(D)=2p(1−p)。基尼指数无需计算平方对数,计算速度极快,是 Scikit-Learn 决策树的默认指标!
|
| ID3 | 信息增益 (Info Gain) | 多叉树 | 经典起步算法,容易偏向多值特征 |
| C4.5 | 增益率 (Gain Ratio) | 多叉树 | 修正 ID3,支持连续特征与缺失值处理 |
| CART | 基尼指数 / MSE | 二叉树 | 工业界标准,计算高效,天然支持分类与回归 |
5. 经典实例推导:10 样本 2 层决策树递归切分分步手算与可视化
1. 计算机没有人类视觉:计算机无法像人类一样用肉眼看'哪些点聚在一起',它必须依靠数值指标(信息熵 H 或基尼指数 Gini)来客观度量集合的混乱程度。
2. 递归深入切分:如果第 1 层切分后某个子节点内部依然不纯(如包含 3 批准 3 拒绝),决策树就会在该子节点处继续递归挑选剩余特征(如年龄)进行二次切分,直到所有叶节点纯净或达到停止条件!
3. 贪心竞标胜出:决策树在每一个节点处遍历候选特征,挑选能带来**最大的不纯度降幅(基尼增益 Delta Gini 或 信息增益 Gain)**的特征作为当前分支节点。
为了让你彻底看懂决策树是如何通过数学指标一步步从根节点递归生长的,请点击下方交互动画实验室,结合 5 步数据卡片与 2 层树图同步观察:
从根节点选“信用记录”到右子树选“年龄”的 2 层深入递归建树全流程
步骤 1 / 5
为什么要进行多层递归切分? 如果第 1 层切分后某个子节点内部依然不纯(如包含 3 批准 3 拒绝),决策树就会在该子节点处继续递归挑选剩余特征(如年龄)进行二次切分,直到所有叶节点纯净!
测量 10 位客户样本的初始混合混乱程度
1. 统计各类占比:p1=0.3,p0=0.7 2. 初始基尼指数:Gini(D)=1−(0.32+0.72)=0.4200 3. 初始信息熵:H(D)=−(0.3log20.3+0.7log20.7)=0.8813 10 位客户混杂在一起(3 批准,7 拒绝)。初始基尼指数 0.4200 表明混乱度较高,算法将遍历候选特征尝试切分。
基尼降幅ΔGini
5.1 10 样本数据集
|
| 1 | 高 | 不良 | 青年 | 拒绝 (0) |
| 2 | 高 | 不良 | 青年 | 拒绝 (0) |
| 3 | 低 | 不良 | 中年 | 拒绝 (0) |
| 4 | 低 | 不良 | 中年 | 拒绝 (0) |
| 5 | 高 | 良好 | 青年 | 拒绝 (0) |
| 6 | 高 | 良好 | 中年 | 批准 (1) |
| 7 | 中 | 良好 | 青年 | 拒绝 (0) |
| 8 | 中 | 良好 | 中年 | 批准 (1) |
| 9 | 低 | 良好 | 青年 | 拒绝 (0) |
| 10 | 低 | 良好 | 中年 | 批准 (1) |
- 总样本量 ∣D∣=10(正例批准 3 人,负例拒绝 7 人)
- 根节点初始正负占比:p批准=103=0.3,p拒绝=107=0.7
- 根节点初始基尼指数:
Gini(D)=1−(p批准2+p拒绝2)=1−(0.32+0.72)=1−(0.09+0.49)=0.4200
- 根节点初始信息熵:
H(D)=−(0.3log20.3+0.7log20.7)=0.8813 Bit
5.2 步骤 1:第 1 层根节点选拔 (三大候选特征全量计算 PK)
决策树遍历数据集的所有候选特征,计算如果以该特征作为第一刀切分,能获得多少基尼降幅(ΔGini):
候选 1:按“信用记录 (x2)”切分
- 信用记录 = 不良(4 人:客户 1, 2, 3, 4 → 0 批准 4 拒绝):
Gini不良=1−(02+1.02)=0.0000(纯净,生成叶节点 A)
- 信用记录 = 良好(6 人:客户 5, 6, 7, 8, 9, 10 → 3 批准 3 拒绝):
Gini良好=1−(0.52+0.52)=0.5000(不纯,仍需二次切分)
- 切分后加权基尼指数:
Ginisplit(D,信用)=104×Gini不良+106×Gini良好=104(0)+106(0.5000)=0.3000
- 基尼降幅:
ΔGini(D,信用)=Gini(D)−Ginisplit=0.4200−0.3000=0.1200
候选 2:按“年龄 (x3)”切分
- 年龄 = 青年(4 人:客户 1, 2, 5, 7 → 0 批准 4 拒绝):
Gini青年=1−(02+1.02)=0.0000
- 年龄 = 中年(6 人:客户 3, 4, 6, 8, 9, 10 → 3 批准 3 拒绝):
Gini中年=1−(0.52+0.52)=0.5000
- 切分后加权基尼指数与降幅:
Ginisplit(D,年龄)=104(0)+106(0.5000)=0.3000
ΔGini(D,年龄)=0.4200−0.3000=0.1200
候选 3:按“年收入 (x1)”切分
- 高收入(4 人:1 批准 3 拒绝):Gini高=1−(0.252+0.752)=0.3750
- 中收入(2 人:1 批准 1 拒绝):Gini中=1−(0.52+0.52)=0.5000
- 低收入(4 人:1 批准 3 拒绝):Gini低=1−(0.252+0.752)=0.3750
- 切分后加权基尼指数与降幅:
Ginisplit(D,收入)=104(0.375)+102(0.5)+104(0.375)=0.4000
ΔGini(D,收入)=0.4200−0.4000=0.0200
第 1 层特征 PK 结果汇总表
|
| 信用记录 (x2) | 0.3000 | 0.1200 | 胜出 (选为第 1 层根节点) |
| 年龄 (x3) | 0.3000 | 0.1200 | 并列 |
| 年收入 (x1) | 0.4000 | 0.0200 | 落败 (仅降低 0.02) |
决策树算法选择“信用记录 (x2)”作为第 1 层根节点!
5.3 步骤 2:第 2 层右节点递归竞标 (年收入 vs 年龄)
第 1 层切分后:
- 左节点(信用不良 4 人):Gini=0.0000,属于纯净叶节点 A,无需再切分;
- 右节点(信用良好 6 人:客户 5, 6, 7, 8, 9, 10):包含 3 批准 3 拒绝,初始 Gini=0.5000,依然混乱!必须在该节点处递归挑选剩余特征展开二次切分。
在右节点 6 位客户中比较剩余特征:
尝试候选 1:按“年收入 (x1)”二次切分
- 高收入(2 人)、中收入(2 人)、低收入(2 人),各组内部均为 1 批准 1 拒绝:
Gini高=0.5000,Gini中=0.5000,Gini低=0.5000
- 加权基尼指数:
Ginisplit=62(0.5)+62(0.5)+62(0.5)=0.5000
- 基尼降幅:
ΔGini=0.5000−0.5000=0.0000(无效切分,混乱度完全没有降低)
尝试候选 2:按“年龄 (x3)”二次切分
- 青年(3 人:客户 5, 7, 9 → 0 批准 3 拒绝):
Gini青年=1−(02+1.02)=0.0000(生成叶节点 B)
- 中年(3 人:客户 6, 8, 10 → 3 批准 0 拒绝):
Gini中年=1−(1.02+02)=0.0000(生成叶节点 C)
- 加权基尼指数:
Ginisplit=63(0.0000)+63(0.0000)=0.0000
- 基尼降幅:
ΔGini=0.5000−0.0000=0.5000(完美切分,混乱度一次性降为 0)
第 2 层竞标结论:特征“年龄 (x3)”以 ΔGini=0.5000≫0 压倒性胜出,选为第 2 层二级节点!
5.4 步骤 3:第 2 层切分与生成最终 2 层决策树形态
经过两轮递归切分后,数据集被 100% 划分归入 3 个纯净叶节点:
|
| 叶节点 A | 信用记录 = 不良 | 客户 1, 2, 3, 4 (4人) | 拒绝贷款 (y=0) | 0.0000 | 40% |
| 叶节点 B | 信用记录 = 良好 且 年龄 = 青年 | 客户 5, 7, 9 (3人) | 拒绝贷款 (y=0) | 0.0000 | 30% |
| 叶节点 C | 信用记录 = 良好 且 年龄 = 中年 | 客户 6, 8, 10 (3人) | 批准贷款 (y=1) | 0.0000 | 30% |
全树不纯度计算:
- 全树加权基尼指数:Gini全树=104(0)+103(0)+103(0)=0.0000
- 全树总基尼降幅:ΔGiniTotal=Gini初始(0.4200)−Gini全树(0.0000)=0.4200
全树所有叶节点不纯度降至 0.0000,递归建树完满成功!彻底解开了决策树如何多层生长、深层递归的数学全貌。
6. 树的生长与过拟合控制 (剪枝 Pruning)
如果允许决策树无限生长,它会为每一个训练样本单独建立一条分支,导致训练集准确率 100%,但在测试集上惨败(严重过拟合)。
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
6.1 预剪枝 (Pre-pruning)
在决策树构造过程中,提前设定停止条件:
max_depth:限制树的最大深度(如 max_depth=3);
min_samples_split:节点再划分所需的最少样本数;
min_samples_leaf:叶子节点必须包含的最少样本数。
6.2 后剪枝 (Post-pruning)
先让决策树完全生长,然后自底向上评估合并分支后在验证集上的泛化误差。若合并后性能不下降,则将子树剪掉。
7. Python 代码实战:决策树训练与特征重要性提取
下面的代码展示了如何使用 Scikit-Learn 的 DecisionTreeClassifier 进行分类、限制树深度、并提取特征重要性 (Feature Importances):
Scikit-Learn 决策树分类与树结构展示代码 import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.metrics import accuracy_score
# 1. 加载乳腺癌分类数据集 (30 个特征)
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 2. 训练单棵决策树 (使用预剪枝 max_depth=3)
dt_model = DecisionTreeClassifier(max_depth=3, criterion='gini', random_state=42)
dt_model.fit(X_train, y_train)
# 3. 评估训练集与测试集准确率
train_acc = dt_model.score(X_train, y_train)
test_acc = accuracy_score(y_test, dt_model.predict(X_test))
print(f"决策树 (max_depth=3) 训练集准确率: {train_acc * 100:.2f}%")
print(f"决策树 (max_depth=3) 测试集准确率: {test_acc * 100:.2f}%")
# 4. 打印文本形式的决策树判断分支规制
tree_rules = export_text(dt_model, feature_names=list(data.feature_names))
print("\n决策树分支规则文本预览:")
print(tree_rules[:400])
# 5. 输出 Top-3 最重要的特征
top_indices = np.argsort(dt_model.feature_importances_)[::-1][:3]
print("\n决策树 Top-3 特征重要性:")
for rank, idx in enumerate(top_indices, 1):
feat_name = data.feature_names[idx]
importance = dt_model.feature_importances_[idx]
print(f"Top {rank}: {feat_name} (重要性得分: {importance:.4f})")
8. 常见错误排查与踩坑指南 (Troubleshooting)
|
| 未限制树深度导致过拟合 | 训练集准确率 100%,测试集表现极差 | 未设置 max_depth,决策树长得过于茂密,死记硬背了训练集噪声 | 必设预剪枝!设置 max_depth \in [3, 8] 或 min_samples_leaf \in [5, 20] |
| 高基数类别特征偏置 | 决策树严重偏向某个无用类别(如订单号) | ID3/CART 在面对包含成千上万独立取值的字段时,容易算出虚高的信息增益 | 对高基数类别改用 Target Encoding 或 Frequency Encoding 预处理后再输入 |
| 不平衡数据集失真 | 少数类被严重忽视,召回率极低 | 默认基尼指数全局最小化,多数类主导了节点切分方向 | 设置 class_weight='balanced' 或在训练前使用过采样工具均衡类别 |
| 误对连续特征做无谓独热编码 | 特征空间维度爆炸,训练变慢 | 决策树天然支持连续特征的数值区间自动切分(如 x > 15) | 连续数值特征直接输入决策树即可,无需提前离散化或 One-Hot 编码 |
9. 检查清单 (Checklist)
10. 课后互动自测
课后互动自测与概念验证
共 4 道精选测试题 · 答题进度已自动保存
11. 下一步与相关章节
- 下一章推荐:学习 ,探索如何通过多棵决策树组建森林!
- 相关前置与扩展阅读:
- :对比最大间隔超平面与决策树正交切分的边界差异
- :复习二分类混淆矩阵与评估指标
12. 参考资料