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
