Chapter 08

决策树与集成学习

从一棵树的决策逻辑到一片森林的集体智慧

01

核心概念:树与森林

决策树(Decision Tree)是一类模仿人类决策流程的模型。它通过一系列"如果...那么..."的判断将数据分到不同的叶节点,每个叶节点对应一个预测结果。决策树的最大优势是可解释性——你可以清晰地画出模型的决策逻辑,这在金融、医疗等需要解释决策原因的领域至关重要。

信息增益与 ID3 算法

决策树的核心问题是:在每个节点,应该选择哪个特征、以什么阈值进行分裂?信息增益(Information Gain)衡量了用某特征分裂后,数据不确定性(熵)的减少量。

(Entropy)度量数据集的混乱程度:

H(D) = −Σc pc·log₂(pc)

信息增益为分裂前后熵的差值:

IG(D, A) = H(D) − Σv (|Dv|/|D|)·H(Dv)

ID3 算法每次选择信息增益最大的特征进行分裂,直到所有叶节点纯净或满足停止条件。

基尼指数与 CART 算法

CART(Classification and Regression Trees)使用基尼指数(Gini Index)作为分裂标准,计算更高效:

Gini(D) = 1 − Σc pc²

基尼指数衡量从数据集中随机抽取两个样本,其类别不一致的概率。Gini 越小,数据集越纯净。CART 生成的树是二叉树,每次将数据分为"是/否"两个分支。

核心洞察

信息增益倾向于选择取值较多的特征(如用户ID),因为多值特征更容易将数据切分为纯净的小块。为修正这一偏置,C4.5 算法采用信息增益率(Gain Ratio),用特征的固有值(Intrinsic Value)对信息增益做归一化。scikit-learn 的 DecisionTreeClassifier 默认使用 Gini,因为它计算更快且效果通常相近。

剪枝:防止树的过度生长

决策树如果不加限制,会生长到每个叶节点只包含一个样本,导致严重的过拟合剪枝(Pruning)是抑制过拟合的关键手段:

集成学习:三个臭皮匠顶个诸葛亮

单个决策树容易过拟合且不稳定(数据微小变化可能导致完全不同的树)。集成学习(Ensemble Learning)通过组合多个基学习器,获得比单一模型更强的泛化能力。

Bagging 与随机森林

Bootstrap Aggregating(Bagging)从原始数据中有放回地抽样生成多个子数据集,每个子集训练一个基学习器,最终通过投票(分类)或平均(回归)得出结果。随机森林(Random Forest)在 Bagging 基础上增加了特征随机性:每棵树的每个节点分裂时,只从随机选取的特征子集中选择最优分裂特征。这进一步降低了树之间的相关性,提升了集成效果。

Boosting:从弱学习器到强学习器

Boosting 的核心思想是串行训练:每棵新树重点关注前面树分错的样本,通过调整样本权重或拟合残差来逐步修正错误。

02

计算方法:增益、纯度与投票

信息增益计算示例

假设数据集 D 有 10 个样本,其中 6 个正例、4 个负例。用特征 A 分裂后,左分支 D₁ 有 4 正 1 负,右分支 D₂ 有 2 正 3 负。

  1. 原始熵:H(D) = −(0.6·log₂0.6 + 0.4·log₂0.4) ≈ 0.971。
  2. 左分支熵:H(D₁) = −(0.8·log₂0.8 + 0.2·log₂0.2) ≈ 0.722。
  3. 右分支熵:H(D₂) = −(0.4·log₂0.4 + 0.6·log₂0.6) ≈ 0.971。
  4. 信息增益:IG = 0.971 − (5/10×0.722 + 5/10×0.971) ≈ 0.125。

Gini 指数计算

对上述相同分裂:

  1. 原始 Gini:Gini(D) = 1 − (0.6² + 0.4²) = 0.48。
  2. 左分支 Gini:Gini(D₁) = 1 − (0.8² + 0.2²) = 0.32。
  3. 右分支 Gini:Gini(D₂) = 1 − (0.4² + 0.6²) = 0.48。
  4. 加权 Gini:0.5×0.32 + 0.5×0.48 = 0.40。Gini 下降量 = 0.48 − 0.40 = 0.08。

集成投票机制

对于分类任务,硬投票(Hard Voting)直接统计各基学习器的预测类别,少数服从多数。软投票(Soft Voting)则对各学习器预测的概率取平均,再选概率最高的类别。软投票通常更优,因为它考虑了各学习器的"置信度"。

软投票概率:P(y=c) = (1/M) Σm Pm(y=c)

对于梯度提升,最终预测是各棵树预测值的累加:F(x) = Σm η·fm(x),其中 η 是学习率(收缩率),防止单棵树的主导作用过强。

03

工程应用

信贷审批决策树

银行和消费金融公司广泛用决策树构建信贷审批规则。树的每个节点对应一个可解释的规则:"年收入 > 20万?""征信查询次数 < 6?""负债率 < 50%?"。审批人员可以直接读取树的决策路径,向客户解释拒贷原因,满足金融监管的可解释性要求(如 GDPR 的"解释权")。

电商用户流失预测

随机森林和梯度提升树是用户流失预测的主力模型。特征包括:最近购买间隔、月均消费金额、优惠券使用率、客服投诉次数等。集成方法不仅预测精度高,还能输出特征重要性,帮助运营团队定位用户流失的关键驱动因素,制定针对性召回策略。

供应链风险评分

制造企业用梯度提升模型评估供应商的违约风险。输入特征涵盖供应商的财务指标、交货准时率、历史质量缺陷率、所在地区政治风险指数等。模型输出的风险分数用于动态调整安全库存水平和备选供应商激活阈值。

医学决策支持

决策树在医学诊断中历史悠久(如 MYCIN 专家系统)。现代应用中,随机森林用于根据患者症状、检验指标和影像特征辅助疾病分型。相比黑盒神经网络,树模型的可解释性使医生能够理解并验证模型的推理路径,在关键决策中建立人机协作的信任。

04

Python 实践:树与集成

1. 决策树分类与可视化

Python
from sklearn.tree import DecisionTreeClassifier, export_text from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split iris = load_iris() X_train, X_test, y_train, y_test = train_test_split( iris.data, iris.target, test_size=0.2, random_state=42, stratify=iris.target ) # 限制深度防止过拟合(预剪枝) tree = DecisionTreeClassifier(criterion="gini", max_depth=3, random_state=42) tree.fit(X_train, y_train) print("决策树准确率:", tree.score(X_test, y_test)) # 文本形式展示树的规则 print(export_text(tree, feature_names=list(iris.feature_names)))

2. 随机森林

Python
from sklearn.ensemble import RandomForestClassifier rf = RandomForestClassifier( n_estimators=100, # 100棵树 max_depth=5, random_state=42, n_jobs=-1 ) rf.fit(X_train, y_train) print("随机森林准确率:", rf.score(X_test, y_test)) # 特征重要性 import pandas as pd imp = pd.Series(rf.feature_importances_, index=iris.feature_names) print(imp.sort_values(ascending=False))

3. 梯度提升树

Python
from sklearn.ensemble import GradientBoostingClassifier gb = GradientBoostingClassifier( n_estimators=100, learning_rate=0.1, max_depth=3, random_state=42 ) gb.fit(X_train, y_train) print("梯度提升准确率:", gb.score(X_test, y_test)) # 逐轮训练得分(用于观察是否过拟合) print("训练得分前5轮:", gb.train_score_[:5])

4. 单棵树 vs 森林:过拟合对比

Python
import numpy as np # 逐渐增加树的数量,观察训练/测试误差变化 estimators = [1, 5, 10, 50, 100, 200] train_scores, test_scores = [], [] for n in estimators: m = RandomForestClassifier(n_estimators=n, max_depth=5, random_state=42) m.fit(X_train, y_train) train_scores.append(m.score(X_train, y_train)) test_scores.append(m.score(X_test, y_test)) for n, tr, te in zip(estimators, train_scores, test_scores): print(f"n={n}: 训练={tr:.3f}, 测试={te:.3f}")
特征重要性排序:花瓣长度与宽度是鸢尾花分类的最关键特征
05

例题与解析

例题 1:信息增益计算

某数据集有 16 个样本,类别分布为 8 正 8 负。特征 "温度" 将数据分为两组:高温组 6 正 2 负,低温组 2 正 6 负。

问题

计算分裂前后的熵和信息增益,并判断该特征是否适合作为根节点分裂特征。

解析

H(原始) = −(0.5·log₂0.5 + 0.5·log₂0.5) = 1.0。
H(高温) = −(0.75·log₂0.75 + 0.25·log₂0.25) ≈ 0.811。
H(低温) = −(0.25·log₂0.25 + 0.75·log₂0.75) ≈ 0.811。
加权熵 = (8/16)×0.811 + (8/16)×0.811 = 0.811。
信息增益 IG = 1.0 − 0.811 = 0.189。

信息增益大于 0,说明该特征有效降低了不确定性。若其他特征的信息增益均低于 0.189,则"温度"是最佳根节点分裂特征。

结果:信息增益 ≈ 0.189,是有效的分裂特征
例题 2:Gini 指数与纯度

比较两个数据集:D₁ 类别比例为 90% / 10%;D₂ 为 50% / 50%。

问题

分别计算 D₁ 和 D₂ 的 Gini 指数,并解释为什么 Gini 可以作为纯度的度量。

解析

Gini(D₁) = 1 − (0.9² + 0.1²) = 1 − 0.82 = 0.18。
Gini(D₂) = 1 − (0.5² + 0.5²) = 1 − 0.50 = 0.50。

D₁ 的 Gini 更小,说明它更"纯净"——随机抽两个样本类别不一致的概率只有 18%。Gini 的极值性质:当某类占 100% 时 Gini=0(完全纯净);当各类均匀分布时 Gini 最大(最混乱)。

结果:Gini(D₁)=0.18, Gini(D₂)=0.50;Gini 越小越纯净
例题 3:决策树分类

某决策树对贷款申请人做如下判断:"年收入 > 30万?"是 → "征信评分 > 700?"是 → 批准;"年收入 > 30万?"否 → "是否有房产?"是 → 批准,否 → 拒绝。

问题

画出该决策树的结构,并说明预剪枝如何可能改变这棵树。

解析

树结构:根节点"年收入 > 30万",左分支(是)→ "征信评分 > 700",右分支(否)→ "是否有房产"。预剪枝可施加以下限制:(1) 最大深度为 2:则"征信评分"和"是否有房产"节点不再分裂,直接以多数类作为叶节点预测;(2) 叶节点最小样本数:若某分支的样本数 < 阈值,则停止分裂。预剪枝牺牲部分训练精度,换取更好的泛化能力。

例题 4:随机森林 vs 单棵树

在相同数据集上,单棵深度 unrestricted 的决策树训练准确率达 100%,测试准确率仅 72%;随机森林(100棵树,max_depth=5)训练准确率 95%,测试准确率 89%。

问题

解释为何单棵树训练准确率更高但测试表现更差?随机森林通过哪些机制提升了泛化能力?

解析

单棵树无限制生长时,记住了训练数据的每个噪声和异常点,导致严重的过拟合(训练100%,测试72%)。随机森林通过三重机制提升泛化:(1) Bootstrap 样本随机性:每棵树看到不同的数据子集,降低方差;(2) 特征随机性:每次分裂只考虑随机子集的特征,减少树之间的相关性;(3) 平均效应:多棵树的预测误差在平均中相互抵消。根据偏差-方差分解,集成保持了低偏差的同时显著降低了方差。

← 上一章:分类算法 下一章:聚类分析 →