Qlib Forecast Model 实战指南:预测模型的训练、预测与自定义扩展
【免费下载链接】qlibQlib is an AI-oriented Quant investment platform that aims to use AI tech to empower Quant Research, from exploring ideas to implementing productions. Qlib supports diverse ML modeling paradigms, including supervised learning, market dynamics modeling, and RL, and is now equipped with https://github.com/microsoft/RD-Agent to automate R&D process.项目地址: https://gitcode.com/GitHub_Trending/qli/qlib
本篇聚焦 Qlib 的 Forecast Model(预测模型)组件,讲解预测模型在 Qlib 中的定位、基类接口Model/ModelFT的设计,以及如何在脱离完整工作流的情况下,把 LightGBM 作为独立模块跑通「数据准备 → 模型训练 → 预测信号生成」全流程;读完你可以掌握如何用配置化方式初始化任意模型、如何通过SignalRecord落盘预测分数,并理解预测分数(pred_score)的默认语义。
1. Forecast Model 在 Qlib 中的定位
Forecast Model的职责是为股票生成prediction score(预测分数)。在 Qlib 中它有两种使用方式:
- 自动工作流方式:通过命令行
qrun XXX.yaml在完整工作流中运行,工作流管理详见 Workflow 文档; - 独立模块方式:由于 Qlib 的组件是松耦合设计,
Forecast Model可以脱离完整工作流,作为一个独立模块单独使用。
本文主要展开第二种方式:手动构建 task 配置、实例化模型与数据集、训练后生成预测信号。
2. 基类与接口:Model、BaseModel 与 ModelFT
Qlib 在 qlib/model/base.py 中提供了所有模型都应继承的基类体系:
qlib.model.base.Model:可学习模型的基类,定义了fit与predict两个核心接口;qlib.model.base.ModelFT:在Model基础上增加finetune抽象方法,用于支持模型微调(Fine-tuning);- 更底层的
qlib.model.base.BaseModel:所有建模对象的父类,它实现了__call__,把模型对象包装得像函数一样可以直接调用(内部转发给predict),并混入了Serializable以支持序列化落盘。
2.1 Model 的 fit 与 predict
fit与predict的签名约定了模型与数据集的交互方式(见 Model 基类实现):
fit(self, dataset, reweighter):从Dataset中学习模型。官方 docstring 给出了一段从 dataset 中取训练数据的标准写法:
# 获取特征与标签 df_train, df_valid = dataset.prepare( ["train", "valid"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L ) x_train, y_train = df_train["feature"], df_train["label"] x_valid, y_valid = df_valid["feature"], df_valid["label"] # 获取样本权重(若 handler 未配置 weight 列,则退化为全 1 权重) try: wdf_train, wdf_valid = dataset.prepare(["train", "valid"], col_set=["weight"], data_key=DataHandlerLP.DK_L) w_train, w_valid = wdf_train["weight"], wdf_valid["weight"] except KeyError as e: w_train = pd.DataFrame(np.ones_like(y_train.values), index=y_train.index) w_valid = pd.DataFrame(np.ones_like(y_valid.values), index=y_valid.index)其中DataHandlerLP.DK_L/DK_I分别指代 learn 处理与 infer 处理后的数据键,这一约定贯穿所有内置模型。
predict(self, dataset, segment="test"):默认对test分段做预测,返回pandas.Series等结构化结果。
一个值得注意的实现细节:基类 docstring 明确要求学习到的模型属性名不能以_开头,否则模型无法被完整 dump 到磁盘——这是为了配合 Qlib 的序列化机制。
2.2 ModelFT 与 finetune
ModelFT(实现见 ModelFT)为支持微调的模型增加了finetune(dataset)抽象方法。官方 docstring 展示了配合工作流 Recorder 的典型用法:
# 第一步:在实验中训练初始模型 with R.start(experiment_name="init models"): model.fit(dataset) R.save_objects(init_model=model) rid = R.get_recorder().id # 第二步:基于已保存的模型做微调 with R.start(experiment_name="finetune model"): recorder = R.get_recorder(recorder_id=rid, experiment_name="init models") model = recorder.load_object("init_model") model.finetune(dataset, num_boost_round=10)即以先前训练好的模型为起点,在新数据上继续训练若干轮,这在滚动更新(rolling)场景下非常实用。
3. 示例:把 LightGBM 作为独立模块运行
Qlib 内置的 Model Zoo 包含LightGBM、MLP、LSTM等模型,它们作为 Forecast Model 的基线。以下示例演示以LightGBM为独立模块的运行方式。
前置步骤:先通过qlib.init初始化 Qlib(初始化方式见 Initialization 文档)。
然后运行如下代码获取预测分数pred_score:
from qlib.contrib.model.gbdt import LGBModel from qlib.contrib.data.handler import Alpha158 from qlib.utils import init_instance_by_config, flatten_dict from qlib.workflow import R from qlib.workflow.record_temp import SignalRecord, PortAnaRecord market = "csi300" benchmark = "SH000300" data_handler_config = { "start_time": "2008-01-01", "end_time": "2020-08-01", "fit_start_time": "2008-01-01", "fit_end_time": "2014-12-31", "instruments": market, } task = { "model": { "class": "LGBModel", "module_path": "qlib.contrib.model.gbdt", "kwargs": { "loss": "mse", "colsample_bytree": 0.8879, "learning_rate": 0.0421, "subsample": 0.8789, "lambda_l1": 205.6999, "lambda_l2": 580.9768, "max_depth": 8, "num_leaves": 210, "num_threads": 20, }, }, "dataset": { "class": "DatasetH", "module_path": "qlib.data.dataset", "kwargs": { "handler": { "class": "Alpha158", "module_path": "qlib.contrib.data.handler", "kwargs": data_handler_config, }, "segments": { "train": ("2008-01-01", "2014-12-31"), "valid": ("2015-01-01", "2016-12-31"), "test": ("2017-01-01", "2020-08-01"), }, }, }, } # model initialization model = init_instance_by_config(task["model"]) dataset = init_instance_by_config(task["dataset"]) # start exp with R.start(experiment_name="workflow"): # train R.log_params(**flatten_dict(task)) model.fit(dataset) # prediction recorder = R.get_recorder() sr = SignalRecord(model, dataset, recorder) sr.generate()上面这段示例代码与仓库中 examples/workflow_by_code.py 的独立模块运行逻辑一致,且该文件还额外演示了SigAnaRecord(信号分析)与PortAnaRecord(组合回测分析)的接续调用;测试配置 qlib/tests/config.py 中的GBDT_MODEL也使用了与文档示例完全相同的一组超参数,说明这组参数是 Qlib 官方的 CSI300 基线配置。
3.1 配置项逐项说明
数据段配置data_handler_config(传给Alpha158):
| 参数 | 示例值 | 含义 |
|---|---|---|
start_time/end_time | 2008-01-01 / 2020-08-01 | 数据处理的总体时间范围 |
fit_start_time/fit_end_time | 2008-01-01 / 2014-12-31 | 学习类处理器(如标准化)的拟合区间,通常与 train 段对齐,避免用未来数据拟合统计量 |
instruments | csi300 | 股票池(instrument 列表) |
模型配置task["model"]:class+module_path指向 LGBModel;kwargs中loss只支持mse/binary两种目标(源码中其余取值直接抛NotImplementedError),其余键(colsample_bytree、learning_rate、subsample、lambda_l1、lambda_l2、max_depth、num_leaves、num_threads)会原样透传给 LightGBM 的params。
数据集配置task["dataset"]:DatasetH包装一个数据处理器(handler)与分段(segments)。segments把时间轴切成 train / valid / test 三段,fit时按 train、valid 取数据,predict时默认取 test 段。
3.2 配置如何变成对象:init_instance_by_config
示例中init_instance_by_config(task["model"])一行就完成了模型实例化。其机制在 qlib/utils/mod.py 中实现:
get_callable_kwargs解析配置:class字段支持"a.b.c.ClassName"全限定写法,或"ClassName"+module_path的分离写法(本示例用后者);- 通过
importlib.import_module(module_path)动态导入模块,取到类对象; - 用
kwargs完成构造,若构造报TypeError(如参数不匹配)会回退重试。
这种「配置即对象」的机制使得同一套 task 字典既能用于 Python 脚本,也能用于qrun XXX.yaml的声明式工作流,两个入口共享同一份配置语义。
3.3 训练与预测信号的落盘
示例进入R.start(experiment_name="workflow")上下文后:
R.log_params(**flatten_dict(task)):把嵌套 task 配置展平为「键.路径=值」的平铺字典后记录为实验参数,flatten_dict的实现见 qlib/utils/init.py;model.fit(dataset):训练模型;SignalRecord(model, dataset, recorder).generate():生成预测信号。
SignalRecord是 Qlib 工作流中的 Record Template(记录模板,更多用法见 Recorder 文档)。它的generate实现(见 SignalRecord)做了三件事:
- 调用
model.predict(dataset)得到预测分数; - 将预测结果保存为
pred.pkl(pd.Series会被转成单列score的 DataFrame),即文档中提到的pred_score; - 从 handler 的原始标签(
DK_R)中取出 test 段标签,保存为label.pkl,便于后续做 IC 等信号分析。
3.4 关于 Alpha158 与预测分数
示例中的Alpha158是 Qlib 提供的数据处理器(158 个技术类因子,详见 Data Handler 文档)。从 Alpha158 源码 可以看到:
- 特征由 K线、价格(
OPEN/HIGH/LOW/VWAP)、滚动统计三类因子组合构成; - 默认标签配置为
Ref($close, -2)/Ref($close, -1) - 1,命名为LABEL0,即 T+1 相对 T 日收盘的次日收益。
这引出文档的最后一句关键结论:模型预测的语义取决于用户设计的 label。在默认设置下,预测分数表示预测模型对标的的评级——分数越高的标的,预期收益越高。因此 Top-K 选股策略「买分数最高的前 K 只」与标签语义是自洽的。
4. LGBModel 实现深潜
作为基线模型,LGBModel 同时继承ModelFT与LightGBMFInt(LightGBM 特征重要性解释器),其核心行为值得对照文档示例理解:
构造参数:loss="mse",early_stopping_rounds=50,num_boost_round=1000,其余**kwargs全部并入 LightGBM 参数表(见 LGBModel.init)。
fit 流程(fit):
_prepare_data优先准备 train 段,valid 段可选——这使模型在只有 train 段的数据集上也能训练;- 标签必须可 squeeze 成一维,LightGBM 不支持多标签训练;
- 可选传入
Reweighter生成样本权重w; - 训练回调包含早停(
lgb.early_stopping)、周期日志与record_evaluation,且每个 epoch 的验证指标会经R.log_metrics写入当前 Recorder,可在实验对比中直接查看。
predict 流程(predict):未训练直接抛ValueError;数据取自DK_I(infer 处理后的特征),返回与x_test索引对齐的pd.Series——这正是SignalRecord能直接落盘的原因。
finetune 流程(finetune):以现有模型为init_model,在 train 段上追加num_boost_round(默认 10)轮训练,与ModelFT.finetune的抽象定义对应。
5. 自定义模型
Qlib 支持自定义模型:只要你的模型继承qlib.model.base.Model(实现fit/predict,属性名避免以_开头),即可通过相同的init_instance_by_config配置机制集成进工作流或独立脚本。完整的自定义模型集成指引见 Custom Model Integration 文档。
6. API 参考
Model、ModelFT、finetune等接口的完整定义见 Model API 参考文档(qlib.model.base模块),源码位于 qlib/model/base.py。
【免费下载链接】qlibQlib is an AI-oriented Quant investment platform that aims to use AI tech to empower Quant Research, from exploring ideas to implementing productions. Qlib supports diverse ML modeling paradigms, including supervised learning, market dynamics modeling, and RL, and is now equipped with https://github.com/microsoft/RD-Agent to automate R&D process.项目地址: https://gitcode.com/GitHub_Trending/qli/qlib
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考