2026年机器学习选股系列研究之二:基于Dask计算图的遗传规划高频因子挖掘框架

遗传规划简介

遗传规划(Genetic Programming,GP)是更广泛的进化计算领域的一个子领 域,其根源可追溯至遗传算法(Genetic Algorithm,GA)。然而,与主要用于 优化参数的传统遗传算法不同,遗传规划旨在进化出算式本身(通常表示为树 结构)作为问题的解决方案。遗传规划的主要优势在于其能够自动发现数据中 复杂的函数关系和模式,而无需用户对解决方案的形式做出强假设。遗传规划 是一种极其通用的问题解决工具,其应用范围跨越了多个学科领域,例如金融 与经济建模、图像与视觉处理、医学与生物信息学、软件工程与计算机科学等 (Zhang & Smart, 2010)。 本研究采用遗传规划进行符号回归,其具体实现基于 python 的 gplearn 库 (Stephens, 2016)。gplearn 最显著的优势在于其严格遵守了 scikit-learn 的 API 设计规范。它提供了 SymbolicRegressor、SymbolicClassifier 和 SymbolicTransformer 三个核心估计器,分别用于回归、分类和特征构造,这 些估计器完美支持 fit()、predict() 和 transform() 方法,极大地简化了模 型训练、预测和集成到现有机器学习工作流的流程。

1.2 遗传规划算法流程

遗传规划的核心可以概括为“随机性”和“方向性”的权衡,算法既要保证种 群往更好的方向进化迭代,又要保证其种群的多样性与进化的随机性,防止陷 入局部最优解。其流程始于随机生成一个由许多上述二叉树组成的初始种群。 每个二叉树根据预定义的适应度函数(目标函数)分配一个适应度值。随后, 应用选择操作来优先选择更好的个体作为父代。这些父代通过交叉和变异操作 来产生新的后代个体。新生成的个体被评估并融入种群,取代较差的个体。这 个过程将不断迭代进行,直到达到早停条件或达到迭代轮次上限。

在原始 gplearn 框架中,主要依赖 joblib 库的 Parallel 模块实现多核并行 计算。具体而言,算法根据用户指定的 n_jobs 参数(即并行使用的 CPU 核心 数),将待进化的种群个体均匀拆分为 n_jobs 个独立任务,并通过多进程方 式分发至各个核心并行执行进化与适应度评估。这一并行设计是 gplearn 实现 计算加速的核心机制——在理想情况下,可用 CPU 核心数越多、单核计算能力 越强,算法的整体运行效率就越高。

1.3 进化的选择

在 gplearn 框架下,提供了 5 种进化的方向,分别是交叉(Crossover)、子树 变异(Subtree Mutation)、提升变异(Hoist Mutation)、点变异(Point Mutation)以及繁殖(Reproduction)。在 gplearn 中,是通过构建随机数的 方式去判断某子代公式选择哪一种进化方式(gplearn.genetic. _parallel_evolve 函数内)。

(1) 交叉:交叉是遗传规划中最主要的遗传操作符。它随机选择两个父代个 体,并分别在每个父代树上选择一个交叉点。然后,交换以这两个交叉点为根的子树,从而生成两个新的后代个体,此操作能有效组合父代中的有 益模块。 (2) 子树变异:子树变异首先在父代个体中随机选择一个节点作为变异点。 然后,将以该节点为根的整个子树删除,并在此处嫁接一棵全新随机生成 的子树。该操作向种群中引入大幅度的新变化,有助于探索新的解空间区 域。 (3) hoist 变异:又称提升变异,hoist 变异是一种特殊的、旨在减小树的大 小的操作符。它首先在父代树中随机选择一个节点(通常不能是根节 点),然后选择这个节点的一个下属子节点(即树中更低层的节点)。最 后,它将这个被选中的下属子节点提升到父代树原本被选中的位置,从而 “提升”它上来,并丢弃其余部分。该操作能有效对抗代码膨胀,简化个 体结构。 (4) 点变异:点变异是一种粒度更细的变异操作。它随机选择树中的一个节 点,但仅改变该节点本身的内容,而不影响其下属的子树。 (5) 繁殖:不进行交叉或变异,直接沿用该父代树至子代。

1.4 gplearn 的参数

在 gplearn 的框架下,提供了多个标准化的训练参数,给予使用者进行算法微 调。

gplearn 的改进

gplearn 库的功能与框架已经非常完备,但要应用于日频+分钟频的混合数据源 的选股因子挖掘,还需改动部分代码和逻辑。

2.1 数据结构与训练数据的分布式储存

原始 gplearn 库主要面向二维输入数据(样本*特征)设计的预测与分类任务, 但日频股票数据通常具有三维结构(交易日*股票*特征),因此原框架的设定在处理此类数据时存在明显不足。为提高计算效率,对于日频级别的数据,我 们将日频特征与预测目标统一重构为形状一致的 numpy 二维数组,其中第一维 表示交易日时间序列 dt,第二维对应各个股票标的名称 symbol,对于缺失数据 统一以 NaN 填充。在该设计下,日频特征间的运算将基于数组指针实现高效操 作,并充分利用 numpy 内置函数库的向量化能力,从而在整体框架中保证计算 性能。进一步的,对于分钟频特征,我们将其统一重构为与日频特征具有相同 第一、二维度的三维数组,其第三维长度固定为 240,代表单个交易日内的 240 个交易分钟。

然而,如果将多个分钟频输入特征以 numpy 数组的形式直接置于内存,其开销 将会很大。以常见的数据规模为例:若交易日长度为 3000,股票数量为 5000, 则单个分钟频特征的三维数组形状为 (3000, 5000, 240),若采用 float32 位 格式存储,单个特征所需内存约为 13GB,若引入数个基础分钟频特征,加上计 算过程中产生的中间数据,内存占用将迅速攀升至数百 GB,远超常规计算节点 的承载能力。为此,必须采用外部存储与按需加载的策略,在保证数据可访问 性的同时避免内存爆炸。 本文对于分钟频训练数据使用了基于 zarr 的分布式储存方案。zarr 是一种专为 科学计算设计的阵列存储格式,支持分块压缩、并行读写及惰性加载。我们将 所有分钟频特征按预设的数据分块(chunk_size,chunk_size,240)存储为独立 的 zarr 数组,并利用其内存映射机制,在计算分钟级算子的过程并行加载当前 所需的多个数据分块。对于日频特征,因其数据量较小,内存开销较小,可直 接常驻内存以加速访问。 相似的解决方案也有 HDF5,但 HDF5 受限于其文件级的全局锁机制和事务日志设 计,在多进程并发写入时存在严重的串行化瓶颈,且其二进制格式对云存储的 原生支持不足,难以在分布式环境中实现高效的并行读写与按需加载。更关键 的是,HDF5 从文件中读取的数据对象无法被 pickle 序列化,这意味着在 joblib 的 Parallel 多进程环境中,子进程无法继承父进程中已打开的 HDF5 数 据句柄,每个进程不得不独立重复打开文件,导致严重的 I/O 竞争与资源浪 费。相较之下,zarr 通过内存映射机制返回的数组可直接在进程间共享,完美 适配并行计算框架。

2.2 基于 dask 计算图的函数库构建

如果仅仅是采用分布式存储解决了输入特征的内存占用问题,计算过程中产生 的中间数据才是真正导致内存溢出的主要因素。在遗传编程的适应度评估阶 段,每个算子在执行时均会生成新的中间数组,若采用即时计算模式,这些中 间结果将同时驻留内存,极易超出硬件承载上限,在多核并行情况下更为明 显。 为解决上述问题,本文引入 dask 构建高频算子库。dask 是一个开源的并行计 算框架,其核心设计在于向上兼容 numpy 的数组接口,向下构建有向无环图 (DAG)实现任务的延迟执行与分布式调度。具体而言,dask 将大型数组拆分 为若干分块,每个分块均以 numpy 数组的形式参与运算,因此所有基于 numpy 编写的算子均可无缝迁移至 dask 环境;与此同时,dask 并不立即执行这些运 算,而是将算子调用记录为计算图中的一个节点。计算图本质上是对整个表达 式求值过程的抽象表示,图中每个节点代表一个计算任务,边则定义了任务间 的数据依赖关系。当最终需要计算结果时,dask 调度器依据计算图将任务分发 至多核或多节点执行,并在执行过程中自动优化中间结果的复用与数据局部 性。通过这一设计,高频算子库既保留了 numpy 的编程便利性,又获得了分布 式环境下的并行能力与内存控制能力。dask 与 zarr 具备原生兼容性,我们可将 磁盘中以 zarr 格式存储的分钟频训练数据加载为 dask 数组,基于 dask 内置算 子构建分钟数据函数库。

2.3 覆盖度控制、多样性控制与入库要求

为保证因子的覆盖度质量,在多线程计算中,每当一个新的子代产生时,都会 监控其时间与股票两个维度的数据覆盖度,剔除数据质量不达标的个体。 为保证种群多样性,在选取 hall_of_fame 群体时,会引入一个相关系数+早停 的控制,即计算整个 hall_of_fame 群体两两之间的相关系数,若平均相关系数 过大,我们认为算法正在逐渐丧失其多样性,并提前跳出循环。 每当一轮新的子代种群产生后,计算其 best_programs 的训练、验证、预测区 间的 IC 与 IR 情况,达到阈值要求后会计算其与既有因子群两两之间的相关系 数,均达到阈值要求后遂可入库。

2.4 适应度函数构造

基于原生的 gplearn 框架,我们构造了多个适应度函数,并用原生的_Fitness 类加以包装:(1)spearman_corr:秩相关系数,更贴近于传统的因子评价体 系;该适应度函数也可衍生出带权重的秩相关系数,例如对数市值加权的秩相 关系数。(2)weight_returns:加权收益率函数,即对截面打分的前若干百分 比的票进行未来收益率的等权加权求和,用于寻找头部分离度优秀的因子。该 适应度函数也可衍生出尾部等权组合收益率,用于寻找尾部分离度优秀的因子;以及取负值后外套 relu 激活函数的版本,使算法关注组合收益中亏损的交 易日,用于寻找组合胜率高的因子。


(本文仅供参考,不代表我们的任何投资建议。如需使用相关信息,请参阅报告原文。)

相关报告