4. 金融数据实验跟踪实战
在金融量化研究中,股票价格预测模型的训练往往不是一次性的“跑通即可”,而是一个反复迭代、不断对比的过程。数据窗口、特征构造方式、标签定义、模型超参数、随机种子,任何一个环节的微小变化都会导致评估指标波动。如果没有实验跟踪机制,研究员很快会陷入“上次那个效果不错的模型到底用了哪些特征”的困境。本节以股票价格预测为场景,完整演示如何用MLflow记录数据版本、特征工程步骤和评估指标,让每一次训练都可追溯、可复现、可比较。
核心原理:实验跟踪记录的是什么
MLflow Tracking的核心抽象是实验(Experiment)与运行(Run)。一个实验对应一个研究主题,例如“沪深300日频收益率预测”;一次运行对应一次具体的训练尝试。每次运行可以写入四类信息:
- 参数(Params):训练前确定的配置,如学习率、树深度、回看窗口长度、特征列表版本号。
- 指标(Metrics):训练中或训练后产生的数值,如RMSE、MAE、IC、夏普比率,支持按step记录形成曲线。
- 标签(Tags):用于检索和分类的元信息,如数据版本、特征工程脚本commit、研究员姓名。
- 制品(Artifacts):文件级产物,如特征重要性图、残差分布图、模型文件、数据快照。
金融场景的特殊性在于:数据本身具有时间属性,特征工程步骤往往比模型结构更影响结果。因此,把“数据版本”和“特征工程步骤”作为一等公民记录下来,比单纯记录超参数更重要。
方法流程:从数据加载到指标落库
下面以“用LightGBM预测未来5日收益率”为例,给出可运行的跟踪骨架。关键点在于:把数据版本和特征工程步骤显式写入tags,而不是只记录模型参数。
import mlflow
import mlflow.lightgbm
import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error, mean_absolute_error
mlflow.set_tracking_uri("http://127.0.0.1:5000")
mlflow.set_experiment("stock_return_prediction")
DATA_VERSION = "hs300_daily_v3_20240101"
FEAT_VERSION = "feat_v2_lag20_std"
with mlflow.start_run(run_name="lgbm_lag20_v2") as run:
# 1. 记录数据版本与特征工程步骤
mlflow.set_tags({
"data_version": DATA_VERSION,
"feature_version": FEAT_VERSION,
"feature_steps": "lag20,rolling_std,volume_zscore",
"label": "future_5d_return",
"split": "time_series_70_15_15"
})
# 2. 记录超参数
params = {"lookback": 20, "learning_rate": 0.05,
"max_depth": 4, "num_leaves": 31, "seed": 42}
mlflow.log_params(params)
# 3. 训练并记录指标
model.fit(X_train, y_train)
pred = model.predict(X_valid)
rmse = mean_squared_error(y_valid, pred, squared=False)
mae = mean_absolute_error(y_valid, pred)
ic = np.corrcoef(pred, y_valid)[0, 1]
mlflow.log_metrics({"rmse": rmse, "mae": mae, "ic": ic})
# 4. 记录模型与图表制品
mlflow.lightgbm.log_model(model, "model")
mlflow.log_artifact("feature_importance.png")
mlflow.log_artifact("residual_dist.png")
运行结束后,在MLflow UI中即可按data_version或feature_version筛选运行,快速定位“同一数据版本下哪组特征工程效果最好”。
案例场景:对比两组特征工程方案
假设研究员构造了两套特征:方案A仅使用价格滞后项,方案B在滞后项基础上加入成交量Z分数和滚动波动率。两次运行写入同一实验,记录如下:
| Run名称 | 特征版本 | 特征步骤 | RMSE | IC |
|---|---|---|---|---|
| lgbm_lag20_v1 | feat_v1 | lag20 | 0.0215 | 0.021 |
| lgbm_lag20_v2 | feat_v2 | lag20,rolling_std,volume_zscore | 0.0182 | 0.043 |
| lgbm_lag20_v2_seed7 | feat_v2 | lag20,rolling_std,volume_zscore | 0.0184 | 0.041 |
通过表格可以直观看到:加入量价衍生特征后IC提升约一倍,而更换随机种子带来的波动远小于特征工程带来的差异。这正是金融实验中“特征优先于调参”的典型证据。
mlflow.set_tag("feat_commit", "a1b2c3d")。当特征逻辑变更时,可以精确回溯到对应代码版本。误区与复盘
- 只记录模型参数,不记录数据版本:金融数据会因复权、停牌处理、成分股调整而变化,同一份代码在不同数据快照上结果不可比。
- 把特征工程步骤写在代码注释里:注释不会进入MLflow,检索时无法按特征步骤过滤。应把步骤摘要写入tags或作为artifact保存特征清单。
- 用随机切分代替时间序列切分:股票数据存在时序依赖,随机切分会造成未来信息泄露,指标虚高。应在tags中明确
split=time_series。 - 指标只记一个RMSE:金融预测更关注排序能力和风险调整后收益,建议同时记录IC、RankIC、多空组合夏普。
- 忽略运行命名:默认运行名是随机ID,后期无法辨认。应使用
run_name体现数据版本与特征版本组合。
./mlruns目录。团队协作时应配置远端跟踪服务器和对象存储(如S3、MinIO),否则本地文件丢失会导致实验记录不可恢复。复盘一次完整的实验跟踪,核心不是“用了MLflow”,而是让每一次训练都回答三个问题:用了哪份数据、做了哪些特征变换、得到了什么指标。当这三个问题都能在UI中一键检索时,金融模型研究才真正从“手工作坊”走向可积累的实验体系。