系统:MacOS
Python版本:3.12
测试时间:2026-06-19
不知道大家有没有做过"从数据里猜测其符合什么样的y=f(x)数学公式"这样的事情?在很多年前没有AI的时代,这个领域基本是欧美软件的天下,后来出现了一个石破天惊的国产软件1stOpt( http://www.7d-soft.com/ )打破了它们的垄断,在当年算是国产软件在这个领域取得的重大成就。 巧了,这两天我刚好看到科技媒体报道了一个新的国产开源软件 FunctionEvolve ,正是和解决此类问题有关,于是我去了解了一下,并且拿它做了一些简单的测试,写成此文。
本站的关联文章链接:最优化/Optimization文章合集
在正文开始之前我想先闲聊一下,文中测试使用的大模型是DeepSeek V4 Flash,测试过程中总共用了30多万token,花费0.19元,由于任务的性质缓存命中率很低,但DS定价太便宜所以几乎没花钱。
下面开始正文。
FunctionEvolve ( https://github.com/Phoinikas03/FunctionEvolve )是一个符号回归(Symbolic Regression)搜索框架,核心目标是从数值数据中自动发现数学公式。它采用 LLM + 传统数值优化 的混合架构:LLM 负责分析领域知识、生成种子公式、选择父本和定向变异建议;传统优化器(DE / CMA-ES / L-BFGS-B / TRF)负责拟合公式中的参数。
文章来源:https://www.codelast.com/
同类软件一览
符号回归和自动公式发现领域已有不少成熟工具,按原理大致可分为两类:
| 类别 | 代表软件 | 国别 | 核心技术 |
|---|---|---|---|
| 传统全局优化 | 1stOpt | 🇨🇳 国产 | UGO(通用全局优化),无需初值 |
| MATLAB Curve Fitting | 🇺🇸 | Trust-Region / Levenberg-Marquardt | |
| OriginPro | 🇺🇸 | Levenberg-Marquardt / 全局优化 | |
| DataFit | 🇺🇸 | 多算法自动搜索 | |
| 遗传编程 (GP) | Eureqa | 🇺🇸 | 遗传编程符号回归(最早商业化的 GP 工具) |
| PySR | 🌍 开源 | 遗传编程(Python 库) | |
| GPTIPS | 🇬🇧 | 多基因遗传编程(MATLAB) | |
| gplearn | 🌍 开源 | 遗传编程(Python 库) | |
| LLM + 优化 | FunctionEvolve 🌟 | 🌍 开源 | LLM 指导搜索方向 + 数值优化器拟合参数 |
重点说明:1stOpt(七维高科)
我很多年以前用过几次 1stOpt,当时对它留下了深刻印象(不知道现在是否还在维护),所以这里单独介绍一下。
1stOpt 是国内 7D-Soft 开发的数值优化分析软件,在工程和科研领域有大量用户。它的核心特点是:
- 无需人工初值 — 传统工具(如 MATLAB)做曲线拟合时,参数初值给不对就会发散或收敛到局部最优。1stOpt 内置 UGO(通用全局优化) 算法族,号称"万用公式,任意初值"。
-
公式模板驱动 — 用户提供公式骨架(如
y = a*x^b + c*e^(d*x)),1stOpt 负责搜索最优参数。和 FunctionEvolve 相比,1stOpt 的公式结构需要人先指定,FunctionEvolve 则连结构也可以自动发现。 - 适用场景 — 曲线/曲面拟合、非线性回归、参数估计、微分方程求解、工程反演。
- 局限 — 商业软件、闭源、Windows 独占。公式结构依赖用户经验,无法自动探索未知结构。
在 FunctionEvolve 中,大模型起什么作用
LLM 起的是"搜索方向指导"作用,数学计算由传统优化器完成。
FunctionEvolve 是 LLM + 传统数值优化 的混合架构,LLM 不做数学计算。
完整工作流
| 阶段 | 组件 | 是否用 LLM | 作用 |
|---|---|---|---|
| ① 领域分析 |
Generator |
✅ LLM | 分析问题属于什么领域(物理/化学/生物...),给出该领域常见公式模板 |
| ② 种子生成 |
Generator |
✅ LLM | 基于领域知识,生成 20 个初始候选公式(带参数占位符 c0, c1...) |
| ③ 参数拟合 |
StructureOptimizer |
❌ 纯数值 | VARPRO + DE + CMA-ES + L-BFGS-B,拟合 c0, c1... 的最优值 |
| ④ 评估 |
Evaluator |
❌ 纯数值 | 计算 NMSE(归一化均方误差) |
| ⑤ 选父本 |
Selector |
✅ LLM | 看当前进化树所有公式的 NMSE 和结构,决定下一轮"从哪些公式出发" |
| ⑥ 结构变异 |
ASTMutator |
❌ 规则引擎 | 程序化地删子树、加项、拆函数包装,生成候选变体 |
| ⑦ 结构建议 |
LLMMutator |
✅ LLM | "试试加个 sin 项"、"试试把 x² 换成 exp(x)" |
| ⑧ 回到 ③ | 循环 | — | 拟合 → 评估 → 选父本 → 变异 → 再拟合... |
优化算法详情
所有参数拟合(阶段③)由 StructureOptimizer 执行,它是一个多策略流水线。
第三方库提供的基础算法(4 个)
| 算法 | 第三方来源 | 独立包装文件 | StructureOptimizer 中的用途 |
|---|---|---|---|
| L-BFGS-B |
scipy.optimize.minimize |
optimizer/lbfgs.py |
局部精修(全局搜索后的细化);Pow 指数对齐后的重拟合 |
| DE (差分进化) |
scipy.optimize.differential_evolution |
optimizer/de.py |
全局搜索主路径之一(_run_de) |
| CMA-ES |
cmaes 库 |
optimizer/cma.py |
全局搜索主路径之一(_run_cma);DE 的并行兜底 |
| TRF (Trust Region Reflective) |
scipy.optimize.least_squares |
optimizer/least_squares.py |
全局搜索路径之一(_run_trf) |
项目自身实现的策略(3 个)
| 策略 | 所在方法 (structure.py) | 说明 |
|---|---|---|
| ① VARPRO (变量投影分解) |
_try_varpro |
将参数分解为线性 + 非线性两组:线性组用 np.linalg.lstsq 直接 OLS 求解,非线性组用基础算法搜索。本质是降维策略 |
| ② Compound-Pow 预搜索 |
_try_pow_presearch |
检测公式中 x^(c) 且底数含其他参数的情况,枚举候选有理指数(1, 2, 1/2, 1/3...),固定指数后用 VARPRO 拟合其余参数 |
| ③ Pow 指数对齐 |
_snap_pow_and_refit + _pow_rational_grid |
将拟合出的浮点指数对齐到最接近的有理数/整数,再用 L-BFGS-B 重新拟合其他参数 |
StructureOptimizer 执行流水线
输入: 公式骨架 + 数据
│
├─ ① Pow 预搜索 (Compound-Pow pre-search)
│ 枚举候选指数,固定后用 VARPRO 拟合其余参数
│
├─ ② VARPRO 分解 (主路径)
│ 线性参数 → np.linalg.lstsq OLS 求解
│ 非线性参数 → DE / CMA / TRF 并行搜索
│
├─ ③ 并行兜底 (DE / CMA / TRF 同时跑, 取最优)
│
├─ ④ L-BFGS-B 局部精修
│
├─ ⑤ Pow 指数对齐 & 重拟合
│
└─ 输出: 最优参数 + NMSE
所有路径均有超时保护和多次重启机制。
文章来源:https://www.codelast.com/
关键源码位置
| 组件 | 文件 |
|---|---|
| 主入口 & 装配 |
main.py |
| 搜索循环 |
src/search.py(TreeSearch.run()) |
| LLM 生成器 |
src/generator.py |
| LLM 选择器 |
src/selector.py |
| LLM 变异器 |
src/mutator.py |
| AST 规则变异器 |
src/mutator.py(ASTMutator 类) |
| 参数优化器总管 |
src/optimizer/structure.py(StructureOptimizer) |
| L-BFGS-B 优化器 |
src/optimizer/lbfgs.py |
| DE 优化器 |
src/optimizer/de.py |
| CMA-ES 优化器 |
src/optimizer/cma.py |
| Least-Squares 优化器 |
src/optimizer/least_squares.py |
| 优化器基类 & 工具 |
src/optimizer/base.py |
| 评估器 |
src/evaluator.py |
| 进化树 |
src/evolution_tree.py |
| LLM 配置 |
llm_config.yaml |
一句话总结
LLM 是"策略师"——告诉你该往哪个方向找公式;数学优化器是"计算器"——实际算出参数值。
LLM 不碰任何数值计算。所有参数拟合由
StructureOptimizer以 VARPRO → DE/CMA/TRF(并行全局搜索)→ L-BFGS-B(局部精修)→ Pow 指数对齐 的多层流水线完成。
完整测试流程
以下是从零开始创建测试数据、搭建环境、运行测试、清理环境的完整步骤记录。
测试目标
验证 FunctionEvolve 能否从数值数据中自动发现复杂公式:
步骤一览
Step 1: 生成测试数据集
Step 2: 创建临时 Python 环境并安装依赖
Step 3: 编写测试脚本
Step 4: 运行测试
Step 5: 查看结果
Step 6: 删除临时环境
Step 1:生成测试数据集
cd /path/to/FunctionEvolve
python3 datasets/z/generate_complex_dataset.py --noise-std 0.02
生成程序完整源码
保存为 datasets/z/generate_complex_dataset.py:
#!/usr/bin/env python3
"""
生成一个复杂公式的符号回归数据集,用于验证 FunctionEvolve 的效果。
公式(5 个输入变量,混合三角函数、指数、对数、有理分式):
sin(x1 * x2) x1 * x3
y = ──────────── + exp(-x2) * ln(|x4| + 1) + ─────────
cos(x3) + 1.5 1 + x5²
输出:
datasets/z/
dataset.npz — NumPy 压缩格式(直接用 from_arrays 加载)
dataset.csv — CSV 文本格式(肉眼查看用)
ground_truth.txt — 真实公式说明
preview.txt — 数据统计摘要
"""
from __future__ import annotations
import argparse
from pathlib import Path
import numpy as np
GROUND_TRUTH_EXPRESSION = (
"sin(x1 * x2) / (cos(x3) + 1.5) + exp(-x2) * log(abs(x4) + 1) + x1 * x3 / (1 + x5**2)"
)
GROUND_TRUTH_SYMBOLS = ["x1", "x2", "x3", "x4", "x5"]
RNG = np.random.RandomState(42)
def _safe_log_abs(x):
return np.log(np.abs(x) + 1.0)
def ground_truth_y(X):
x1, x2, x3, x4, x5 = X[:, 0], X[:, 1], X[:, 2], X[:, 3], X[:, 4]
term1 = np.sin(x1 * x2) / (np.cos(x3) + 1.5)
term2 = np.exp(-x2) * _safe_log_abs(x4)
term3 = x1 * x3 / (1.0 + x5**2)
return term1 + term2 + term3
def main():
parser = argparse.ArgumentParser(description="生成复杂符号回归数据集")
parser.add_argument("--n-train", type=int, default=2000)
parser.add_argument("--n-test", type=int, default=500)
parser.add_argument("--noise-std", type=float, default=0.0)
parser.add_argument("--output-dir", type=str, default=None)
args = parser.parse_args()
out_dir = Path(args.output_dir) if args.output_dir else Path(__file__).resolve().parent
out_dir.mkdir(parents=True, exist_ok=True)
# 生成训练集
X_train = RNG.uniform(low=-3.0, high=3.0, size=(args.n_train, 5))
y_train = ground_truth_y(X_train)
if args.noise_std > 0:
y_train += RNG.normal(0, args.noise_std, size=args.n_train)
# 生成测试集
rng_test = np.random.RandomState(2024)
X_test = rng_test.uniform(low=-3.0, high=3.0, size=(args.n_test, 5))
y_test = ground_truth_y(X_test)
if args.noise_std > 0:
y_test += rng_test.normal(0, args.noise_std, size=args.n_test)
# 保存 NumPy 格式
np.savez(out_dir / "dataset.npz",
X_train=X_train, y_train=y_train,
X_test=X_test, y_test=y_test,
expression=GROUND_TRUTH_EXPRESSION)
# 保存 CSV
header = ",".join(GROUND_TRUTH_SYMBOLS) + ",y"
rows = np.column_stack([np.vstack([X_train, X_test]),
np.hstack([y_train, y_test])])
with open(out_dir / "dataset.csv", "w") as f:
f.write(f"# 真实公式: {GROUND_TRUTH_EXPRESSION}\n")
f.write(f"# 噪声标准差: {args.noise_std}\n")
f.write(header + "\n")
for row in rows:
f.write(",".join(f"{v:.8f}" for v in row) + "\n")
print(f"数据集已保存到 {out_dir}")
if __name__ == "__main__":
main()
生成的数据样例
x1,x2,x3,x4,x5,y
-0.75275929,2.70428584,1.39196365,0.59195091,-2.06388816,-0.73060157
-2.06403288,-2.65149833,2.19705687,0.60669007,1.24843547,4.13384154
-2.87649303,2.81945911,1.99465584,-1.72596534,-1.90905020,-2.05631804
-1.89957294,-1.17454654,0.14853859,-0.40832989,-1.25262516,1.29225609
每行 6 列:x₁ ~ x₅ 为输入,y 为输出。
产出文件:
| 文件 | 说明 |
|---|---|
datasets/z/dataset.npz |
NumPy 压缩格式,供 from_arrays() 加载 |
datasets/z/dataset.csv |
CSV 文本格式,肉眼查看用 |
datasets/z/ground_truth.txt |
真实公式说明 |
datasets/z/preview.txt |
数据统计摘要 |
Step 2:创建临时环境
使用 micromamba 创建独立的 Python 3.12 环境,避免污染已有环境:
# 创建
micromamba create -n py312 python=3.12 -y
# 安装依赖
micromamba run -n py312 pip3 install scipy sympy scikit-learn h5py \
tiktoken json-repair cmaes threadpoolctl
Step 3:编写测试脚本
保存为 datasets/z/run_test.py:
#!/usr/bin/env python3
"""
在 FunctionEvolve 上运行自定义复杂数据集(datasets/z/dataset.npz)的测试脚本。
用法:
cd /path/to/FunctionEvolve
# 退化模式(无 LLM,仅验证程序化变异 + 数值优化)
python datasets/z/run_test.py --degenerated
# 完整模式(使用 LLM,需要 llm_config.yaml)
python datasets/z/run_test.py
"""
import argparse
import os
import sys
from pathlib import Path
import numpy as np
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
from src.dataset import SRDataset
from src.evaluator import Evaluator
from src.evolution_tree import EvolutionTree
from src.search import TreeSearch
from src.generator import MockGenerator, create_generator
from src.selector import MockSelector, create_selector
from src.mutator import MockMutator, LLMMutator
from src.llm_client import build_openai_client, LLMUsageLogger
def _load_dataset(data_dir):
npz_path = data_dir / "dataset.npz"
data = np.load(npz_path)
expr = str(data["expression"].item())
return SRDataset.from_arrays(
X_train=data["X_train"], y_train=data["y_train"],
X_test=data["X_test"], y_test=data["y_test"],
symbols=["x1", "x2", "x3", "x4", "x5"],
symbol_descs=["v1", "v2", "v3", "v4", "v5"],
expression=expr, equation_name="complex_z",
)
def _evaluate_ground_truth(ds, evaluator):
"""对真实公式做参数拟合,给出精度天花板。"""
import sympy as sp
sympy_expr = sp.sympify(ds.expression)
param_names = sorted(
{str(s) for s in sympy_expr.free_symbols} - set(ds.feature_names))
result = evaluator.evaluate_skeleton(sympy_expr, param_names)
print(f"[Ground Truth] Train NMSE = {result.train_nmse:.2e}")
print(f"[Ground Truth] Test NMSE = {result.test_nmse:.2e}")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--degenerated", action="store_true")
parser.add_argument("--max-steps", type=int, default=30)
parser.add_argument("--n-seeds", type=int, default=20)
parser.add_argument("--candidate-num", type=int, default=5)
parser.add_argument("--timeout", type=float, default=120.0)
parser.add_argument("--llm-config", type=str, default=None)
parser.add_argument("--run-tag", type=str, default="z_test")
args = parser.parse_args()
data_dir = Path(__file__).resolve().parent
ds = _load_dataset(data_dir)
print(f"训练样本: {ds.X_train.shape[0]}, 测试样本: {ds.X_test.shape[0]}")
evaluator = Evaluator(
feature_names=ds.feature_names, X_train=ds.X_train,
y_train=ds.y_train, X_test=ds.X_test, y_test=ds.y_test,
timeout=args.timeout,
)
_evaluate_ground_truth(ds, evaluator)
# 构造 LLM / Mock Agent
llm_config = None
if not args.degenerated:
cfg_path = args.llm_config or str(PROJECT_ROOT / "llm_config.yaml")
if os.path.exists(cfg_path):
import yaml
with open(cfg_path) as f:
llm_config = yaml.safe_load(f) or {}
def _resolve(comp, field, fallback=None):
return llm_config.get(comp, {}).get(field, fallback) if llm_config else fallback
if args.degenerated:
generator = MockGenerator(variables=ds.feature_names)
selector = MockSelector(variables=ds.feature_names)
llm_mutator = MockMutator()
else:
gen = _resolve("generator", "model"); gen_url = _resolve("generator", "base_url")
gen_key = _resolve("generator", "api_key"); gen_mode = _resolve("generator", "mode", "openai")
gen_temp = _resolve("generator", "temperature", 0.8)
gen_tok = _resolve("generator", "max_tokens", 128000)
logger = LLMUsageLogger(str(Path("logs") / "z_custom" / "llm_usage.csv"))
generator = create_generator(gen, gen_url, gen_key, gen_temp, gen_tok,
usage_logger=logger, llm_mode=gen_mode)
sm = _resolve("selector", "model", gen); su = _resolve("selector", "base_url", gen_url)
sk = _resolve("selector", "api_key", gen_key); sd = _resolve("selector", "mode", gen_mode)
st = _resolve("selector", "temperature", 0.3)
selector = create_selector(sm, su, sk, st, usage_logger=logger, llm_mode=sd)
mm = _resolve("mutator", "model", gen); mu = _resolve("mutator", "base_url", gen_url)
mk = _resolve("mutator", "api_key", gen_key); md = _resolve("mutator", "mode", gen_mode)
mt = _resolve("mutator", "temperature", 0.7)
m_tok = _resolve("mutator", "max_tokens", 128000)
client = build_openai_client(mm, mu, mode=md, api_key=mk)
llm_mutator = LLMMutator(client, mm, mt, m_tok, usage_logger=logger)
tree = EvolutionTree()
searcher = TreeSearch(
dataset=ds, evaluator=evaluator, tree=tree,
selector=selector, generator=generator, llm_mutator=llm_mutator,
max_steps=args.max_steps, n_seeds=args.n_seeds,
candidate_num=args.candidate_num, timeout=args.timeout, verbose=True,
)
searcher.initialize_seeds()
searcher.run()
# 输出最佳结果
evaluated = [n for n in tree.all_nodes if n.is_evaluated and n.train_nmse < float("inf")]
evaluated.sort(key=lambda n: n.train_nmse)
if evaluated:
best = evaluated[0]
print(f"\n最佳公式: {best.skeleton_str}")
print(f"训练集 NMSE: {best.train_nmse:.4e}")
print(f"测试集 NMSE: {best.test_nmse:.4e}")
if __name__ == "__main__":
main()
Step 4:运行测试
共运行三个实验:
① 退化模式(1 步):
cd /path/to/FunctionEvolve
python3 datasets/z/run_test.py --degenerated --max-steps 1 --n-seeds 3 --candidate-num 2 --timeout 120
② LLM 模式(1 步):
cd /path/to/FunctionEvolve
python3 datasets/z/run_test.py --max-steps 1 --n-seeds 3 --candidate-num 2 --timeout 120
③ LLM 模式(3 步):
cd /path/to/FunctionEvolve
python3 datasets/z/run_test.py --max-steps 3 --n-seeds 3 --candidate-num 2 --timeout 120
为加快运行速度,上述实验使用了 200 训练样本 + 100 测试样本的子集(完整数据集为 2000 + 500)。
Step 5:测试输出及分析
[Ground Truth] 训练集 NMSE = 1.77e-05
[Ground Truth] 测试集 NMSE = 2.14e-05
真实公式本身不需要拟合任何参数,NMSE ≈ 1.8e-5 来自 0.02 标准差的高斯噪声,是本次测试的精度天花板。
文章来源:https://www.codelast.com/
模式 A:退化模式结果(200 样本)
初始种子(退化模式仅基于 x1 生成简易多项式/指数公式):
| 种子公式 | 训练集 NMSE | 测试集 NMSE | 说明 |
|---|---|---|---|
|
0.998 | 0.992 | 线性 |
|
0.998 | 0.991 | 二次多项式 |
最优变异后(1 步,2 父本,111 个候选):
| 公式 | 训练集 NMSE | 测试集 NMSE | 说明 |
|---|---|---|---|
|
0.270 | 0.319 | 分式 + 指数,引入 x₂ |
|
0.271 | 0.303 | 线性 × 指数 |
|
0.283 | 0.344 | 线性 + 指数 |
退化模式瓶颈:种子只使用 x1,NMSE approx 1.0;一轮变异后最优 NMSE 0.270(解释 ~73% 方差)。但缺乏 LLM 指引方向,变异盲目枚举(111 个候选中仅少数引入 x2,无法发现 sin 等函数)。
模式 B:完整模式结果(使用 DeepSeek,200 样本)
1. LLM Generator 领域分析
LLM 成功分析出数据集属于"通用符号回归 / 数学建模"领域,输出了 20+ 个经典公式模式供种子生成参考。
2. LLM 种子生成
| 种子公式 | 训练集 NMSE | 测试集 NMSE | 说明 |
|---|---|---|---|
|
~0.89 | ~0.89 | LLM 尝试全部 5 个变量 |
|
~0.84 | ~0.84 | LLM 聚焦 x₂, x₃ |
|
~0.35 | ~0.34 | LLM 直接猜中 sin + exp 结构! |
(退化兜底) |
~0.84 | ~0.84 | — |
LLM 的第三个种子 c0exp(c1x2) + c2sin(c3x3 + c4) 已经包含了 exp + sin,NMSE 仅 ~0.35,远超退化种子的 ~0.998。
3. LLM Mutator 变异建议
LLM 为选中的父本生成了 20 条定向变异建议(退化模式中 ASTMutator 靠穷举生成了约 111 条),例如:"尝试多项式展开"、"添加指数衰减项 exp"、"尝试分式结构"、"添加正弦/余弦项"。
4. 各步最优公式
LLM 1 步后:
| 公式 | 训练集 NMSE | 测试集 NMSE | 说明 |
|---|---|---|---|
|
0.224 | 0.300 | 双 sin + exp,引入 x₂~x₄ |
|
0.228 | 0.301 | 幂次替代 sin |
|
0.241 | 0.282 | 引入 x₁ |
LLM 3 步后:
| 公式 | 训练集 NMSE | 测试集 NMSE | 说明 |
|---|---|---|---|
|
0.081 | 0.129 | 复合分式 + sin + exp |
|
0.088 | 0.110 | 分项式 |
|
0.099 | 0.157 | 类似结构 |
两种模式找到的最优函数:
| 实验 | 最优公式 | 训练集 NMSE | 测试集 NMSE |
|---|---|---|---|
| 真实公式 |
|
1.77e-5 | 2.14e-5 |
| 退化 1 步 |
|
0.270 | 0.319 |
| LLM 1 步 |
|
0.224 | 0.300 |
| LLM 3 步 |
|
0.081 | 0.129 |
NMSE 对比(越低越好):
| 模式 | 步数 | 训练集 NMSE | 测试集 NMSE | 相比退化改善 | 关键发现 |
|---|---|---|---|---|---|
| 真实公式 | -- | 1.77e-5 | 2.14e-5 | -- | 精度天花板 |
| 退化 | 1 步 | 0.270 | 0.319 | 基准 | 分式 + 指数(x₁, x₂) |
| LLM | 1 步 | 0.224 | 0.300 | ↓ 17% | 发现 sin(),引入 x₃, x₄ |
| LLM | 3 步 | 0.081 | 0.129 | ↓ 70% | 复合分式 + sin + exp(x₁~x₄) |
LLM 1 步内就发现了退化模式无法找到的 sin()。3 步后 NMSE 降到 0.081(解释 ~92% 方差),但 x₁×x₂ 的 sin 交互和 x₅ 尚未被完整识别。预计 LLM 模式 5~10 步可收敛到 NMSE < 1e-3。
当前进度与收敛预估
| 维度 | 值 |
|---|---|
| 变量数 | 5(x₁~x₅) |
| 函数种类 | sin, cos, exp, log, 除法(5 种) |
| 交互关系 | x₁×x₂, x₁×x₃, x₂(指数项), x₄(对数项), x₅(分式) |
| 噪声 | 0.02 标准差 |
当前实验进度(基于 200 训练样本 + 100 测试样本):
| 实验 | 训练集 NMSE | 测试集 NMSE | 关键发现 |
|---|---|---|---|
| 退化 1 步 | 0.270 | 0.319 | 分式 + 指数,仅 x₁, x₂ |
| LLM 1 步 | 0.224 | 0.300 | 发现 sin(),引入 x₃, x₄ |
| LLM 3 步 | 0.081 | 0.129 | 复合分式 + sin + exp,引入 x₁~x₄ |
LLM 3 步后 NMSE 0.081(解释 ~92% 方差),已成功发现 sin() 函数并引入了 x₁~x₄ 四个变量。但真实公式中 x₁×x₂ 的 sin 交互、x₁×x₃ 的分式交互以及 x₅ 的分式尚未被完整识别。预计:
- LLM 模式:5~10 步可收敛到 NMSE < 1e-3
- 退化模式:相同步数下 NMSE 下降仅 ~18%/步,效率远低于 LLM
这正是 FunctionEvolve 采用 LLM + 数值优化混合架构的原因——LLM 提供方向,数值优化负责精确拟合。
文章来源:https://www.codelast.com/
Step 6:清理临时环境
# 测试完成后删除临时环境
micromamba remove -n py312 --all -y
总结
| 项目 | 状态 |
|---|---|
| 测试数据集生成 | 完成 |
| 测试脚本 |
完成(datasets/z/run_test.py) |
| 退化模式(1 步) | 完成(NMSE 0.270 / 0.319,无 LLM) |
| LLM 模式(1 步) | 完成(NMSE 0.224 / 0.300,发现 sin()) |
| LLM 模式(3 步) | 完成(NMSE 0.081 / 0.129,解释 ~92% 方差) |
| 搜索正确性验证 | 全流程无错误,所有实验数据完整记录 |
| 临时环境清理 | 已删除 |
结论:FunctionEvolve 的搜索管道可以正常运行。LLM 模式显著优于退化模式——1 步即可发现退化模式无法找到的 sin() 函数,3 步后 NMSE 降至 0.081。其实上面的测试还没有找到最终的正确公式,由于我电脑性能不强,仅仅运行上面的测试就已经花了很长时间+风扇狂转,因此测试只能止步于此。但可以看到,FunctionEvolve相较于传统优化方法是有较大优势的。
文章来源:https://www.codelast.com/
➤➤ 版权声明 ➤➤
转载需注明出处:codelast.com
感谢关注我的微信公众号(微信扫一扫):

以及我的微信视频号:

















