使用Ceres Solver估计参数不确定性的问题求助
问题
使用Ceres Solver对小型数据集做非线性曲线拟合时,能得到与数据匹配良好的参数,但计算参数方差时结果异常——方差比参数本身大数个数量级。数据集点数与拟合模型参数数量接近,比如4个数据点对应3个参数。已知残差缩放会影响协方差矩阵计算的可信度,但缺乏数学背景,不清楚如何调整,求改进建议。
代码片段
代价函数
struct BiExponential1 { BiExponential1( double x, double y, double s ) : x_( x ), y_( y ), s_( s ) {} template <typename T> bool operator()( const T* const a, const T* const b, const T* const c, T* residual ) const { residual[0] = y_ - a[0] * ( exp( -b[0] * x_ ) - exp( -c[0] * x_ ) ); // observed - estimated = y - ( a' [exp( -b' * x ) - exp(-c' * x)] ) return true; } private: const double x_; const double y_; };
Solver/Covariance 使用代码
double a = init_value_a; double b = init_value_b; double c = init_value_c; double data_x[nData] = {<dummy data>}; double data_y[nData] = {<dummy data>}; for ( int i = 0; i < nData; ++i ) { problem.AddParameterBlock( &a, 1 ); problem.AddParameterBlock( &b, 1 ); problem.AddParameterBlock( &c, 1 ); problem.AddResidualBlock( new ceres::AutoDiffCostFunction<BiExponential1, 1, 1, 1, 1>( new BiExponential1( data_x[i], data_y[i] ) ), nullptr, &a, &b, &c ); } // Run the solver and record the results. ceres::Solve( solverOptions, &problem, &summary ); // Variance estimates // Code adapted from: http://ceres-solver.org/nnls_covariance.html#example-usage Covariance::Options covOptions; // TESTED non-default algorithm type - no effect. //covOptions.algorithm_type = ceres::CovarianceAlgorithmType::DENSE_SVD; Covariance covariance( covOptions ); std::vector<std::pair<const double*, const double*> > covariance_blocks; covariance_blocks.push_back( std::make_pair( &a, &a ) ); covariance_blocks.push_back( std::make_pair( &b, &b ) ); covariance_blocks.push_back( std::make_pair( &c, &c ) ); covariance_blocks.push_back( std::make_pair( &a, &b ) ); covariance_blocks.push_back( std::make_pair( &a, &c ) ); covariance_blocks.push_back( std::make_pair( &b, &c ) ); CHECK( covariance.Compute( covariance_blocks, &problem ) ); // Get the diagonal variance terms double covariance_aa[1 * 1]; double covariance_bb[1 * 1]; double covariance_cc[1 * 1]; covariance.GetCovarianceBlock( &a, &a, covariance_aa ); covariance.GetCovarianceBlock( &b, &b, covariance_bb ); covariance.GetCovarianceBlock( &c, &c, covariance_cc );
关键问题与改进建议
- 参数块重复添加:循环中每次都调用
AddParameterBlock添加a、b、c,会让Ceres把同一个参数当成多个独立参数处理,严重干扰协方差计算。正确做法是仅在循环外添加一次参数块,循环内只添加残差块。 - 数据集自由度不足:4个数据点对应3个参数,自由度仅为1,这种情况下协方差矩阵的估计本身就极不稳定,方差偏大是数学上的必然结果。如果可能,尽量收集更多数据点,让自由度(数据点数-参数数)至少大于3,才能得到可靠的方差估计。
- 残差未做归一化处理:Ceres计算协方差时默认假设残差是归一化的(残差方差为1),如果残差实际量级很大,会直接导致协方差被放大。可以通过两种方式修正:
- 利用数据的噪声水平
s_(代价函数中定义但未使用的参数)归一化残差,把残差计算改成residual[0] = (y_ - a[0] * (exp(-b[0]*x_) - exp(-c[0]*x_))) / s_; - 给残差块添加缩放损失函数,比如使用
ceres::ScaledLoss手动调整残差量级。
- 利用数据的噪声水平
- 协方差算法选择:当自由度低导致矩阵病态时,启用
DENSE_SVD算法(取消代码中对应行的注释),SVD对病态矩阵的处理能力更强,能提升协方差计算的稳定性。
内容的提问来源于stack exchange,提问作者Spencer Manwell
相关产品推荐
相关产品推荐

