SciPy 数值优化:从最小二乘到非线性求解,遇到什么问题该用什么
写代码做数据处理,很多问题最终都能归结成一个核心需求:找到一组最优的参数。
- 拟合一条曲线 → 找参数使误差最小
- 控制一个系统 → 找参数使性能最好
- 解一个方程组 → 找一组值使所有等式成立
SciPy 的 optimize 模块就是干这个的。但问题是:算法太多了——leastsq、curve_fit、fmin、fmin_bfgs、fsolve……新手看了直接懵。
最小二乘拟合:数据点 + 模型 = 找参数
90% 的优化需求都能归到这一类:你有数据点,你有一个带参数的模型,你想让模型尽可能贴合数据。
标准写法(老派):
1 | from scipy.optimize import leastsq |
更友好的写法(推荐):
1 | from scipy.optimize import curve_fit |
curve_fit 比 leastsq 好用得多——不用自己写残差函数,不用操心 args 传参方式,直接定义模型函数就行。新代码优先用 curve_fit。
拟合正弦波(复杂模型示例):
1 | def model(x, A, freq, phase, offset): |
曲线拟合的核心原则:初始值要给对。 给错了,结果可能完全不收敛。如果不知道初始值怎么给,先画图看一眼数据的大致形态,再反推参数范围。
函数最小值:没有数据点,只有函数
有些问题没有”数据点”,只有一个目标函数——你找一组参数,让这个函数的值最小。
典型例子:Rosenbrock 函数——优化算法的”试金石”。
f(x,y)=(1−x)2+100(y−x2)2f(x,y)=(1−x)2+100(y−x2)2
它在 (1,1) 处有最小值 0,但有一条狭窄的”山谷”,算法很容易在里面打转。
不用导数(Nelder-Mead,简单问题用):
1 | from scipy.optimize import fmin |
用导数(BFGS,收敛快):
1 | from scipy.optimize import fmin_bfgs |
关键判断:如果你的目标函数能求导,一定要给导数。 BFGS 比 Nelder-Mead 快得多,变量越多差距越大。
算法选择的决策逻辑:
1 | 问题是什么类型? |
非线性方程组:找一组值让多个等式同时成立
你需要找一组 (x, y, z),让一堆方程同时等于 0。
1 | from scipy.optimize import fsolve |
fsolve 的两个核心问题:
1. 初始值很关键。 给的不好,收敛到奇怪的结果或者干脆不收敛。
2. 方程组越大,越要提供雅可比矩阵。 雅可比矩阵就是”每个方程对每个变量的偏导数”。
1 | def jacobian(p): |
实际经验: 50 个未知数、每个方程只跟 6 个变量相关的稀疏系统,提供雅可比矩阵能让求解速度提高 4-5 倍。