递归分割的数学表述
回归树将特征空间划分为 \(J\) 个不重叠的区域 \(R_1, R_2, \ldots, R_J\):
\[
\hat{f}(x) = \sum_{j=1}^{J} \hat{c}_j \cdot \mathbf{1}(x \in R_j)
\]
其中 \(\hat{c}_j = \text{ave}(y_i \mid x_i \in R_j)\) 是区域 \(R_j\) 内响应变量的均值。
分割准则:在每一步选择变量 \(X_j\) 和分割点 \(s\),使得以下RSS最小:
\[
\sum_{i: x_i \in R_1(j,s)} (y_i - \hat{c}_1)^2 + \sum_{i: x_i \in R_2(j,s)} (y_i - \hat{c}_2)^2
\]
- \(R_1(j,s) = \{X \mid X_j < s\}\):左子节点
- \(R_2(j,s) = \{X \mid X_j \ge s\}\):右子节点
递归二元分割:贪婪的自上而下搜索
由于穷举所有分区在计算上不可行,我们采用递归二元分割:
- 自上而下:从所有数据开始,逐步分割
- 贪婪:每步选择当前最优分割,不向前看
对于特征 \(X_j\) 和切割点 \(s\),定义两个半平面:
\[
R_1(j, s) = \{X | X_j < s\}, \quad R_2(j, s) = \{X | X_j \geq s\}
\]
寻找使下式最小化的 \(j\) 和 \(s\):
\[
\sum_{i: x_i \in R_1} (y_i - \hat{y}_{R_1})^2 + \sum_{i: x_i \in R_2} (y_i - \hat{y}_{R_2})^2
\]
类比:就像玩”20个问题”游戏,每次问最能缩小答案范围的问题。
案例:回归树预测上市公司ROE
Code
from sklearn.tree import DecisionTreeRegressor, plot_tree # 回归树模型与可视化
features_matrix = sampled_data[['Margin', 'Turnover']] # 取两个杜邦因子作为特征
target_roe = sampled_data['ROE'] # 回归目标:净资产收益率
# 拟合深度为2的回归树(便于可视化和解释)
regression_tree = DecisionTreeRegressor(max_depth=2, random_state=42) # 初始化浅层回归树
regression_tree.fit(features_matrix, target_roe) # 训练模型
plt.figure(figsize=(12, 7)) # 设置画布大小
plot_tree(regression_tree, feature_names=['净利率', '周转率'], # 绘制树结构
filled=True, rounded=True, fontsize=10, impurity=False, precision=3) # 填色圆角、隐藏不纯度
plt.title('回归树: 基于杜邦分析预测ROE', fontsize=14) # 图表标题
plt.show() # 显示图表
树剪枝:防止过拟合的关键
完全生长的树容易过拟合训练数据。解决策略:成本复杂度剪枝(最弱链接剪枝)。
对每个调优参数 \(\alpha \geq 0\),寻找子树 \(T \subset T_0\) 使下式最小化:
\[
\sum_{m=1}^{|T|} \sum_{i: x_i \in R_m} (y_i - \hat{y}_{R_m})^2 + \alpha |T|
\]
- \(|T|\):终端节点数量(树的复杂度)
- \(\alpha = 0\):\(T = T_0\)(完全树,仅衡量训练误差)
- \(\alpha\) 增大:惩罚复杂树,倾向更简单的子树
| \(\alpha = 0\) |
不剪枝,等于完全树 |
| \(\alpha\) 适中 |
偏差-方差最佳平衡 |
| \(\alpha\) 很大 |
仅保留根节点 |
代价复杂度剪枝的数学原理
代价复杂度剪枝(Cost Complexity Pruning)的目标函数:
\[
C_\alpha(T) = \sum_{m=1}^{|T|} \sum_{i: x_i \in R_m} (y_i - \hat{c}_m)^2 + \alpha |T|
\]
- \(|T|\):树的叶节点数(复杂度度量)
- \(\alpha \ge 0\):调节参数(类似Lasso中的 \(\lambda\))
\(\alpha\) 的直觉理解:
| \(\alpha = 0\) |
不惩罚复杂度 → 全树 |
完全不正则化 |
| \(\alpha\) 小 |
轻微剪枝 |
弱正则化 |
| \(\alpha\) 大 |
重度剪枝 → 树桩 |
强正则化 |
| \(\alpha \to \infty\) |
只剩根节点 |
截距模型 |
选择最优 \(\alpha\):通过K折交叉验证选择使测试误差最小的 \(\alpha\)
分类树与不纯度度量
分类树用于预测定性响应,关键区别在于分裂准则:
| 分类误差率 |
\(E = 1 - \max_k(\hat{p}_{mk})\) |
直观但不够敏感 |
| 基尼指数 |
\(G = \sum_{k=1}^{K} \hat{p}_{mk}(1-\hat{p}_{mk})\) |
节点纯度度量 |
| 熵 |
\(D = -\sum_{k=1}^{K} \hat{p}_{mk}\log\hat{p}_{mk}\) |
信息不确定性 |
其中 \(\hat{p}_{mk}\) 是第 \(m\) 节点中属于第 \(k\) 类的样本比例。
关键洞见:基尼指数是交叉熵的一阶近似(通过Taylor展开 \(\log p \approx p - 1\)),但计算更高效,因此是sklearn的默认选择。
三种不纯度度量的数学定义
设节点 \(m\) 中第 \(k\) 类的比例为 \(\hat{p}_{mk}\),三种不纯度度量:
分类错误率(Classification Error):
\[
E = 1 - \max_k(\hat{p}_{mk})
\]
基尼指数(Gini Index):
\[
G = \sum_{k=1}^{K} \hat{p}_{mk}(1-\hat{p}_{mk})
\]
交叉熵(Cross-Entropy):
\[
D = -\sum_{k=1}^{K} \hat{p}_{mk} \log(\hat{p}_{mk})
\]
为什么基尼指数和交叉熵优于分类错误率?
- 分类错误率对节点纯度不够敏感
- 基尼指数和交叉熵对纯度变化的梯度更大
- 用分类错误率可能错过改善纯度的有效分割
案例:分类树识别高盈利公司
Code
from sklearn.tree import DecisionTreeClassifier, plot_tree # 分类树模型与可视化
# 构造二分类目标:ROE>15%为高盈利
sampled_data_cls = sampled_data.copy() # 复制数据避免修改原始样本
sampled_data_cls['High_ROE'] = (sampled_data_cls['ROE'] > 0.15).astype(int) # 高盈利标记
sampled_data_cls = sampled_data_cls[sampled_data_cls['Leverage'] < 10] # 剔除杠杆极端值
cls_features = sampled_data_cls[['Margin', 'Turnover', 'Leverage']] # 杜邦三因子特征
cls_target = sampled_data_cls['High_ROE'] # 二分类目标
cls_tree = DecisionTreeClassifier(max_depth=3, criterion='gini', random_state=42) # Gini准则分类树
cls_tree.fit(cls_features, cls_target) # 训练分类树
plt.figure(figsize=(14, 9)) # 设置画布大小
plot_tree(cls_tree, feature_names=['净利率', '周转率', '杠杆'], # 绘制分类树
class_names=['普通', '高盈利'], filled=True, rounded=True, fontsize=10) # 类别标签与样式
plt.title('分类树: 识别高盈利公司 (ROE > 15%)', fontsize=14) # 图表标题
plt.show() # 显示图表
树与线性模型的对比
线性模型假设:\(f(X) = \beta_0 + \sum_{j=1}^{p} X_j \beta_j\)
树模型假设:\(f(X) = \sum_{m=1}^{M} c_m \cdot \mathbf{1}(X \in R_m)\)
| 线性关系 |
线性回归 |
能利用线性结构 |
| 非线性交互 |
决策树 |
自然捕捉交互作用 |
| 混合变量类型 |
决策树 |
无需虚拟变量编码 |
| 高维稀疏 |
线性回归 |
树在\(p \gg n\)时易过拟合 |
| 需要外推 |
线性回归 |
树无法外推 |
| 可解释性优先 |
决策树 |
规则直观明了 |
从单棵树到森林:集成学习的核心思想
单棵决策树的局限:高方差、不稳定——数据微小变化可能导致完全不同的树。
集成方法的核心:组合多棵树,取长补短。
集成学习的统计学基础
为什么集成能降低方差?
设 \(Z_1, \ldots, Z_n\) 为独立同分布随机变量,方差为 \(\sigma^2\),则:
\[
\text{Var}\left(\frac{1}{n}\sum_{i=1}^n Z_i\right) = \frac{\sigma^2}{n}
\]
方差随 \(n\) 增大线性下降! 这就是Bagging的理论基础。
但如果变量不独立,相关系数为 \(\rho\):
\[
\text{Var}\left(\frac{1}{n}\sum_{i=1}^n Z_i\right) = \rho\sigma^2 + \frac{1-\rho}{n}\sigma^2
\]
- 第一项 \(\rho\sigma^2\) 无法通过增加 \(n\) 消除
- 随机森林的核心创新:通过随机选特征降低 \(\rho\)!
Bagging:通过自助法平均降低方差
Bootstrap聚合的核心思想:
- 独立观测均值的方差:\(\text{Var}(\bar{Z}) = \sigma^2 / n\)
- 生成 \(B\) 个自助训练集 → 训练 \(B\) 棵深树 → 平均预测
\[
\hat{f}_{\text{bag}}(x) = \frac{1}{B} \sum_{b=1}^{B} \hat{f}^{*b}(x)
\]
关键特性:
- 每棵树不剪枝(高方差、低偏差)
- 平均后降低方差,偏差基本不变
- 袋外误差(OOB):每棵树约 1/3 样本未被使用,可直接估计测试误差
OOB误差:免费的交叉验证
Bagging的一个独特优势:袋外(Out-of-Bag)误差估计
原理:每棵自助法树大约只用到 \(\frac{2}{3}\) 的训练数据
\[
P(\text{样本}\ i\ \text{被选中}) = 1 - \left(1 - \frac{1}{n}\right)^n \approx 1 - e^{-1} \approx 0.632
\]
- 剩余约 \(\frac{1}{3}\) 的样本对这棵树来说是”测试数据”
- 对每个样本,只用没有包含它的那些树来预测
- 汇总这些预测就得到OOB误差
OOB误差 ≈ LOOCV误差,但计算成本低得多!
随机森林:通过去相关进一步降低方差
随机森林在Bagging基础上的关键改进:
每次分裂时,只从 \(m\) 个随机选取的特征中选择最佳分裂点(通常 \(m \approx \sqrt{p}\))。
为什么? 如果存在一个强预测变量,Bagging的所有树都会在顶部使用它:
- 树与树高度相关 → 平均效果有限
- 随机森林强制忽略部分特征 → 去相关 → 平均方差更低
\[
\text{Var}(\bar{f}) = \rho\sigma^2 + \frac{1-\rho}{B}\sigma^2
\]
当 \(B \to \infty\) 时,方差趋于 \(\rho\sigma^2\)。相关性 \(\rho\) 越小,集成效果越好。
随机森林的超参数选择
随机森林的关键超参数及选择指南:
| 树的数量 |
\(B\) |
500 |
越多越好,直到OOB误差稳定 |
| 每次候选特征数 |
\(m\) |
\(\sqrt{p}\)(分类)/ \(p/3\)(回归) |
用OOB误差网格搜索 |
| 最大深度 |
max_depth |
None(不限) |
回归可限制;分类通常不限 |
| 叶节点最小样本数 |
min_samples_leaf |
1 |
增大可防过拟合 |
核心参数 \(m\) 的直觉:
- \(m = p\):退化为Bagging(树高度相关)
- \(m = 1\):完全随机选特征(树不相关但每棵树很弱)
- \(m = \sqrt{p}\):平衡点——足够随机以去相关,又保留足够信息以维持单树质量
BART:贝叶斯加性回归树
BART结合了Bagging的随机性和Boosting的残差修正思想:
- 维护 \(K\) 棵回归树,迭代 \(B\) 次
- 每次迭代更新一棵树:计算部分残差 → 对上一轮树进行随机扰动
- 扰动方式:添加/修剪分支、改变叶节点预测值
\[
\hat{f}(x) = \frac{1}{B - L} \sum_{b=L+1}^{B} \hat{f}^b(x)
\]
其中 \(L\) 为预烧期(burn-in),丢弃早期不稳定的迭代结果。
独特优势:自然提供不确定性估计(贝叶斯后验分布)。