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;

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