遗传规划入门:让计算机自己“进化”出因子
大家好,我是老蓝。今天咱们聊一个特别有意思的话题——遗传规划。说白了,就是让计算机像生物进化一样,自己“长”出有效的因子来。我第一次接触这个概念时,心里想的是:“这不就是让代码生孩子吗?”后来发现,还真差不多。
核心思想:遗传规划(Genetic Programming, GP)是进化算法的一种。它不直接给你答案,而是给你一套“进化规则”,让程序自己迭代出最优解。在因子挖掘里,GP 能自动组合各种基础算子,生成你根本想不到的复杂因子。
GP 的基本原理:达尔文在代码世界
遗传规划的核心流程,其实就四步:
- 初始化种群——随机生成一堆“因子表达式”,就像一堆随机生成的数学公式。
- 评估适应度——每个因子在历史数据上跑一遍,看谁预测能力最强。
- 选择与繁殖——表现好的因子被选出来,通过“交叉”(交换子树)和“变异”(随机修改节点)生成下一代。
- 迭代终止——重复几十上百代,直到找到满意的因子。
我刚开始做的时候,总觉得这玩意儿太随机了,能靠谱吗?后来发现,只要适应度函数设计得好,它真能挖出一些人类直觉想不到的因子。比如有一次,GP 生成了一个因子:(close - open) / (high - low) * volume,我一看,这不就是日内波动率加成交量的组合吗?但之前我从来没想过把它们这样乘起来。
我的经验:GP 的“进化”过程其实很暴力。你给它 1000 个随机因子,跑 50 代,最后可能只有 3-5 个是真正有效的。但就是这 3-5 个,往往能带来超额收益。别嫌少,质量比数量重要。
GP 在因子挖掘中的应用场景
你可能会问:为什么不用深度学习?嗯,深度学习是黑箱,GP 至少给你一个可解释的数学表达式。这在量化里太重要了——你总得知道你的因子为什么赚钱吧?
具体来说,GP 在因子挖掘中主要干三件事:
- 特征组合——把基础数据(价格、成交量、财务指标)通过加减乘除、开方、取对数等操作组合起来。
- 非线性关系发现——比如
log(volume) / sqrt(close)这种人类不太容易想到的组合。 - 自动特征选择——进化过程中,无效的表达式会被自然淘汰,留下的都是相对有效的。
我记得有一次做 A 股因子挖掘,GP 生成了一个因子:close / ma(close, 20) - 1。这不就是乖离率吗?但 GP 还生成了另一个:(close - ma(close, 5)) / (high - low),这个组合我之前从没见过,回测效果居然还不错。
避坑指南:我曾经犯过一个错误——让 GP 自由组合所有算子,结果生成了大量过拟合的因子。比如 close / close * close / close 这种毫无意义的表达式。后来我加了“复杂度惩罚”,表达式越长,适应度扣分越多。这才把那些“垃圾因子”过滤掉。
Python 库 gplearn 快速上手
说到工具,Python 里最成熟的 GP 库就是 gplearn。它封装了遗传规划的全流程,你只需要定义好输入数据和适应度函数就行。
先安装:
pip install gplearn
然后看一个最简单的例子:
import numpy as np
import pandas as pd
from gplearn.genetic import SymbolicRegressor
from gplearn.functions import _function_map
# 生成模拟数据
X = np.random.rand(100, 5) # 5个基础特征
y = X[:, 0] ** 2 + np.sin(X[:, 1]) + 0.1 * np.random.randn(100)
# 定义GP模型
gp = SymbolicRegressor(
population_size=500, # 每代种群大小
generations=20, # 进化代数
tournament_size=20, # 锦标赛选择大小
stopping_criteria=0.01, # 提前停止条件
p_crossover=0.7, # 交叉概率
p_subtree_mutation=0.1, # 子树变异概率
p_hoist_mutation=0.05, # 提升变异概率
p_point_mutation=0.1, # 点变异概率
max_samples=0.9, # 每代使用90%样本
verbose=1 # 打印进化过程
)
# 训练
gp.fit(X, y)
# 查看最优表达式
print(gp._program)
# 预测
y_pred = gp.predict(X)
跑完之后,你会看到类似这样的输出:
| Population Average | Best Individual |
---- ------------------------ ------------------------------------------
Gen Length Fitness Length Fitness OOB Fitness Time Left
0 12.34 0.2345 8 0.4567 0.4231 1.23m
1 11.89 0.2567 8 0.4789 0.4456 1.15m
...
19 8.45 0.3456 6 0.5678 0.5567 0.00m
Best expression: add(mul(X0, X0), sin(X1))
你看,它找到了 X0^2 + sin(X1),和我们的真实公式几乎一样。这就是 GP 的威力——它不需要你告诉它公式长什么样,它自己就能“进化”出来。
我的建议:刚开始用 gplearn 时,别把参数调得太激进。我一般先用 population_size=500、generations=20 跑一轮,看看结果。如果效果不错,再慢慢增加代数。另外,max_samples 我习惯设成 0.8-0.9,这样能防止过拟合。
核心参数速查表
| 参数 | 默认值 | 我的推荐 | 说明 |
|---|---|---|---|
| population_size | 1000 | 500-2000 | 种群越大,搜索空间越广,但速度越慢 |
| generations | 20 | 20-50 | 代数太少可能没收敛,太多容易过拟合 |
| p_crossover | 0.8 | 0.7-0.9 | 交叉概率,太高会破坏优秀个体 |
| p_subtree_mutation | 0.1 | 0.05-0.15 | 子树变异,引入新结构的关键 |
| max_samples | 1.0 | 0.8-0.9 | 每代使用的样本比例,小于1可防过拟合 |
| stopping_criteria | 0.0 | 0.01-0.05 | 适应度提升小于此值时提前停止 |
GP 因子挖掘的完整流程
下面这张图,是我自己总结的 GP 因子挖掘全流程。你照着这个走,基本不会跑偏。
你看,整个流程其实就是一个循环:生成 -> 评估 -> 选择 -> 变异 -> 再评估。直到你满意为止。
重要提醒:GP 不是万能的。我见过有人把 GP 当成黑盒,扔进去一堆数据就等着收因子。结果呢?跑出来一堆过拟合的垃圾。记住:GP 只是工具,你的金融直觉和回测验证才是关键。因子挖出来后,一定要做样本外测试和逻辑检验。
好了,关于遗传规划入门,今天就聊到这儿。GP 这东西,上手不难,但用好需要经验。我建议你先拿模拟数据跑一跑,感受一下“进化”的过程。等熟悉了,再上真实数据。嗯,就这样。