ceres-非线性最小二乘

  • ceres可以解决有边界限制的非线性最小二乘问题
    官方链接
    非线性最小二乘
    非线性最小二乘应用广泛,从统计中的曲线拟合,到从到从计算机视觉中的照片构建三维模型。
    对异常值
    降低非线性二乘的异常值的影响
    在这里插入图片描述
    是()的重载函数,当类型是Jacobians,T=Jet

微分

像其他优化函数一样,ceres可自动评估值以及求目标函数对任意参数的微分。
ceres提供了很多的求导方式,除了Automatic Differentiation自动求导,还有解析和数值微分

  • Numeric Derivatives 数值微分
    很多时候不能定义一个模板的损失因子(templated cost functor),比如当残差包含了一个库函数无法控制的调用
CostFunction* cost_function =
new NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>(new NumericDiffCostFunctor);
problem.AddResidualBlock(cost_function, NULL, &x);
  • Automatic Differentiation 自动微分
CostFunction* cost_function =new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
problem.AddResidualBlock(cost_function, NULL, &x);

一般来说,我们推荐自动微分而不是数值微分。使用C++模板使自动微分有效,而数值微分是昂贵的,容易出现数字误差,并导致收敛较慢。
微分方法简介

各种微分方法的优缺点

  • 数值微分法:根据微分的原始定义,h取很小的数值来求解微分,实现简单,可快速检验其他算法的正确性。缺点:有截断误差

  • 符号微分法:采用代数软件来替代手动代数求解,但是仅针对不能有循环结构、条件结构的closed form的数学表达式。缺点:当表达式急剧膨胀时,问题的求解速度也会变慢。

  • 自动微分法:自动微分存在的原则:所有的数值计算归根结底是一系列有限的微分算子的组合。
    自动微分法是一种介于数值微分(直接带值计算)和符号微分(代数求解)之间的一种方法。仅对基本函数运用符号微分,可以灵活结合编程语言中的循环、条件结构等,使用自动微分和不使用自动微分对代码总体改动非常小,并且由自动微分是一种图计算,可以进行很多优化,因此在现代深度学习系统中应用广泛。

  • 解析微分Analytic Derivatives
    当计算closed form中的微分时,解析微分比自动微分更有效
    因为可提供自己的残差和雅可比计算式(一阶导数),定义CostFunction 或 SizedCostFunction(编译时如果知道参数和残差大小的话)的子类。
    也称手动求解法:传统的BackProp,求解出梯度公式,编写代码,代入实际数值,得出真实的梯度。缺点:模型改变,需重新求解

#include<ceres/ceres.h>
#include<iostream>

//线性问题
using namespace std;
using namespace ceres;
//自动微分
//此处必须采用模板T类型,
//且要严格按照bool operator()(const T*const  x, T* residual) const{}方法严格定义,const不能少
struct costFunctor {
	template <typename T>
	//x为一维数组,const T*const  x,指针的指向和指针指向的内容都不可以修改
	//如果const位于星号左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的 右侧,const就是修饰指针本身,即指针本身是常量。
	bool operator()(const T*const  x, T* residual) const{
		residual[0] = T(10.0) - x[0];
		return true;
	}
};
//数值微分
//自动微分可采用模板函数
//数值微分和解析微分的数值类型是确定的double类型
struct NumericDiffCostFunctor {
	bool operator()(const double* const x, double* residual) const {
		residual[0] = 10.0 - x[0];
		return true;
	}
};
//解析微分
//有几个参数就有几个对应的雅可比的表达式
//一般来说观测数据是具有映射关系的一组数据,二维数组parameters会输入所有的参数(包括自变量和因变量)
//二维数组的每一行先放自变量再放因变量,二维数组的个数是观测数据的组数
//SizedCostFunction<1, 1>的模板中第一个参数为残差的数目(即待拟合的参数的数目),第二个参数为size of first parameter,个人认为是parameter行的数目,即观测数据的组数
//解析微分中,需手动提供Evaluate()方法,该方法中给出了残差residuals和jacobians的计算方式,而自动微分和数值微分中只需要在costFunctor中重载括号运算符给出残差的计算。
class QuadraticCostFunction : public ceres::SizedCostFunction<1, 1> {
public:
	virtual ~QuadraticCostFunction() {}
	virtual bool Evaluate(double const* const* parameters,
		//二维指针的指向的内容不可以改变
		//an input array为输入数组
		//parameters为二维数组
		double* residuals,
		double** jacobians) const {
		const double x = parameters[0][0];
		residuals[0] = 10 - x;

		// Compute the Jacobian if asked for.
		if (jacobians != NULL && jacobians[0] != NULL) {
			jacobians[0][0] = -1;
		}
		return true;
	}
};
int main_ceres_1(int argc,char** argv)
//int main(int argc, char** argv)
{
	google::InitGoogleLogging(argv[0]);
	double x_init = 5.0;
	double x_iter = x_init;

	Problem problem;
	//自动微分
	CostFunction* cost_function = new AutoDiffCostFunction<costFunctor, 1, 1>(new costFunctor);
	problem.AddResidualBlock(cost_function,NULL,&x_iter);//x_iter为待拟合的参数值,在迭代过程中会不断变化

	//数值微分
	//CostFunction* cost_function =
	//	new NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>(
	//		new NumericDiffCostFunctor);
	//problem.AddResidualBlock(cost_function, NULL, &x_iter);

	//解析微分
	//CostFunction* cost_function = new QuadraticCostFunction;
	//problem.AddResidualBlock(cost_function, NULL, &x_iter);//x_iter在迭代过程中会不断变化

	Solver::Options options;
	options.linear_solver_type = ceres::DENSE_QR;
	options.minimizer_progress_to_stdout = true;//最小化的过程是否进行标准化的输出
	Solver::Summary summary;
	Solve(options, &problem, &summary);
 
	cout<< summary.BriefReport() << endl;
	cout << "x_init : " << x_init << endl;
	cout << " x :  " << x_iter << endl;

	return 0;
}

调用Solve后会输出日志

  iter      cost      cost_change | gradient | | step | tr_ratio  tr_radius  ls_iter  iter_time  total_time
  0  1.250000e+01    0.00e+00    5.00e+00   0.00e+00   0.00e+00  1.00e+04        0    4.51e-05    1.30e-04
  1  1.249750e-07    1.25e+01    5.00e-04   5.00e+00   1.00e+00  3.00e+04        1    4.67e-05    6.50e-04
  2  1.388518e-16    1.25e-07    1.67e-08   5.00e-04   1.00e+00  9.00e+04        1    1.35e-05    7.94e-04
 Ceres Solver Report : Iterations: 2, Initial cost : 1.250000e+01, Final cost : 1.388518e-16, Termination : CONVERGENCE

release模式下控制台界面不会停留,也可调试
debug模式下无法运行
x_init : 5
x : 10
实际函数进行了3次迭代,虽然两次迭代就已经完全收敛了

求解参数

官网参数说明
最全参数说明
double Solver::Options::function_tolerance = 1e-6;
损失差值的变化量除以原始损失大小
sover_options.parameter_tolerance = 1e-6;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章