优化算法学习(LM算法)

2023-11-15

推荐书籍

建议学习,METHODS FOR NON-LINEAR LEAST SQUARES PROBLEMS:
http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf
篇幅不长,容易理解
学习的时候可以参考另一篇,UNCONSTRAINED OPTIMIZATION:http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3217/pdf/imm3217.pdf

Numerical Optimization 2nd --Jorge Nocedal Stephen J. Wright:
http://www.bioinfo.org.cn/~wangchao/maa/Numerical_Optimization.pdf
《视觉SLAM十四讲》第六讲 https://github.com/gaoxiang12/slambook

理论理解

知乎上看到一个回答非常好:

LM算法可以理解为Gauss-Newton算法与最速下降法的结合,如果理解了如何用上述算法求解目标函数最小值的问题,自然也能理解LM。

其实算法的本质就是 a. 站在当前位置( x k x_k xk ),我们需要一个预言(oracle)告诉我们往哪走能找到目的地(最优解可能的方向,比如梯度方向);b. 我们沿着该方向走了一段距离之后(stepsize),更新当前位置信息( x k + 1 x_{k+1} xk+1 ),再问预言家我们下一步往哪走,以此反复。

所以,梯度下降法,给的 oracle 就是当前位置的梯度信息(损失方程关于变量的一阶导数):

x k + 1 = x k − α g k x_{k+1}=x_k-\alpha g_k xk+1=xkαgk

如果是牛顿法,给的 oracle 就是Hessian matrix(损失方程关于变量的二阶导数):

x k + 1 = x k − H k − 1 g k x_{k+1}=x_k-H_k^{-1}g_k xk+1=xkHk1gk(1)

为什么是一阶导数和二阶导数?因为我们知道,对于任意(处处可导的)方程,在其任意一点,我们都可以用泰勒展开式对其拟合,阶数越高,精度越高。但是,考虑到高阶导数的计算复杂度,以及三阶以上函数的非凸性,也不会使用高阶导数。

好了,那么LM算法的优势是什么?牛顿法虽然收敛速度快,但是需要计算 Hessian matrix,对于高维的问题,计算二阶导数会很复杂。因此我们有了Gauss-Newton算法。Gauss-Newton算法不直接计算Hessian matrix,而是通过 Jacobian matrix 对 Hessian matrix 进行拟合:

H ≈ J T J H\approx J^TJ HJTJ

但是,用 Jacobian matrix 拟合Hessian matrix,所计算出来的结果不一定可逆。所以在此基础上,我们引入了一个identity matrix:

H ≈ J T J + μ I H\approx J^TJ+\mu I HJTJ+μI

这也就得到了LM算法。如果我们把上述式子带入之前的公式(1),可以得到

x k + 1 = x k − ( J k T J k + μ I ) − 1 g k x_{k+1}=x_k-(J_k^TJ_k+\mu I)^{-1}g_k xk+1=xk(JkTJk+μI)1gk

所以我们发现,当 μ \mu μ接近于0时,这个算法近似于Gauss-Newton算法;当 μ \mu μ很大时,这个算法近似于最速下降法。因此,这也是为什么LM算法称为Gauss-Newton算法与最速下降法的结合。最后,上一张图表示几种算法之间的关系:
在这里插入图片描述
参考文献:Wilamowski, B. M., & Yu, H. (2010). Improved computation for Levenberg–Marquardt training. IEEE transactions on neural networks, 21(6), 930-937.

一个回答:Matlab 的话现成的代码也是很多的;比如,Solve nonlinear least-squares (nonlinear data-fitting) problems,或者 Levenberg-Marquardt-Fletcher algorithm for nonlinear least squares problems。你可以在网站里面搜搜有没有适合你的。

作者:Sixiang
链接:https://www.zhihu.com/question/269579938/answer/349205519
来源:知乎

这是最上面推荐的书,英文不难:
在这里插入图片描述

gradient matrix, hessian matrix, jacobian matrix:gradient hessian jacobian matrix
https://www.value-at-risk.net/functions/

csdn 有个不错的博客:数值优化(Numerical Optimization)学习系列-目录:https://blog.csdn.net/fangqingan_java/article/details/48951191

程序实现

视觉SLAM十四讲里推荐了**Ceres库**,Ceres solver 是谷歌开发的一款用于非线性优化的库,在谷歌的开源激光雷达slam项目cartographer中被大量使用。
安装和使用参考:
https://zhaoxuhui.top/blog/2018/04/04/ceres&ls.html
下面把关键操作贴出来:

ceres安装

  • 下载源码
    git clone https://github.com/ceres-solver/ceres-solver.git
  • 安装依赖:
sudo apt-get install cmake
# google-glog + gflags
sudo apt-get install libgoogle-glog-dev libgtest-dev libgflags-dev
# BLAS & LAPACK
sudo apt-get install libatlas-base-dev liblapack-dev
# Eigen3
sudo apt-get install libeigen3-dev
# SuiteSparse and CXSparse (optional)
sudo apt-get install libsuitesparse-dev libcxsparse3.1.4

这里libcxsparse可能存在版本问题(出现找不到对应版本),解决办法:

sudo apt-get install bash-completion
sudo gedit /etc/bash.bashrc

将这一部分取消注释,并保存,即可自动补全:
在这里插入图片描述
sudo apt-get install libsuitesparse-dev libcxsparse(按tab)

cd ceres-solver/
mkdir build
cd build
cmake ..
make -j3
sudo make install

代码:

利用Ceres简单实现最小二乘曲线拟合。首先需要生成数据,这里采用OpenCV的随机数生成器生成误差。

#include <iostream>
#include <opencv2/core/core.hpp>
#include <ceres/ceres.h>

using namespace std;
using namespace cv;
using namespace ceres;

//vector,用于存放x、y的观测数据
//待估计函数为y=3.5x^3+1.6x^2+0.3x+7.8
vector<double> xs;
vector<double> ys;

//定义CostFunctor结构体用于描述代价函数
struct CostFunctor{
  
  double x_guan,y_guan;
  
  //构造函数,用已知的x、y数据对其赋值
  CostFunctor(double x,double y)
  {
    x_guan = x;
    y_guan = y;
  }
  
  //重载括号运算符,两个参数分别是估计的参数和由该参数计算得到的残差
  //注意这里的const,一个都不能省略,否则就会报错
  template <typename T>
  bool operator()(const T* const params,T* residual)const
  {
    residual[0]=y_guan-(params[0]*x_guan*x_guan*x_guan+params[1]*x_guan*x_guan+params[2]*x_guan+params[3]);
    return true;  
  }
};

//生成实验数据
void generateData()
{
  RNG rng;
  //RNG::gaussian( σ) 返回一个均值为0,标准差为σ的随机数。
  double w_sigma = 1.0;
  for(int i=0;i<100;i++)
  {
    double x = i;
    double y = 3.5*x*x*x+1.6*x*x+0.3*x+7.8;
    xs.push_back(x);
    ys.push_back(y+rng.gaussian(w_sigma));
  }
  for(int i=0;i<xs.size();i++)
  {
    cout<<"x:"<<xs[i]<<" y:"<<ys[i]<<endl;
  }
}

//简单描述我们优化的目的就是为了使我们估计参数算出的y'和实际观测的y的差值之和最小
//所以代价函数(CostFunction)就是y'-y,其对应每一组观测值与估计值的残差。
//由于我们优化的是残差之和,因此需要把代价函数全部加起来,使这个函数最小,而不是单独的使某一个残差最小
//默认情况下,我们认为各组的残差是等权的,也就是核函数系数为1。
//但有时可能会出现粗差等情况,有可能不等权,但这里不考虑。
//这个求和以后的函数便是我们优化的目标函数
//通过不断调整我们的参数值,使这个目标函数最终达到最小,即认为优化完成
int main(int argc, char **argv) {
  
  generateData();
  
  //创建一个长度为4的double数组用于存放参数
  double params[4]={1.0};

  //第一步,创建Problem对象,并对每一组观测数据添加ResidualBlock
  //由于每一组观测点都会得到一个残差,而我们的目的是最小化所有残差的和
  //所以采用for循环依次把每个残差都添加进来
  Problem problem;
  for(int i=0;i<xs.size();i++)
  {
    //利用我们之前写的结构体、仿函数,创建代价函数对象,注意初始化的方式
    //尖括号中的参数分别为误差类型,输出维度(因变量个数),输入维度(待估计参数的个数)
    CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor,1,4>(new CostFunctor(xs[i],ys[i]));
    //三个参数分别为代价函数、核函数和待估参数
    problem.AddResidualBlock(cost_function,NULL,params);
  }
  
  //第二步,配置Solver
  Solver::Options options;
  //配置增量方程的解法
  options.linear_solver_type=ceres::DENSE_QR;
  //是否输出到cout
  options.minimizer_progress_to_stdout=true;
  
  //第三步,创建Summary对象用于输出迭代结果
  Solver::Summary summary;
  
  //第四步,执行求解
  Solve(options,&problem,&summary);
  
  //第五步,输出求解结果
  cout<<summary.BriefReport()<<endl;
  
  cout<<"p0:"<<params[0]<<endl;
  cout<<"p1:"<<params[1]<<endl;
  cout<<"p2:"<<params[2]<<endl;
  cout<<"p3:"<<params[3]<<endl;
  return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)
project(ceres_test)

set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )

# 添加cmake模块以使用ceres库
list( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules )

# 寻找Ceres库并添加它的头文件
find_package( Ceres REQUIRED )
include_directories( ${CERES_INCLUDE_DIRS} )

# OpenCV
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_DIRS} )

add_executable(ceres_test main.cpp)

# 与Ceres和OpenCV链接
target_link_libraries( ceres_test ${CERES_LIBRARIES} ${OpenCV_LIBS} )

install(TARGETS ceres_test RUNTIME DESTINATION bin)

另外,有个levmar的C/C++的库:(这个还不会用)
levmar : Levenberg-Marquardt nonlinear least squares algorithms in C/C++
http://users.ics.forth.gr/~lourakis/levmar/index.html#download
http://users.ics.forth.gr/~lourakis/sparseLM/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

优化算法学习(LM算法) 的相关文章

  • 返回链表的中间结点

    返回链表的中间结点 给定一个带有头结点 head 的非空单链表 返回链表的中间结点 如果有两个中间结点 则返回第二个中间结点 用快慢指针来写 Node Fast Node Slow 先初始化 让Fast和Slow都指向第一个链表节点 然后让
  • 算法笔记之GD,BGD,SGD

    在讨论GBDT前 先来看看什么是GD BGD和SGD GD Gradient Descent 梯度下降 求损失函数最小值 梯度下降 求损失函数最大值 梯度上升 假设线性模型 其中 是参数 损失函数为 那么每次GD的更新算法为 BGD Bat
  • 大话算法之动态规划——初探

    对于动态规划 之前学习过了 但是总感觉理解不深刻 今天正好讲道动态规划算法 感觉有了一些新的认识和看法 打算详细的写下来 一是帮助自己理清 二是希望给刚刚接触的ACMer一个简明的理解思路吧 大话算法之动态规划 初探 一 引例 数塔问题 之
  • 【算法导论学习】分治法求最大子数组

    最大子数组 分治法 问题分析方向 对时间复杂度的分析 样例分析 问题分析 分解问题 解决问题 合并问题 对代码的设想 中间部分的处理 递归部分 时间复杂度分析 完整代码 分治法 所谓分治法 就是把问题不断分割变小 常见的是把数组分割为两部分
  • 刷题之移动零

    给定一个数组 nums 编写一个函数将所有 0 移动到数组的末尾 同时保持非零元素的相对顺序 示例 输入 0 1 0 3 12 输出 1 3 12 0 0 说明 必须在原数组上操作 不能拷贝额外的数组 尽量减少操作次数 来源 力扣 Leet
  • 小波变换

    原文地址 1 小波变换 小波变换是一种信号的时间 尺度 时间 频率 分析方法 它具有多分辨分析的特点 而且在时频两域都具有表征信号局部特征的能力 是一种窗口大小固定不变但其形状可改变 时间窗和频率窗都可以改变的时频局部化分析方法 即在低频部
  • PID算法的理论分析

    PID算法的理论和应用 PID算法基本原理 PID算法的离散化 PID算法伪代码 PID算法C 实现 pid cpp pid h pid example cpp Python代码 仿真结果 PID算法基本原理 PID算法是控制行业最经典 最
  • 算法:单调栈

    栈 先进后出 最好是找一些简单的数据用纸和笔模拟一下具体过程 清清楚楚地表现出来 理解一个比较难懂的程序最快的方式是对照着一个程序一步一步将过程模拟出来 那么就很容易理解整个程序的意思了 不要光看程序代码 不动笔 这样可能会耗很多时间并且最
  • 旋转图像(二维数组的旋转)——LeetCode数组算法题

    旋转图像
  • 模型选择、欠拟合和过拟合学习笔记

    训练误差 泛化误差 训练误差 模型在训练数据集上表现出的误差 泛化误差 指模型在任意一个测试数据样本上表现出的误差的期望 并常常通过测试数据集上的误差来近似 过拟合 欠拟合 过拟合 训练误差远小于其在测试数据集上的误差 欠拟合 模型无法得到
  • 刷题之字符串的排列 以及双指针滑动窗口

    刷题 给你两个字符串 s1 和 s2 写一个函数来判断 s2 是否包含 s1 的排列 如果是 返回 true 否则 返回 false 换句话说 s1 的排列之一是 s2 的 子串 示例 1 输入 s1 ab s2 eidbaooo 输出 t
  • 算法:链表

    单链表 单链表是一种链式存取的数据结构 链表中的数据是以结点来表示的 每个结点存储两个数据 一是该结点本身的值 二是其指向的下一结点的下标 用e i 表示节点i的值 用ne i 表示结点i指向的下一结点的坐标 head表示头结点的下标 id
  • 7-1 图的先深搜索+7-2 图的先广搜索

    由于本人用指针 链表实现数据结构算法时经常有使用堆叠字节的警告以及栈溢出报错 于是就都用数组或者C stl模拟了 输出无向图的给定起点的先广序列 输入格式 输入第一行给出三个正整数 分别表示无向图的节点数N 1
  • 代码随想录一刷-Day01

    代码随想录一刷 Day01 LeetCode704 二分查找 这道属于是入门必刷了 但是虽然能做出来 在细节上还是不够注意 public int search int nums int target if nums null nums le
  • 优化算法学习(LM算法)

    文章目录 推荐书籍 理论理解 程序实现 ceres安装 代码 推荐书籍 建议学习 METHODS FOR NON LINEAR LEAST SQUARES PROBLEMS http www2 imm dtu dk pubdb views
  • 算法与数据结构—LeetCode刷题笔记

    算法刷题笔记 一 动态规划 53 最大子序和 300 最长上升子序列 70 爬楼梯 242 有效的字母异位词 463 岛屿的周长 文章与视频资源多平台更新 微信公众号 知乎 B站 头条 AI研习图书馆 一 动态规划 53 最大子序和 典型的
  • unsigned long long妙用

    洛谷 P2181 对角线 使用unsigned long long可以防止爆精度 以下是各精度的范围 include
  • 第十四届蓝桥杯模拟赛(第三期)试题与题解 C++

    目录 一 填空题 一 最小的十六进制 答案 2730 二 Excel的列 答案 BYT 三 相等日期 答案 70910 四 多少种取法 答案 189 五 最大连通分块 答案 148 二 编程题 一 哪一天 二 信号覆盖 三 清理水草 四 最
  • 刷题之搜索插入位置

    给定一个排序数组和一个目标值 在数组中找到目标值 并返回其索引 如果目标值不存在于数组中 返回它将会被按顺序插入的位置 请必须使用时间复杂度为 O log n 的算法 来源 力扣 LeetCode 链接 https leetcode cn
  • 哈夫曼编解码算法(C实现)

    记得在毕业前去找工作 应聘康佳集团移动应用工程师的笔试题出了这么一道题 传输文本字符 BADCADFEED 只能出现 ABCDEF 这六个字符 使用以下的编码方式 如传输字符 BADCADFEED 接收编码 0010000110100000

随机推荐

  • 计算机系统课程 笔记总结 CSAPP第七章 链接(7.1-7.13)

    GitHub计算机系统CSAPP课程资源 计算机系统课程 笔记总结 CSAPP第二章 信息的表示和处理 2 1 2 2 计算机系统课程 笔记总结 CSAPP第二章 信息的表示和处理 2 3 2 4 计算机系统课程 笔记总结 CSAPP第三章
  • Ubuntu复现NeuS(用体绘制学习神经隐式曲面用于多视图重建 )——NeRF应用:表面重建

    目录 一 系统配置 二 安装 可能会遇到的问题 1 pytorch安装报错 2 缺少安装依赖项 三 数据集文件夹设置 1 数据集链接 2 数据集组织 3 以dtu scan24为例 四 训练 以dtu scan24为例 1 无掩码训练 2
  • 在Linux系统中部署zabbix监控服务

    今天学习安装zabbix 以下参考网上各种安装方法及自己做实验 一 zabbix简介 zabbix z biks 是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案 zabbix能监视各种网络参数 保证服务器系统
  • 查找---散列表查找定义

    当我们进行查找时 如果是顺序表查找 要找的关键字的记录 是从表头开始 挨个的比较记录a i 与key的值是等于还是不等于 有序表查找时 利用折半查询或者插值查询 直到相等时成功返回i 最终我们的目的都是为了找到那个i 其实也就是相对的下标
  • 使用TortoiseGit

    初衷 脱离命令行的方式 使用gui的界面化工具完成工作需要的版本控制操作 同时还对git运行机制有一定的了解 达到工作需要的基本 提高工作效率 准备工作 安装git 至于为什么 我就不废话了 点我下载git 安装TortoiseGit 理由
  • 【vue3引入高德地图】

    vue3引入高德地图 文章目录 vue3引入高德地图 前言 一 准备工作 1 开发文档 2 添加应用 二 使用步骤 1 npm 安装 2 地图容器创建 3 组件引入 4 js api 安全密钥 5 初始化地图 6 图层 6 1 添加 设置
  • 在 Windows 10 编译 Qt 5.15

    译好的下载链接 Qt5 15 8 Windows x86 VS2017 Qt5 15 8 Windows x86 64 VS2017 Qt5 15 8 Windows x86 VS2019 Qt5 15 8 Windows x86 64 V
  • 【Unity】通过代码控制编译器的暂停

    暂停编译器 EditorApplication isPaused true 结束编译器 EditorApplication isPlaying false
  • sklearn决策树之random_state & splitter

    在上一篇博文 决策树的sklearn实现 中 我们建立了一棵完整的决策树 但是如果在建立模型时不设置random state的数值 score会在某个值附近波动 引起画出来的每一棵树都 一样 它为什么会 稳定呢 如果使用其他数据集 它还会不
  • 互斥机制之自旋锁(spinlock)

    一 基础 自旋锁 如果测试结果表明锁仍被占用 程序将在一个小的循环内重复这个 测试并设置 操作 即进行所谓的 自旋 1 定义自旋锁 spinlock t spin 2 初始化自旋锁 spin lock init lock 该宏用于动态初始化
  • 【论文笔记】Masked Autoencoders Are Scalable Vision Learners

    论文 论文标题 Masked Autoencoders Are Scalable Vision Learners 发表于 CVPR2021 论文链接 https arxiv org pdf 2111 06377 pdf 论文代码 https
  • WebGL学习系列-片元着色器简介

    前言 到目前为止 我们绘制过点 三角形 矩形等 但使用的都是单色系 之前曾经说过着色器的概念 着色器分为顶点着色器和片元着色器 我们一直在使用顶点着色器 而对片元着色器基本没有提及过 本小节将展开对片元着色器的简单介绍 彩色的点 之前提到过
  • Sybase IQ常用函数大全--杂项函数

    Sybase IQ常用函数大全 杂项函数 查询索引 COALESCE 函数 返回列表中的第一个非 NULL 表达式 IFNULL 函数 返回第一个非空值表达式或 NULL ISNULL 函数 返回参数列表中的第一个非 NULL 表达式的值
  • 笔记~【软件测试基础知识】——黑盒测试和白盒测试

    这里写目录标题 一 黑盒测试 二 白盒测试 一 黑盒测试 黑盒测试概述 黑盒测试也称功能测试或数据驱动测试 它已知产品所应具有的功能 通过测试来检测每个功能是否能够正常使用 主要针对软件界面和软件功能 在测试时 把程序看作一个不能打开的黑盒
  • cv::Mat的翻转和转置

    cv Mat的本质是矩阵 openCV对Mat类型的处理 实际上也是矩阵操作 这里给个小例子 介绍转置操作和翻转操作 这段代码受了 http www tuicool com articles emIr2u 的启发 原图 Mat m1 imr
  • 【数据预处理】Pandas缺失的数据处理

    目录 缺少数据基础 何时 为何 数据丢失 被视为 缺失 的值 日期时间 插入缺失数据 缺少数据的计算 Sum Prod of Empties Nans GroupBy中的NA值 清理 填写缺失数据 填充缺失值 fillna 用PandasO
  • flutter -- 自定义音乐播放器/视频播放器

    写在前头 flutter 自定义实现音乐播放的文章挺多的 但是在开发中还是碰见了超级无语的情况 没想到需求竟然要音频的1倍到2倍的播放倍速 我一度质疑这个功能的实际用途 但是既然提出来了 开发就得撅屁股实现 这里采用了第三方的视频播放器来实
  • 如何使用BurpSuite(后续)

    前面那篇文章是我前几天写的 我发现我把简单的问题弄得复杂了 今天我给大家再写一篇关于BurpSuite的使用 1 下载安装免费版或者收费版 这里就不演示了 2 运行软件 一直NEXT就可以 3 打开工具 此时拦截状态显示OFF 4 在打开的
  • Python中类成员变量与实例成员变量相互影响的原因超详细解释

    今天在看python学习手册时看到了两句话 一 第26章中 类对象提供默认行为 二 第26章中 实例对象是具体的元素 书中给的例子是这样的 但上网查了一下好像第二句话不是非常准确 如下面的文章 原文 https www jb51 net a
  • 优化算法学习(LM算法)

    文章目录 推荐书籍 理论理解 程序实现 ceres安装 代码 推荐书籍 建议学习 METHODS FOR NON LINEAR LEAST SQUARES PROBLEMS http www2 imm dtu dk pubdb views