如何 10 分钟用 PySR 从数据里挖出可解释的数学公式
【免费下载链接】PySRSymbolic Learning in Python and Julia项目地址: https://gitcode.com/GitHub_Trending/py/PySR
PySR 是一个用 Python 写、用 Julia 跑的符号回归(Symbolic Regression)工具:你给它一张数值表,它自己搜索出一段能解释这张表的数学公式。如果你正被"模型预测很准但没人看得懂"困扰,或者手上一堆实验数据想反推背后的规律,PySR 把"从数据还原公式"这件事压缩成了几行 scikit-learn 风格的代码。
为什么需要符号回归
神经网络越堆越大,"黑盒"问题也越严重——它告诉你答案,但说不清理由。符号回归换了个思路:不猜结构,而是让算法在一棵棵表达式树里做进化搜索,直接产出像 $2.5382\cos(x_3) + x_0^2 - 0.5$ 这样人能读写的公式。
它的好处很具体:
- 公式本身即解释,可以直接写进论文和报告;
- 表达式计算成本远低于神经网络,外推行为也更好预测;
- PySR 的搜索引擎用 Julia 实现(与 SymbolicRegression.jl 对接),多线程并行,Python 侧只负责调度和导出。
需要注意的是:符号回归在低维数据(几十列以内、几千行)上效果最好。高维场景一般先用"符号蒸馏"把神经网络转成公式,再让 PySR 接手(README.md 中有说明)。
上图是一次典型搜索的产出对比:横轴是真实值,纵轴是模型用发现出的公式做出的预测,点贴着对角线说明公式基本还原了数据规律。
安装与 5 分钟上手
安装只需一行,Julia 依赖会在第一次 import 时自动拉取(首次会稍慢,属正常现象):
pip install pysr # 或者 conda install -c conda-forge pysr最小可运行路径,直接粘进 IPython(作者推荐 IPython 而非 Jupyter,进度条和按q提前停止的体验更好):
import numpy as np from pysr import PySRRegressor # 造一个带 5 个特征的样本集,目标是 2.5382*cos(x3) + x0^2 - 0.5 X = 2 * np.random.randn(100, 5) y = 2.5382 * np.cos(X[:, 3]) + X[:, 0] ** 2 - 0.5 # 只允许这些算子,搜索空间越小越容易收敛 model = PySRRegressor( niterations=40, # 迭代轮数,想要更好结果就调大 binary_operators=["+", "*"], unary_operators=["cos", "exp", "sin"], elementwise_loss="loss(prediction, target) = (prediction - target)^2", ) model.fit(X, y) # 内部启动 Julia 进程做多线程搜索 print(model) # 打印排行榜,箭头标记被选中的公式model.fit(X, y)返回后,model.equations_是一个 pandas DataFrame,存着所有候选公式及其 loss、complexity;model.sympy()拿到 SymPy 表达式,model.predict(X)用自动选中的公式做预测(model.predict(X, 3)则指定用排行榜第 3 条)。每次运行还会落盘hall_of_fame...csv(公式列表)和同名.pkl(搜索状态),之后可用PySRRegressor.from_file("xxx.pkl")直接恢复。
更完整的演示脚本可以对照 example.py,交互教程在 examples/pysr_demo.ipynb。
关键参数:改哪个、为什么改
不用背全部参数,下面这几个是真正影响结果的:
| 参数 | 改什么 | 建议 |
|---|---|---|
niterations | 搜索迭代轮数,每轮含数十万次变异和求值 | 最终跑可以设得很大,配合停止条件即可 |
populations | 并行种群数 | 官方建议3 * 物理核数(docs/src/tuning.md) |
binary_operators/unary_operators | 搜索的算子词汇表 | 只留"你觉得它会用到"的,算子越少越收敛;冗余算子(如neg和二元-)不要同时给 |
maxsize | 公式的最大复杂度 | 比期望的最终大小略大一点,比如想要 size 30 就设 35,给搜索留探索空间 |
early_stop_condition | 提前停止条件 | 写成 Julia 谓词,如stop_if(loss, complexity) = loss < 1e-6 && complexity < 10 |
timeout_in_seconds | 兜底超时 | 长时间任务务必设一个,防止"跑到天荒地老" |
warm_start | 从上次搜索状态续跑 | 连续实验很方便,但改maxsize、换算子等参数后必须model.reset(),否则报错 |
denoise/select_k_features | 训练前用高斯过程去噪 / 先筛选 k 个最重要特征 | 有噪声或特征特别多时值得开,作者本人日常并不常用 |
另外两个进阶旋钮:constraints/nested_constraints可以禁止某些嵌套(比如exp(exp(x))),官方调优笔记里反复强调"能卡多严卡多严",对探索效率帮助很大;turbo=True开启实验性循环向量化,约能提速 20%。完整参数列表见 pysr/sr.py 中的PySRRegressordocstring。
典型场景一:带噪声的回归
数据带噪时,直接把噪声喂给搜索器会白费搜索预算。denoise=True会先用高斯过程把目标去噪,再在去噪后的目标上搜索,官方示例(docs/src/examples/getting-started.md)就用了这个组合:
X = np.random.randn(100, 5) y = np.exp(X[:, 0]) + X[:, 1] + X[:, 2] + 0.1 * np.random.randn(100) # 目标加噪 model = PySRRegressor( binary_operators=["+", "-", "*", "/"], unary_operators=["exp"], denoise=True, # 先用高斯过程平滑 y,再开始符号搜索 ) model.fit(X, y)输入是 100×5 的噪声表,过程是"去噪→进化搜索",产出是一条能还原exp(x0)+x1+x2结构、常数项干净的公式。如果你的数据有每行的信噪比信息,还可以给fit传weights,并把elementwise_loss写成带weight参数的三参版本。
典型场景二:不止回归——从规则表反推游戏定律
PySR 的输入不限于浮点数。仓库里的 examples/game_of_life.py 把康威生命格的转移表(细胞当前状态 + 邻居数 → 下一状态)当作"数据集",只用and/or/not/eq几个布尔算子搜索,结果把 B3/S23 规则完整还原了出来。同样的思路在 examples/magnetic_field.py 里用TemplateExpressionSpec+ 自定义Force类型,从纯力测量数据里解出了磁场分量B_x(t), B_y(t), B_z(t)的解析形式——输入是 1000 组(t, v, T, F),产出是三个磁场分量的函数表达式。这说明"从数据反推规律"这件事,数值之外也能做。
符号回归的天花板不低:社区用它从模拟数据里挖出星系质量关系、从贸易数据里复现了上式的"引力模型"(见 docs/papers.yml 收录的研究案例)。
常见问题与踩坑提示
- import 时崩溃,报
GLIBCXX_...not found:某个依赖加载了错误的libstdc++。把 Julia 的库目录加进LD_LIBRARY_PATH(README 的 Troubleshooting 一节有示例命令)。 - Jupyter 里没有进度条、不能按
q停止:这是已知取舍。日常调参用 IPython;Jupyter 里中断只会返回当前已找到的公式。 - 改了参数后
fit报 warm start 冲突:warm_start状态下改maxsize、换算子等都会让旧状态失效,先调model.reset()。 - 数据超过 1000 行:官方建议要么随机下采样到约 1000 行(低维、噪声少时),要么开
batching=True(高维或噪声大、每条数据都要用到时)。 - 公式越搜越复杂、卡住不动:试试
warmup_maxsize_by,强制从简单公式起步逐步放复杂度;或对不喜欢的算子在complexity_of_operators里加价。 - 数值不放心:公式嵌套深、幅度大时把
precision=64打开做高精度求值。
进阶与生态
- 结果导出链路很全:SymPy(pysr/export_sympy.py)、LaTeX(pysr/export_latex.py)、可微的 JAX / PyTorch 函数(
model.jax()、model.pytorch(),对应 pysr/export_jax.py、pysr/export_torch.py),拿去做后续微分优化都没问题。 - 集群扩展:
cluster_manager="slurm"一键把搜索铺到整个 SLURM 分配上,多节点配置参考 docs/src/slurm.md;容器化可用 Dockerfile 或 Apptainer.def。 - 自定义值类型:
TypeSpec让你定义自己的数据形态(向量、布尔格、矩阵……),配合自定义损失实现上文的规则发现类玩法,用法见 docs/src/examples/expression-specifications.md。 - 给编码代理用的使用指南在 skills/pysr/SKILL.md。
小结与下一步
一句话回顾:PySR 用 Python 的接口 + Julia 的引擎,把"从数据反推公式"变成了一次fit调用——先给最小配置跑通,再用niterations、算子表和maxsize三个旋钮收紧搜索。
下一步建议:把 example.py 换成你自己的数据跑一遍,然后照 docs/src/tuning.md 的清单调参;跑出一版满意的结果后,顺手把案例提 PR 到社区的研究展示页。🚀
【免费下载链接】PySRSymbolic Learning in Python and Julia项目地址: https://gitcode.com/GitHub_Trending/py/PySR
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考