LM算法+推导+C++代码实践

LM算法+推导+C++代码实践

一、算法推导

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、代码实践

#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <iostream>
#include <iomanip>
#include <math.h>

using namespace std;
using namespace Eigen;

const double DERIV_STEP = 1e-5;
const int MAX_ITER = 100;

#define max(a,b) (((a)>(b))?(a):(b))

double func(const VectorXd& input, const VectorXd& output, const VectorXd& params, double objIndex)
{
	// obj = A * sin(Bx) + C * cos(D*x) - F
	double x1 = params(0);
	double x2 = params(1);
	double x3 = params(2);
	double x4 = params(3);

	double t = input(objIndex);
	double f = output(objIndex);

	return x1 * sin(x2 * t) + x3 * cos(x4 * t) - f;
}

//return vector make up of func() element.
VectorXd objF(const VectorXd& input, const VectorXd& output, const VectorXd& params)
{
	VectorXd obj(input.rows());
	for (int i = 0; i < input.rows(); i++)
		obj(i) = func(input, output, params, i);

	return obj;
}

//F = (f ^t * f)/2
double Func(const VectorXd& obj)
{
	//平方和,所有误差的平方和
	return obj.squaredNorm() / 2;
}

double Deriv(const VectorXd& input, const VectorXd& output, int objIndex, const VectorXd& params,
	int paraIndex)
{
	VectorXd para1 = params;
	VectorXd para2 = params;

	para1(paraIndex) -= DERIV_STEP;
	para2(paraIndex) += DERIV_STEP;

	double obj1 = func(input, output, para1, objIndex);
	double obj2 = func(input, output, para2, objIndex);

	return (obj2 - obj1) / (2 * DERIV_STEP);
}

MatrixXd Jacobin(const VectorXd& input, const VectorXd& output, const VectorXd& params)
{
	int rowNum = input.rows();
	int colNum = params.rows();

	MatrixXd Jac(rowNum, colNum);

	for (int i = 0; i < rowNum; i++)
	{
		for (int j = 0; j < colNum; j++)
		{
			Jac(i, j) = Deriv(input, output, i, params, j);
		}
	}
	return Jac;
}
double maxMatrixDiagonale(const MatrixXd& Hessian)
{
	int max = 0;
	for (int i = 0; i < Hessian.rows(); i++)
	{
		if (Hessian(i, i) > max)
			max = Hessian(i, i);
	}

	return max;
}
//L(h) = F(x) + h^t*J^t*f + h^t*J^t*J*h/2
//deltaL = h^t * (u * h - g)/2
double linerDeltaL(const VectorXd& step, const VectorXd& gradient, const double u)
{
	double L = step.transpose() * (u * step - gradient);
	return L;
}

void levenMar(const VectorXd& input, const VectorXd& output, VectorXd& params)
{
	int errNum = input.rows();      //error num
	int paraNum = params.rows();    //parameter num

	//initial parameter 
	VectorXd obj = objF(input, output, params);     //得到误差
	MatrixXd Jac = Jacobin(input, output, params);  //得到雅可比矩阵
	MatrixXd A = Jac.transpose() * Jac;             //Hessian矩阵,此处为4x4的矩阵
	VectorXd gradient = Jac.transpose() * obj;      //gradient

	//initial parameter tao v epsilon1 epsilon2
	double tao = 1e-3;
	long long v = 2;
	double eps1 = 1e-12, eps2 = 1e-12;
	double u = tao * maxMatrixDiagonale(A);   //找到雅可比矩阵对角线上最大的值,并乘tao
	bool found = gradient.norm() <= eps1;     //判断是否小于阈值,小于这个阈值,即可退出。
	if (found) return;

	double last_sum = 0;
	int iterCnt = 0;

	while (iterCnt < MAX_ITER)
	{
		VectorXd obj = objF(input, output, params);

		MatrixXd Jac = Jacobin(input, output, params);  //jacobin
		MatrixXd A = Jac.transpose() * Jac;             //Hessian
		VectorXd gradient = Jac.transpose() * obj;      //gradient

		if (gradient.norm() <= eps1)
		{
			cout << "stop g(x) = 0 for a local minimizer optimizer." << endl;
			break;
		}

		cout << "A: " << endl << A << endl;

		VectorXd step = (A + u * MatrixXd::Identity(paraNum, paraNum)).inverse() * gradient; //negtive Hlm.

		cout << "step: " << endl << step << endl;

		if (step.norm() <= eps2 * (params.norm() + eps2))
		{
			cout << "stop because change in x is small" << endl;
			break;
		}

		VectorXd paramsNew(params.rows());
		paramsNew = params - step; //h_lm = -step;

		//compute f(x)
		obj = objF(input, output, params);

		//compute f(x_new)
		VectorXd obj_new = objF(input, output, paramsNew);

		double deltaF = Func(obj) - Func(obj_new);
		double deltaL = linerDeltaL(-1 * step, gradient, u);

		double roi = deltaF / deltaL;
		cout << "roi is : " << roi << endl;
		if (roi > 0)
		{
			params = paramsNew;
			u *= max(1.0 / 3.0, 1 - pow(2 * roi - 1, 3));
			v = 2;
		}
		else
		{
			u = u * v;
			v = v * 2;
		}
		cout << "u = " << u << " v = " << v << endl;

		iterCnt++;
		cout << "Iterator " << iterCnt << " times, result is :" << endl << endl;
	}
}
int main(int argc, char* argv[])
{
	// obj = A * sin(Bx) + C * cos(D*x) - F
	//there are 4 parameter: A, B, C, D.
	int num_params = 4;

	//generate random data using these parameter
	int total_data = 100;

	VectorXd input(total_data);
	VectorXd output(total_data);

	double A = 5, B = 1, C = 10, D = 2;
	//load observation data
	for (int i = 0; i < total_data; i++)
	{
		//generate a random variable [-10 10]
		double x = 20.0 * ((rand() % 1000) / 1000.0) - 10.0;
		double deltaY = 2.0 * (rand() % 1000) / 1000.0;
		double y = A * sin(B*x) + C * cos(D*x) + deltaY;

		input(i) = x;
		output(i) = y;
	}

	//gauss the parameters
	VectorXd params_gaussNewton(num_params);
	//init gauss
	params_gaussNewton << 3.6, 1.3, 7.2, 1.7;

	VectorXd params_levenMar = params_gaussNewton;

	levenMar(input, output, params_levenMar);
	cout << "Levenberg-Marquardt parameter: " << endl << params_levenMar << endl << endl << endl;

}

参考

1:https://zhuanlan.zhihu.com/p/136143299
2:https://blog.csdn.net/stihy/article/details/52737723
3:参考文献:A Brief Description of the
Levenberg-Marquardt Algorithm Implemened

LM算法,全称为Levenberg-Marquard算法,它可用于解决非线性最小二乘问题,多用于曲线拟合等场合。 LM算法的实现并不算难,它的关键是用模型函数 f 对待估参数向量 p 在其邻域内做线性近似,忽略掉二阶以上的导数项,从而转化为线性最小二乘问题,它具有收敛速度快等优点。LM算法属于一种“信赖域”——所谓的信赖域,此处稍微解释一下:在最优化算法中,都是要求一个函数的极小值,每一步迭代中,都要求目标函数值是下降的,而信赖域,顾名思义,就是从初始点开始,先假设一个可以信赖的最大位移 s ,然后在以当前点为中心,以 s 为半径的区域内,通过寻找目标函数的一个近似函数(二次的)的最优点,来求解得到真正的位移。在得到了位移之后,再计算目标函数值,如果其使目标函数值的下降满足了一定条件,那么就说明这个位移是可靠的,则继续按此规则迭代计算下去;如果其不能使目标函数值的下降满足一定的条件,则应减小信赖域的范围,再重新求解。 事实上,你从所有可以找到的资料里看到的LM算法的说明,都可以找到类似于“如果目标函数值增大,则调整某系数再继续求解;如果目标函数值减小,则调整某系数再继续求解”的迭代过程,这种过程与上面所说的信赖域是非常相似的,所以说LM算法是一种信赖域
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值