- 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;