核心问题:如何从数据中发现规律?
统计学习 (Statistical Learning) 是一套从数据中提取模式、支持商业决策的工具框架。
- 监督学习:已知”正确答案”,学习输入→输出的映射
- 无监督学习:没有标签,发现数据内部的隐含结构
在金融、电商、医疗等领域,统计学习已成为决策的基础设施。
监督学习:从已知答案中学习规律
监督学习的核心特征:训练数据中包含标签(正确答案)。
- 回归问题:标签是连续数值
- 例:根据公司财务数据预测净利润(万元)
- 例:根据房屋特征预测房价
- 分类问题:标签是离散类别
- 例:根据历史收益率预测明天涨/跌
- 例:根据财务指标判断公司是否会违约
关键词:有标签 → 有”老师”指导 → “监督”
无监督学习:从无标签数据中发现结构
无监督学习的核心特征:训练数据中没有标签。
- 聚类 (Clustering):将相似的样本自动分组
- 降维 (Dimensionality Reduction):在保留关键信息的前提下压缩数据
类比:没有老师,学生自己整理笔记、归纳知识体系。
直觉理解:为什么需要学习 \(f\)?
想象你是一位基金经理,面对两个问题:
问题一(预测):下个季度海康威视的净利润是多少?
- 你需要一个”函数”,输入公司特征,输出利润预测值
- 关心的是预测的准确性
问题二(推断):广告投入真的能提升销售额吗?
- 你需要理解输入和输出之间的因果关系
- 关心的是哪些因素真正起作用
统计学习要做的,就是从数据中找到这个 \(f\)。
统计学习的核心公式
假设响应变量 \(Y\)(如净利润)和特征向量 \(X = (X_1, \dots, X_p)\) 之间存在关系:
\[ \large{Y = f(X) + \epsilon} \]
- \(f\):未知的系统性映射函数 — 我们要”学习”的对象
- \(\epsilon\):随机误差项,均值为零,与 \(X\) 独立
统计学习的本质:用算法从经验数据中估计 \(f\)。
公式拆解:理解每个符号的含义
\[ \large{Y = f(X) + \epsilon} \]
| \(Y\) |
响应变量(输出) |
公司净利润、股票收益率 |
| \(X = (X_1, \dots, X_p)\) |
\(p\) 个特征变量(输入) |
营收、资产、行业、PE比率 |
| \(f(X)\) |
系统性部分 — 可学习 |
营收和利润的确定性关系 |
| \(\epsilon\) |
随机噪声 — 不可学习 |
政策冲击、黑天鹅事件 |
关键认知:\(\epsilon\) 设定了预测精度的天花板,再好的模型也无法消除噪声。
估计 \(f\) 的两大目的:预测与推断
目的一:预测 (Prediction)
\[ \large{\hat{Y} = \hat{f}(X)} \]
- 关注 \(\hat{Y}\) 与真实 \(Y\) 的接近程度
- \(f\) 可以是”黑盒”——只要预测准就行
目的二:推断 (Inference)
- 关注 \(X\) 与 \(Y\) 之间的因果关系
- 需要理解 \(f\) 的形式——不能是黑盒
- 例:哪些因素真正影响公司利润?影响方向和大小?
统计学习在中国金融领域的应用
| 智能信贷风控 |
XGBoost、深度学习 |
蚂蚁集团、京东金融 |
| 量化投资策略 |
Lasso、随机森林、LSTM |
幻方量化、明汯投资 |
| 保险精算定价 |
Cox比例风险模型 |
中国人寿、平安保险 |
| 供应链优化 |
Prophet、ARIMA-GARCH |
京东物流 |
核心挑战:从商业噪音中滤出确定性规律(\(f\)),防范随机噪音(\(\epsilon\))带来的误判。
长三角:中国经济的核心引擎
为什么聚焦长三角(YRD)?
- 覆盖上海、江苏、浙江、安徽四省市
- GDP总量占全国约24%,是中国经济最活跃的区域之一
- 拥有大量优质上市公司:海康威视、上汽集团、宁波银行、恒瑞医药…
本书的数据策略:以长三角上市公司为核心样本,用真实财务数据验证每一个模型。
从理论到实战:第一个案例
接下来,我们通过两个真实案例来体验统计学习的力量:
案例一:回归问题 — 预测长三角上市公司的净利润
案例二:分类问题 — 预测上汽集团股票的涨跌方向
每个案例都将展示完整的分析流程:
数据获取 → 特征构建 → 可视化探索 → 模型拟合 → 结果解读
案例一:长三角上市公司财务分析
研究目标:影响长三角地区上市公司净利润的因素
- 营业收入 (Revenue) — 最有力的单一预测指标
- 总资产 (Total Assets) — 正相关但波动更大
- 行业分类 — 不同行业利润率差异显著
这是一个典型的回归问题 (Regression Problem)。
数据准备:加载与环境配置
import numpy as np # 导入numpy库,用于数值计算
import pandas as pd # 导入pandas库,用于数据框操作
import matplotlib.pyplot as plt # 导入matplotlib库,用于绑图
import seaborn as sns # 导入seaborn库,用于高级统计图表
import os # 导入os模块,用于跨平台路径处理
plt.rcParams['font.sans-serif'] = ['Source Han Serif SC', 'SimHei', 'Arial Unicode MS'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 根据操作系统自动选择数据根目录
DATA_ROOT = 'C:/qiufei/data' if os.name == 'nt' else '/home/ubuntu/r2_data_mount/qiufei/data'
FINANCIAL_STMT_PATH = os.path.join(DATA_ROOT, 'stock/financial_statement.h5') # 财务报表路径
STOCK_BASIC_PATH = os.path.join(DATA_ROOT, 'stock/stock_basic_data.h5') # 公司基本信息路径
数据合并与地区筛选
financial_statements_data = pd.read_hdf(FINANCIAL_STMT_PATH) # 读取上市公司财务报表
stock_basic_info = pd.read_hdf(STOCK_BASIC_PATH) # 读取公司基本信息
financial_data_2023 = financial_statements_data[
financial_statements_data['quarter'] == '2023q4'
].copy() # 筛选2023年年报数据
join_key = 'order_book_id' # 合并键:股票代码
merged_company_data = pd.merge(
financial_data_2023,
stock_basic_info[[join_key, 'province', 'industry_name']],
on=join_key, how='inner'
) # 按股票代码内连接合并两张表
yrd_provinces_list = ['上海市', '江苏省', '浙江省', '安徽省'] # 长三角四省市
yrd_companies_data = merged_company_data[
merged_company_data['province'].isin(yrd_provinces_list)
].copy() # 筛选长三角地区公司
为什么选择对数变换?
金融数据的典型特征:右偏分布、量级差异巨大
- 工商银行年营收:~8000亿元
- 小型上市公司年营收:~5000万元
- 相差 16000倍!
直接画散点图 → 小公司全部挤在原点附近,无法观察规律
\(\log_{10}\) 变换后 → 不同量级的公司在图上均匀展开
\[ \large{\log_{10}(8000亿) \approx 11.9, \quad \log_{10}(5000万) \approx 7.7} \]
差距从16000倍缩小到4.2个单位,可视化效果大幅改善。
数据清洗与对数变换
is_valid_record = (
(yrd_companies_data['revenue'] > 1e6) & # 营业收入大于100万
(yrd_companies_data['total_assets'] > 1e6) & # 总资产大于100万
(yrd_companies_data['net_profit'] > 0) # 净利润为正
) # 构建有效性过滤条件
yrd_cleaned_data = yrd_companies_data[is_valid_record].copy() # 保留有效记录
yrd_cleaned_data['log_revenue'] = np.log10(yrd_cleaned_data['revenue']) # 对营收取对数
yrd_cleaned_data['log_assets'] = np.log10(yrd_cleaned_data['total_assets']) # 对资产取对数
yrd_cleaned_data['log_profit'] = np.log10(yrd_cleaned_data['net_profit']) # 对利润取对数
top_industries_names = yrd_cleaned_data['industry_name'].value_counts().head(5).index # 选取前5大行业
plotting_data = yrd_cleaned_data[yrd_cleaned_data['industry_name'].isin(top_industries_names)] # 筛选绘图数据
为什么做对数变换? 工商银行营收数千亿 vs 小企业数千万,直接绑图中小企业全被挤到原点。\(\log_{10}\) 让不同量级公司在图上”均匀展开”。
财务数据可视化:净利润与营收、资产、行业
Code
academic_colors_palette = ['#E3120B', '#2C3E50', '#008080', '#F0A700', '#8E9EAA'] # 学术配色
fig, axes = plt.subplots(1, 3, figsize=(16, 5)) # 创建1行3列子图
axes[0].scatter(plotting_data['log_revenue'], plotting_data['log_profit'], alpha=0.4, s=25, c='#2C3E50') # 左图散点
poly_coeff = np.polyfit(plotting_data['log_revenue'], plotting_data['log_profit'], 1) # 线性拟合
axes[0].plot(plotting_data['log_revenue'], np.polyval(poly_coeff, plotting_data['log_revenue']), color='#E3120B', lw=2.5) # 趋势线
axes[0].set_xlabel('Log10 营业收入', fontsize=11) # x轴标签
axes[0].set_ylabel('Log10 净利润', fontsize=11) # y轴标签
axes[0].set_title('净利润与营收呈强线性关系', fontweight='bold') # 标题
axes[1].scatter(plotting_data['log_assets'], plotting_data['log_profit'], alpha=0.4, s=25, c='#008080') # 中图散点
poly_coeff_assets = np.polyfit(plotting_data['log_assets'], plotting_data['log_profit'], 1) # 线性拟合
axes[1].plot(plotting_data['log_assets'], np.polyval(poly_coeff_assets, plotting_data['log_assets']), color='#E3120B', lw=2.5) # 趋势线
axes[1].set_xlabel('Log10 总资产', fontsize=11) # x轴标签
axes[1].set_title('盈利随资产规模同步增长', fontweight='bold') # 标题
sns.boxplot(x='industry_name', y='log_profit', data=plotting_data, ax=axes[2], palette=academic_colors_palette) # 右图箱线图
axes[2].set_xlabel('细分行业', fontsize=11) # x轴标签
axes[2].set_title('行业间利润分布差异显著', fontweight='bold') # 标题
plt.xticks(rotation=30) # 旋转标签
for ax in axes: # 添加网格
ax.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout() # 自动调整间距
plt.show() # 显示图形
从图表中读出的三个关键结论
结论一:营收是最强的单一预测变量
- 散点紧密围绕趋势线 → \(f\) 在营收维度上接近线性
结论二:资产规模的预测力不如营收
- 离散程度更大 → 同等资产规模的公司,利润差异更大
结论三:行业是重要的分类变量
- 不同行业的利润中位数和离散度差异显著
- → 模型中需要同时使用连续变量和分类变量
这就是多元回归的动机——第3章的核心内容。
从回归到分类:另一类核心问题
刚才我们预测的是连续数值(净利润)→ 回归问题
接下来我们预测的是离散类别(涨/跌)→ 分类问题
| 输出类型 |
连续数值 |
离散类别 |
| 损失函数 |
均方误差 (MSE) |
交叉熵 / 误分类率 |
| 典型方法 |
线性回归、Ridge |
逻辑回归、LDA、KNN |
| 金融案例 |
预测利润金额 |
预测涨跌方向 |
案例二:股票市场涨跌预测
研究对象:上汽集团 (600104.SH),长三角汽车行业龙头
问题类型:分类问题 (Classification) — 预测明天”上涨”还是”下跌”
方法:利用过去几天的历史收益率(滞后项 Lags)作为特征
核心发现:历史收益率对未来涨跌的区分度极弱 → 市场效率
什么是特征工程?
特征工程 (Feature Engineering):将原始数据转化为模型可用的特征。
在股票涨跌预测中:
- 原始数据:每天的收盘价序列 \(P_1, P_2, \dots, P_T\)
- 第一步:计算日收益率 \(r_t = (P_t - P_{t-1}) / P_{t-1}\)
- 第二步:构建滞后特征 — 用 \(r_{t-1}, r_{t-2}, \dots, r_{t-5}\) 预测今天的涨跌
关键操作:shift() — 把时间序列”错位”,实现回溯
这是将时间序列问题转化为标准表格问题的经典手法。
特征工程:构建滞后收益率
PRICE_DATA_PATH = os.path.join(DATA_ROOT, 'stock/stock_price_pre_adjusted.h5') # 前复权价格路径
stock_price_history = pd.read_hdf(PRICE_DATA_PATH).reset_index() # 读取全市场日度价格数据
saic_motor_data = stock_price_history[stock_price_history['order_book_id'] == '600104.XSHG'].copy() # 筛选上汽集团
saic_motor_data['date'] = pd.to_datetime(saic_motor_data['date']) # 转换日期格式
saic_motor_data = saic_motor_data.sort_values('date') # 按日期排序
saic_motor_data['daily_return'] = saic_motor_data['close'].pct_change() # 计算日收益率
num_lags = 5 # 构建5个滞后特征
for lag in range(1, num_lags + 1): # 循环创建滞后列
saic_motor_data[f'lag_return_{lag}'] = saic_motor_data['daily_return'].shift(lag) # shift实现回溯
saic_motor_data['market_direction'] = (saic_motor_data['daily_return'] > 0).astype(int) # 定义涨跌标签
saic_analysis_dataset = saic_motor_data[saic_motor_data['date'] >= '2015-01-01'].dropna() # 筛选2015年后数据
关键手法:将时间序列预测转化为标准分类问题 — 量化投资的基础范式
历史收益率几乎无法区分涨跌方向
Code
fig, axes = plt.subplots(1, 3, figsize=(15, 5)) # 创建1行3列子图
lag_indices = [1, 2, 3] # 选择前3个滞后项
for i, lag_idx in enumerate(lag_indices): # 遍历绘制箱线图
sns.boxplot(
x='market_direction', y=f'lag_return_{lag_idx}',
data=saic_analysis_dataset, ax=axes[i],
palette=['#E3120B', '#008080'], showfliers=False
) # 按涨跌方向分组绘制
axes[i].set_xticklabels(['下跌日', '上涨日']) # 替换标签
axes[i].set_title(f'滞后项 Lag {lag_idx} 收益分布', fontweight='bold') # 标题
axes[i].set_xlabel('今日方向') # x轴
axes[i].set_ylabel('过去收益率') # y轴
axes[i].grid(True, axis='y', alpha=0.3) # 网格
plt.tight_layout() # 调整间距
plt.show() # 显示
理解这张图:有效市场假说
箱线图中上涨日和下跌日的分布几乎完全重叠,意味着什么?
有效市场假说 (Efficient Market Hypothesis, EMH):
- 弱式有效:历史价格信息已充分反映在当前价格中
- 推论:仅凭历史收益率无法预测未来涨跌
- 这正是我们图中观察到的现象!
对投资的启示:
- 简单的技术分析策略(如均线交叉)长期难以稳定盈利
- 想要获取超额收益,需要非价格信息(基本面、另类数据)
四种分类模型的预测表现
from sklearn.linear_model import LogisticRegression # 导入逻辑回归
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis # 导入LDA/QDA
from sklearn.neighbors import KNeighborsClassifier # 导入KNN
from sklearn.metrics import accuracy_score # 导入准确率函数
feature_columns = [f'lag_return_{i}' for i in range(1, 6)] # 5个滞后特征
features_matrix = saic_analysis_dataset[feature_columns] # 提取特征矩阵
target_vector = saic_analysis_dataset['market_direction'] # 提取目标标签
split_point = int(0.75 * len(features_matrix)) # 75%分位点
features_train, features_test = features_matrix.iloc[:split_point], features_matrix.iloc[split_point:] # 划分特征
target_train, target_test = target_vector.iloc[:split_point], target_vector.iloc[split_point:] # 划分标签
classifiers_dict = {
'逻辑回归': LogisticRegression(),
'LDA': LinearDiscriminantAnalysis(),
'QDA': QuadraticDiscriminantAnalysis(),
'K-近邻 (K=3)': KNeighborsClassifier(n_neighbors=3)
} # 四种分类器
prediction_accuracies = {} # 存储准确率
for name, clf in classifiers_dict.items(): # 遍历训练
clf.fit(features_train, target_train) # 拟合模型
predictions = clf.predict(features_test) # 预测
prediction_accuracies[name] = accuracy_score(target_test, predictions) # 计算准确率
时间序列数据划分的注意事项
金融数据划分的黄金法则:绝不能随机打乱!
| 时间顺序划分 |
前75%训练 → 后25%测试 |
✅ 正确 |
| 随机打乱划分 |
随机抽取75%训练 → 25%测试 |
❌ 错误 |
为什么? 随机打乱会导致”未来信息泄漏”:
- 模型在训练时”偷看”了未来的数据
- 测试集准确率虚高,但实际应用中必然失败
这是量化投资中最常见的回测陷阱之一。
所有模型的准确率都接近随机水平
Code
plt.figure(figsize=(10, 6)) # 创建画布
bar_colors = ['#2C3E50', '#008080', '#F0A700', '#8E9EAA'] # 分配颜色
plt.barh(list(prediction_accuracies.keys()), list(prediction_accuracies.values()), color=bar_colors, alpha=0.8) # 水平条形图
plt.axvline(x=0.5, color='#E3120B', linestyle='--', label='随机水平 (50%)') # 基准线
plt.xlim(0.48, 0.55) # 放大观察范围
plt.xlabel('测试集预测准确率') # x轴标签
plt.title('上汽集团次日涨跌预测准确率对比', fontweight='bold') # 标题
plt.legend() # 图例
plt.grid(axis='x', linestyle=':', alpha=0.6) # 网格
plt.show() # 显示
核心洞察:无论是线性还是非线性分类器,准确率都在50%附近 → 市场有效性假说的实证证据。
两个案例的核心启示
| 目标 |
预测净利润(连续值) |
预测涨跌(类别) |
| 信号强度 |
强 — 营收与利润线性相关 |
弱 — 历史收益率无区分度 |
| \(f\) 是否容易学习 |
是 — 线性函数即可捕捉 |
否 — \(f\) 可能不存在 |
| 实践意义 |
基本面分析有效 |
简单技术分析无效 |
关键认知:并非所有问题都有好的 \(f\),识别”可预测性”本身就是统计学习的任务。
进入更复杂的世界:高维数据
在实际的量化投资中,分析师不会只用5个简单的滞后特征。
真实场景:
- 财务因子(PE、PB、ROE…):100+个
- 价量因子(动量、波动率、换手率…):500+个
- 情绪因子(新闻舆情、分析师评级…):500+个
- 另类数据因子(卫星数据、信用卡消费…):1000+个
总计:\(p = 2000+\) 个特征
但沪深300每月只有 \(n = 300\) 个样本…
高维数据挑战:当特征比样本还多
量化多因子模型的典型困境:
- 特征 \(p = 2000+\)(财务因子、价量因子、情绪因子…)
- 样本 \(n = 300\)(如沪深300成分股的月度截面)
当 \(p > n\) 时,矩阵 \(\mathbf{X}^T\mathbf{X}\) 奇异不可逆 → OLS 彻底崩溃
模型会”完美记忆”训练数据的每一丝随机波动 → 过拟合 (Overfitting)
直觉理解:为什么 \(p > n\) 会导致过拟合?
类比:你手上有300个数据点(样本),但有2000个”旋钮”(参数)可以调节。
- 旋钮太多 → 你总能找到一组参数让训练数据完美拟合
- 但这种”完美”是虚假的 — 模型记住了噪声而非规律
想象:用2000次多项式去拟合300个点
- 训练集:误差为零(完美通过每个点)
- 测试集:预测误差爆炸
这就是过拟合的本质:模型在训练数据上”过度学习”了随机噪声。
解决方案:正则化与特征降维
| Lasso 回归 |
将冗余因子权重压缩为零 |
稀疏模型,可解释性强 |
| 岭回归 (Ridge) |
限制所有参数幅度 |
处理多重共线性,稳定性好 |
这些技术已被国内头部券商和量化私募广泛使用。
本书第六章将系统介绍这些正则化方法。
Lasso vs Ridge:核心区别一目了然
Lasso(L1惩罚):\(\sum |\beta_j|\) — 像”剪刀”,直接把不重要的系数剪为零
Ridge(L2惩罚):\(\sum \beta_j^2\) — 像”弹簧”,把所有系数均匀压缩
- 不会把任何系数变成零
- 在高度相关的变量间平均分配权重
在量化投资中:Lasso适合因子筛选,Ridge适合因子组合。
偏差-方差权衡:统计学习的核心矛盾
选择模型时,始终面临一个根本性的权衡 (Trade-off):
\[ \large{\text{总误差} = \text{偏差}^2 + \text{方差} + \text{不可约误差}} \]
| 偏差 (Bias) |
模型过于简单,系统性偏离真相 |
用直线拟合曲线 |
| 方差 (Variance) |
模型过于复杂,对训练数据过度敏感 |
用2000次多项式拟合 |
欠拟合 = 高偏差,过拟合 = 高方差 → 需要找到最佳平衡点
统计学习 vs 机器学习 vs 人工智能
统计学习 不仅关注”预测准不准”,更关注”为什么准”、“置信区间多宽”。
三者的核心差异
| 核心关注 |
推断与解释 |
预测精度 |
模拟人类智能 |
| 可解释性 |
高(参数有明确含义) |
中低(黑盒模型) |
低 |
| 数据规模 |
中小规模 |
大规模 |
超大规模 |
| 代表方法 |
回归、LDA |
随机森林、XGBoost |
深度神经网络、GPT |
| 理论基础 |
概率论、数理统计 |
优化理论 |
多学科交叉 |
本书的立场:用统计学习的理论深度来理解机器学习的实践工具。
本书内容概览
基础方法
- 第2章:统计学习理论基础
- 第3章:线性回归
- 第4章:分类方法
- 第5章:重采样方法
- 第6章:模型选择与正则化
高级方法
- 第7章:非线性方法
- 第8章:基于树的方法
- 第9章:支持向量机
- 第10章:深度学习
- 第11章:生存分析
- 第12章:无监督学习
- 第13章:多重检验
学习路径建议
第一阶段:打好基础(第2-5章)
- 理解统计学习的核心概念:偏差-方差权衡、交叉验证
- 掌握线性回归和分类的基本方法
第二阶段:进阶扩展(第6-9章)
- 学习处理高维数据和非线性关系的方法
- 树模型和集成学习是工业界最常用的工具
第三阶段:前沿探索(第10-13章)
- 深度学习、生存分析、无监督学习
- 这些方法在特定场景下有独特优势
建议:按顺序学习,每章动手跑代码,不要只看不练!
数据与工具
全部案例数据来自中国本土真实市场,本地离线可用。
| 公司基本信息 |
stock_basic_data.h5 |
筛选、分类 |
| 财务报表 |
financial_statement.h5 |
面板分析 |
| 日度行情 |
stock_price_*.h5 |
收益率、技术分析 |
| 估值因子 |
valuation_factors_*.h5 |
多因子模型 |
| 指数/期货/基金 |
index/, future/, fund/ |
市场分析 |
Python工具包:NumPy, Pandas, Matplotlib, Scikit-learn, Statsmodels, PyTorch
数学符号约定
本书采用以下标准记号:
| 小写字母 |
标量 |
\(x, y, z\) |
| 粗体小写 |
向量 |
\(\mathbf{x}, \mathbf{y}\) |
| 粗体大写 |
矩阵 |
\(\mathbf{X}, \mathbf{Y}\) |
| 大写字母 |
随机变量 |
\(X, Y, Z\) |
| 帽子记号 |
估计量 |
\(\hat{\beta}, \hat{y}\) |
回归中:\(n\) = 样本量,\(p\) = 特征数,\(\mathbf{X}\) = \(n \times p\) 设计矩阵
关键概念回顾
让我们快速回顾本章引入的核心术语:
| 统计学习 |
从数据中估计 \(f\) 的方法论 |
| 监督学习 |
有标签数据,学习 \(X \to Y\) 的映射 |
| 无监督学习 |
无标签数据,发现数据内部结构 |
| 回归 |
预测连续数值(如利润) |
| 分类 |
预测离散类别(如涨/跌) |
| 过拟合 |
模型记住了噪声而非规律 |
| 偏差-方差权衡 |
模型复杂度的核心矛盾 |
下一章预告:统计学习的理论基石
第2章将系统讲解统计学习的理论基础:
- 偏差-方差分解的数学推导
- 训练误差 vs 测试误差的本质区别
- 灵活性-可解释性权衡 — 如何选择合适的模型
- K近邻法 (KNN) — 你的第一个非参数模型
这些理论将为后续所有章节提供统一的分析框架。