Ceres學習-1.CostFunction


CostFunction的概念參考 https://www.cnblogs.com/vivian187/p/15398068.html

Ceres求解器,像所有基於梯度的優化算法一樣,依賴於能夠評估目標函數及其在其域內任意點的導數。實際上,定義目標函數及其雅可比矩陣是用戶在使用Ceres求解器求解優化問題時需要執行的主要任務。正確、高效的雅可比矩陣計算是獲得良好性能的關鍵

非線性優化涉及到對目標函數進行求導,從而迭代優化。

Ceres Solver提供了三種求導方式:

  • 自動求導: 自動求導是通過定義一個仿函數,然后傳給AutoDiffCostFunction/...,就可以讓Ceres自己去求導。

  • 數值求導: 有時,無法定義自動求導的模板仿函數,比如參數的估計調用了無法控制的庫函數或外部函數。這種情況無法使用自動求導了,數值求導便可以派上用場了。
             數值求導用法類似,先定義仿函數,然后傳遞給NumericDiffCostFunction/...,然后去構造問題求解。

  • 解析求導: 有些情況,自己寫求導解析式,計算效率會更高一些。
             如果使用解析求導的方式,就要自行計算殘差和雅克比。

1.自動求導

1.1 定義仿函數

仿函數,其實是一個類,只不過這個類的作用像函數,所以叫仿函數。原理就是類實現了operator()函數。
仿函數參考: https://www.cnblogs.com/vivian187/p/15329482.html

// 來自ceres-solver-1.14.0/examples/helloworld.cc
struct CostFunctor {
  template <typename T> bool operator()(const T* const x, T* residual) const {
    residual[0] = 10.0 - x[0];
    return true;
  }
};

要獲得自動求導代價函數,必須定義一個帶有模板化operator()(仿函數)的類,該操作符根據模板形參T計算代價函數。
operator()函數必須在最后一個實參(唯一的非const實參,即殘差)中寫入計算值,並返回true表示成功。

1.2 構造代價函數

// 來自ceres-solver-1.14.0/examples/helloworld.cc
CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);

AutoDiffCostFunction的模板參數說明:

第1個模板參數是仿函數CostFunctor
第2個模板參數是殘差塊中殘差的數量
第3個模板參數是第一個參數塊中參數的數量
如果有多個參數塊,則第4/5...個模板參數: 依次寫出各個參數塊中參數的數量

1.3 Ceres提供的類

Ceres提供的自動求導仿函數:

AutoDiffCostFunction類(上面的例子)
DynamicAutoDiffCostFunction類

參考:
https://www.cnblogs.com/vivian187/p/15349110.html#3autodiffcostfunction

2.數值求導

2.1 定義仿函數

// 來自ceres-solver-1.14.0/examples/helloworld_numeric_diff.cc
struct CostFunctor {
  bool operator()(const double* const x, double* residual) const {
    residual[0] = 10.0 - x[0];
    return true;
  }
};

與自動求導的仿函數不同的是,數值求導的operator()函數不是模板函數,而是直接使用了double

2.2 構造代價函數

// 來自ceres-solver-1.14.0/examples/helloworld_numeric_diff.cc
  CostFunction* cost_function =
      new NumericDiffCostFunction<CostFunctor, CENTRAL, 1, 1> (new CostFunctor);

和自動求導方法相比,在用數值求導時需要額外給定一個參數ceres::CENTRAL 。這個參數告訴計算機如何計算導數。

NumericDiffCostFunction的模板參數說明:

第1個模板參數是仿函數CostFunctor
第2個模板參數是數值求導的方式。這里選用了CENTRAL,還有FORWARD、RIDDERS等
第3個模板參數是殘差塊中殘差的數量
第4個模板參數是第一個參數塊中參數的數量
如果有多個參數塊,則第4/5...個模板參數: 依次寫出各個參數塊中參數的數量

2.3 Ceres提供的類

Ceres提供的數值求導仿函數:

NumericDiffCostFunction類(上面的例子)
DynamicNumericDiffCostFunction類

參考:
https://www.cnblogs.com/vivian187/p/15349110.html#3autodiffcostfunction

3.解析求導

3.1 定義代價函數類

// 來自ceres-solver-1.14.0/examples/helloworld_analytic_diff.cc
// A CostFunction implementing analytically derivatives for the
// function f(x) = 10 - x.
class QuadraticCostFunction
  : public SizedCostFunction<1 /* number of residuals */,
                             1 /* size of first parameter */> {
 public:
  virtual ~QuadraticCostFunction() {}

  virtual bool Evaluate(double const* const* parameters,
                        double* residuals,
                        double** jacobians) const {
    double x = parameters[0][0];

    // f(x) = 10 - x.
    residuals[0] = 10 - x;

    // f'(x) = -1. Since there's only 1 parameter and that parameter
    // has 1 dimension, there is only 1 element to fill in the
    // jacobians.
    //
    // Since the Evaluate function can be called with the jacobians
    // pointer equal to NULL, the Evaluate function must check to see
    // if jacobians need to be computed.
    //
    // For this simple problem it is overkill to check if jacobians[0]
    // is NULL, but in general when writing more complex
    // CostFunctions, it is possible that Ceres may only demand the
    // derivatives w.r.t. a subset of the parameter blocks.
    if (jacobians != NULL && jacobians[0] != NULL) {
      jacobians[0][0] = -1;
    }

    return true;
  }
};

自定義的代價函數類要繼承自CostFunction或者SizedCostFunction。其實SizedCostFunction是繼承自CostFunction的,只是確定了size(各個參數塊的數量)。
Evaluate()函數中計算了殘差和雅克比。

3.2 構造代價函數

// 來自ceres-solver-1.14.0/examples/helloworld_analytic_diff.cc
CostFunction* cost_function = new QuadraticCostFunction;

3.3 Ceres提供的類

Ceres提供的解析求導代價函數類:

CostFunction類
SizedCostFunction類(上面的例子)

參考:
https://www.cnblogs.com/vivian187/p/15349110.html#3autodiffcostfunction

3.4 什么情況下使用解析求導

按照官方說明,以下情況可以使用解析求導

函數式簡單,便於求出導數解析式
能使用Matlab Maple Mathmatic SymPy等數學軟件求出了導數的解析式
性能極致要求
沒有其他好的方法去求導
喜歡手算導數

綜上所述,建議優先使用自動求導和數值求導的方式,對雅克比計算擅長者和極致性能追求者可考慮使用解析求導的方式。

參考
https://www.jianshu.com/p/39d8e0f31bbf
https://blog.csdn.net/ktigerhero3/article/details/85062092


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM