第9章:决策树与集成学习 | Chapter 9: Decision Trees & Ensemble Learning
阶段定位 | Stage: 第二阶段 — 神经网络与进阶算法 预计学时 | Duration: 3~4 小时
---
学习目标 | Learning Objectives
中文:
- 理解决策树的分裂标准:信息增益与 Gini 不纯度
- 理解决策树过拟合的倾向及剪枝策略
- 掌握 Bagging(随机森林)和 Boosting(XGBoost)的核心区别
- 理解为什么随机森林能减少方差,XGBoost 能减少偏差
- 能在表格数据上正确选择决策树或神经网络
English:
- Understand decision tree splitting criteria: information gain and Gini impurity
- Understand decision tree overfitting tendency and pruning strategies
- Master the core differences between Bagging (Random Forest) and Boosting (XGBoost)
- Understand why Random Forest reduces variance and XGBoost reduces bias
- Correctly choose between decision trees and neural networks for tabular data
---
9.1 决策树 | Decision Trees
中文解释
核心思想
通过一系列如果-那么规则把数据分到不同区域:
如果 年龄 < 30 且 收入 > 5000:
→ 批准贷款
否则:
→ 拒绝贷款分裂标准
| 标准 | 公式 | 特点 |
|---|---|---|
| 信息增益 | IG = H(parent) - Σ(p_i * H(child_i)) | 基于熵,ID3/C4.5 使用 |
| Gini 不纯度 | Gini = 1 - Σ(p_i²) | 计算更快,CART 使用 |
Gini 不纯度直觉
- Gini = 0:节点内所有样本属于同一类(最纯)
- Gini = 0.5:节点内两类各占 50%(最混乱)
决策树的优点
- 可解释性极强(能画出树结构)
- 无需特征缩放
- 能处理类别特征和数值特征
- 训练速度快
决策树的缺点
- 极易过拟合:不加限制时,树可以生长到每个叶节点只有一个样本
- 对数据波动敏感:换几个样本,树结构可能完全不同
- 无法捕捉特征间的复杂交互(除非很深)
English Explanation
Splitting Criteria:
| Criterion | Formula | Used By |
|---|---|---|
| Information Gain | H(parent) - Σp_i·H(child) | ID3, C4.5 |
| Gini Impurity | 1 - Σp_i² | CART |
Pros: interpretable, no scaling needed, fast training Cons: prone to overfitting, sensitive to data changes
---
9.2 随机森林 | Random Forest
中文解释
核心思想:Bagging + 随机特征
| 技术 | 含义 | 作用 |
|---|---|---|
| Bagging | Bootstrap Aggregating:从训练集有放回抽样,训练多棵树 | 减少方差 |
| 随机特征 | 每棵树每次分裂时,只考虑随机子集的特征 | 增加树之间的多样性 |
为什么能降低方差?
单棵决策树是高方差模型(对训练数据敏感)。多棵树取平均:
Var(average of n trees) = Var(single tree) / n (假设树之间独立)虽然树之间不是完全独立的(共享部分数据),但随机特征子集让它们足够不同,平均后仍显著降低方差。
关键超参数
| 参数 | 含义 | 调参建议 |
|---|---|---|
n_estimators | 树的数量 | 越多越好(边际收益递减),通常 100~500 |
max_depth | 最大深度 | 限制过拟合,默认 None(不限制) |
max_features | 每分裂考虑的特征数 | "sqrt"(分类)或 "log2" |
min_samples_leaf | 叶节点最少样本数 | 增大可减少过拟合 |
English Explanation
Why Random Forest reduces variance:
Var(average of n trees) ≈ Var(single tree) / nRandom feature subsets ensure trees are diverse enough for this averaging to work.
---
9.3 XGBoost | Extreme Gradient Boosting
中文解释
核心思想:Boosting
与 Bagging 的"并行训练多棵树然后投票"不同,Boosting 是串行训练:
- 训练第一棵树,得到预测
F₁(x) - 计算残差(真实值 - 预测):
r₁ = y - F₁(x) - 训练第二棵树去拟合残差:
F₂(x) ≈ r₁ - 更新预测:
F(x) = F₁(x) + η·F₂(x)(η 是学习率) - 重复直到 N 棵树
为什么能降低偏差?
每一棵新树都在纠正前面所有树的错误。随着树越来越多,模型能力越来越强,偏差越来越低。
XGBoost 的改进
| 改进点 | 效果 |
|---|---|
| 二阶泰勒展开 | 用梯度和 Hessian 同时优化,收敛更快 |
| 正则化项 | Ω = γT + ½λΣw²,控制树的复杂度 |
| 列抽样 | 类似随机森林,防止过拟合 |
| 缺失值处理 | 自动学习缺失值的最优分裂方向 |
| 并行化 | 在特征层面并行,训练速度快 |
XGBoost vs 随机森林
| 特性 | 随机森林 | XGBoost |
|---|---|---|
| 策略 | Bagging(并行) | Boosting(串行) |
| 目标 | 降低方差 | 降低偏差 |
| 训练速度 | 快(完全并行) | 较快(特征并行) |
| 调参难度 | 简单 | 较复杂 |
| 表格数据效果 | 很好 | 通常更好 |
| 大数据 | 好 | 极好(支持分布式) |
English Explanation
Boosting vs Bagging:
- Bagging: train trees in parallel, vote/average → reduces variance
- Boosting: train trees sequentially, each corrects previous errors → reduces bias
XGBoost improvements: second-order Taylor expansion, regularization, column sampling, missing value handling, feature-level parallelism.
---
9.4 完整实现:决策树与集成学习
代码案例
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.ensemble import RandomForestClassifier
import matplotlib.pyplot as plt
np.random.seed(42)
# ========== 1. 生成数据 ==========
X, y = make_classification(n_samples=1000, n_features=4, n_redundant=0,
n_informative=4, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
feature_names = [f'F{i}' for i in range(4)]
# ========== 2. 单棵决策树 ==========
tree = DecisionTreeClassifier(max_depth=5, random_state=42)
tree.fit(X_train, y_train)
print(f"单棵决策树 (max_depth=5):")
print(f" 训练准确率: {tree.score(X_train, y_train):.4f}")
print(f" 测试准确率: {tree.score(X_test, y_test):.4f}")
# 不加限制时的过拟合
tree_unrestricted = DecisionTreeClassifier(random_state=42)
tree_unrestricted.fit(X_train, y_train)
print(f"\n不加限制的决策树:")
print(f" 训练准确率: {tree_unrestricted.score(X_train, y_train):.4f}") # 可能 1.0
print(f" 测试准确率: {tree_unrestricted.score(X_test, y_test):.4f}") # 可能下降
# ========== 3. 随机森林 ==========
rf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
rf.fit(X_train, y_train)
print(f"\n随机森林 (100 trees, max_depth=5):")
print(f" 训练准确率: {rf.score(X_train, y_train):.4f}")
print(f" 测试准确率: {rf.score(X_test, y_test):.4f}")
# 特征重要性
print(f"\n特征重要性:")
for name, imp in zip(feature_names, rf.feature_importances_):
print(f" {name}: {imp:.3f}")
# ========== 4. 可视化单棵树 ==========
fig, ax = plt.subplots(figsize=(16, 8))
plot_tree(tree, max_depth=3, fontsize=8, ax=ax, filled=True,
feature_names=feature_names, class_names=['Class 0', 'Class 1'])
plt.title('Decision Tree Structure (depth=3 shown)')
plt.tight_layout()
plt.savefig('ch09_decision_tree.png')
print("\n树结构可视化已保存")
# ========== 5. 树数量与准确率的关系 ==========
n_trees_range = [1, 5, 10, 25, 50, 100, 200]
train_scores = []
test_scores = []
for n in n_trees_range:
rf_n = RandomForestClassifier(n_estimators=n, max_depth=5, random_state=42)
rf_n.fit(X_train, y_train)
train_scores.append(rf_n.score(X_train, y_train))
test_scores.append(rf_n.score(X_test, y_test))
plt.figure(figsize=(8, 5))
plt.plot(n_trees_range, train_scores, 'o-', label='Train Accuracy')
plt.plot(n_trees_range, test_scores, 's-', label='Test Accuracy')
plt.xlabel('Number of Trees')
plt.ylabel('Accuracy')
plt.title('Random Forest: Accuracy vs Number of Trees')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('ch09_rf_n_estimators.png')
print("随机森林树数量对比已保存")典型输出:
单棵决策树 (max_depth=5):
训练准确率: 0.9238
测试准确率: 0.8900
不加限制的决策树:
训练准确率: 1.0000
测试准确率: 0.8550
随机森林 (100 trees, max_depth=5):
训练准确率: 0.9287
测试准确率: 0.9100
特征重要性:
F0: 0.412
F1: 0.234
F2: 0.198
F3: 0.156不加限制的决策树训练准确率 100%(过拟合),测试准确率反而下降。随机森林通过平均降低了方差,测试准确率更高。
---
9.5 决策树 vs 神经网络:如何选择 | Trees vs Neural Nets
中文解释
表格数据(结构化数据)
| 场景 | 推荐 | 原因 |
|---|---|---|
| 特征 < 100,样本 < 10K | XGBoost / LightGBM | 效果好、训练快、无需调参 |
| 特征中有大量类别变量 | XGBoost | 对类别特征处理更好 |
| 需要可解释性 | 决策树 / 随机森林 | 能输出规则、特征重要性 |
| 表格数据 + 海量样本 | XGBoost / TabNet | XGBoost 支持分布式 |
非结构化数据
| 场景 | 推荐 | 原因 |
|---|---|---|
| 图像 | CNN(第16章起) | 空间结构需要卷积 |
| 文本 / 序列 | RNN / Transformer(第20章起) | 时序/语义依赖 |
| 音频 | CNN + RNN | 频谱特征 + 时序 |
混合数据
很多实际场景是混合的:用户画像(表格)+ 行为序列(文本)+ 头像(图像)。
- 分别用适合的模型提取特征
- 最后拼接用全连接层或 XGBoost 做最终预测
English Explanation
Tabular Data: XGBoost / LightGBM usually win Unstructured Data: Neural networks (CNN for images, Transformer for text) Mixed Data: Combine specialized models, then ensemble
---
本章总结 | Chapter Summary
中文:
- 决策树通过 if-else 规则划分数据,Gini 不纯度衡量节点纯度
- 单棵决策树极易过拟合,需要限制深度或剪枝
- 随机森林 = Bagging + 随机特征,通过平均多棵树降低方差
- XGBoost = Boosting + 正则化,通过串行纠错降低偏差
- 表格数据上,XGBoost/LightGBM 通常优于神经网络
- 非结构化数据上,神经网络是必选
- 混合场景:各取所长,最后融合
English:
- Decision trees split data via if-else rules; Gini measures node purity
- Single trees overfit easily; need depth limits or pruning
- Random Forest = Bagging + random features, reduces variance by averaging
- XGBoost = Boosting + regularization, reduces bias by sequential correction
- For tabular data, XGBoost/LightGBM usually outperform neural nets
- For unstructured data, neural networks are essential
- Mixed scenarios: combine specialized models
---
课后练习 | Homework
- Gini 计算:一个节点有 100 个样本,其中 70 个类别 A,30 个类别 B。计算 Gini 不纯度。如果分裂后左子节点 40A+10B,右子节点 30A+20B,计算信息增益。
- 过拟合控制:在同一数据上,分别训练 max_depth=[1, 3, 5, 10, None] 的决策树。画出训练/测试准确率随深度的变化曲线。
- Bagging vs Boosting:用相同数据分别训练随机森林和 XGBoost(或 GradientBoosting)。对比训练准确率、测试准确率、训练时间。
- 特征重要性验证:在随机森林中,把最重要的特征删除后重新训练,观察测试准确率下降幅度。验证特征重要性的可靠性。
- 实际选型:假设你要做一个"预测用户是否会购买"的任务,数据包含:用户年龄、收入、历史购买金额、最近 30 天浏览商品列表(文本)、用户头像(图像)。设计一个模型方案,说明每部分数据用什么模型处理,最后如何融合。