3. 实验管理核心概念

实验管理核心概念 学习卡片

在量化研究中,真正拖慢团队效率的往往不是模型本身,而是“这次结果到底是怎么跑出来的”说不清楚。Qlib 的实验管理模块(基于 MLflow 的封装)正是为了解决这个问题:它把一次研究活动拆解为可追踪、可比较、可复现的结构化对象。理解这些对象的定义与关系,是后续做信号评估、模型对比和回滚复现的前提。

一、核心原理:六个概念的分层结构

Qlib 实验管理中的六个核心概念并非平级,而是存在明确的“容器—归属—度量”三层关系。可以先用一句话概括:实验是容器,运行是执行单元,记录是运行的元数据快照,参数与指标是记录的输入与输出,产物是运行留下的文件资产。

  • 实验(Experiment):最高层组织单位,通常对应一个研究主题,例如“Alpha158 因子集在 CSI300 上的验证”。它本身不产生结果,只负责聚合多次运行,便于横向比较。
  • 运行(Run):一次具体的执行实例,例如“用 LightGBM 训练 2020-2022 数据”。每次运行有唯一 ID,是参数、指标、产物的实际挂载点。
  • 记录(Record):运行过程中写入的元数据条目,包括参数、指标、标签、时间戳等。它是“运行”的可查询视图,也是 MLflow UI 中展示的最小信息单元。
  • 参数(Param):运行开始前确定的输入配置,如学习率、最大深度、因子列表版本。参数一旦写入,原则上不应在运行中修改。
  • 指标(Metric):运行过程中或结束后产生的数值结果,如 IC、ICIR、年化收益、最大回撤。指标可随时间步记录,形成曲线。
  • 产物(Artifact):运行输出的文件,如模型权重、预测分数 CSV、因子重要性图、回测净值曲线。产物体积大,通常存于对象存储或本地目录。
关键区分:参数是“因”,指标是“果”,产物是“证据”。三者都挂在同一次运行下,而运行归属于某个实验。缺少任何一层,复现链条就会断裂。

二、方法流程:一次标准实验的生命周期

下面以 Qlib 中一次典型的模型训练与评估为例,展示这些概念如何在实际代码中落地。Qlib 提供了 R 记录器接口,底层对接 MLflow。

from qlib.workflow import R
from qlib.workflow.record_temp import SignalRecord, PortAnaRecord

# 1. 创建/获取实验(容器)
with R.start(experiment_name="alpha158_lgb_csi300"):
    # 2. 启动一次运行(执行单元)
    R.log_params(
        model="LightGBM",
        learning_rate=0.05,
        num_leaves=64,
        factor_set="Alpha158",
        train_period="2020-01-01~2022-12-31"
    )

    # 3. 训练模型并记录指标
    model.fit(dataset)
    pred = model.predict(dataset)
    R.log_metrics(ic=0.042, icir=0.31, rank_ic=0.038)

    # 4. 保存产物
    R.save_objects(model=model, prediction=pred)
    R.log_artifact("feature_importance.png")

    # 5. 生成标准记录(SignalRecord / PortAnaRecord)
    sr = SignalRecord(model, dataset, recorder=R)
    sr.generate()
    par = PortAnaRecord(recorder=R, config=backtest_config)
    par.generate()

执行后,MLflow 中会形成如下结构:实验 alpha158_lgb_csi300 下挂载一次运行,运行内包含 5 个参数、3 个标量指标、2 个对象产物和 1 个图片产物。若再跑一次不同学习率的配置,则新增一次运行,两次运行共享同一实验,可直接在 UI 中对比指标。

概念典型示例存储位置是否可比较
实验alpha158_lgb_csi300MLflow backend否(容器)
运行run_id=a1b2c3MLflow backend是(跨运行)
参数learning_rate=0.05运行元数据是
指标IC=0.042运行元数据是
产物model.pkl / pred.csvartifact store否(需下载)

三、案例场景:用实验管理定位“信号衰减”问题

假设团队发现某因子上周 IC 为 0.05,本周骤降到 0.01。若没有实验管理,只能凭记忆猜测。有了结构化记录后,排查路径如下:

  1. 在实验 alpha158_lgb_csi300 中筛选最近两周的运行。
  2. 对比两次运行的参数:发现本周运行将 train_period 延长到了最新数据,但 factor_set 未更新版本。
  3. 查看指标曲线:IC 在训练集内正常,在验证集上衰减,说明是因子失效而非代码 bug。
  4. 下载产物中的 feature_importance.png,确认原核心因子权重被新数据稀释。
  5. 基于历史运行回滚到旧参数组合,快速验证假设。
实践建议:为每次运行打上业务标签,例如 R.set_tags(stage="validation", owner="quant_a")。标签虽不属于六大核心概念,但能显著提升实验的可检索性。

四、误区与复盘

误区一:把实验当运行。 有人每跑一次就新建一个实验,导致实验数量爆炸,无法横向对比。正确做法是一个研究主题一个实验,多次调参对应多次运行。

误区二:指标与产物混用。 把预测分数 CSV 当作指标记录,导致 MLflow 元数据膨胀。指标应是标量或时间序列数值,大文件必须走产物通道。

误区三:参数记录不全。 只记录模型超参,却漏掉数据版本、因子集版本、随机种子。复现时“差之毫厘,谬以千里”。建议将数据快照 ID 也作为参数写入。

误区四:忽略记录(Record)的中间态。 只记录最终 IC,不记录训练过程中的 loss 曲线,导致无法判断过拟合发生的 epoch。Qlib 的 log_metrics(step=...) 支持逐步记录。

复盘要点:实验管理的价值不在“记录”,而在“可比较”。如果两次运行之间没有共享实验、没有统一参数命名、没有一致指标口径,那么记录再多也无法支撑决策。建议团队在项目启动时先约定参数命名规范与核心指标清单。