Powell’s Function

考虑一个略微复杂点的例子,最小化 Powell's 函数。 其中 x=[x1,x2,x3,x4]x=\left[x_{1}, x_{2}, x_{3}, x_{4}\right] 残差项为:

f1(x)=x1+10x2f2(x)=5(x3x4)f3(x)=(x22x3)2f4(x)=10(x1x4)2F(x)=[f1(x),f2(x),f3(x),f4(x)]\begin{aligned} f_{1}(x) & =x_{1}+10 x_{2} \\ f_{2}(x) & =\sqrt{5}\left(x_{3}-x_{4}\right) \\ f_{3}(x) & =\left(x_{2}-2 x_{3}\right)^{2} \\ f_{4}(x) & =\sqrt{10}\left(x_{1}-x_{4}\right)^{2} \\ F(x) & =\left[f_{1}(x), f_{2}(x), f_{3}(x), f_{4}(x)\right] \end{aligned}

则在该例子中 fi(x)f_i(x) 就是 ResidualBlock,xx 就是整个参数块,例如 f1(x)f_1(x) 对应的参数块为 [x1,x2][x_1,x_2]。Cost 的计算方式为 x1+10x2x_1+10x_2F(x)F(x) 拥有四个参数,拥有四个残差项,我们想要最小化 12F(x)2\frac{1}{2}||F(x)||^2。接下来我们使用 Ceres 来解决该问题。

同样,第一步是定义评估目标函数中每个残差项的 functor。评估 f4(x1,x4)f_4(x_1,x_4) 的 functor 如下:

struct F4 {
  template <typename T>
  bool operator()(const T* const x1, const T* const x4, T* residual) const {
    residual[0] = sqrt(10.0) * (x1[0] - x4[0]) * (x1[0] - x4[0]);
    return true;
  }
};

类似的我们可以定义 F1,F2,F3 去评估 f1(x1,x2),f2(x3,x4),f3(x3,x2)f_1(x_1,x_2), f_2(x_3,x_4), f_3(x_3,x_2)。最终构建优化问题的方式如下:

double x1 =  3.0; double x2 = -1.0; double x3 =  0.0; double x4 = 1.0;

Problem problem;

// Add residual terms to the problem using the autodiff
// wrapper to get the derivatives automatically.
problem.AddResidualBlock(
  new AutoDiffCostFunction<F1, 1, 1, 1>(), nullptr, &x1, &x2);
problem.AddResidualBlock(
  new AutoDiffCostFunction<F2, 1, 1, 1>(), nullptr, &x3, &x4);
problem.AddResidualBlock(
  new AutoDiffCostFunction<F3, 1, 1, 1>(), nullptr, &x2, &x3);
problem.AddResidualBlock(
  new AutoDiffCostFunction<F4, 1, 1, 1>(), nullptr, &x1, &x4);

AutoDiffCostFunction<F1, 1, 1, 1>() 的模板参数分别代表 Cost 的计算方式,残差的维度,第一个参数的维度,第二个参数的维度。这里需要注意的是,每个 ResidualBlock 仅依赖于相应残差函数所依赖的两个参数,而不依赖于所有四个参数,例如 f1(x1,x2)f_1(x_1,x_2) 仅仅依赖于 x1,x2x_1,x_2。编译并运行 examples/powell.cc 可以得到如下结果:

不难看出,当目标函数值为 0 时,最优解位于 x1=0,x2=0,x3=0,x4=0x_{1}=0, x_{2}=0, x_{3}=0, x_{4}=0 处。 经过 15 次迭代,Ceres 得到的 cost 为 1.120029e151.120029e-15

Footnotes

Last updated