02 统计学习

核心问题:什么是统计学习?

统计学习是一套用于理解数据的工具集。

  • 监督学习:基于输入变量,构建统计模型来预测或估计输出变量
  • 无监督学习:没有明确的输出变量,从数据中发现结构和模式

核心思想:假设输出变量 \(Y\) 与输入变量 \(X = (X_1, X_2, \dots, X_p)\) 之间存在某种关系:

\[ \large{Y = f(X) + \epsilon} \]

  • \(f\):未知的系统性函数,捕捉 \(X\)\(Y\) 的影响
  • \(\epsilon\):随机误差项,均值为零,与 \(X\) 独立

直觉理解:\(f\) 是连接输入与输出的桥梁

统计学习核心概念图 展示输入变量X通过函数f映射到输出Y,加上随机误差项epsilon 输入 X X₁: 研发投入 X₂: 营收规模 X₃: 资产负债率 f(X) 未知系统性函数 输出 Y 利润增长率 + ε 随机误差 Y = f(X) + ε 统计学习的目标:估计这个未知的 f
Figure 1: 统计学习的核心概念:输入变量 \(X\) 通过函数 \(f\) 映射到输出 \(Y\)

实例:上市公司营收与利润的关系

以长三角地区上市公司为例,探索营业收入与净利润之间的系统性关系。

Code
import pandas as pd  # 导入pandas用于数据框操作
import numpy as np  # 导入numpy用于数值计算
import matplotlib.pyplot as plt  # 导入matplotlib用于可视化
import os  # 导入os模块用于跨平台路径

# 根据操作系统自动选择本地数据路径
DATA_ROOT = 'C:/qiufei/data' if os.name == 'nt' else '/home/ubuntu/r2_data_mount/qiufei/data'

# 读取上市公司基本信息和财务报表数据
stock_basic_info = pd.read_hdf(os.path.join(DATA_ROOT, 'stock/stock_basic_data.h5'))  # 上市公司基本信息
financial_statements = pd.read_hdf(os.path.join(DATA_ROOT, 'stock/financial_statement.h5'))  # 财务报表数据
Figure 2

数据可视化:营收-利润散点图

Code
# 筛选长三角四省市上市公司
yrd_provinces = ['上海市', '江苏省', '浙江省', '安徽省']  # 长三角四省市列表(与数据中省份全称匹配)
yrd_stock_codes = stock_basic_info[stock_basic_info['province'].isin(yrd_provinces)]['order_book_id'].tolist()  # 获取长三角地区股票代码

# 筛选2023年年报数据(quarter列格式为'2023q4'表示年报)
annual_report_2023 = financial_statements[
    (financial_statements['order_book_id'].isin(yrd_stock_codes)) &  # 限定长三角公司
    (financial_statements['quarter'] == '2023q4')  # 筛选2023年年报(第四季度)
].copy()  # 创建副本避免链式赋值警告

# 取营业收入和净利润两列,去除缺失值
revenue_profit_data = annual_report_2023[['revenue', 'net_profit']].dropna()  # 提取核心财务指标
# 转换为亿元单位便于阅读
revenue_profit_data['revenue_billion'] = revenue_profit_data['revenue'] / 1e8  # 营业收入转为亿元
revenue_profit_data['profit_billion'] = revenue_profit_data['net_profit'] / 1e8  # 净利润转为亿元
# 剔除极端值:仅保留营收在(0, 500亿)区间的公司
plot_data = revenue_profit_data[
    (revenue_profit_data['revenue_billion'] > 0) &  # 排除营收为负或零的公司
    (revenue_profit_data['revenue_billion'] < 500)  # 排除超大型企业以免影响可视化
].copy()  # 创建绘图用数据子集
Figure 3
Code
# 创建散点图画布
fig, ax_scatter = plt.subplots(figsize=(10, 6))  # 设置画布尺寸
ax_scatter.scatter(
    plot_data['revenue_billion'],  # X轴:营业收入(亿元)
    plot_data['profit_billion'],  # Y轴:净利润(亿元)
    alpha=0.4, s=30, color='#00A4E6'  # 设置透明度、点大小和颜色
)

# 添加LOWESS平滑趋势线展示系统性关系f(X)
from statsmodels.nonparametric.smoothers_lowess import lowess  # 导入LOWESS非参数平滑
sorted_data = plot_data.sort_values('revenue_billion')  # 按营收升序排列
smoothed_trend = lowess(
    sorted_data['profit_billion'],  # 因变量:净利润
    sorted_data['revenue_billion'],  # 自变量:营收
    frac=0.3  # 平滑参数:使用30%数据点的局部窗口
)
ax_scatter.plot(smoothed_trend[:, 0], smoothed_trend[:, 1], 'r-', linewidth=2.5, label='f̂(X) — LOWESS趋势')  # 绘制趋势线

ax_scatter.set_xlabel('营业收入(亿元)', fontsize=12)  # X轴标签
ax_scatter.set_ylabel('净利润(亿元)', fontsize=12)  # Y轴标签
ax_scatter.legend(fontsize=11)  # 显示图例
ax_scatter.grid(True, alpha=0.3)  # 添加网格线
plt.tight_layout()  # 自动调整布局
plt.show()  # 显示散点图
Figure 4: 长三角上市公司营收vs净利润 (2023年报)

为什么要估计 \(f\)?—— 两大目的

估计 \(f\) 的两个主要原因:

目的 核心关注 \(\hat{f}\) 的要求 金融应用案例
预测 \(\hat{Y}\) 的准确性 精度高即可,可为”黑箱” 股价趋势预测、信用违约概率
推断 \(X\) 如何影响 \(Y\) 必须可解释 股权激励 → ROE?研发投入 → 利润增长?

预测:将 \(\hat{f}\) 视为黑箱

当目标是预测时:

\[ \large{\hat{Y} = \hat{f}(X)} \]

预测误差可分解为两部分:

\[ \large{E(Y - \hat{Y})^2 = \underbrace{[f(X) - \hat{f}(X)]^2}_{\text{可约误差}} + \underbrace{\text{Var}(\epsilon)}_{\text{不可约误差}}} \]

  • 可约误差:通过改进模型 \(\hat{f}\) 可以减小
  • 不可约误差:数据中固有的随机噪声,模型无法消除

关键启示:即使估计出完美的 \(f\),预测仍然存在误差。

推断:理解 \(X\)\(Y\) 的关系

当目标是推断时,我们关心的核心问题:

  1. 哪些变量与 \(Y\) 相关?
    • 在众多财务指标中,哪些真正影响股价表现?
  2. \(X\)\(Y\) 的关系方向与强度?
    • 研发投入增加10%,利润增长多少?
  3. 这种关系是线性还是非线性?
    • 资产负债率与破产概率是否呈加速递增关系?

推断要求模型必须是可解释的——这就是为什么在学术论文和政策研究中,线性模型仍然是主流。

如何估计 \(f\)?—— 参数化方法

参数化方法分两步:

第一步:假设函数形式

例如,假设 \(f\) 是线性的:

\[ \large{f(X) = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \cdots + \beta_p X_p} \]

第二步:用数据拟合参数

使用最小二乘法(OLS)等方法估计 \(\beta_0, \beta_1, \dots, \beta_p\)

优势:参数少,估计简单,可解释性强

风险:如果真实 \(f\) 不是线性的,模型将永远有偏差

如何估计 \(f\)?—— 非参数化方法

非参数化方法:不对 \(f\) 的函数形式做假设,让数据决定形状。

常见方法:

  • 样条回归 (Splines):局部多项式拟合
  • 核回归 (Kernel Regression):基于距离的加权平均
  • 决策树 (Decision Trees):递归划分特征空间
  • 随机森林 (Random Forests):多棵树的集成
  • 神经网络 (Neural Networks):多层非线性变换

优势:可以捕捉任意复杂的非线性关系

风险:需要大量数据;容易过拟合;可解释性差

核心权衡:灵活性 vs 可解释性

灵活性与可解释性权衡图 展示不同统计学习方法在灵活性和可解释性之间的取舍关系 灵活性 (Flexibility) → 可解释性 (Interpretability) → 子集选择 Lasso OLS GAM 决策树 随机森林 SVM 深度学习
Figure 5: 不同统计学习方法在灵活性-可解释性谱系上的位置

回归 vs 分类:取决于输出类型

维度 回归 (Regression) 分类 (Classification)
输出变量 连续型(定量) 离散型(定性/类别)
典型场景 预测股价收益率 预测公司是否违约
评估指标 MSE, RMSE, R² 准确率, AUC, F1
典型方法 线性回归, Ridge, Lasso 逻辑回归, SVM, 决策树

注意:有些方法(如KNN、决策树、神经网络)既适用于回归也适用于分类。

衡量模型好坏:均方误差 (MSE)

在回归问题中,最常用的评估指标是均方误差 (Mean Squared Error):

\[ \large{\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{f}(x_i))^2} \]

关键区分:

  • 训练 MSE:在训练数据上计算 → 衡量模型对已知数据的拟合程度
  • 测试 MSE:在未见过的数据上计算 → 衡量模型的泛化能力

核心原则:我们真正关心的永远是测试 MSE,而非训练 MSE。

过拟合现象:训练误差与测试误差的分道扬镳

Code
from sklearn.preprocessing import PolynomialFeatures  # 导入多项式特征变换器
from sklearn.linear_model import LinearRegression  # 导入线性回归模型
from sklearn.metrics import mean_squared_error  # 导入均方误差评估函数

# 使用A股上市公司数据模拟过拟合实验
np.random.seed(42)  # 固定随机种子确保实验可重复
sample_size = 120  # 设定模拟样本数量
x_simulation = np.sort(np.random.uniform(0, 10, sample_size))  # 生成均匀分布的自变量
# 真实函数:非线性关系 f(x) = sin(x) + 0.3x
true_function_values = np.sin(x_simulation) + 0.3 * x_simulation  # 计算真实函数值
# 添加高斯噪声模拟现实数据中的不可约误差
y_observed = true_function_values + np.random.normal(0, 1.5, sample_size)  # 生成含噪声的观测值

# 按60%/40%划分训练集和测试集
train_cutoff = 70  # 训练集包含前70个样本
x_train_2d = x_simulation[:train_cutoff].reshape(-1, 1)  # 训练集自变量(转为二维数组)
y_train_vec = y_observed[:train_cutoff]  # 训练集因变量
x_test_2d = x_simulation[train_cutoff:].reshape(-1, 1)  # 测试集自变量
y_test_vec = y_observed[train_cutoff:]  # 测试集因变量
Figure 6

过拟合实验结果:U型曲线的经典展示

Code
polynomial_degrees = range(1, 12)  # 尝试1至11阶多项式模型
training_mse_list = []  # 存储各阶数的训练MSE
testing_mse_list = []  # 存储各阶数的测试MSE

for degree in polynomial_degrees:  # 遍历每个多项式阶数
    poly_transform = PolynomialFeatures(degree=degree, include_bias=False)  # 创建指定阶数的多项式变换器
    x_train_poly = poly_transform.fit_transform(x_train_2d)  # 对训练数据进行多项式展开
    x_test_poly = poly_transform.transform(x_test_2d)  # 对测试数据应用相同变换
    ols_model = LinearRegression().fit(x_train_poly, y_train_vec)  # 在多项式特征上拟合OLS
    training_mse_list.append(mean_squared_error(y_train_vec, ols_model.predict(x_train_poly)))  # 记录训练MSE
    testing_mse_list.append(mean_squared_error(y_test_vec, ols_model.predict(x_test_poly)))  # 记录测试MSE

# 绘制训练MSE与测试MSE双曲线图
fig, ax_mse = plt.subplots(figsize=(10, 6))  # 创建画布
ax_mse.plot(list(polynomial_degrees), training_mse_list, 'd-', color='#10B981', linewidth=2, label='训练 MSE')  # 训练曲线
ax_mse.plot(list(polynomial_degrees), testing_mse_list, 's-', color='#EF4444', linewidth=2.5, label='测试 MSE')  # 测试曲线
optimal_degree = list(polynomial_degrees)[np.argmin(testing_mse_list)]  # 找到测试MSE最低的阶数
ax_mse.axvline(x=optimal_degree, color='#4B5563', linestyle='--', alpha=0.6, label=f'最优阶数 = {optimal_degree}')  # 最优阶数垂线
ax_mse.set_xlabel('多项式阶数(模型复杂度)', fontsize=12)  # X轴标签
ax_mse.set_ylabel('均方误差 (MSE)', fontsize=12)  # Y轴标签
ax_mse.set_yscale('log')  # Y轴使用对数刻度
ax_mse.legend(fontsize=11)  # 显示图例
ax_mse.grid(True, alpha=0.3)  # 添加网格线
plt.tight_layout()  # 自动调整布局
plt.show()  # 显示图表
Figure 7: 过拟合演示:训练MSE持续下降,但测试MSE先降后升形成U型曲线

偏差-方差权衡:过拟合的理论解释

测试 MSE 可以严格分解为三部分:

\[ \large{E(y_0 - \hat{f}(x_0))^2 = \underbrace{[\text{Bias}(\hat{f}(x_0))]^2}_{\text{偏差}^2} + \underbrace{\text{Var}(\hat{f}(x_0))}_{\text{方差}} + \underbrace{\sigma^2_\epsilon}_{\text{不可约误差}}} \]

分量 含义 灵活性↑时的变化
偏差² 模型假设与真实 \(f\) 的差距 ↓ 下降
方差 模型对不同训练集的敏感度 ↑ 上升
不可约误差 数据固有噪声 — 不变

偏差-方差权衡的可视化

Code
# 生成灵活性轴:从1到10的100个等间距点
flexibility_range = np.linspace(1, 10, 100)  # 模型灵活性连续变化

# 构建偏差-方差理论曲线
bias_squared = 20 / flexibility_range  # 偏差平方随灵活性增加而衰减(反比例)
variance_component = 0.8 * (flexibility_range - 1)**1.8  # 方差随灵活性增加而按幂律膨胀
irreducible_error = 2.0  # 不可约误差为常数
total_test_mse = bias_squared + variance_component + irreducible_error  # 总测试MSE = 三部分之和

# 创建偏差-方差权衡图
fig, ax_bv = plt.subplots(figsize=(10, 6))  # 创建画布
ax_bv.plot(flexibility_range, bias_squared, color='#DC2626', linewidth=2, label='偏差² (Bias²)')  # 偏差曲线
ax_bv.plot(flexibility_range, variance_component, color='#2563EB', linewidth=2, label='方差 (Variance)')  # 方差曲线
ax_bv.axhline(y=irreducible_error, color='#94A3B8', linestyle='--', label='不可约误差 Var(ε)')  # 噪声底线
ax_bv.plot(flexibility_range, total_test_mse, color='#1E293B', linewidth=3, label='总测试 MSE')  # 总误差曲线
# 标注最优点
optimal_idx = np.argmin(total_test_mse)  # 找到U型曲线最低点索引
ax_bv.plot(flexibility_range[optimal_idx], total_test_mse[optimal_idx], 'o', color='gold', markersize=12, markeredgecolor='black')  # 金色圆点标记最优
ax_bv.annotate('最优拟合点', xy=(flexibility_range[optimal_idx], total_test_mse[optimal_idx]),
               xytext=(flexibility_range[optimal_idx]+1, total_test_mse[optimal_idx]+4),
               arrowprops=dict(facecolor='black', shrink=0.05), fontsize=11, fontweight='bold')  # 箭头标注
ax_bv.set_xlabel('模型灵活性 →', fontsize=12)  # X轴标签
ax_bv.set_ylabel('误差 →', fontsize=12)  # Y轴标签
ax_bv.set_ylim(0, 25)  # Y轴范围
ax_bv.legend(fontsize=11)  # 显示图例
ax_bv.grid(True, alpha=0.3)  # 添加网格
plt.tight_layout()  # 调整布局
plt.show()  # 显示图表
Figure 8: 偏差-方差权衡:总测试误差的U型曲线

直觉比喻:射击靶心

偏差-方差射击靶心比喻 用四个靶心图示直觉理解偏差和方差的组合效应 偏差-方差的靶心比喻 低偏差 + 低方差 理想状态 ✓ 低偏差 + 高方差 过拟合 高偏差 + 低方差 欠拟合 高偏差 + 高方差 最差状态 ✗
Figure 9: 用射击靶心直觉理解偏差与方差的四种组合

分类问题的评估:错误率

在分类问题中,MSE 不再适用。我们使用错误率

\[ \large{\text{错误率} = \frac{1}{n} \sum_{i=1}^{n} I(y_i \neq \hat{y}_i)} \]

  • \(I(\cdot)\):指示函数,预测错误时取 1,正确时取 0
  • 准确率 = 1 − 错误率

金融应用中的评估指标:

指标 公式 金融场景
准确率 \((TP+TN)/(TP+TN+FP+FN)\) 整体分类正确比例
精确率 \(TP/(TP+FP)\) 预测违约中真正违约的比例
召回率 \(TP/(TP+FN)\) 真正违约被模型捕获的比例

偏差-方差分解的严格数学推导

设真实生成过程 \(y_0 = f(x_0) + \epsilon\)\(E[\epsilon]=0\)\(\text{Var}(\epsilon)=\sigma^2\)

第一步:展开总误差

\[ E[(y_0 - \hat{f}(x_0))^2] = E[(f(x_0) - \hat{f}(x_0))^2] + \sigma^2 \]

(利用 \(\epsilon\) 独立且均值为零消除交叉项)

第二步:令 \(\bar{f}(x_0) = E[\hat{f}(x_0)]\),加减 \(\bar{f}\)

\[E[(f - \hat{f})^2] = (f - \bar{f})^2 + E[(\bar{f} - \hat{f})^2]\]

\[ = [\text{Bias}]^2 + \text{Var}(\hat{f}) \]

最终结果

\[ E[(y_0 - \hat{f}(x_0))^2] = [\text{Bias}]^2 + \text{Var}(\hat{f}) + \sigma^2 \]

本章核心总结

核心概念 要点
统计学习框架 \(Y = f(X) + \epsilon\),目标是估计 \(f\)
预测 vs 推断 预测关注 \(\hat{Y}\) 准确度;推断关注 \(X \to Y\) 因果链
参数化 vs 非参数化 有假设 vs 无假设;简单 vs 灵活
灵活性-可解释性权衡 更灵活 → 通常更难解释
训练误差 vs 测试误差 真正关心的是泛化能力
偏差-方差分解 MSE = Bias² + Variance + 不可约误差
最优模型 在偏差和方差之间找到最佳平衡点

下一章预告:我们将学习最经典的参数化方法——线性回归,深入理解 OLS 估计及其性质。