何时在 Ceres 中定义多个 Residual 块?

When to define multiple Residual blocks in Ceres?

我正在使用 Ceres Solver tutorial

鲍威尔函数

鲍威尔函数映射自R^4 -> R^4,因此定义一个残差块似乎很直观,它接受一个4元素数组x并填充4 元素数组 residual.

相反,教程中的示例定义了 4 个不同的残差块,它们映射 R^2 -> R^1

当然,如果我们试图最小化 1/2 || F(x) ||^2,那么最小化 F 的每个元素将隐含地产生与直接最小化 1/2 || F(x) ||^2 相同的解决方案(即我的建议是return 单个残差向量 F 而不是 F1...F4 分开)。 (我已经使用下面的成本函子验证了这一点)。

struct F {
    template<typename T>
    bool operator() (const T* const x, T* residual) const {

        residual[0] = x[0] + 10.0 * x[1];
        residual[1] = sqrt(5.0) * (x[2] - x[3]);
        residual[2] = (x[1] - 2.0*x[2]) * (x[1] - 2.0*x[2]);
        residual[3] = T(sqrt(10.0)) * (x[0]  - x[3]) * (x[0] - x[3]);

        return true;
    }
};
  1. 为残差向量的每个元素定义单独的残差块(和隐式参数块)有什么好处F

  2. 如果残差F1取决于参数x1x2而残差F2取决于x3x4F 相对于 x1 的成本是否会影响 x3 的价值?

曲线拟合

另一个示例试图找到参数 mc 以曲线 y=e^(mx + c)

它定义了一些ExponentialResidual,它简单地输出T(y_) - exp(m[0] * T(x_) + c[0]),其中(x_, y_)是一个数据点。

然后他们继续为每个观察添加一个残差块

double m = 0.0;
double c = 0.0;

Problem problem;
for (int i = 0; i < kNumObservations; ++i) {
  CostFunction* cost_function =
       new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>(
           new ExponentialResidual(data[2 * i], data[2 * i + 1]));
  problem.AddResidualBlock(cost_function, NULL, &m, &c);
}
  1. 虽然我懒得自己重现这个例子,但我怀疑这也可以只用一个映射R^2 -> R^1的残差块来完成,其中一维残差只是所有[=36]的总和=] 所有 (x_, y_) ?是否有必要为每个观察定义一个残差块?

感谢阅读这篇长文 post!

主要原因是 Ceres 仅在参数和残差 级别考虑稀疏性,而不考虑单个项。对于真正密集的问题,其中每个残差项都取决于每个参数,问题的表示方式对运行时性能没有显着影响。

然而,Ceres 旨在处理非常大的 sparse 问题,其中每个残差项仅取决于几个参数,但有 lot 的参数和残差。利用这些问题的稀疏结构来有效地解决它们是绝对关键的——即避免所有项都为零的大量无意义的计算。虽然线性代数在数学上是相同的,但在处理稀疏矩阵时,线性系统的表示和求解方法却大不相同。

第二个原因是它使问题建模更容易。损失函数应用于以下结果:r^T * r(其中 r 是残差块)。如果只有一个残差块,则损失函数会有效地缩放总成本,而不是仅降低具有较大误差的问题部分的权重(异常值拒绝)。 Ceres还可以对残差块的评估进行线程化,从而加快对大问题的评估。

你问题的最后一部分不一样。在曲线拟合示例中,有两个参数和 kNumObservations 残差,问题严重超定。但是,如果您改为使用单个残差在内部计算 kNumObservations 点的误差,但仅返回它们的总和,则所解决的问题具有两个参数但只有一个残差并且是欠定的。作为一个单独的说明,返回它们的总和也会有所不同,因为您没有返回平方和,因此不同残差中不同符号的误差可以有效地相互抵消 - none 求解器可以观察到。