05 重采样方法 (Resampling Methods)

核心问题:没有独立测试集时,如何评估模型?

在统计学习中,我们反复遇到同一个困境:

  • 模型评估:已训练好的模型,在新数据上表现如何?
  • 模型选择:多个候选模型中,哪个泛化能力最强?

重采样方法(Resampling Methods)的核心思想:

已有数据自身反复构造”伪新数据”,来估计模型在真正新数据上的表现。

本章介绍两大核心工具:交叉验证自助法

为什么训练误差不能用于模型评估?

训练误差总是会低估模型在新数据上的真实表现:

  • 过拟合的本质:模型不仅学习了数据中的真实信号,还记住了随机噪声
  • 训练误差随模型复杂度单调递减,但测试误差呈U形曲线
  • 用训练误差选模型 → 总是选最复杂的模型 → 泛化能力最差

A股市场的典型陷阱

  • 量化基金用100个技术指标拟合过去5年的沪深300 → 训练 \(R^2 = 0.95\)
  • 实盘运行3个月 → 实际 \(R^2 < 0.01\)
  • 原因:模型过拟合了历史市场结构,而市场是非平稳

测试误差的核心概念

测试误差(Test Error / Generalization Error):

\[ \large{ \text{Err} = E\left[L(Y, \hat{f}(X))\right] } \tag{1}\]

其中期望是对新的未见过的数据点 \((X, Y)\) 取的。

误差类型 定义 用途 问题
训练误差 在训练集上的平均损失 模型拟合 严重低估真实误差
测试误差 在新数据上的平均损失 模型评估 需要独立测试数据
CV估计 对测试误差的估计 模型选择 是一种近似

核心矛盾:我们需要测试误差,但没有真正的新数据 → 重采样方法正是为了解决这个矛盾而生。

模型评估 vs. 模型选择:两个不同的目标

模型评估(Model Assessment)与模型选择(Model Selection)虽然都用CV,但目标不同:

  • 模型选择:在多个候选模型中选出最优的一个
    • 比较不同复杂度的模型(如1阶 vs. 5阶多项式)
    • 选择最佳超参数(如正则化参数 \(\lambda\)
    • 关键:需要CV误差的相对排序正确即可
  • 模型评估:估计最终选定模型的泛化性能
    • 回答”这个模型部署后能达到什么精度?”
    • 关键:需要CV误差的绝对值准确

最佳实践(嵌套交叉验证):

  1. 外层CV:评估整个建模流程的泛化能力
  2. 内层CV:在每个外层训练集内做模型选择

为什么训练误差不能用于模型评估?

训练误差总是会低估模型在新数据上的真实表现:

  • 过拟合的本质:模型不仅学习了数据中的真实信号,还记住了随机噪声
  • 训练误差随模型复杂度单调递减,但测试误差呈U形曲线
  • 用训练误差选模型 → 总是选最复杂的模型 → 泛化能力最差

A股市场的典型陷阱

  • 量化基金用100个技术指标拟合过去5年的沪深300 → 训练 \(R^2 = 0.95\)
  • 实盘运行3个月 → 实际 \(R^2 < 0.01\)
  • 原因:模型过拟合了历史市场结构,而市场是非平稳

测试误差的核心概念

测试误差(Test Error / Generalization Error):

\[ \large{ \text{Err} = E\left[L(Y, \hat{f}(X))\right] } \tag{2}\]

其中期望是对新的未见过的数据点 \((X, Y)\) 取的。

误差类型 定义 用途 问题
训练误差 在训练集上的平均损失 模型拟合 严重低估真实误差
测试误差 在新数据上的平均损失 模型评估 需要独立测试数据
CV估计 对测试误差的估计 模型选择 是一种近似

核心矛盾:我们需要测试误差,但没有真正的新数据 → 重采样方法正是为了解决这个矛盾而生。

模型评估 vs. 模型选择:两个不同的目标

模型评估(Model Assessment)与模型选择(Model Selection)虽然都用CV,但目标不同:

  • 模型选择:在多个候选模型中选出最优的一个
    • 比较不同复杂度的模型(如1阶 vs. 5阶多项式)
    • 选择最佳超参数(如正则化参数 \(\lambda\)
    • 关键:需要CV误差的相对排序正确即可
  • 模型评估:估计最终选定模型的泛化性能
    • 回答”这个模型部署后能达到什么精度?”
    • 关键:需要CV误差的绝对值准确

最佳实践(嵌套交叉验证):

  1. 外层CV:评估整个建模流程的泛化能力
  2. 内层CV:在每个外层训练集内做模型选择

两大重采样方法概览

重采样方法概览 展示交叉验证和自助法两大重采样方法的对比关系图 重采样方法 (Resampling Methods) 从已有数据中反复抽样 交叉验证 (Cross-Validation) 目标:估计测试误差 / 模型选择 验证集法 LOOCV k折CV 时间序列CV(金融数据专用) 自助法 (Bootstrap) 目标:量化估计量的不确定性 标准误估计 置信区间 投资组合优化 · 回归系数 实战案例:海康威视 · 宁波银行 · A股数据
Figure 1: 重采样方法的两大分支:交叉验证用于模型评估与选择,自助法用于量化不确定性

重采样方法的历史与直觉

重采样方法的发展历程

  • 1931年:Mahalanobis首次提出交叉验证的雏形
  • 1968年:Lachenbruch & Mickey 系统化留一交叉验证(LOOCV)
  • 1974年:Stone 提出广义交叉验证理论
  • 1979年:Bradley Efron 发明Bootstrap——被《美国统计学家》评为20世纪最重要的统计创新之一

核心直觉

“如果我能回到市场重新收集一组数据,结果会有多大不同?”

重采样方法巧妙地回答了这个问题——不需要真正回到市场

重采样 vs. 信息准则:两条不同的路径

除了重采样,还有另一类方法估计测试误差——信息准则

方法 代表 核心思想 适用场景
重采样方法 CV, Bootstrap 直接模拟新数据 通用,任何模型
信息准则 AIC, BIC, \(C_p\) 惩罚训练误差 参数模型

AIC(赤池信息准则):

\[ \large{ \text{AIC} = -2\ln(L) + 2p } \tag{3}\]

  • \(L\):似然函数值,\(p\):参数个数
  • 本质:训练误差 + 模型复杂度惩罚

为什么还需要CV?

  • 信息准则只适用于参数模型
  • 对于随机森林、神经网络等非参数模型,只有CV可用
  • CV对模型假设的依赖更少,更加稳健

重采样方法的历史与直觉

重采样方法的发展历程

  • 1931年:Mahalanobis首次提出交叉验证的雏形
  • 1968年:Lachenbruch & Mickey 系统化留一交叉验证(LOOCV)
  • 1974年:Stone 提出广义交叉验证理论
  • 1979年:Bradley Efron 发明Bootstrap——被《美国统计学家》评为20世纪最重要的统计创新之一

核心直觉

“如果我能回到市场重新收集一组数据,结果会有多大不同?”

重采样方法巧妙地回答了这个问题——不需要真正回到市场

重采样 vs. 信息准则:两条不同的路径

除了重采样,还有另一类方法估计测试误差——信息准则

方法 代表 核心思想 适用场景
重采样方法 CV, Bootstrap 直接模拟新数据 通用,任何模型
信息准则 AIC, BIC, \(C_p\) 惩罚训练误差 参数模型

AIC(赤池信息准则):

\[ \large{ \text{AIC} = -2\ln(L) + 2p } \tag{4}\]

  • \(L\):似然函数值,\(p\):参数个数
  • 本质:训练误差 + 模型复杂度惩罚

为什么还需要CV?

  • 信息准则只适用于参数模型
  • 对于随机森林、神经网络等非参数模型,只有CV可用
  • CV对模型假设的依赖更少,更加稳健

验证集方法:最简单的思路

验证集方法示意图 将数据随机分为训练集和验证集两半的示意图 验证集方法 (Validation Set Approach) 原始数据集 (n个观测) obs₁, obs₂, obs₃, ... , obsₙ ↓ 随机分割 训练集 (约50%) 验证集 (约50%) 在训练集上拟合模型 将模型应用到验证集 计算验证集 MSE ✓ 简单直观 ✗ 高方差(依赖切分方式) ✗ 只用一半数据训练
Figure 2: 验证集方法:随机将数据一分为二,一半训练、一半验证

验证集方法的数学表述

形式化定义:设原始数据集为 \(\{(x_1,y_1), \ldots, (x_n,y_n)\}\)

  1. 随机选取指标集 \(\mathcal{V} \subset \{1,\ldots,n\}\)\(|\mathcal{V}| = m\)(通常 \(m = n/2\)
  2. 训练集\(\mathcal{T} = \{1,\ldots,n\} \setminus \mathcal{V}\)
  3. \(\mathcal{T}\) 上拟合模型 \(\hat{f}\)
  4. 验证集MSE

\[ \large{ \text{MSE}_{\mathcal{V}} = \frac{1}{m}\sum_{i \in \mathcal{V}}(y_i - \hat{f}(x_i))^2 } \tag{5}\]

两个根本缺陷

  • 高方差\(\mathcal{V}\) 的随机选取导致每次结果不同
  • 高偏差:只用 \(n/2\) 个观测训练,模型性能系统性偏低

在海康威视数据上的具体实现

数据说明

  • 海康威视(002415.XSHE):中国安防龙头,总部位于杭州
  • 取最近500个交易日的前复权日收盘价
  • 计算日收益率:\(r_t = (P_t - P_{t-1}) / P_{t-1}\)
  • 预测任务:用前一日收益率(\(r_{t-1}\))预测当日收益率(\(r_t\)
  • 尝试1-5阶多项式回归

为什么选择海康威视?

  • 长三角代表性科技企业(杭州,浙江省)
  • 日均交易量大,数据质量高
  • 股价波动适中,适合展示回归模型的性能差异

验证集方法的数学表述

形式化定义:设原始数据集为 \(\{(x_1,y_1), \ldots, (x_n,y_n)\}\)

  1. 随机选取指标集 \(\mathcal{V} \subset \{1,\ldots,n\}\)\(|\mathcal{V}| = m\)(通常 \(m = n/2\)
  2. 训练集\(\mathcal{T} = \{1,\ldots,n\} \setminus \mathcal{V}\)
  3. \(\mathcal{T}\) 上拟合模型 \(\hat{f}\)
  4. 验证集MSE

\[ \large{ \text{MSE}_{\mathcal{V}} = \frac{1}{m}\sum_{i \in \mathcal{V}}(y_i - \hat{f}(x_i))^2 } \tag{6}\]

两个根本缺陷

  • 高方差\(\mathcal{V}\) 的随机选取导致每次结果不同
  • 高偏差:只用 \(n/2\) 个观测训练,模型性能系统性偏低

在海康威视数据上的具体实现

数据说明

  • 海康威视(002415.XSHE):中国安防龙头,总部位于杭州
  • 取最近500个交易日的前复权日收盘价
  • 计算日收益率:\(r_t = (P_t - P_{t-1}) / P_{t-1}\)
  • 预测任务:用前一日收益率(\(r_{t-1}\))预测当日收益率(\(r_t\)
  • 尝试1-5阶多项式回归

为什么选择海康威视?

  • 长三角代表性科技企业(杭州,浙江省)
  • 日均交易量大,数据质量高
  • 股价波动适中,适合展示回归模型的性能差异

验证集方法的致命缺陷:高方差

Code
import numpy as np  # 导入数值计算库
import pandas as pd  # 导入数据处理库
import matplotlib.pyplot as plt  # 导入绑图库
import platform  # 导入平台检测模块
import os  # 导入操作系统模块
from sklearn.preprocessing import PolynomialFeatures  # 导入多项式特征生成器
from sklearn.linear_model import LinearRegression  # 导入线性回归模型
from sklearn.metrics import mean_squared_error  # 导入均方误差计算函数

# 根据操作系统自动选择数据路径
data_base_path = 'C:/qiufei/data' if platform.system() == 'Windows' else '/home/ubuntu/r2_data_mount/qiufei/data'  # 跨平台路径适配

# 读取海康威视前复权日度股价数据
stock_price_path = f'{data_base_path}/stock/stock_price_pre_adjusted.h5'  # 拼接股价文件路径
stock_price_data = pd.read_hdf(stock_price_path)  # 从本地h5文件加载股价数据
Figure 3
Code
# 提取海康威视的收盘价序列
if 'order_book_id' in stock_price_data.index.names:  # 判断是否为多层索引
    haikang_close_prices = stock_price_data.xs('002415.XSHE', level='order_book_id').sort_index()['close']  # 从多层索引提取
else:  # 普通索引分支
    stock_reset = stock_price_data.reset_index()  # 重置索引为普通列
    haikang_close_prices = stock_reset[stock_reset['order_book_id'] == '002415.XSHE'].set_index('date')['close']  # 按条件筛选

# 计算日收益率并取最近500个交易日
haikang_daily_returns = haikang_close_prices.pct_change().dropna().iloc[-500:]  # 计算百分比收益率并截取近500天
haikang_returns_array = haikang_daily_returns.values  # 转为NumPy数组方便后续运算

# 构造特征:用前一日收益率预测当日收益率
feature_x = haikang_returns_array[:-1].reshape(-1, 1)  # 前一日收益率作为自变量,reshape为列向量
target_y = haikang_returns_array[1:]  # 当日收益率作为因变量
Figure 4
# 10次不同随机切分,每次计算1-5阶多项式的验证集MSE
fig, ax = plt.subplots(figsize=(10, 5))  # 创建画布
polynomial_degrees = range(1, 6)  # 多项式阶数从1到5

for split_iteration in range(10):  # 循环10次不同的随机切分
    np.random.seed(split_iteration)  # 每次使用不同的随机种子
    total_samples = len(feature_x)  # 总样本数
    shuffled_indices = np.random.permutation(total_samples)  # 生成随机排列的索引
    split_point = total_samples // 2  # 对半分割点
    train_indices = shuffled_indices[:split_point]  # 前一半作为训练集索引
    validation_indices = shuffled_indices[split_point:]  # 后一半作为验证集索引
    mse_per_degree = []  # 存储各阶多项式的MSE
    for degree in polynomial_degrees:  # 遍历每个多项式阶数
        poly_transformer = PolynomialFeatures(degree=degree, include_bias=False)  # 构造多项式特征转换器
        x_train_poly = poly_transformer.fit_transform(feature_x[train_indices])  # 对训练集生成多项式特征
        x_val_poly = poly_transformer.transform(feature_x[validation_indices])  # 对验证集生成同样的多项式特征
        model = LinearRegression().fit(x_train_poly, target_y[train_indices])  # 拟合线性回归模型
        val_predictions = model.predict(x_val_poly)  # 在验证集上预测
        mse_per_degree.append(mean_squared_error(target_y[validation_indices], val_predictions))  # 计算并记录MSE
    ax.plot(list(polynomial_degrees), mse_per_degree, marker='o', alpha=0.6)  # 绘制当次切分的MSE曲线

ax.set_xlabel('多项式阶数 (Polynomial Degree)', fontsize=12)  # X轴标签
ax.set_ylabel('验证集 MSE', fontsize=12)  # Y轴标签
ax.set_title('验证集方法的高方差:10次随机切分结果差异显著', fontsize=14)  # 标题
ax.grid(True, alpha=0.3)  # 添加网格线
plt.tight_layout()  # 调整布局
plt.show()  # 显示图形

高方差的根本原因分析

验证集方法高方差的三重来源

1. 训练集的随机性

  • 不同切分 → 不同训练集 → 不同模型 \(\hat{f}\)
  • 训练集的差异直接导致模型参数的差异

2. 验证集的随机性

  • 同一模型在不同验证集上的MSE不同
  • 验证集中”容易预测”和”难以预测”的观测比例随机

3. 训练集大小不足

  • 只用50%数据训练 → 模型估计精度下降
  • 不同切分产生的”弱模型”差异更大

类比

就像只考一道题来评价学生水平——成绩波动极大,因为这道题恰好是擅长/不擅长的概率各占一半。

从验证集法到交叉验证的思维跃迁

验证集法的改进方向

问题 解决思路 对应方法
只评估一次 多次评估取平均 k折交叉验证
训练集太小 最大化训练数据使用 LOOCV
验证集随机 让每个点都做验证 k折CV / LOOCV

核心升级

  • 验证集法:一次随机切分 → 一个 MSE 估计
  • k折CV:k次系统切分 → k个 MSE → 取平均 → 更稳定

\[ \large{ \text{方差降低:} \text{Var}(\overline{X}) = \frac{\text{Var}(X)}{k} } \]

如果k个MSE是独立的,方差降为原来的 \(1/k\)

实际上它们不完全独立,但方差仍然显著降低

高方差的根本原因分析

验证集方法高方差的三重来源

1. 训练集的随机性

  • 不同切分 → 不同训练集 → 不同模型 \(\hat{f}\)
  • 训练集的差异直接导致模型参数的差异

2. 验证集的随机性

  • 同一模型在不同验证集上的MSE不同
  • 验证集中”容易预测”和”难以预测”的观测比例随机

3. 训练集大小不足

  • 只用50%数据训练 → 模型估计精度下降
  • 不同切分产生的”弱模型”差异更大

类比

就像只考一道题来评价学生水平——成绩波动极大,因为这道题恰好是擅长/不擅长的概率各占一半。

从验证集法到交叉验证的思维跃迁

验证集法的改进方向

问题 解决思路 对应方法
只评估一次 多次评估取平均 k折交叉验证
训练集太小 最大化训练数据使用 LOOCV
验证集随机 让每个点都做验证 k折CV / LOOCV

核心升级

  • 验证集法:一次随机切分 → 一个 MSE 估计
  • k折CV:k次系统切分 → k个 MSE → 取平均 → 更稳定

\[ \large{ \text{方差降低:} \text{Var}(\overline{X}) = \frac{\text{Var}(X)}{k} } \]

如果k个MSE是独立的,方差降为原来的 \(1/k\)

实际上它们不完全独立,但方差仍然显著降低

LOOCV:留一交叉验证,消除随机性

留一交叉验证(Leave-One-Out Cross-Validation)的思路:每次只留出1个观测做验证,其余所有数据做训练。

\[ \large{ \text{CV}_{(n)} = \frac{1}{n}\sum_{i=1}^{n}\text{MSE}_i } \tag{7}\]

  • 重复 \(n\) 次(\(n\) = 样本量),每次留出不同的观测
  • \(\text{MSE}_i\):第 \(i\) 个观测被留出时的预测误差
  • 训练集大小:每次用 \(n-1\) 个观测,偏差极小
  • 结果确定:没有随机性,每次运行结果完全相同

快捷公式(仅适用于线性回归 / 多项式回归):

\[ \large{ \text{CV}_{(n)} = \frac{1}{n}\sum_{i=1}^{n}\left(\frac{y_i - \hat{y}_i}{1 - h_i}\right)^2 } \tag{8}\]

其中 \(h_i\) 是帽子矩阵的第 \(i\) 个对角元素(杠杆值),衡量第 \(i\) 个观测对自身预测的影响力。

LOOCV快捷公式的推导

补充说明:杠杆值 \(h_i\) 的含义与快捷公式推导

在线性回归中,预测值可以写为:

\[ \large{ \hat{\mathbf{y}} = \mathbf{H}\mathbf{y}, \quad \mathbf{H} = \mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T } \tag{9}\]

\(\mathbf{H}\) 称为帽子矩阵(Hat Matrix),\(h_i = H_{ii}\) 是其第 \(i\) 个对角元素。

\(h_i\) 的性质

  • \(0 \leq h_i \leq 1\),且 \(\sum_i h_i = p\)\(p\) 为参数个数)
  • \(h_i\) 越大 → 第 \(i\) 个观测对自身预测的影响力越大
  • \(h_i\) 大的点是高杠杆点——在特征空间中远离其他数据点

快捷公式的直觉

\[ \large{ \text{LOOCV}_i = \left(\frac{y_i - \hat{y}_i}{1 - h_i}\right)^2 } \]

  • 分子 \(y_i - \hat{y}_i\):全量模型的残差
  • 分母 \(1 - h_i\):对杠杆值的修正——高杠杆点的残差被放大

LOOCV的优势与代价

优势

  • 无随机性:结果完全确定,可复现
  • 低偏差:每次用 \(n-1\) 个观测训练,接近全量数据
  • ✓ 有快捷公式时,计算高效(如线性模型)

代价

  • 高方差(反直觉!):\(n\) 个训练集之间高度重叠(只差1个观测)
    • 各折模型几乎相同 → MSE 高度相关 → 平均后方差不降反升
  • 计算成本:对于复杂模型,需拟合 \(n\)
    • 深度学习模型:\(n = 10000\) 意味着训练10000次!
  • ✗ 不适合时间序列数据(打破时间顺序)

LOOCV快捷公式的推导

补充说明:杠杆值 \(h_i\) 的含义与快捷公式推导

在线性回归中,预测值可以写为:

\[ \large{ \hat{\mathbf{y}} = \mathbf{H}\mathbf{y}, \quad \mathbf{H} = \mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T } \tag{10}\]

\(\mathbf{H}\) 称为帽子矩阵(Hat Matrix),\(h_i = H_{ii}\) 是其第 \(i\) 个对角元素。

\(h_i\) 的性质

  • \(0 \leq h_i \leq 1\),且 \(\sum_i h_i = p\)\(p\) 为参数个数)
  • \(h_i\) 越大 → 第 \(i\) 个观测对自身预测的影响力越大
  • \(h_i\) 大的点是高杠杆点——在特征空间中远离其他数据点

快捷公式的直觉

\[ \large{ \text{LOOCV}_i = \left(\frac{y_i - \hat{y}_i}{1 - h_i}\right)^2 } \]

  • 分子 \(y_i - \hat{y}_i\):全量模型的残差
  • 分母 \(1 - h_i\):对杠杆值的修正——高杠杆点的残差被放大

LOOCV的优势与代价

优势

  • 无随机性:结果完全确定,可复现
  • 低偏差:每次用 \(n-1\) 个观测训练,接近全量数据
  • ✓ 有快捷公式时,计算高效(如线性模型)

代价

  • 高方差(反直觉!):\(n\) 个训练集之间高度重叠(只差1个观测)
    • 各折模型几乎相同 → MSE 高度相关 → 平均后方差不降反升
  • 计算成本:对于复杂模型,需拟合 \(n\)
    • 深度学习模型:\(n = 10000\) 意味着训练10000次!
  • ✗ 不适合时间序列数据(打破时间顺序)

k折交叉验证:偏差与方差的最佳权衡

k折交叉验证示意图 5折交叉验证的数据划分方式,每次一折作为验证集 5折交叉验证 (5-Fold CV) 第1轮 第2轮 第3轮 第4轮 第5轮 验证 训练 训练 训练 训练 → MSE₁ 验证 → MSE₂ 验证 → MSE₃ 验证 → MSE₄ 验证 → MSE₅ CV₍₅₎ = (MSE₁ + MSE₂ + MSE₃ + MSE₄ + MSE₅) / 5
Figure 5: 5折交叉验证:数据分为5折,每折轮流作为验证集,最终取平均MSE

\[ \large{ \text{CV}_{(k)} = \frac{1}{k}\sum_{i=1}^{k}\text{MSE}_i } \tag{11}\]

k折CV的实现步骤

算法流程

  1. \(n\) 个观测随机打乱,然后均匀分为 \(k\)
  2. 对于 \(i = 1, 2, \ldots, k\)
    • 将第 \(i\) 折作为验证集(大小 \(\approx n/k\)
    • 用其余 \(k-1\) 折作为训练集(大小 \(\approx n(k-1)/k\)
    • 拟合模型并在验证集上计算 \(\text{MSE}_i\)
  3. 最终估计:\(\text{CV}_{(k)} = \frac{1}{k}\sum_{i=1}^{k}\text{MSE}_i\)

分层k折CV(Stratified k-Fold):

  • 确保每一折中目标变量的分布与原始数据一致
  • 对于分类问题:每折中各类别的比例与总体相同
  • 对于不平衡数据尤其重要(如信用违约预测:违约率仅1%)

为什么 k=5 或 k=10 是最优选择?

偏差-方差权衡的数学分析

设真实测试误差为 \(\text{Err}\),CV估计为 \(\widehat{\text{Err}}\)

偏差\(\text{Bias} = E[\widehat{\text{Err}}] - \text{Err}\)

  • \(k\) 越大 → 训练集大小 \(n(k-1)/k\) 越接近 \(n\) → 偏差越小
  • \(k=5\):训练集为 \(0.8n\)\(k=10\):训练集为 \(0.9n\)

方差\(\text{Var}(\widehat{\text{Err}}) = \text{Var}\left(\frac{1}{k}\sum_{i=1}^k \text{MSE}_i\right)\)

  • 如果各折MSE独立\(\text{Var} = \frac{\sigma^2}{k}\)\(k\) 越大越好)
  • 但实际上各折MSE正相关(训练集重叠)
  • \(k\) 越大 → 重叠越大 → 相关性越强 → 方差增大

Hastie等人(2009)的结论\(k = 5\)\(k = 10\) 在大量实验中表现最稳定。

CV误差的标准误估计

“一倍标准误规则”(One-Standard-Error Rule):

不选CV误差最小的模型,而选CV误差在最小值加一个标准误以内最简单模型

\[ \large{ \text{SE}(\text{CV}_{(k)}) = \sqrt{\frac{1}{k}\cdot\frac{1}{k-1}\sum_{i=1}^{k}(\text{MSE}_i - \overline{\text{MSE}})^2} } \tag{12}\]

实际操作

  1. 计算每个候选模型的 \(\text{CV}_{(k)}\)\(\text{SE}\)
  2. 找到 \(\text{CV}\) 最小的模型,记其 \(\text{CV}\) 值为 \(\text{CV}_{\min}\)
  3. 选择满足 \(\text{CV} \leq \text{CV}_{\min} + \text{SE}_{\min}\)最简单模型

奥卡姆剃刀:在预测能力相当时,选择更简单的模型——更可解释、更稳健、更不容易过拟合。

k折CV的实现步骤

算法流程

  1. \(n\) 个观测随机打乱,然后均匀分为 \(k\)
  2. 对于 \(i = 1, 2, \ldots, k\)
    • 将第 \(i\) 折作为验证集(大小 \(\approx n/k\)
    • 用其余 \(k-1\) 折作为训练集(大小 \(\approx n(k-1)/k\)
    • 拟合模型并在验证集上计算 \(\text{MSE}_i\)
  3. 最终估计:\(\text{CV}_{(k)} = \frac{1}{k}\sum_{i=1}^{k}\text{MSE}_i\)

分层k折CV(Stratified k-Fold):

  • 确保每一折中目标变量的分布与原始数据一致
  • 对于分类问题:每折中各类别的比例与总体相同
  • 对于不平衡数据尤其重要(如信用违约预测:违约率仅1%)

为什么 k=5 或 k=10 是最优选择?

偏差-方差权衡的数学分析

设真实测试误差为 \(\text{Err}\),CV估计为 \(\widehat{\text{Err}}\)

偏差\(\text{Bias} = E[\widehat{\text{Err}}] - \text{Err}\)

  • \(k\) 越大 → 训练集大小 \(n(k-1)/k\) 越接近 \(n\) → 偏差越小
  • \(k=5\):训练集为 \(0.8n\)\(k=10\):训练集为 \(0.9n\)

方差\(\text{Var}(\widehat{\text{Err}}) = \text{Var}\left(\frac{1}{k}\sum_{i=1}^k \text{MSE}_i\right)\)

  • 如果各折MSE独立\(\text{Var} = \frac{\sigma^2}{k}\)\(k\) 越大越好)
  • 但实际上各折MSE正相关(训练集重叠)
  • \(k\) 越大 → 重叠越大 → 相关性越强 → 方差增大

Hastie等人(2009)的结论\(k = 5\)\(k = 10\) 在大量实验中表现最稳定。

CV误差的标准误估计

“一倍标准误规则”(One-Standard-Error Rule):

不选CV误差最小的模型,而选CV误差在最小值加一个标准误以内最简单模型

\[ \large{ \text{SE}(\text{CV}_{(k)}) = \sqrt{\frac{1}{k}\cdot\frac{1}{k-1}\sum_{i=1}^{k}(\text{MSE}_i - \overline{\text{MSE}})^2} } \tag{13}\]

实际操作

  1. 计算每个候选模型的 \(\text{CV}_{(k)}\)\(\text{SE}\)
  2. 找到 \(\text{CV}\) 最小的模型,记其 \(\text{CV}\) 值为 \(\text{CV}_{\min}\)
  3. 选择满足 \(\text{CV} \leq \text{CV}_{\min} + \text{SE}_{\min}\)最简单模型

奥卡姆剃刀:在预测能力相当时,选择更简单的模型——更可解释、更稳健、更不容易过拟合。

k的选择:偏差-方差权衡

k值 偏差 方差 计算成本 评价
k=2 (验证集法) 最低 不推荐
k=5 适中偏高 推荐
k=10 适中偏低 适中 适中 推荐
k=n (LOOCV) 最低 最高 最高 特殊场景

核心直觉

  • \(k\) 越大 → 训练集越大 → 偏差越小(模型拟合得更充分)
  • \(k\) 越大 → 各折训练集重叠越多 → 预测误差之间相关性更强方差越大
  • 经验法则\(k=5\)\(k=10\) 是最优平衡点

CV在模型选择中的完整工作流

实际操作的标准流程

  1. 定义候选模型集合:如多项式阶数 \(d \in \{1,2,3,4,5\}\)
  2. 对每个候选模型执行k折CV
    • 记录每折的验证误差 \(\text{MSE}_1, \ldots, \text{MSE}_k\)
    • 计算 \(\text{CV}_{(k)}\)\(\text{SE}(\text{CV}_{(k)})\)
  3. 绘制CV曲线:横轴为模型复杂度,纵轴为CV误差(附误差棒)
  4. 选择最优模型:使用一倍标准误规则
  5. 最终训练:在全部数据上重新拟合所选模型
  6. 报告性能:使用独立测试集或嵌套CV

关键细节

  • 最终模型应在全部训练数据上重新训练(不是只用某一折的训练集!)
  • 报告的CV误差是模型选择过程的副产品,不是最终性能

CV在模型选择中的完整工作流

实际操作的标准流程

  1. 定义候选模型集合:如多项式阶数 \(d \in \{1,2,3,4,5\}\)
  2. 对每个候选模型执行k折CV
    • 记录每折的验证误差 \(\text{MSE}_1, \ldots, \text{MSE}_k\)
    • 计算 \(\text{CV}_{(k)}\)\(\text{SE}(\text{CV}_{(k)})\)
  3. 绘制CV曲线:横轴为模型复杂度,纵轴为CV误差(附误差棒)
  4. 选择最优模型:使用一倍标准误规则
  5. 最终训练:在全部数据上重新拟合所选模型
  6. 报告性能:使用独立测试集或嵌套CV

关键细节

  • 最终模型应在全部训练数据上重新训练(不是只用某一折的训练集!)
  • 报告的CV误差是模型选择过程的副产品,不是最终性能

实证:LOOCV与10折CV在海康威视数据上的对比

Code
from sklearn.model_selection import cross_val_score, KFold, LeaveOneOut  # 导入交叉验证工具

fig, axes = plt.subplots(1, 2, figsize=(14, 5))  # 创建1行2列子图布局

# 左图:LOOCV
loocv_mse_per_degree = []  # 存储LOOCV各阶MSE
for degree in range(1, 6):  # 遍历1到5阶多项式
    poly_transformer = PolynomialFeatures(degree=degree, include_bias=False)  # 多项式特征转换器
    x_poly_features = poly_transformer.fit_transform(feature_x)  # 生成多项式特征
    loocv_splitter = LeaveOneOut()  # 创建LOOCV分割器
    neg_mse_scores = cross_val_score(LinearRegression(), x_poly_features, target_y, cv=loocv_splitter, scoring='neg_mean_squared_error')  # 计算LOOCV负MSE
    loocv_mse_per_degree.append(-neg_mse_scores.mean())  # 取负得到正MSE并记录

axes[0].plot(range(1, 6), loocv_mse_per_degree, 'b-o', linewidth=2, markersize=8)  # 绘制LOOCV曲线
axes[0].set_xlabel('多项式阶数', fontsize=12)  # X轴标签
axes[0].set_ylabel('LOOCV MSE', fontsize=12)  # Y轴标签
axes[0].set_title('LOOCV(结果唯一确定)', fontsize=14)  # 标题
axes[0].grid(True, alpha=0.3)  # 网格线
Figure 6: LOOCV与10折CV的对比:左图为LOOCV曲线(确定性),右图为9次不同10折CV(展示低方差)
# 右图:9次不同随机种子的10折CV
for cv_iteration in range(9):  # 重复9次10折CV
    kfold_mse_per_degree = []  # 存储当次实验各阶MSE
    for degree in range(1, 6):  # 遍历1到5阶多项式
        poly_transformer = PolynomialFeatures(degree=degree, include_bias=False)  # 多项式特征转换器
        x_poly_features = poly_transformer.fit_transform(feature_x)  # 生成多项式特征
        kfold_splitter = KFold(n_splits=10, shuffle=True, random_state=cv_iteration)  # 10折分割器,每次不同随机种子
        neg_mse_scores = cross_val_score(LinearRegression(), x_poly_features, target_y, cv=kfold_splitter, scoring='neg_mean_squared_error')  # 计算10折CV负MSE
        kfold_mse_per_degree.append(-neg_mse_scores.mean())  # 取负得到正MSE
    axes[1].plot(range(1, 6), kfold_mse_per_degree, marker='o', alpha=0.6)  # 绘制当次曲线

axes[1].set_xlabel('多项式阶数', fontsize=12)  # X轴标签
axes[1].set_ylabel('10折 CV MSE', fontsize=12)  # Y轴标签
axes[1].set_title('10折CV(9次不同切分,方差远低于验证集法)', fontsize=14)  # 标题
axes[1].grid(True, alpha=0.3)  # 网格线
plt.tight_layout()  # 调节布局间距
plt.show()  # 显示图表
<Figure size 960x480 with 0 Axes>

实证结果的关键发现

从海康威视的CV实证中,我们观察到:

发现一:多项式阶数对MSE影响微弱

  • 1阶到5阶多项式的CV误差差异很小
  • 说明前一日收益率对次日收益率的非线性预测能力有限
  • 这与金融学中的弱式有效市场假说一致

发现二:LOOCV和10折CV给出一致结论

  • 两种方法的排序结果几乎相同
  • 但10折CV的计算速度远快于LOOCV

发现三:10折CV的稳定性远优于验证集法

  • 验证集法的10条曲线差异显著
  • 10折CV的9条曲线几乎重叠
  • 这就是k折CV被广泛采用的原因

实际含义:对于海康威视的日收益率预测,简单的线性模型(1阶)已经足够。

实证结果的关键发现

从海康威视的CV实证中,我们观察到:

发现一:多项式阶数对MSE影响微弱

  • 1阶到5阶多项式的CV误差差异很小
  • 说明前一日收益率对次日收益率的非线性预测能力有限
  • 这与金融学中的弱式有效市场假说一致

发现二:LOOCV和10折CV给出一致结论

  • 两种方法的排序结果几乎相同
  • 但10折CV的计算速度远快于LOOCV

发现三:10折CV的稳定性远优于验证集法

  • 验证集法的10条曲线差异显著
  • 10折CV的9条曲线几乎重叠
  • 这就是k折CV被广泛采用的原因

实际含义:对于海康威视的日收益率预测,简单的线性模型(1阶)已经足够。

金融数据的特殊陷阱:时间序列交叉验证

金融时间序列数据不能随机打乱! 随机切分会造成严重的数据泄露(Data Leakage)。

时间序列交叉验证 滚动窗口和扩展窗口两种时间序列CV方法示意图 时间序列交叉验证(Walk-Forward Validation) ⚠️ 金融数据严禁随机打乱!必须保持时间顺序 ⚠️ 过去 未来 滚动 窗口 扩展 窗口 训练窗口(固定长度) 训练窗口(不断增长) 测试窗口
Figure 7: 时间序列交叉验证的两种核心策略:滚动窗口与扩展窗口

数据泄露:量化投资中的头号杀手

数据泄露(Data Leakage):训练过程中无意间使用了未来信息

金融领域的典型案例

  1. 前视偏差(Look-Ahead Bias):
    • 用2024年的财报数据训练模型,去预测2023年12月的股价
    • 实际上2024年的财报在2024年4月才发布!
  2. 幸存者偏差(Survivorship Bias):
    • 只用现在还在交易的股票做回测
    • 忽略了已经退市的ST股票(它们可能大幅亏损)
  3. 随机CV的时间泄露
    • 将2020-2024年的数据随机打乱做5折CV
    • 可能用2024年的数据训练,预测2021年 → 模型”看到了未来”

防范措施:对时间序列数据,只能用Walk-Forward验证。

时间序列CV的Python实现要点

scikit-learn中的TimeSeriesSplit

from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)

两种策略对比

策略 训练窗口 优点 缺点
扩展窗口 不断增长 数据利用率高 早期数据可能已过时
滚动窗口 固定大小 适应市场变化 浪费早期数据

A股市场的经验法则

  • 牛熊转换周期约3-5年 → 训练窗口至少覆盖一个完整周期
  • 滚动窗口更适合A股(市场结构变化快)
  • 测试窗口不宜太长(1-3个月为佳)
  • 考虑在训练集和测试集之间留一个间隔期(Gap),避免信息泄露

数据泄露:量化投资中的头号杀手

数据泄露(Data Leakage):训练过程中无意间使用了未来信息

金融领域的典型案例

  1. 前视偏差(Look-Ahead Bias):
    • 用2024年的财报数据训练模型,去预测2023年12月的股价
    • 实际上2024年的财报在2024年4月才发布!
  2. 幸存者偏差(Survivorship Bias):
    • 只用现在还在交易的股票做回测
    • 忽略了已经退市的ST股票(它们可能大幅亏损)
  3. 随机CV的时间泄露
    • 将2020-2024年的数据随机打乱做5折CV
    • 可能用2024年的数据训练,预测2021年 → 模型”看到了未来”

防范措施:对时间序列数据,只能用Walk-Forward验证。

时间序列CV的Python实现要点

scikit-learn中的TimeSeriesSplit

from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)

两种策略对比

策略 训练窗口 优点 缺点
扩展窗口 不断增长 数据利用率高 早期数据可能已过时
滚动窗口 固定大小 适应市场变化 浪费早期数据

A股市场的经验法则

  • 牛熊转换周期约3-5年 → 训练窗口至少覆盖一个完整周期
  • 滚动窗口更适合A股(市场结构变化快)
  • 测试窗口不宜太长(1-3个月为佳)
  • 考虑在训练集和测试集之间留一个间隔期(Gap),避免信息泄露

分类问题的交叉验证

对于分类问题,交叉验证使用分类错误率替代MSE:

\[ \large{ \text{CV}_{(k)} = \frac{1}{k}\sum_{i=1}^{k}\text{Err}_i, \quad \text{Err}_i = \frac{1}{n_i}\sum_{j \in C_i} I(y_j \neq \hat{y}_j) } \tag{14}\]

其中 \(C_i\) 是第 \(i\) 折的验证集,\(I(\cdot)\) 是指示函数。

海康威视涨跌预测实例

  • 目标:预测海康威视次日涨跌(二分类)
  • 特征:Lag1(前1日收益率)、Lag2(前2日收益率)
  • 模型:逻辑回归(Logistic Regression)
  • 评估方法:10折交叉验证的分类错误率

此任务的训练误差往往远低于CV误差,因为模型会过度适配训练数据中的噪声。

分类CV中的评价指标选择

分类错误率不总是最佳选择

指标 公式 适用场景
错误率 \(\frac{\text{FP}+\text{FN}}{n}\) 各类别平衡
AUC-ROC ROC曲线下面积 各类别不平衡
F1-Score \(\frac{2 \cdot P \cdot R}{P + R}\) 关注少数类
Log Loss \(-\frac{1}{n}\sum[y\log\hat{p}+(1-y)\log(1-\hat{p})]\) 概率校准

金融应用中的指标选择

  • 信用违约预测:违约率仅1% → 用AUC-ROC或F1
    • 即使把所有人预测为”不违约”,错误率也只有1%!
  • 涨跌预测:类别大致平衡 → 用错误率即可
  • 量化选股:关注排序能力 → 用AUC-ROC

CV与过拟合诊断

如何用CV识别过拟合?

关键信号:训练误差远低于CV误差

场景 训练误差 CV误差 诊断
训练≈CV,两者都低 ✅ 良好拟合
训练≈CV,两者都高 ⚠️ 欠拟合
训练≪CV 很低 过拟合
训练≈0 ~0 很高 严重过拟合

A股量化策略的典型模式

  • 在样本内(2018-2022)年化收益40%,最大回撤5%
  • 10折CV显示年化收益仅8%,最大回撤30%
  • 结论:策略严重过拟合历史数据

分类CV中的评价指标选择

分类错误率不总是最佳选择

指标 公式 适用场景
错误率 \(\frac{\text{FP}+\text{FN}}{n}\) 各类别平衡
AUC-ROC ROC曲线下面积 各类别不平衡
F1-Score \(\frac{2 \cdot P \cdot R}{P + R}\) 关注少数类
Log Loss \(-\frac{1}{n}\sum[y\log\hat{p}+(1-y)\log(1-\hat{p})]\) 概率校准

金融应用中的指标选择

  • 信用违约预测:违约率仅1% → 用AUC-ROC或F1
    • 即使把所有人预测为”不违约”,错误率也只有1%!
  • 涨跌预测:类别大致平衡 → 用错误率即可
  • 量化选股:关注排序能力 → 用AUC-ROC

CV与过拟合诊断

如何用CV识别过拟合?

关键信号:训练误差远低于CV误差

场景 训练误差 CV误差 诊断
训练≈CV,两者都低 ✅ 良好拟合
训练≈CV,两者都高 ⚠️ 欠拟合
训练≪CV 很低 过拟合
训练≈0 ~0 很高 严重过拟合

A股量化策略的典型模式

  • 在样本内(2018-2022)年化收益40%,最大回撤5%
  • 10折CV显示年化收益仅8%,最大回撤30%
  • 结论:策略严重过拟合历史数据

自助法的核心思想:用”已有”模拟”未知”

自助法核心思想 自助法通过有放回抽样从原始数据中模拟重复实验的过程 自助法 (Bootstrap) 核心思想 原始样本 Z = {z₁, z₂, ..., zₙ} ↓ 有放回抽样 (同一观测可被多次抽中) Z*¹ → α̂*¹ Z*² → α̂*² ··· Z*ᴮ → α̂*ᴮ ↓ 收集B个估计值 {α̂*¹, α̂*², ..., α̂*ᴮ} → 经验分布 ↓ 从经验分布中提取 标准误 SE(α̂) 95%置信区间 Bootstrap 标准误公式: SE_B(α̂) = √[ 1/(B-1) · Σᵣ (α̂*ʳ - mean(α̂*))² ]
Figure 8: 自助法的完整流程:有放回抽样 → 计算统计量 → 构建经验分布 → 提取标准误和置信区间

Bootstrap的统计学原理

Bootstrap的理论基础插值原理(Plug-in Principle)

  • 总体分布 \(F\) 未知 → 用经验分布 \(\hat{F}_n\) 替代
  • 经验分布把概率 \(1/n\) 均匀分配到每个观测 \(z_i\)
  • \(\hat{F}_n\) 抽样 = 有放回地从原始样本中抽取

形式化表述

设统计量 \(\hat{\theta} = s(\mathbf{Z})\) 是关于数据 \(\mathbf{Z}\) 的函数。

  • 理想世界:从 \(F\) 反复抽取新样本,得到 \(\hat{\theta}\) 的抽样分布
  • Bootstrap世界:从 \(\hat{F}_n\) 反复抽取,得到 \(\hat{\theta}^*\) 的Bootstrap分布

\[ \large{ \hat{F}_n \xrightarrow{n \to \infty} F \implies \text{Bootstrap分布} \xrightarrow{n \to \infty} \text{真实抽样分布} } \]

大样本理论保证:当 \(n \to \infty\),Bootstrap分布一致收敛到真实抽样分布。

Bootstrap样本的构造过程

有放回抽样的具体操作

设原始样本有 \(n = 5\) 个观测:\(\{z_1, z_2, z_3, z_4, z_5\}\)

Bootstrap样本 抽取结果 独特观测 重复观测
\(Z^{*1}\) \(\{z_2, z_1, z_2, z_5, z_2\}\) \(z_1, z_2, z_5\) \(z_2\)出现3次
\(Z^{*2}\) \(\{z_3, z_3, z_1, z_4, z_3\}\) \(z_1, z_3, z_4\) \(z_3\)出现3次
\(Z^{*3}\) \(\{z_5, z_2, z_4, z_2, z_1\}\) \(z_1, z_2, z_4, z_5\) \(z_2\)出现2次

关键特征

  • 每个Bootstrap样本大小与原始样本相同(都是 \(n\)
  • 某些观测可能出现多次,某些观测可能不出现
  • 平均每个样本包含原始数据的 63.2% 独特观测

投资组合优化案例:寻找最小方差配比

问题:将资金投资于海康威视(X)和宁波银行(Y),如何确定最优配比 \(\alpha\) 使投资组合风险最小?

\[ \large{ \alpha = \frac{\sigma_Y^2 - \sigma_{XY}}{\sigma_X^2 + \sigma_Y^2 - 2\sigma_{XY}} } \tag{15}\]

  • \(\sigma_X^2 = \text{Var}(X)\):海康威视收益率方差
  • \(\sigma_Y^2 = \text{Var}(Y)\):宁波银行收益率方差
  • \(\sigma_{XY} = \text{Cov}(X, Y)\):两资产收益率协方差
  • \(\alpha\):投资在海康威视上的比例,\(1-\alpha\) 投资在宁波银行

关键问题\(\hat{\alpha}\)不确定性有多大?仅凭100天数据估计的 \(\alpha\),置信区间是多少?

→ 这正是 Bootstrap 大显身手的场景!

最小方差投资组合的推导

补充说明:\(\alpha\) 最优解的推导过程

投资组合收益率:\(R_p = \alpha X + (1-\alpha)Y\)

投资组合方差:

\[ \large{ \text{Var}(R_p) = \alpha^2\sigma_X^2 + (1-\alpha)^2\sigma_Y^2 + 2\alpha(1-\alpha)\sigma_{XY} } \tag{16}\]

\(\alpha\) 求导并令导数为零:

\[ \large{ \frac{d\text{Var}(R_p)}{d\alpha} = 2\alpha\sigma_X^2 - 2(1-\alpha)\sigma_Y^2 + 2(1-2\alpha)\sigma_{XY} = 0 } \]

整理得:

\[ \large{ \alpha(\sigma_X^2 + \sigma_Y^2 - 2\sigma_{XY}) = \sigma_Y^2 - \sigma_{XY} } \]

\[ \large{ \alpha^* = \frac{\sigma_Y^2 - \sigma_{XY}}{\sigma_X^2 + \sigma_Y^2 - 2\sigma_{XY}} } \]

直觉解读

  • 如果 \(\sigma_X^2 = \sigma_Y^2\)\(\sigma_{XY} = 0\)\(\alpha^* = 0.5\)(等权)
  • \(Y\) 波动越大 → \(\alpha\) 越大(更多投海康威视)
  • 两资产正相关越强 → 分散化效果越差

最小方差投资组合的推导

补充说明:\(\alpha\) 最优解的推导过程

投资组合收益率:\(R_p = \alpha X + (1-\alpha)Y\)

投资组合方差:

\[ \large{ \text{Var}(R_p) = \alpha^2\sigma_X^2 + (1-\alpha)^2\sigma_Y^2 + 2\alpha(1-\alpha)\sigma_{XY} } \tag{17}\]

\(\alpha\) 求导并令导数为零:

\[ \large{ \frac{d\text{Var}(R_p)}{d\alpha} = 2\alpha\sigma_X^2 - 2(1-\alpha)\sigma_Y^2 + 2(1-2\alpha)\sigma_{XY} = 0 } \]

整理得:

\[ \large{ \alpha(\sigma_X^2 + \sigma_Y^2 - 2\sigma_{XY}) = \sigma_Y^2 - \sigma_{XY} } \]

\[ \large{ \alpha^* = \frac{\sigma_Y^2 - \sigma_{XY}}{\sigma_X^2 + \sigma_Y^2 - 2\sigma_{XY}} } \]

直觉解读

  • 如果 \(\sigma_X^2 = \sigma_Y^2\)\(\sigma_{XY} = 0\)\(\alpha^* = 0.5\)(等权)
  • \(Y\) 波动越大 → \(\alpha\) 越大(更多投海康威视)
  • 两资产正相关越强 → 分散化效果越差

Bootstrap实证:海康威视与宁波银行

Code
# 读取两只股票的收盘价序列并对齐交易日
if 'order_book_id' in stock_price_data.index.names:  # 判断多层索引
    haikang_prices = stock_price_data.xs('002415.XSHE', level='order_book_id').sort_index()['close']  # 提取海康威视收盘价
    ningbo_bank_prices = stock_price_data.xs('002142.XSHE', level='order_book_id').sort_index()['close']  # 提取宁波银行收盘价
else:  # 普通索引分支
    stock_reset = stock_price_data.reset_index()  # 重置索引
    haikang_prices = stock_reset[stock_reset['order_book_id'] == '002415.XSHE'].set_index('date')['close']  # 筛选海康威视
    ningbo_bank_prices = stock_reset[stock_reset['order_book_id'] == '002142.XSHE'].set_index('date')['close']  # 筛选宁波银行

# 对齐交易日期并计算日收益率
merged_prices = pd.concat([haikang_prices, ningbo_bank_prices], axis=1, join='inner').dropna()  # 内连接对齐并删除缺失值
merged_prices.columns = ['haikang_close', 'ningbo_close']  # 重命名列
daily_returns = merged_prices.pct_change().dropna()  # 计算日收益率
daily_returns.columns = ['X_haikang', 'Y_ningbo']  # X=海康威视收益率,Y=宁波银行收益率
sample_returns = daily_returns.iloc[-100:]  # 取最近100个交易日作为样本
print(f'样本量: {len(sample_returns)} 个交易日')  # 输出样本量
print(f'海康威视日均收益: {sample_returns["X_haikang"].mean():.6f}')  # 海康威视平均日收益
print(f'宁波银行日均收益: {sample_returns["Y_ningbo"].mean():.6f}')  # 宁波银行平均日收益
样本量: 100 个交易日
海康威视日均收益: 0.000397
宁波银行日均收益: 0.000020
Figure 9
# 定义最优投资比例α的计算函数(马科维茨最小方差公式)
def compute_optimal_alpha(returns_data, sample_indices):  # 定义计算最优α的函数
    '''根据给定索引的收益率子集,计算使投资组合方差最小的α值'''
    x_returns = returns_data.iloc[sample_indices, 0].values  # 海康威视收益率子集
    y_returns = returns_data.iloc[sample_indices, 1].values  # 宁波银行收益率子集
    variance_x = np.var(x_returns, ddof=1)  # 海康威视收益率方差(无偏估计)
    variance_y = np.var(y_returns, ddof=1)  # 宁波银行收益率方差(无偏估计)
    covariance_xy = np.cov(x_returns, y_returns, ddof=1)[0, 1]  # 两资产协方差
    optimal_alpha = (variance_y - covariance_xy) / (variance_x + variance_y - 2 * covariance_xy)  # 马科维茨最优配比
    return optimal_alpha  # 返回最优投资比例

# 用完整样本计算α的点估计
observation_count = len(sample_returns)  # 样本观测数
original_alpha_estimate = compute_optimal_alpha(sample_returns, range(observation_count))  # 原始样本的α估计
print(f'原始α估计值: {original_alpha_estimate:.4f}')  # 输出α的点估计
print(f'解读: 约{original_alpha_estimate*100:.1f}%配置海康威视,{(1-original_alpha_estimate)*100:.1f}%配置宁波银行')  # 配比解读
原始α估计值: 0.3322
解读: 约33.2%配置海康威视,66.8%配置宁波银行

解读Bootstrap实证结果

从数据中学到了什么?

  • \(\hat{\alpha} \approx 0.33\):约1/3资金配置海康威视,2/3配置宁波银行
  • 直觉:海康威视(科技股)波动率高于宁波银行(银行股),所以应少配

Bootstrap揭示的不确定性

  • 标准误 \(\text{SE}(\hat{\alpha}) \approx 0.07\)
  • 95%近似置信区间\(\hat{\alpha} \pm 1.96 \times \text{SE} \approx [0.19, 0.47]\)
  • 含义:投资比例可能在19%到47%之间波动!

对基金经理的启示

  • 点估计 \(\hat{\alpha} = 0.33\) 看似精确
  • 但Bootstrap告诉我们:仅凭100天数据,这个估计很不稳定
  • 如果要更精确,需要更多数据额外约束(如行业配置限制)

解读Bootstrap实证结果

从数据中学到了什么?

  • \(\hat{\alpha} \approx 0.33\):约1/3资金配置海康威视,2/3配置宁波银行
  • 直觉:海康威视(科技股)波动率高于宁波银行(银行股),所以应少配

Bootstrap揭示的不确定性

  • 标准误 \(\text{SE}(\hat{\alpha}) \approx 0.07\)
  • 95%近似置信区间\(\hat{\alpha} \pm 1.96 \times \text{SE} \approx [0.19, 0.47]\)
  • 含义:投资比例可能在19%到47%之间波动!

对基金经理的启示

  • 点估计 \(\hat{\alpha} = 0.33\) 看似精确
  • 但Bootstrap告诉我们:仅凭100天数据,这个估计很不稳定
  • 如果要更精确,需要更多数据额外约束(如行业配置限制)

1000次Bootstrap揭示α的不确定性

Code
np.random.seed(42)  # 固定随机种子确保可复现

# 执行1000次Bootstrap重采样
bootstrap_iterations = 1000  # Bootstrap重复次数
bootstrap_alpha_values = []  # 存储每次Bootstrap的α估计

for _ in range(bootstrap_iterations):  # 循环1000次
    resampled_indices = np.random.choice(range(observation_count), size=observation_count, replace=True)  # 有放回抽样100个索引
    bootstrap_alpha = compute_optimal_alpha(sample_returns, resampled_indices)  # 在重抽样数据上计算α
    bootstrap_alpha_values.append(bootstrap_alpha)  # 记录本次Bootstrap的α

bootstrap_alpha_values = np.array(bootstrap_alpha_values)  # 转换为NumPy数组
bootstrap_se = np.std(bootstrap_alpha_values, ddof=1)  # Bootstrap标准误 = α估计值们的标准差
print(f'Bootstrap标准误: {bootstrap_se:.4f}')  # 输出标准误
Bootstrap标准误: 0.0717
Figure 10
# 生成模拟"真实"分布作为对比基准(教学用途:假设已知总体参数)
simulated_alpha_values = []  # 存储模拟分布的α
population_mean = sample_returns.mean()  # 用样本均值近似总体均值
population_cov = sample_returns.cov()  # 用样本协方差近似总体协方差

for _ in range(1000):  # 从"已知总体"中模拟1000次抽样
    simulated_data = np.random.multivariate_normal(population_mean, population_cov, observation_count)  # 从多元正态分布抽取新样本
    simulated_df = pd.DataFrame(simulated_data)  # 转换为DataFrame
    sim_alpha = compute_optimal_alpha(simulated_df, range(observation_count))  # 计算模拟样本的α
    simulated_alpha_values.append(sim_alpha)  # 追加记录

simulated_alpha_values = np.array(simulated_alpha_values)  # 转换为NumPy数组
# 绘制三张对比图
fig, axes = plt.subplots(1, 3, figsize=(18, 5))  # 创建1行3列子图

# 左图:模拟的"真实"抽样分布
axes[0].hist(simulated_alpha_values, bins=40, alpha=0.7, color='#3498DB', edgecolor='black')  # 绘制直方图
axes[0].axvline(np.mean(simulated_alpha_values), color='red', linestyle='--', label=f'均值: {np.mean(simulated_alpha_values):.3f}')  # 标记均值
axes[0].set_xlabel('α', fontsize=12)  # X轴标签
axes[0].set_title('模拟抽样分布\n(上帝视角基准)', fontsize=13)  # 标题
axes[0].legend()  # 图例
axes[0].grid(True, alpha=0.3)  # 网格

# 中图:Bootstrap分布
axes[1].hist(bootstrap_alpha_values, bins=40, alpha=0.7, color='#E74C3C', edgecolor='black')  # 绘制直方图
axes[1].axvline(original_alpha_estimate, color='blue', linewidth=2, label=f'原始估计: {original_alpha_estimate:.3f}')  # 标记原始估计
axes[1].set_xlabel('α', fontsize=12)  # X轴标签
axes[1].set_title(f'Bootstrap分布\n(SE = {bootstrap_se:.3f})', fontsize=13)  # 标题
axes[1].legend()  # 图例
axes[1].grid(True, alpha=0.3)  # 网格

# 右图:箱线图对比
axes[2].boxplot([simulated_alpha_values, bootstrap_alpha_values], labels=['模拟分布', 'Bootstrap'], patch_artist=True, boxprops=dict(facecolor='lightblue'))  # 箱线图
axes[2].set_title('分布对比', fontsize=13)  # 标题
axes[2].grid(True, alpha=0.3, axis='y')  # Y轴网格

plt.tight_layout()  # 调整布局
plt.show()  # 显示图表

Bootstrap置信区间的三种构造方法

方法 公式 优点 缺点
正态近似法 \(\hat{\theta} \pm z_{\alpha/2} \cdot \text{SE}_B\) 最简单 假设正态分布
百分位法 \([\hat{\theta}^*_{(\alpha/2)}, \hat{\theta}^*_{(1-\alpha/2)}]\) 不假设正态 样本量要求高
BCa法 偏差校正加速 最精确 计算复杂

百分位法的实现

# 1000个Bootstrap估计值
ci_lower = np.percentile(bootstrap_alphas, 2.5)
ci_upper = np.percentile(bootstrap_alphas, 97.5)

BCa法(Bias-Corrected and Accelerated):

  • 校正Bootstrap分布的偏斜偏差
  • 在小样本和非对称分布下更准确
  • Python中的 scipy.stats.bootstrap 已内置BCa方法

Bootstrap重采样次数B如何确定?

B的选择指南

目标 推荐B值 原因
标准误估计 B = 200-1000 SE收敛较快
95%置信区间 B ≥ 1000 尾部百分位需要更多样本
99%置信区间 B ≥ 5000 极端百分位需要更多样本
假设检验p值 B ≥ 10000 p值精度要求高

收敛性检验

  • 在不同B值下重复运行Bootstrap
  • 如果SE估计不再显著变化,说明B已足够
  • 实践经验:B = 1000 对大多数应用已足够

计算时间的权衡

  • \(B = 1000\),简单统计量:< 1秒
  • \(B = 1000\),复杂模型(如随机森林):可能数小时
  • 此时可考虑并行Bootstrap:各次重采样独立,天然支持并行计算

Bootstrap置信区间的三种构造方法

方法 公式 优点 缺点
正态近似法 \(\hat{\theta} \pm z_{\alpha/2} \cdot \text{SE}_B\) 最简单 假设正态分布
百分位法 \([\hat{\theta}^*_{(\alpha/2)}, \hat{\theta}^*_{(1-\alpha/2)}]\) 不假设正态 样本量要求高
BCa法 偏差校正加速 最精确 计算复杂

百分位法的实现

# 1000个Bootstrap估计值
ci_lower = np.percentile(bootstrap_alphas, 2.5)
ci_upper = np.percentile(bootstrap_alphas, 97.5)

BCa法(Bias-Corrected and Accelerated):

  • 校正Bootstrap分布的偏斜偏差
  • 在小样本和非对称分布下更准确
  • Python中的 scipy.stats.bootstrap 已内置BCa方法

Bootstrap重采样次数B如何确定?

B的选择指南

目标 推荐B值 原因
标准误估计 B = 200-1000 SE收敛较快
95%置信区间 B ≥ 1000 尾部百分位需要更多样本
99%置信区间 B ≥ 5000 极端百分位需要更多样本
假设检验p值 B ≥ 10000 p值精度要求高

收敛性检验

  • 在不同B值下重复运行Bootstrap
  • 如果SE估计不再显著变化,说明B已足够
  • 实践经验:B = 1000 对大多数应用已足够

计算时间的权衡

  • \(B = 1000\),简单统计量:< 1秒
  • \(B = 1000\),复杂模型(如随机森林):可能数小时
  • 此时可考虑并行Bootstrap:各次重采样独立,天然支持并行计算

为什么Bootstrap有效?63.2%定律

每个Bootstrap样本大约包含原始数据的63.2%独特观测

数学推导:对于第 \(i\) 个观测点:

  • 单次抽取中未被选中的概率:\(1 - \frac{1}{n}\)
  • \(n\) 次抽取中始终未被选中:\(\left(1 - \frac{1}{n}\right)^n\)
  • \(n \to \infty\)\(\left(1 - \frac{1}{n}\right)^n \to \frac{1}{e} \approx 0.368\)

因此:

\[ \large{ P(\text{被选中}) = 1 - \left(1 - \frac{1}{n}\right)^n \approx 1 - \frac{1}{e} \approx 0.632 } \tag{18}\]

袋外数据(OOB):未被选中的约36.8%的观测可作为天然验证集,无需显式数据切分。

63.2%定律的数值验证

理论推导回顾

\[ \large{ \lim_{n \to \infty}\left(1 - \frac{1}{n}\right)^n = \frac{1}{e} \approx 0.3679 } \]

不同样本量下的实际比例

样本量 \(n\) 未被选中概率 \((1-1/n)^n\) 被选中概率
5 0.3277 0.6723
10 0.3487 0.6513
50 0.3642 0.6358
100 0.3660 0.6340
1000 0.3677 0.6323
\(\infty\) 0.3679 0.6321

即使 \(n\) 很小(如50),近似也已经很好了。

63.2%定律的数值验证

理论推导回顾

\[ \large{ \lim_{n \to \infty}\left(1 - \frac{1}{n}\right)^n = \frac{1}{e} \approx 0.3679 } \]

不同样本量下的实际比例

样本量 \(n\) 未被选中概率 \((1-1/n)^n\) 被选中概率
5 0.3277 0.6723
10 0.3487 0.6513
50 0.3642 0.6358
100 0.3660 0.6340
1000 0.3677 0.6323
\(\infty\) 0.3679 0.6321

即使 \(n\) 很小(如50),近似也已经很好了。

Bootstrap标准误的正式公式

Bootstrap标准误的计算公式如下:

\[ \large{ \text{SE}_B(\hat{\alpha}) = \sqrt{\frac{1}{B-1}\sum_{r=1}^{B}\left(\hat{\alpha}^{*r} - \overline{\hat{\alpha}^*}\right)^2} } \tag{19}\]

其中:

  • \(B\):Bootstrap重采样次数(通常 \(B = 1000\) 或更大)
  • \(\hat{\alpha}^{*r}\):第 \(r\) 个Bootstrap样本计算得到的统计量
  • \(\overline{\hat{\alpha}^*} = \frac{1}{B}\sum_{r=1}^{B}\hat{\alpha}^{*r}\):所有Bootstrap估计的均值

Bootstrap置信区间(百分位法):

\[ \large{ \text{CI}_{95\%} = \left[\hat{\alpha}^*_{(0.025)},\; \hat{\alpha}^*_{(0.975)}\right] } \tag{20}\]

即取1000个 \(\hat{\alpha}^*\) 值的第2.5百分位数和第97.5百分位数。

Bootstrap vs. 传统标准误:何时使用Bootstrap?

传统方法(解析公式):

  • 样本均值的SE:\(\text{SE}(\bar{X}) = s / \sqrt{n}\)
  • 回归系数的SE:\(\text{SE}(\hat{\beta}) = \sqrt{\hat{\sigma}^2(\mathbf{X}^T\mathbf{X})^{-1}}\)
  • 优点:计算快,有理论保证
  • 缺点:依赖分布假设(如正态性、同方差性)

Bootstrap方法

  • 不需要解析公式
  • 不依赖分布假设
  • 适用于任何统计量——哪怕是中位数、分位数、比率等复杂统计量

何时应该用Bootstrap?

  1. 统计量的抽样分布没有解析公式(如中位数、夏普比率)
  2. 数据不满足传统公式的假设(如非正态、异方差)
  3. 需要复杂统计量的标准误(如两只股票夏普比率之差

Bootstrap vs. 传统标准误:何时使用Bootstrap?

传统方法(解析公式):

  • 样本均值的SE:\(\text{SE}(\bar{X}) = s / \sqrt{n}\)
  • 回归系数的SE:\(\text{SE}(\hat{\beta}) = \sqrt{\hat{\sigma}^2(\mathbf{X}^T\mathbf{X})^{-1}}\)
  • 优点:计算快,有理论保证
  • 缺点:依赖分布假设(如正态性、同方差性)

Bootstrap方法

  • 不需要解析公式
  • 不依赖分布假设
  • 适用于任何统计量——哪怕是中位数、分位数、比率等复杂统计量

何时应该用Bootstrap?

  1. 统计量的抽样分布没有解析公式(如中位数、夏普比率)
  2. 数据不满足传统公式的假设(如非正态、异方差)
  3. 需要复杂统计量的标准误(如两只股票夏普比率之差

Bootstrap的适用范围与局限性

维度 说明
适用范围 几乎任何统计方法的标准误和置信区间
核心优势 不依赖强分布假设,实现简单
局限1:小样本 样本量过小时,经验分布不能很好代表总体
局限2:非iid数据 时间序列需用分块Bootstrap (Block Bootstrap)
局限3:极端统计量 对极值统计量(如最大值)效果不佳
局限4:计算成本 大数据集 + 复杂模型时计算开销显著

金融领域特别注意

  • 股票收益率存在自相关波动率聚集(GARCH效应)
  • 标准Bootstrap假设iid可能不成立
  • 建议使用移动分块Bootstrap野生Bootstrap (Wild Bootstrap)

重采样方法在机器学习流程中的位置

完整的机器学习工作流

  1. 数据预处理:清洗、特征工程
  2. 模型选择:用k折CV比较候选模型 ← 交叉验证
  3. 超参数调优:用CV搜索最优参数 ← 交叉验证
  4. 性能评估:用CV或独立测试集评估 ← 交叉验证
  5. 不确定性量化:用Bootstrap计算置信区间 ← 自助法

注意:数据预处理必须包含在CV循环内!

  • ❌ 错误:先对全部数据标准化,再做CV
    • 验证集的均值和方差信息泄露到了训练过程中
  • ✅ 正确:每折内部独立做标准化
    • 用训练折的统计量标准化验证折

本章核心公式速查表

方法 核心公式 关键参数
验证集MSE \(\text{MSE}_\mathcal{V} = \frac{1}{m}\sum_{i\in\mathcal{V}}(y_i-\hat{y}_i)^2\) 切分比例
k折CV \(\text{CV}_{(k)} = \frac{1}{k}\sum_{i=1}^k \text{MSE}_i\) \(k=5\)\(10\)
LOOCV \(\text{CV}_{(n)} = \frac{1}{n}\sum_{i=1}^n\left(\frac{y_i-\hat{y}_i}{1-h_i}\right)^2\) 杠杆值 \(h_i\)
Bootstrap SE \(\text{SE}_B = \sqrt{\frac{1}{B-1}\sum_{r=1}^B(\hat{\alpha}^{*r}-\bar{\hat{\alpha}}^*)^2}\) \(B \geq 1000\)
最优 \(\alpha\) \(\alpha^* = \frac{\sigma_Y^2 - \sigma_{XY}}{\sigma_X^2 + \sigma_Y^2 - 2\sigma_{XY}}\) 方差、协方差
63.2%定律 \(P(\text{入选}) = 1-(1-1/n)^n \approx 0.632\) 样本量 \(n\)

重采样方法在机器学习流程中的位置

完整的机器学习工作流

  1. 数据预处理:清洗、特征工程
  2. 模型选择:用k折CV比较候选模型 ← 交叉验证
  3. 超参数调优:用CV搜索最优参数 ← 交叉验证
  4. 性能评估:用CV或独立测试集评估 ← 交叉验证
  5. 不确定性量化:用Bootstrap计算置信区间 ← 自助法

注意:数据预处理必须包含在CV循环内!

  • ❌ 错误:先对全部数据标准化,再做CV
    • 验证集的均值和方差信息泄露到了训练过程中
  • ✅ 正确:每折内部独立做标准化
    • 用训练折的统计量标准化验证折

本章核心公式速查表

方法 核心公式 关键参数
验证集MSE \(\text{MSE}_\mathcal{V} = \frac{1}{m}\sum_{i\in\mathcal{V}}(y_i-\hat{y}_i)^2\) 切分比例
k折CV \(\text{CV}_{(k)} = \frac{1}{k}\sum_{i=1}^k \text{MSE}_i\) \(k=5\)\(10\)
LOOCV \(\text{CV}_{(n)} = \frac{1}{n}\sum_{i=1}^n\left(\frac{y_i-\hat{y}_i}{1-h_i}\right)^2\) 杠杆值 \(h_i\)
Bootstrap SE \(\text{SE}_B = \sqrt{\frac{1}{B-1}\sum_{r=1}^B(\hat{\alpha}^{*r}-\bar{\hat{\alpha}}^*)^2}\) \(B \geq 1000\)
最优 \(\alpha\) \(\alpha^* = \frac{\sigma_Y^2 - \sigma_{XY}}{\sigma_X^2 + \sigma_Y^2 - 2\sigma_{XY}}\) 方差、协方差
63.2%定律 \(P(\text{入选}) = 1-(1-1/n)^n \approx 0.632\) 样本量 \(n\)

本章知识体系总结

重采样方法知识体系 交叉验证和自助法的完整知识体系总结图 第5章 知识体系总结 交叉验证 (Cross-Validation) 验证集法 → 高方差 LOOCV → 低偏差高方差 k折CV → 最优平衡 时间序列CV → 金融专用 CV₍ₖ₎ = (1/k) Σ MSEᵢ 推荐 k=5 或 k=10 自助法 (Bootstrap) 有放回抽样B次 63.2%独特观测 标准误 SE(α̂) 百分位置信区间 SE_B = √[1/(B-1) Σ(α̂*ʳ - mean)²] 通常 B=1000 或更大 共同本质:用"数据自身"模拟"重复实验" 实际应用建议 模型选择 k折CV (k=5或10) 模型评估 独立测试集 / CV 不确定性量化 Bootstrap标准误 金融数据 时间序列CV 本章实证:海康威视多项式回归CV · 海康威视/宁波银行Bootstrap投资组合优化
Figure 11: 重采样方法知识体系总结:交叉验证用于模型评估与选择,自助法用于量化不确定性