Ceres-Solver库使用(三)--导数(Derivatives)

ceres-solver类似于大多数的优化包一样,依赖于能够计算目标函数在任意参数值上的每项函数值和每项导数值,这些计算值的准确性和高效性是获得好的优化结果的基本要素。Ceres Solver提供了一系列的计算方法。之前在helloworld例子中已经看到了其中的一项,即自动微分法(Automatic Differentiation).
接下来我们来考虑其他的两种计算方法:解析导数和数值导数。
数值导数 在某些情况下,无法定义一个模板价值函数(cost function),如剩余函数(residual function)计算时需要调用一个你不能控制的库函数。在这种情形下,就需要使用数值微分。用户可以通过创建一个结构体NumericDiffCostFunction 来定义一个计算剩余函数的算子(functor),如 f(x)=10?x对应的算子可以定义为:

struct NumericDiffCostFunctor { bool operator()(const double* const x, double* residual) const { residual[0] = 10.0 - x[0]; return true; } };

Problem中设置如下:
CostFunction* cost_function = new NumericDiffCostFunction( new NumericDiffCostFunctor) problem.AddResidualBlock(cost_function, NULL, &x);

可以相比较我们之前在helloworld的自动差分算法中的设置:
CostFunction* cost_function = new AutoDiffCostFunction(new CostFunctor); problem.AddResidualBlock(cost_function, NULL, &x);

两者结构看起来基本一致,除了一个额外的模板参数,其为标识计算数值微分的有限差分格式。具体细节可详见有关NumericDiffCostFunction 的文档。
一般来说,我们推荐使用自动微分替代数值微分。因为在C++模板类使得自动微分非常有效,而采用数值微分却划不来,由于其的数值误差会使得的收敛变慢。
解析导数 在某些情况下,使用自动微分行不通。例如在某种情况下,在封闭形式下计算导数比自动微分依赖的链式法则计算更有效。在这种情况下,需要你自己提供计算剩余函数和Jacobian矩阵的代码,根据编译时参数和剩余量(residuals)的维数定义CostFunctionSizedCostFunction的子类。
以 f(x)=10?x 定义类QuadraticCostFunction
class QuadraticCostFunction : public ceres::SizedCostFunction<1, 1> { public: virtual ~QuadraticCostFunction() {} virtual bool Evaluate(double const* const* 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; } };

QuadraticCostFunction::Evaluate包含了输入变量parameters和输出剩余值residuals、Jacobians矩阵jacobians,其中jacobians 为可选参数,如果非空,则在函数Evaluate 中会计算剩余函数的导数值。在以上的例子中,由于剩余函数为线性函数,所以Jacobian是一个常数。
从上述的代码片中可以看到,实现CostFunction 对象有点乏味。我们推荐如果没有一个很好的理由去管理Jacobian计算的话,你最好还是使用AutoDiffCostFunctionNumericDiffCostFunction 去构建你的剩余函数模块。
关于导数的更多内容 【Ceres-Solver库使用(三)--导数(Derivatives)】到目前为止,导数计算是Ceres-Solver库中最为复杂的一个部分,根据不同的情形,用户可能需要更为复杂的方式去计算导数。我们也仅仅只是介绍了Ceres库可以提供计算导数的一些粗浅的函数。当你已经熟悉使用NumericDiffCostFunctionAutoDiffCostFunction之后,我们推荐你可以使用DynamicAutoDiffCostFunction, CostFunctionToFunctor, NumericDiffFunctorConditionedCostFunction 函数使更为高级的方法去构建计算价值函数的值。
参考
  1. Ceres-Solver入门 http://www.ceres-solver.org/nnls_tutorial.html#derivatives

    推荐阅读