Levenberg-Marquardt算法详解和C++代码示例

Levenberg-Marquardt(LM)算法是非线性最小二乘问题中常用的一种优化算法,它融合了高斯-牛顿法梯度下降法的优点,在数值计算与SLAM、图像配准、机器学习等领域中应用广泛。


一、Levenberg-Marquardt算法基本原理

1.1 问题定义

我们希望最小化一个非线性残差平方和目标函数:

min ⁡ x f ( x ) = 1 2 ∑ i = 1 m r i ( x ) 2 = 1 2 ∥ r ( x ) ∥ 2 \min_{\mathbf{x}} \, f(\mathbf{x}) = \frac{1}{2} \sum_{i=1}^m r_i(\mathbf{x})^2 = \frac{1}{2} \| \mathbf{r}(\mathbf{x}) \|^2 xminf(x)=21i=1mri(x)2=21r(x)2

其中,

  • x ∈ R n \mathbf{x} \in \mathbb{R}^n xRn:参数向量
  • r ( x ) = [ r 1 ( x ) , … , r m ( x ) ] T \mathbf{r}(\mathbf{x}) = [r_1(\mathbf{x}), \ldots, r_m(\mathbf{x})]^T r(x)=[r1(x),,rm(x)]T:残差向量

我们要最小化的是残差的平方和。


二、高斯-牛顿法回顾

在当前点 x k \mathbf{x}_k xk 处,对残差函数进行一阶泰勒展开:

r ( x k + Δ x ) ≈ r ( x k ) + J ( x k ) Δ x \mathbf{r}(\mathbf{x}_k + \Delta \mathbf{x}) \approx \mathbf{r}(\mathbf{x}_k) + J(\mathbf{x}_k) \Delta \mathbf{x} r(xk+Δx)r(xk)+J(xk)Δx

其中 J ∈ R m × n J \in \mathbb{R}^{m \times n} JRm×n 是 Jacobian:

J i j = ∂ r i ∂ x j J_{ij} = \frac{\partial r_i}{\partial x_j} Jij=xjri

代入目标函数:

min ⁡ Δ x 1 2 ∥ r + J Δ x ∥ 2 \min_{\Delta \mathbf{x}} \frac{1}{2} \| \mathbf{r} + J \Delta \mathbf{x} \|^2 Δxmin21r+JΔx2

导出正规方程(Normal Equation):

J T J Δ x = − J T r J^T J \Delta \mathbf{x} = - J^T \mathbf{r} JTJΔx=JTr

这就是高斯-牛顿法。


三、LM算法推导:阻尼的高斯-牛顿

LM法通过引入一个阻尼因子 λ \lambda λ 来平衡 Gauss-Newton 与 Gradient Descent:

( J T J + λ I ) Δ x = − J T r (J^T J + \lambda I) \Delta \mathbf{x} = - J^T \mathbf{r} (JTJ+λI)Δx=JTr

  • λ → 0 \lambda \to 0 λ0,接近高斯-牛顿法;
  • λ → ∞ \lambda \to \infty λ,趋于梯度下降法。

为了更稳定地调整 λ \lambda λ,可以采用如下对角矩阵:

( J T J + λ ⋅ diag ( J T J ) ) Δ x = − J T r (J^T J + \lambda \cdot \text{diag}(J^T J)) \Delta \mathbf{x} = - J^T \mathbf{r} (JTJ+λdiag(JTJ))Δx=JTr

这种处理使 LM 更具有数值稳定性。


四、LM算法伪代码

x = x0
lambda = lambda_initwhile not converged:r = residual(x)J = jacobian(x)H = J^T * Jg = J^T * rsolve (H + lambda * diag(H)) * dx = -gif cost(x + dx) < cost(x):x = x + dxlambda = lambda / factorelse:lambda = lambda * factor

五、Levenberg-Marquardt 算法实现步骤

步骤 1:初始化

  • 初始化参数向量 x 0 \mathbf{x}_0 x0
  • 设置初始阻尼系数 λ \lambda λ,通常取 10 − 3 ∼ 10 − 1 10^{-3} \sim 10^{-1} 103101
  • 设置调整系数(如增长因子 ν = 2 \nu = 2 ν=2
  • 设置收敛条件(如最大迭代次数、步长阈值、误差阈值)

步骤 2:计算当前残差与 Jacobian

  • 在当前参数 x \mathbf{x} x 处计算残差向量 r ( x ) \mathbf{r}(\mathbf{x}) r(x)
  • 计算残差的 Jacobian 矩阵 J ( x ) J(\mathbf{x}) J(x)

步骤 3:构建 LM 修正的正规方程

构造修正的线性系统:

( J T J + λ ⋅ diag ( J T J ) ) Δ x = − J T r (J^T J + \lambda \cdot \text{diag}(J^T J)) \Delta \mathbf{x} = -J^T \mathbf{r} (JTJ+λdiag(JTJ))Δx=JTr

  • J T J J^T J JTJ:近似 Hessian 矩阵
  • λ ⋅ diag ( J T J ) \lambda \cdot \text{diag}(J^T J) λdiag(JTJ):用于平滑(阻尼),避免步长过大

步骤 4:求解增量 Δ x \Delta \mathbf{x} Δx

  • 使用 Cholesky / LDLT 分解求解线性方程组,得到参数增量 Δ x \Delta \mathbf{x} Δx
  • 可选:添加线性求解器条件数检查以保证稳定性

步骤 5:评估新参数点

  • 更新新参数: x new = x + Δ x \mathbf{x}_{\text{new}} = \mathbf{x} + \Delta \mathbf{x} xnew=x+Δx

  • 计算新误差 ∥ r ( x new ) ∥ 2 \| \mathbf{r}(\mathbf{x}_{\text{new}}) \|^2 r(xnew)2

  • 如果误差变小,接受更新,并降低 λ \lambda λ

    λ ← λ / factor \lambda \leftarrow \lambda / \text{factor} λλ/factor

  • 否则,拒绝更新,提高阻尼系数以减小步长:

    λ ← λ × factor \lambda \leftarrow \lambda \times \text{factor} λλ×factor


步骤 6:检查收敛条件

  • 如果满足以下任一条件,则终止:

    • 残差变化非常小: ∥ Δ x ∥ < ϵ \| \Delta \mathbf{x} \| < \epsilon ∥Δx<ϵ
    • 最大迭代次数达到
    • 梯度足够小: ∥ J T r ∥ < ϵ \| J^T \mathbf{r} \| < \epsilon JTr<ϵ
  • 否则返回步骤 2,继续迭代


六、总结为流程图结构

初始化 x, lambda↓
计算残差 r(x), Jacobian J↓
构建系统 (JᵀJ + λI)Δx = -Jᵀr↓
求解 Δx↓
计算新误差 cost(x + Δx)↓
误差减少?┌─────────────┐↓             ↓
Yes           No
↓              ↓
x ← x + Δx     λ ← λ × factor
λ ← λ / factor ↓↓
满足终止条件?↓Yes → 退出No  → 回到迭代

七、应用示例:拟合二次函数 y = a x 2 + b x + c y = ax^2 + bx + c y=ax2+bx+c

我们以拟合二次函数为例,给定数据点 ( x i , y i ) (x_i, y_i) (xi,yi),最小化以下残差:

r i ( a , b , c ) = y i − ( a x i 2 + b x i + c ) r_i(a, b, c) = y_i - (a x_i^2 + b x_i + c) ri(a,b,c)=yi(axi2+bxi+c)

Jacobian:

J i = [ − x i 2 , − x i , − 1 ] J_i = \left[ -x_i^2, -x_i, -1 \right] Ji=[xi2,xi,1]


八、C++代码实现

#include <iostream>
#include <vector>
#include <Eigen/Dense>using namespace Eigen;
using namespace std;struct DataPoint {double x, y;
};struct LMResult {Vector3d params;double final_cost;int iterations;
};LMResult LevenbergMarquardt(const vector<DataPoint>& data, Vector3d init, int max_iter = 100) {Vector3d x = init;double lambda = 1e-3;double v = 2.0;int n = data.size();double last_cost = 1e20;for (int iter = 0; iter < max_iter; ++iter) {MatrixXd J(n, 3);VectorXd r(n);for (int i = 0; i < n; ++i) {double xi = data[i].x;double yi = data[i].y;double yi_est = x(0) * xi * xi + x(1) * xi + x(2);r(i) = yi - yi_est;J(i, 0) = -xi * xi;J(i, 1) = -xi;J(i, 2) = -1.0;}Matrix3d H = J.transpose() * J;Vector3d g = J.transpose() * r;Matrix3d H_lm = H + lambda * H.diagonal().asDiagonal();Vector3d dx = H_lm.ldlt().solve(-g);Vector3d x_new = x + dx;// compute new costdouble new_cost = 0.0;for (int i = 0; i < n; ++i) {double xi = data[i].x;double yi = data[i].y;double yi_est = x_new(0) * xi * xi + x_new(1) * xi + x_new(2);double ri = yi - yi_est;new_cost += ri * ri;}if (new_cost < last_cost) {x = x_new;lambda *= 0.8;last_cost = new_cost;} else {lambda *= 2.0;}if (dx.norm() < 1e-6) break;}return {x, last_cost, max_iter};
}

九、输出与测试

int main() {vector<DataPoint> data;for (int i = 0; i <= 10; ++i) {double x = i;double y = 2.0 * x * x + 3.0 * x + 1.0 + ((rand() % 100) / 50.0 - 1.0); // 加噪声data.push_back({x, y});}Vector3d init(0.0, 0.0, 0.0);auto result = LevenbergMarquardt(data, init);cout << "Estimated parameters: " << result.params.transpose() << endl;cout << "Final cost: " << result.final_cost << endl;return 0;
}

十、总结

方法特点
梯度下降法收敛稳定但慢
高斯-牛顿法快速但易发散
Levenberg-Marquardt二者结合,自动调节,收敛稳定

实用建议

  • 阻尼初始化值 λ \lambda λ:设置为初始 Hessian 的最大对角元素的某个比例(如 λ = τ ⋅ max ⁡ ( diag ( J T J ) ) \lambda = \tau \cdot \max(\text{diag}(J^T J)) λ=τmax(diag(JTJ))

  • 梯度与步长判断条件

    • 使用 ∥ Δ x ∥ < 1 e − 6 \| \Delta \mathbf{x} \| < 1e{-6} ∥Δx<1e6 ∥ g ∥ < 1 e − 6 \| g \| < 1e{-6} g<1e6

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/83775.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

理解网络协议

1.查看网络配置 : ipconfig 2. ip地址 : ipv4(4字节, 32bit), ipv6, 用来标识主机的网络地址 3.端口号(0~65535) : 用来标识主机上的某个进程, 1 ~ 1024 知名端口号, 如果是服务端的话需要提供一个特定的端口号, 客户端的话是随机分配一个端口号 4.协议 : 简单来说就是接收数据…

如何计算光伏工程造价预算表?

在光伏工程的推进过程中&#xff0c;造价预算表的编制是至关重要的环节&#xff0c;传统的光伏工程造价预算编制方法&#xff0c;往往依赖人工收集数据、套用定额&#xff0c;再进行繁琐的计算与汇总&#xff0c;不仅效率低下&#xff0c;而且容易出现人为误差&#xff0c;导致…

新闻速递|Altair 与佐治亚理工学院签署合作备忘录,携手推动航空航天领域创新

近日&#xff0c;全球计算智能领域领先企业 Altair 与佐治亚理工学院正式签署合作备忘录&#xff0c;旨在深化航空航天领域的技术创新合作。 根据协议&#xff0c;佐治亚理工学院的航空航天系统设计实验室 (ASDL) 将获得 Altair 的技术支持&#xff0c;运用仿真与数据分析 (DA)…

PLSQLDeveloper配置OracleInstantClient连接Oracle数据库

PL/SQLDeveloper配置Oracle Instant Client连接Oracle数据库 文章目录 PL/SQLDeveloper配置Oracle Instant Client连接Oracle数据库 1. Oracle Instant Client下载与配置1. Oracle Instant Client下载2. Oracle Instant Client解压配置1. 解压2. 配置 2. PL/SQL Developer下载、…

数据库系统学习

关系型数据库 关系型数据库建立在关系模型基础上的数据库&#xff0c;关系型数据库是由多张能相互相连的二维表组成的数据库 优点&#xff1a; 都是使用表结构&#xff0c;格式一致&#xff0c;易于维护使用通用的sql语言操作&#xff0c;使用方便&#xff0c;可用于复杂查询…

美国大休斯顿都会区电网数据

美国大休斯顿都会区&#xff08;Houston-The Woodlands-Sugar Land Metropolitan Area&#xff09;电网数据。数据包括&#xff1a;发电、输电、变电、配电。而且配电线路也很完善&#xff01;下面是截图&#xff1a; 输电线路 配电线路 变电站 开关站 电厂

信创主机性能测试实例(升腾P860)

文章目录 一、引言二、基准测试&#xff08;Unixbench &#xff09;三、CPU测试&#xff08;SPEC CPU 2006&#xff09;四、GPU测试&#xff08;Glmark2&#xff09;五、IO测试&#xff08;Iozone &#xff09;六、内存基准测试&#xff08;Stream &#xff09;七、网络性能基准…

Web前端基础:HTML-CSS

1.标题 1.1标题排版 超链接 a 标签&#xff1a; 标签&#xff1a;<a href"....." target".....">央视网</a> 属性&#xff1a; href: 指定资源访问的urltarget: 指定在何处打开资源链接 _self: 默认值&#xff0c;在当前页面打开_blank: 在…

Python数学可视化:3D参数曲面与隐式曲面绘制技术

Python数学可视化&#xff1a;3D参数曲面与隐式曲面绘制技术 引言 在科学研究、工程设计和数学教学中&#xff0c;3D可视化技术是理解复杂几何形状和空间关系的重要工具。本文将介绍如何使用Python实现参数曲面和隐式曲面的3D可视化&#xff0c;通过数学公式和代码示例展示球…

传输层:udp与tcp协议

目录 再谈端口号 端口号范围划分 认识知名端口号(Well-Know Port Number) 两个问题 netstat pidof 如何学习下三层协议 UDP协议 UDP协议端格式 UDP的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 TCP协议 TCP协议段格式 1.源端口号…

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…

Keil调试模式下,排查程序崩溃简述

在Keil调试模式下&#xff0c;若程序崩溃&#xff0c;可以通过以下步骤来定位崩溃的位置&#xff1a; 一、查看调用栈&#xff08;Call Stack&#xff09; 打开调用栈窗口&#xff1a; 在Keil的调试模式下&#xff0c;点击菜单栏的“View” -> “Call Stack Window”&…

深度解析Mysql中MVCC的工作机制

MVCC,多版本并发控制 定义&#xff1a;维护一个数据的多个版本&#xff0c;使读写操作没有冲突&#xff0c;依赖于&#xff1a;隐藏字段&#xff0c;undo log日志&#xff0c;readView MVCC会为每条版本记录保存三个隐藏字段 DB_TRX_ID: 记录最近插入或修改该记录的事务IDDB_R…

uniapp+vue3实现CK通信协议(基于jjc-tcpTools)

1. TCP 服务封装 (tcpService.js) export class TcpService {constructor() {this.connections uni.requireNativePlugin(jjc-tcpTools)this.clients new Map() // 存储客户端连接this.servers new Map() // 存储服务端实例}// 创建 TCP 服务端 (字符串模式)createStringSe…

学习设计模式《十二》——命令模式

一、基础概念 命令模式的本质是【封装请求】命令模式的关键是把请求封装成为命令对象&#xff0c;然后就可以对这个命令对象进行一系列的处理&#xff08;如&#xff1a;参数化配置、可撤销操作、宏命令、队列请求、日志请求等&#xff09;。 命令模式的定义&#xff1a;将一个…

Webpack的基本使用 - babel

Mode配置 Mode配置选项可以告知Webpack使用相应模式的内置优化 默认值是production&#xff08;什么都不设置的情况下&#xff09; 可选值有&#xff1a;none | development | production; 这几个选项有什么区别呢&#xff1f; 认识source-map 我们的代码通常运行在浏览器…

「基于连续小波变换(CWT)和卷积神经网络(CNN)的心律失常分类算法——ECG信号处理-第十五课」2025年6月6日

一、引言 心律失常是心血管疾病的重要表现形式&#xff0c;其准确分类对临床诊断具有关键意义。传统的心律失常分类方法主要依赖于人工特征提取和经典机器学习算法&#xff0c;但这些方法往往受限于特征选择的主观性和模型的泛化能力。 随着深度学习技术的发展&#xff0c;基于…

C++.OpenGL (11/64)材质(Materials)

材质(Materials) 真实感材质系统 #mermaid-svg-NjBjrmlcpHupHCFQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NjBjrmlcpHupHCFQ .error-icon{fill:#552222;}#mermaid-svg-NjBjrmlcpHupHCFQ .error-text{fill:…

P1345 [USACO5.4] 奶牛的电信Telecowmunication

P1345 [USACO5.4] 奶牛的电信Telecowmunication 突然发现 USACO 好喜欢玩谐音梗。 题意就是给定一个无向图&#xff0c;问你要删多少点才能使 s , t s,t s,t 不连通。 注意是删点而不是删边&#xff0c;所以不能直接使用最小割来求。所以考虑变换一下题目模型。 经典 tric…

EXCEL如何快速批量给两字姓名中间加空格

EXCEL如何快速批量给姓名中间加空格 优点&#xff1a;不会导致排版混乱 缺点&#xff1a;无法输出在原有单元格上&#xff0c;若需要保留原始数据&#xff0c;可将公式结果复制后“选择性粘贴为值” 使用场景&#xff1a;在EXCEL中想要快速批量给两字姓名中间加入空格使姓名对…