#非线性 #拟合 #函数 #回归 #最小二乘 #线性代数

varpro

使用可变投影算法的简单非线性最小二乘拟合库

13 个版本 (重大更新)

0.10.0 2024年7月27日
0.8.0 2024年2月9日
0.7.1 2023年10月16日
0.6.0 2023年4月30日
0.2.0 2021年3月16日

#79 in 算法

Download history 119/week @ 2024-05-03 193/week @ 2024-05-10 631/week @ 2024-05-17 473/week @ 2024-05-24 527/week @ 2024-05-31 347/week @ 2024-06-07 369/week @ 2024-06-14 279/week @ 2024-06-21 512/week @ 2024-06-28 584/week @ 2024-07-05 877/week @ 2024-07-12 423/week @ 2024-07-19 814/week @ 2024-07-26 639/week @ 2024-08-02 710/week @ 2024-08-09 207/week @ 2024-08-16

每月下载量 2,490

MIT 许可证

290KB
4K SLoC

Rust 3.5K SLoC // 0.1% comments Objective-C 739 SLoC Python 46 SLoC // 0.3% comments Shell 3 SLoC // 0.4% comments

varpro

build tests lints crates Coverage Status maintenance-status

使非线性函数拟合变得简单。这个库提供了对广泛类别的模型函数到数据的鲁棒和快速的最小二乘拟合。它使用 VarPro 算法来实现,因此得名。

支持

如果你喜欢这个包,可以向其他人推荐它。如果你愿意,你也可以 买我一杯咖啡

简介

此包实现了一个强大的算法,用于将模型函数拟合到数据,但它仅限于所谓的 可分离 模型。由于本网站上缺少公式,难以详细说明,但在下一节中提供了一个简要概述。有关包括数学在内的所有详细信息,请参阅文档

什么是可分离模型?

简单来说,可分离模型是可以写成一些非线性基函数的线性组合的非线性函数。VarPro 的一个常见用例是拟合指数和,这是一个众所周知的病态问题。

什么是 VarPro?

可变投影(VarPro)是一种算法,它利用其拟合问题可以分离成线性和非线性参数的事实。首先,使用一些巧妙的线性代数消除了线性参数。然后,将拟合问题重写为只依赖于非线性参数。最后,使用通用非线性最小化算法(如 Levenberg-Marquardt 算法)解决这个简化的问题。

何时应该尝试它?

与使用通用非线性最小二乘拟合算法相比,VarPro 可以显着提高拟合过程的鲁棒性和速度。当

  • 你想要拟合的模型函数是非线性函数的线性组合,
  • 并且 你知道所有这些函数的解析导数

,你应该尝试它。还请考虑下面的全局拟合部分,它提供了此包的另一个优秀用例。

示例用法

以下示例展示了如何使用此包将双指数衰减与常数偏移拟合到在时间点 t 获得的数据向量 y。有关更深入的指南,请参阅文档

use varpro::prelude::*;
use varpro::solvers::levmar::{LevMarProblemBuilder, LevMarSolver};
use nalgebra::{dvector,DVector};

// Define the exponential decay e^(-t/tau).
// Both of the nonlinear basis functions in this example
// are exponential decays.
fn exp_decay(t :&DVector<f64>, tau : f64) 
  -> DVector<f64> {
  t.map(|t|(-t/tau).exp())
}

// the partial derivative of the exponential
// decay with respect to the nonlinear parameter tau.
// d/dtau e^(-t/tau) = e^(-t/tau)*t/tau^2
fn exp_decay_dtau(t: &DVector<f64>,tau: f64) 
  -> DVector<f64> {
  t.map(|t| (-t / tau)
    .exp() * t / tau.powi(2))
}

// temporal (or spatial) coordintates of the observations
let t = dvector![0.,1.,2.,3.,4.,5.,6.,7.,8.,9.,10.];
// the observations we want to fit
let y = dvector![6.0,4.8,4.0,3.3,2.8,2.5,2.2,1.9,1.7,1.6,1.5];

// 1. create the model by giving only the nonlinear parameter names it depends on
let model = SeparableModelBuilder::<f64>::new(&["tau1", "tau2"])
  // provide the nonlinear basis functions and their derivatives.
  // In general, base functions can depend on more than just one parameter.
  // first function:
  .function(&["tau1"], exp_decay)
  .partial_deriv("tau1", exp_decay_dtau)
  // second function and derivatives with respect to all parameters
  // that it depends on (just one in this case)
  .function(&["tau2"], exp_decay)
  .partial_deriv("tau2", exp_decay_dtau)
  // a constant offset is added as an invariant basefunction
  // as a vector of ones. It is multiplied with its own linear coefficient,
  // creating a fittable offset
  .invariant_function(|v|DVector::from_element(v.len(),1.))
  // give the coordinates of the problem
  .independent_variable(t)
  // provide guesses only for the nonlinear parameters in the
  // order that they were given on construction.
  .initial_parameters(vec![2.5,5.5])
  .build()
  .unwrap();
// 2. Cast the fitting problem as a nonlinear least squares minimization problem
let problem = LevMarProblemBuilder::new(model)
  .observations(y)
  .build()
  .unwrap();
// 3. Solve the fitting problem
let fit_result = LevMarSolver::default()
    .fit(problem)
    .expect("fit must exit successfully");
// 4. obtain the nonlinear parameters after fitting
let alpha = fit_result.nonlinear_parameters();
// 5. obtain the linear parameters
let c = fit_result.linear_coefficients().unwrap();

有关更深入的示例,请参阅包文档。

拟合统计

除了成员函数 fit 之外,LevMarSolver 还提供了一个 fit_with_statistics 函数,该函数计算了大量有用的附加统计信息。

多个右侧的全球拟合

在上面的例子中,我们传递了一个单列向量作为观测值。库还允许通过通过构造一个通过 LevMarProblemBuilder::mrhs 的问题来拟合多个右侧。当拟合多个右侧时,vapro 将执行一个 全局拟合,其中非线性参数在整个右侧中优化,但拟合的线性系数针对每个右侧分别优化。

这又是 varpro 真正大放异彩的另一个应用场景,因为它可以利用问题的可分性质。它允许我们在合理的时间内(几秒到几分钟的分数)对数千甚至数万个右侧进行全局拟合,而传统的纯非线性求解器必须做更多的工作。

最大性能和高级用例

上面的示例代码将比仅使用非线性求解器而无需 varpro 的魔法快得多。但这个 crate 提供了另一种方法来榨取最后一点性能。

可以通过手动实现 SeparableNonlinearModel 特征来描述模型函数。这通常允许我们从计算中削减最后几百微秒,例如通过缓存中间计算。crate 文档中包含了详细的示例。

这不仅对性能有帮助,而且对于仅使用 SeparableModelBuilder 难以或不可能适应的用例也很有用。构建器是为了易用性和性能而创建的,但它有一些设计上的限制。

致谢

我要感谢教授 Dianne P. O'LearyBert W. Rust ✝,他们发表了这篇论文,使我能够理解 varpro 并提出这个实现。O'Leary 教授还慷慨地回答了我关于她的论文和一些实现细节的问题。

参考文献和进一步阅读

(O'Leary2013) O’Leary, D.P., Rust, B.W. Variable projection for nonlinear least squares problems. Comput Optim Appl 54, 579–593 (2013). DOI: 10.1007/s10589-012-9492-9

注意:O'Leary 的论文包含了一些错误,这些错误在我这篇博客文章中被修正了(所以我希望):这篇博客文章

(Golub2003) Golub, G. , Pereyra, V Separable nonlinear least squares: the variable projection method and its applications. Inverse Problems 19 R1 (2003) https://iopscience.iop.org/article/10.1088/0266-5611/19/2/201

依赖关系

~4MB
~88K SLoC