08 基于树的方法

本章概览

基于树的方法通过将预测变量空间分割成若干简单区域来进行预测:

  • 决策树:简单直观,但预测精度有限
  • Bagging:通过自助法平均降低方差
  • 随机森林:在Bagging基础上引入特征子采样去相关
  • Boosting:顺序拟合残差,逐步改进模型
  • BART:贝叶斯框架下的加性回归树

核心思想:单棵树易于解释但不够准确;集成多棵树可显著提升预测性能

数据准备:A股上市公司财务数据

import numpy as np  # 数值计算基础库
import pandas as pd  # 表格数据处理库
import matplotlib.pyplot as plt  # 数据可视化库
import os  # 跨平台路径处理

plt.rcParams['font.sans-serif'] = ['Source Han Serif SC']  # 配置中文字体
plt.rcParams['axes.unicode_minus'] = False  # 正确显示负号

# 根据操作系统选择本地数据根目录
DATA_DIR = 'C:/qiufei/data' if os.name == 'nt' else '/home/ubuntu/r2_data_mount/qiufei/data'
financial_path = os.path.join(DATA_DIR, 'stock/financial_statement.h5')  # 财务报表文件路径
financial_data = pd.read_hdf(financial_path)  # 读取上市公司财务报表数据
# 基于杜邦分析框架构造核心财务比率
financial_data['Margin'] = financial_data['net_profit'] / financial_data['operating_revenue']  # 销售净利率
financial_data['Turnover'] = financial_data['operating_revenue'] / financial_data['total_assets']  # 资产周转率
financial_data['ROE'] = financial_data['net_profit'] / financial_data['equity_parent_company']  # 净资产收益率
financial_data['Leverage'] = financial_data['total_assets'] / financial_data['equity_parent_company']  # 权益乘数
financial_data['Log_Assets'] = np.log(financial_data['total_assets'] + 1)  # 对数化总资产(规模效应)

# 清洗:替换inf为NaN,按分析列去除缺失值
analysis_cols = ['Margin', 'Turnover', 'ROE', 'Leverage', 'Log_Assets']  # 分析所需列
financial_data[analysis_cols] = financial_data[analysis_cols].replace([np.inf, -np.inf], np.nan)  # inf替换为NaN
financial_data = financial_data.dropna(subset=analysis_cols)  # 按分析列去除缺失值
# 过滤极端值以保证模型稳定性
financial_data = financial_data[
    (financial_data['Margin'].between(-0.5, 0.5)) &  # 净利率合理范围
    (financial_data['Turnover'].between(0, 5)) &  # 周转率合理范围
    (financial_data['ROE'].between(-0.5, 0.5)) &  # ROE合理范围
    (financial_data['Leverage'].between(1, 10))  # 杠杆倍数合理范围
]

# 安全采样1000个样本用于后续演示
sampled_data = financial_data.sample(n=min(1000, len(financial_data)), random_state=42)  # 随机抽样
print(f'样本量: {len(sampled_data)}, 特征: Margin, Turnover, ROE, Leverage, Log_Assets')  # 输出数据概况
样本量: 1000, 特征: Margin, Turnover, ROE, Leverage, Log_Assets

决策树的基础

回归树的核心思想

回归树通过将预测变量空间分割成若干不重叠区域进行预测:

  1. \(X_1, X_2, \ldots, X_p\) 的空间分成 \(J\) 个区域 \(R_1, R_2, \ldots, R_J\)
  2. 对落入区域 \(R_j\) 的观测,使用该区域内训练样本的 响应均值 作为预测值

目标:找到使 残差平方和 (RSS) 最小的分割:

\[ \sum_{j=1}^{J} \sum_{i \in R_j} (y_i - \hat{y}_{R_j})^2 \]

其中 \(\hat{y}_{R_j}\) 是区域 \(R_j\) 中训练观测的响应均值。

递归二元分割:贪婪的自上而下搜索

由于穷举所有分区在计算上不可行,我们采用递归二元分割

  • 自上而下:从所有数据开始,逐步分割
  • 贪婪:每步选择当前最优分割,不向前看

对于特征 \(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()  # 显示图表
Figure 1: 回归树: 基于杜邦分析预测上市公司ROE(max_depth=2)

树剪枝:防止过拟合的关键

完全生长的树容易过拟合训练数据。解决策略:成本复杂度剪枝(最弱链接剪枝)。

对每个调优参数 \(\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\) 很大 仅保留根节点

分类树与不纯度度量

分类树用于预测定性响应,关键区别在于分裂准则:

准则 公式 特点
分类误差率 \(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的默认选择。

案例:分类树识别高盈利公司

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()  # 显示图表
Figure 2: 分类树: 识别高盈利公司 (ROE>15%),使用杜邦三因子

树与线性模型的对比

线性模型假设:\(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\)时易过拟合
需要外推 线性回归 树无法外推
可解释性优先 决策树 规则直观明了

集成方法

从单棵树到森林:集成学习的核心思想

单棵决策树的局限:高方差、不稳定——数据微小变化可能导致完全不同的树。

集成方法的核心:组合多棵树,取长补短

集成学习方法对比 展示Bagging(并行平均)、Random Forest(去相关平均)和Boosting(串行残差)三种集成策略的核心差异 Bagging Bootstrap样本1→树1 Bootstrap样本2→树2 Bootstrap样本B→树B ↓ 平均/投票 ↓ 最终预测 并行 · 降方差 随机森林 Bootstrap+特征子集→树1 Bootstrap+特征子集→树2 Bootstrap+特征子集→树B ↓ 去相关平均 ↓ 最终预测 并行 · 去相关 · 降方差 Boosting 原始数据→浅树1 ↓ 更新残差 残差→浅树2 ↓ 更新残差 残差→浅树B ↓ λ加权求和 ↓ 最终预测 串行 · 降偏差+降方差

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 样本未被使用,可直接估计测试误差

随机森林:通过去相关进一步降低方差

随机森林在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\) 越小,集成效果越好

Boosting:串行残差学习

Boosting与Bagging的核心区别:树是顺序生长的,每棵树修正前面树的残差

算法(回归树Boosting)

  1. 初始化 \(\hat{f}(x) = 0\),残差 \(r_i = y_i\)
  2. \(b = 1, 2, \ldots, B\)
    • 拟合浅树 \(\hat{f}^b\) 到残差 \((X, r)\)(仅 \(d\) 个分裂)
    • 更新:\(\hat{f}(x) \leftarrow \hat{f}(x) + \lambda \hat{f}^b(x)\)
    • 更新残差:\(r_i \leftarrow r_i - \lambda \hat{f}^b(x_i)\)
  3. 输出:\(\hat{f}(x) = \sum_{b=1}^{B} \lambda \hat{f}^b(x)\)

三个调优参数:树的数量 \(B\)、学习率 \(\lambda\)、交互深度 \(d\)

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),丢弃早期不稳定的迭代结果。

独特优势:自然提供不确定性估计(贝叶斯后验分布)。

案例比较

案例1:四种树方法预测公司ROE

from sklearn.tree import DecisionTreeRegressor  # 回归树
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor  # 随机森林与梯度提升
from sklearn.model_selection import train_test_split  # 数据集划分
from sklearn.metrics import mean_squared_error  # MSE评估

# 使用更大样本量的数据进行集成学习
modeling_data = financial_data.sample(n=min(2000, len(financial_data)), random_state=42)  # 抽取2000样本
X_all = modeling_data[['Margin', 'Turnover', 'Leverage', 'Log_Assets']]  # 四维特征矩阵
y_all = modeling_data['ROE']  # ROE目标

# 70%/30%划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_all, y_all, test_size=0.3, random_state=42)
# 训练四种模型
single_tree = DecisionTreeRegressor(random_state=42)  # 单棵回归树(无限制深度)
single_tree.fit(X_train, y_train)  # 拟合

bagging = RandomForestRegressor(n_estimators=100, max_features=None, random_state=42)  # Bagging(全特征)
bagging.fit(X_train, y_train)  # 拟合

rf = RandomForestRegressor(n_estimators=100, max_features='sqrt', random_state=42)  # 随机森林(特征子采样)
rf.fit(X_train, y_train)  # 拟合

boosting = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, max_depth=3, random_state=42)  # 梯度提升
boosting.fit(X_train, y_train)  # 拟合

集成方法显著优于单棵树

Code
# 计算四种方法的测试集MSE
mse_values = [  # MSE列表
    mean_squared_error(y_test, single_tree.predict(X_test)),  # 单棵树MSE
    mean_squared_error(y_test, bagging.predict(X_test)),  # Bagging MSE
    mean_squared_error(y_test, rf.predict(X_test)),  # 随机森林MSE
    mean_squared_error(y_test, boosting.predict(X_test))  # Boosting MSE
]
methods = ['单棵树', 'Bagging', '随机森林', 'Boosting']  # 方法标签

plt.figure(figsize=(10, 6))  # 设置画布
bars = plt.bar(methods, mse_values, color=['#E3120B', '#2C3E50', '#008080', '#F0A700'])  # 柱状图
for bar, val in zip(bars, mse_values):  # 添加数值标签
    plt.text(bar.get_x() + bar.get_width()/2., bar.get_height(), f'{val:.4f}',
             ha='center', va='bottom', fontsize=11)  # 柱顶标注MSE值
plt.title('ROE预测: 不同树方法的测试集MSE比较', fontsize=14)  # 图表标题
plt.ylabel('均方误差 (MSE)')  # Y轴标签
plt.grid(axis='y', alpha=0.3)  # Y轴网格线
plt.tight_layout()  # 自动布局
plt.show()  # 显示图表
Figure 3: ROE预测:四种树方法的测试集MSE对比。集成方法大幅优于单棵决策树。

案例2:随机森林与XGBoost预测A股涨跌

from sklearn.ensemble import RandomForestClassifier  # 随机森林分类器
from sklearn.metrics import accuracy_score, roc_auc_score  # 准确率与AUC评估
import warnings  # 警告控制
warnings.filterwarnings('ignore')  # 关闭冗余警告

# 加载海康威视前复权股价数据
stock_path = os.path.join(DATA_DIR, 'stock/stock_price_pre_adjusted.h5')  # 股价文件路径
stock_data = pd.read_hdf(stock_path)  # 读取前复权股价数据

if 'order_book_id' in stock_data.index.names:  # 若在索引中则重置
    stock_data = stock_data.reset_index()

stock_data = stock_data[stock_data['order_book_id'] == '002415.XSHE'].copy()  # 筛选海康威视
stock_data = stock_data.sort_values('date')  # 按日期排序
# 构建金融技术特征
stock_data['Ret'] = stock_data['close'].pct_change()  # 日收益率
stock_data['Target'] = (stock_data['Ret'].shift(-1) > 0).astype(int)  # 明天是否上涨

for i in range(1, 4):  # 构造滞后收益率特征
    stock_data[f'Lag_{i}'] = stock_data['Ret'].shift(i)

stock_data['Vol_5'] = stock_data['Ret'].rolling(5).std()  # 5日波动率
stock_data['Vol_Change'] = stock_data['volume'].pct_change()  # 成交量变化率

stock_data = stock_data.replace([np.inf, -np.inf], np.nan).dropna()  # 清洗inf和NaN
feat_cols = [f'Lag_{i}' for i in range(1, 4)] + ['Vol_5', 'Vol_Change']  # 特征列

# 时间前向切分(70%训练/30%测试)严格保持时间顺序
split = int(len(stock_data) * 0.7)  # 70%分界点
X_tr, X_te = stock_data[feat_cols].values[:split], stock_data[feat_cols].values[split:]  # 特征切分
y_tr, y_te = stock_data['Target'].values[:split], stock_data['Target'].values[split:]  # 目标切分

金融时间序列的低信噪比挑战

Code
from sklearn.ensemble import GradientBoostingClassifier  # 梯度提升分类器

# 训练随机森林
rf_cls = RandomForestClassifier(n_estimators=300, max_depth=5, max_features='sqrt', random_state=42)
rf_cls.fit(X_tr, y_tr)  # 拟合

# 训练梯度提升(强正则化应对低信噪比)
gb_cls = GradientBoostingClassifier(n_estimators=300, max_depth=3, learning_rate=0.01, random_state=42)
gb_cls.fit(X_tr, y_tr)  # 拟合

models_dict = {'Random Forest': rf_cls, 'Gradient Boosting': gb_cls}  # 模型字典
plt.figure(figsize=(14, 5))  # 设置画布
for idx, (name, mdl) in enumerate(models_dict.items()):  # 遍历模型
    proba = mdl.predict_proba(X_te)[:, 1]  # 正类预测概率
    auc = roc_auc_score(y_te, proba)  # 计算AUC
    plt.subplot(1, 2, idx+1)  # 子图
    imp = mdl.feature_importances_  # 特征重要性
    order = np.argsort(imp)  # 升序索引
    plt.barh(range(len(order)), imp[order], color='#3498DB')  # 水平条形图
    plt.yticks(range(len(order)), [feat_cols[i] for i in order])  # 特征名称
    plt.xlabel('特征重要性')  # X轴标签
    plt.title(f'{name} (AUC={auc:.3f})')  # 子图标题
plt.tight_layout()  # 自动布局
plt.show()  # 显示图表
Figure 4: 随机森林与梯度提升在A股涨跌预测中的特征重要性对比

实验部分

Lab:拟合分类树

from sklearn.tree import DecisionTreeClassifier, export_text  # 分类树与规则导出
from sklearn.metrics import classification_report, confusion_matrix  # 分类评估

# 准备数据:预测公司是否盈利
lab_data = financial_data.sample(n=min(2000, len(financial_data)), random_state=42)  # 抽取子样本
lab_data['Profitable'] = (lab_data['ROE'] > 0).astype(int)  # 二分类目标:是否盈利

lab_features = lab_data[['Margin', 'Turnover', 'Leverage', 'Log_Assets']]  # 四维特征
lab_target = lab_data['Profitable']  # 目标变量

# 70%/30%随机划分
lab_X_tr, lab_X_te, lab_y_tr, lab_y_te = train_test_split(
    lab_features, lab_target, test_size=0.3, random_state=42)
# 拟合分类树(Gini准则,最大深度3层)
lab_cls_tree = DecisionTreeClassifier(criterion='gini', max_depth=3, random_state=42)
lab_cls_tree.fit(lab_X_tr, lab_y_tr)  # 训练分类树

lab_pred = lab_cls_tree.predict(lab_X_te)  # 测试集预测
print('分类报告:')  # 输出标题
print(classification_report(lab_y_te, lab_pred))  # 输出精确率、召回率、F1
print(f'混淆矩阵:\n{confusion_matrix(lab_y_te, lab_pred)}')  # 输出混淆矩阵
分类报告:
              precision    recall  f1-score   support

           0       1.00      0.97      0.98        88
           1       0.99      1.00      1.00       512

    accuracy                           0.99       600
   macro avg       1.00      0.98      0.99       600
weighted avg       1.00      0.99      0.99       600

混淆矩阵:
[[ 85   3]
 [  0 512]]
# 导出树规则为文本(便于硬编码进生产系统)
tree_rules = export_text(lab_cls_tree, feature_names=list(lab_features.columns))  # 导出if-else规则
print('树规则:')  # 输出标题
print(tree_rules)  # 打印规则文本
树规则:
|--- Margin <= -0.00
|   |--- class: 0
|--- Margin >  -0.00
|   |--- class: 1

Lab:拟合回归树

from sklearn.metrics import r2_score  # R²评估

# 回归目标:预测具体ROE数值
lab_reg_target = lab_data['ROE']  # 连续目标变量
lab_reg_X_tr, lab_reg_X_te, lab_reg_y_tr, lab_reg_y_te = train_test_split(
    lab_features, lab_reg_target, test_size=0.3, random_state=42)  # 数据划分

# 拟合回归树(最大深度4层)
lab_reg_tree = DecisionTreeRegressor(max_depth=4, random_state=42)  # 初始化
lab_reg_tree.fit(lab_reg_X_tr, lab_reg_y_tr)  # 训练

reg_pred = lab_reg_tree.predict(lab_reg_X_te)  # 测试集预测
print(f'回归树 测试集MSE: {mean_squared_error(lab_reg_y_te, reg_pred):.4f}')  # 输出MSE
print(f'回归树 测试集R²: {r2_score(lab_reg_y_te, reg_pred):.4f}')  # 输出R²
回归树 测试集MSE: 0.0025
回归树 测试集R²: 0.6222

Lab:随机森林显著提升R²

# 随机森林(100棵树)
lab_rf = RandomForestRegressor(n_estimators=100, random_state=42)  # 初始化随机森林
lab_rf.fit(lab_reg_X_tr, lab_reg_y_tr)  # 训练

rf_pred = lab_rf.predict(lab_reg_X_te)  # 测试集预测
print(f'随机森林 测试集MSE: {mean_squared_error(lab_reg_y_te, rf_pred):.4f}')  # MSE
print(f'随机森林 测试集R²: {r2_score(lab_reg_y_te, rf_pred):.4f}')  # R²

# 特征重要性排名
importance_df = pd.DataFrame(  # 构建重要性DataFrame
    {'importance': lab_rf.feature_importances_},  # 特征重要性值
    index=lab_features.columns  # 特征名称作为索引
).sort_values('importance', ascending=False)  # 按重要性降序排列

print('\n特征重要性 (Random Forest):')  # 输出标题
print(importance_df)  # 打印排名表
随机森林 测试集MSE: 0.0006
随机森林 测试集R²: 0.9103

特征重要性 (Random Forest):
            importance
Margin        0.529034
Turnover      0.325472
Leverage      0.122666
Log_Assets    0.022828

Lab:Boosting回归

# 梯度提升树(100棵浅树,学习率0.1)
lab_gb = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, max_depth=3, random_state=42)
lab_gb.fit(lab_reg_X_tr, lab_reg_y_tr)  # 训练

gb_pred = lab_gb.predict(lab_reg_X_te)  # 测试集预测
print(f'Boosting 测试集MSE: {mean_squared_error(lab_reg_y_te, gb_pred):.4f}')  # MSE
print(f'Boosting 测试集R²: {r2_score(lab_reg_y_te, gb_pred):.4f}')  # R²

# 三种方法横向对比
print('\n--- 三种方法对比 ---')  # 输出标题
print(f'单棵树  MSE={mean_squared_error(lab_reg_y_te, reg_pred):.4f}, R²={r2_score(lab_reg_y_te, reg_pred):.4f}')
print(f'随机森林 MSE={mean_squared_error(lab_reg_y_te, rf_pred):.4f}, R²={r2_score(lab_reg_y_te, rf_pred):.4f}')
print(f'Boosting MSE={mean_squared_error(lab_reg_y_te, gb_pred):.4f}, R²={r2_score(lab_reg_y_te, gb_pred):.4f}')
Boosting 测试集MSE: 0.0006
Boosting 测试集R²: 0.9132

--- 三种方法对比 ---
单棵树  MSE=0.0025, R²=0.6222
随机森林 MSE=0.0006, R²=0.9103
Boosting MSE=0.0006, R²=0.9132

本章小结

基于树的方法总结

基于树的方法总结对比 对比单棵树、Bagging、随机森林、Boosting和BART五种方法的核心特征 方法 核心策略 优势 劣势 单棵树 递归二元分割+剪枝 易解释、可视化 高方差、不稳定 Bagging Bootstrap+平均 降方差、OOB误差 树间相关性高 随机森林 Bootstrap+特征子采样 去相关、更稳健 解释性下降 Boosting 串行残差+学习率 灵活、精度高 B过大可能过拟合 BART 贝叶斯扰动+平均 不确定性估计 计算开销大

实践建议

何时使用基于树的方法?

  • 非线性交互关系:如杜邦分析中的乘法交互
  • 混合变量类型:定量+定性变量(无需虚拟变量编码)
  • 可解释性优先:单棵树提供完美的”决策流程图”
  • 预测精度优先:使用集成方法(随机森林或Boosting)

关键注意事项

要点 说明
树无法外推 预测值不超出训练数据范围
金融数据信噪比低 树集成在股票预测中AUC通常仅略高于0.5
特征重要性可能误导 相关特征间重要性会被分摊
超参数调优关键 树深、学习率、特征子集大小需交叉验证