Ceres-Solver学习笔记(7)

安得万里风,飘飖吹我裳。

求解最小二乘问题

Ceres的有效使用需要熟悉非线性最小二乘解算器的基本组成部分,因此在我们描述如何配置和使用解析器之前,我们将简要地了解一下Ceres的一些核心优化算法是如何工作的。

定义 xn 是一个n-维的向量 (向量存储的都是变量), F(x)=[f1(x),...,fm(x)] 是一个m维的关于x的方程,我们的目的是解决优化问题

(1)argminx12F(x)2 .LxU

由于对F(x)的全局最小化是一个棘手的问题,我们将不得不满足于寻找局部最小值。

F(x) 的Jacobian J(x) 是一个m×n的矩阵, Jij(x)=jfi(x) ,梯度向量 g(x)=12|F(x)|2=J(x)F(x) .

解决非线性优化问题的一般策略是,解决原始问题的一系列逼近问题。在每次迭代中,这个近似都得到了解决,以确定Δx修正向量x。对于非线性最小二乘,可以利用线性化 F(x+Δx)F(x)+J(x)Δx 来构造一个近似,这就引出了下面的线性最小二乘问题:

(2)minΔx12J(x)Δx+F(x)2

不幸的是,天真地解决了一系列的问题并更新了 xx+Δx ,可能导致算法不会收敛。为了得到一个收敛的算法,我们需要控制步骤Δx的大小。根据Δx的大小如何控制,非线性优化算法可以分为两大类:

  1. 置信域置信域的方法近似目标方程,是在搜索空间的一个子集(置信域)中使用模型函数(通常是平方的)。如果模型函数成功地最小化了真正的目标函数,那么信任区域就会展开;反过来,置信域就会收缩,模型优化问题又会得到解决。
  2. 线性搜索线性搜索方法首先找到一个下降方向,目标函数将被减少,然后计算一个步长,决定沿着这个方向移动的距离。许多方法其可用来计算利用下降方向例如梯度下降法、牛顿法和拟牛顿法,步长也可以精确的或不精确的确定。

在某种意义上,置信域方法是线性搜索方法的对偶:任区域方法首先选择一个步长(置信域的大小),然后是下降方向,而行搜索方法首先选择一个下降方向,然后是步长。Ceres对这两种类别都实现了多种算法。

Trust Region Methods

基本的信任区域算法是这样的

  1. 给定一个初始点x和一个信任区域半径μ。
  2. 求解
    argminΔxsuch that12J(x)Δx+F(x)2D(x)Δx2μLx+ΔxU.
  3. ρ=F(x+Δx)2F(x)2J(x)Δx+F(x)2F(x)2
  4. 如果 ρ>ϵ ,那么 x=x+Δx
  5. 如果 ρ>η1 ,那么 μ=2μ
  6. 否则如果 ρ<η2 ,那么 μ=0.5μ
  7. 跳转到2

μ是置信域半径,D(x)是一个矩阵定义F(x)域上的一个度量,ρ 便是步长Δx的质量,例如线性模型预测非线性目标值的下降有多好。其思想是: 增加或减少信任区域的半径取决于线性化预测非线性目标的行为的好坏,而这反过来又反映在ρ的值上。

在置信域算法中的关键计算步骤是解决约束优化问题的方法

(3)argminΔxsuch that12J(x)Δx+F(x)2D(x)Δx2μLx+ΔxU.

解决这个问题有许多不同的方法,每一个都产生一个不同的具体的置信域算法。目前,Ceres实现了两个置信域算法LM和Dogleg。如果有边界约束,那么每一个都被加了一个线性搜索。用户可以通过设置Solver::Options::trust_region_strategy_type.来选择它们。

Levenberg-Marquardt

LM算法是解决非线性最小二乘问题最流行的方法,它也是最先开发出来的置信域方法。Ceres实现了LM算法的一个精确步骤和一个不精确步骤变体。

可以证明,解决(3)可以通过解决具有如下形式的非约束优化来获得

argminΔx12J(x)Δx+F(x)2+λD(x)Δx2

λ 是拉格朗日乘数与 μ 逆相关

argminΔx12J(x)Δx+F(x)2+1μD(x)Δx2

矩阵D(x)是一个非负的对角矩阵,通常是矩阵 J(x)J(x) 的对角线的平方根。

我们假设矩阵D(x)已经在矩阵 J(x)J(x) 的底部被串接起来了。一个零向量已经被加到向量f的底部,接下来我们将就 J 和 f 讨论,例如线性最小二乘问题

(5)minΔx12J(x)Δx+f(x)2.

除了最小的问题,LM 算法的每一次迭代中(5)的求解是Ceres的主要计算成本。Ceres为解决(5)提供了许多不同的选择,有两种主要的方法——分解和迭代。

分解方法是精确的求解(4),使用 Cholesky 或 QR分解的方法,得到精确的step。但是还不清楚在LM算法的每一步中求解(4)的精确值对于解决(1)是否是必要的。事实上,我们已经看到证据表明,事实并非如此,因为(4)本身是一个规范化的版本(2)。实际上,构造线性化问题的非线性优化算法是可行的。这些算法被称为不精确牛顿或截断牛顿法。

一种不精确的牛顿法需要两种原料。首先,一种近似解线性方程组的廉价方法。一般来说,像 Conjugate Gradients 法这样的迭代线性求解是用于这个目的的。第二,迭代求解程序的终止规则。典型的终止规则是这样的

6H(x)Δx+g(x)ηkg(x).

k表示LM迭代数, 0<ηk<1 被称为forcing sequence。.[WrightHolt] 证明了使用基于(6)的不精确Newton step的截断LM算法,它收敛于任何序列 ηkη0<1 ,而收敛速度取决于forcing sequence ηk 的选择。

Ceres支持精确和不精确的步骤解决方案。当用户选择基于分解的线性分解器时,使用了精确的步骤LM算法。当用户选择一个迭代的线性解算器时,将使用不精确的步骤LM算法。

Dogleg

另一种解决置信域问题(3)的策略是由 M. J. D. Powell提出的。这里的关键思想是计算两个向量

ΔxGauss-NewtonΔxCauchy=argminΔx12J(x)Δx+f(x)2.=g(x)2J(x)g(x)2g(x).

注意向量 ΔxGauss-Newton 是(2)的解,如果我们限制自己沿着梯度方向运动的话, ΔxCauchy 是最小化线性近似的向量。Dogleg方法寻找一个由 ΔxGauss-Newton ΔxCauchy 定义的解决置信域问题的向量 Δx 。Ceres支持两种变体,可以通过设置Solver::Options::dogleg_type选择。

TRADITIONAL_DOGLEG 就像 Powell 所描述的,用高斯-牛顿和柯西矢量构造两条线段,发现沿着这条线最远处的点,形状就像一条狗腿(因此得名)。关于具体的推理和计算的更多细节,请参见 Madsen et al [Madsen]。

SUBSPACE_DOGLEG 是一种更复杂的方法 ,它考虑了由这两个向量形成的2维子空间,并在这个子空间中找到最小化置信域问题的点。

对LM来说,Dogleg的关键优势在于,如果对μ的某一特定选择计算的step并没有导致目标函数值的明显的减少,那么LM用更小μ值从头开始解决线性近似,另一方面,Dogleg只需要计算高斯-牛顿和柯西矢量之间的值,它们都不依赖于 μ的值。

Dogleg方法只能与基于分解的线性求解器一起使用。

Inner Iterations

一些非线性最小二乘问题在参数块相互作用的方式中有额外的结构,这有利于修改信任区域步骤的计算方式。例如,考虑以下回归问题

y=a1eb1x+a2eb3x2+c1

对于一组点对,用户希望去估计 a1,a2,b1,b2 c1

注意,表达式的左边是a1和a2的线性表达式,对于任何的b1、b2和c1值,可以使用线性回归来估计a1和a2的最优值。从问题的角度分析完全可以消除变量a1和a2。像这样的问题被称为可分离的最小二乘问题而最著名的解决方法是 Golub & Pereyra 发明的Variable Projection算法。

在矩阵分解中,也可以找到类似的结构,去掉数据。对应的算法被称为Wiberg的算法。

实现变量投影是乏味且昂贵的。Ruhe & Wedin提出了一种更简单的算法,具有类似的收敛性,他们称之为Algorithm II 。Algorithm II在计算一个成功的牛顿步骤之后,执行一个额外的优化步骤来估计a1和a2。

这个思想可以推广到在a1和a2,残差不是线性的情况。例如:

y=f1(a1,eb1x)+f2(a2,eb3x2+c1)

在这种情况下,我们将求解整个问题的信任区域,然后使用它作为起点,进一步优化a1和a2。对于线性情况,这相当于做一个线性最小二乘解。对于非线性问题,任何解决a1和a2优化问题的方法都可以。对于a1和a2(如果它们在两个不同的参数块),惟一的约束是它们不会出现在一个残差块中。

这一思想可以进一步推广,不仅仅是优化(a1,a2),而是将与Hessian矩阵的稀疏结构相对应的图形分解成一组不重叠的独立集,并对它们进行优化。

将 Solver::Options::use_inner_iterations 设置为 true,使用Ruhe&Wedin算法II的非线性泛化。这个版本的Ceres具有更高的迭代复杂度,但是在每次迭代中也表现出更好的收敛性。
推荐将 Solver::Options::num_thread 设置为最大数目,

Non-monotonic Steps

注意,置信域方法是一种下降算法,如果一个点严格地降低目标函数的值,它就会接受它。

放宽这一要求,可以使算法在长期内更有效,代价是增加了目标函数局部的值。

这是因为允许非下降的目标函数值允许算法”跳过巨石”,因为该方法不局限于在保留其收敛属性的情况下移动到狭窄的山谷中。

将 Solver::Options::use_nonmonotonic_steps设为 true 使能Conn, Gould & Toint 提出的非单调置信域算法。

尽管目标函数的值可能大于在优化过程中遇到的最小值,但是返回给用户的最终参数是与所有迭代的最小cost对应的。

对于所有置信域策略,非单调步骤的选项是可用的。

Line Search Methods

目前,Ceres的线性搜索方法不能处理边界约束,因此只能用于解决不受约束的问题。

线性搜索方法

  1. 给定一个初始点x
  2. Δx=H1(x)g(x)
  3. argminμ12F(x+μΔx)2
  4. x=x+μΔx
  5. 跳转到2

H(x) 是目标函数Hessian的近似, g(x) 是x处的梯度。依靠对 H(x) 的选择我们得到各种不同的搜索方向 Δx
第4步,这是一个一维的优化或者是沿着\Delta x$的线性搜索,这给这类方法名字。

不同的线搜索算法在选择搜索方向 Δx 和沿着 Δx 的一维优化方法上有不同的选择。 H(x) 的选择是这些方法中计算复杂度的主要来源。目前,Ceres Solver支持4种搜索方向的选择,都是针对大尺度的问题。

  1. STEEPEST_DESCENT 这对应于 H(x) 是单位矩阵。除了最简单的问题,这并不是一个好的搜索方向。它只是为了完整性而被包含在这里。
  2. NONLINEAR_CONJUGATE_GRADIENT 将共轭梯度法推广到非线性函数中。推广可以以多种不同的方式进行,从而产生不同的搜索方向。Ceres Solver目前支持FLETCHER_REEVES、POLAK_RIBIERE和HESTENES_STIEFEL方向。
  3. BFGS 将Secant的方法推广到多个维中,在这种情况下,得到了一种完全的、密集的近似于逆Hessian的近似,并用来计算拟牛顿step。BFGS是目前已知的最著名的拟牛顿算法。
  4. LBFGS 有限的内存近似于完整的BFGS方法,在此方法中,最后的M次迭代被用来计算拟牛顿Hessian的逆的近似。

目前,Ceres Solver同时支持回溯和插值的基于 Armijo线性搜索的算法,以及一种分割/缩放插值(strong)Wolfe 条件线性搜索算法。然而,注意,为了保证BFGS和LBFGS方法的基础假设,应该使用Wolfe的搜索算法。

LinearSolver

回想一下,在上面描述的两个置信域方法中,关键的计算成本是一个具有如下形式的线性最小二乘问题

(7)minΔx12J(x)Δx+f(x)2.

H(x)=J(x)J(x) , g(x)=J(x)f(x) 。为了方便起见,我们也减少了对x的依赖。很容易看出解(7)等价于解一般的方程
(8)HΔx=g

Ceres提供了许多不同的方案解决(8)。

DENSE_QR

对于较小的问题(几百个参数和几千个残差),相对稠密的Jacobians,DENSE_QR 是一个选择。让 J=QR J QR 分解。 Q 是一个正交矩阵, R 是一个上三角矩阵。然后我们能得到(8)的解

Δx=R1Qf

Ceres使用了Eigen的稠密QR分解步骤。


DENSE_NORMAL_CHOLESKY & SPARSE_NORMAL_CHOLESKY

大型非线性最小二乘问题通常是稀疏的。在这种情况下,使用稠密的QR分解是低效的。让 H=RR 是普通方程的Cholesky 分解, R 是一个上三角矩阵,对(8)的解是

Δx=R1Rg.

细心的读者会注意到, H 的Cholesky分解中的 R J 的QR分解中的 R 是相同的上三角矩阵。由于Q是正交矩阵, J=QR 可以得到 JJ=RQQR=RR ,有两种不同的Cholesky分解——稀疏和稠密。


DENSE_NORMAL_CHOLESKY
正如这个名字所暗示的,它会对正常的方程进行密集的Cholesky分解。Ceres使用了Eigen的稠密的LDLT分解程序。

SPARSE_NORMAL_CHOLESKY
正如这个名字所暗示的,在正常方程中执行稀疏的Cholesky分解。这就让大的稀疏问题节省了大量的时间和内存。


CGNR

对于一般的稀疏问题来说,如果这个问题对CHOLMOD来说太大,或者一个稀疏的线性代数库没有链接到Ceres,那么另一个选择就是CGNR方案。这个方案在普通方程上使用共轭梯度求解,但不需要显式地规范化方程。它利用

Hx=JJx=J(Jx)

共轭梯度的收敛依赖于调节数 κ(H) 。通常情况下,H的条件很差,必须使用预调机才能获得合理的性能。目前只有 JACOBI 预调机可用来与CGNR一起使用。它使用了“H”的块对角来预处理正常的方程。

当用户选择CGNR作为线性解决程序时,Ceres自动从精确的步骤算法切换到不精确的步骤算法。


DENSE_SCHUR & SPARSE_SCHUR

虽然可以使用SPARSE_NORMAL_CHOLESKY来解决BA问题,但BA问题有一个特殊的结构,并且可以构造一个更有效的方案解决(8)。

假设SfM问题由p个摄像机和q个点组成,而变量向量x有块结构 x=[y1,...,yp,z1,...,zq] 。y和z对应的是相机和点参数。此外,让摄像机块大小为c,并且点块大小为s(对于大多数问题,c=6-9和s=3)。Ceres并没有对这些块大小施加任何恒定性要求,但是选择它们是常量,简化了说明。

BA问题的一个关键特性是,没有一个 fi 包含两个或多个点块。这就意味着矩阵的H的形式是

(9)H=[BEEC] ,

Bpc×pc 是一个有p个c×c大小的稀疏矩阵块, Cqs×qs 是一个有q个s×s大小的对角矩阵块, Epc×qs 是一个对每个观察有c×s大小的稀疏矩阵块。现在我们来分割块 Δx=[Δy,Δz] , g=[v,w] 重新分析(8)作为块结构化线性系统
(10)[BEEC][ΔyΔz]=[vw] ,

并应用高斯消元。正如我们上面所提到的,C是一个块对角矩阵,对角块大小s×s。因此,计算C的逆矩阵通过每个块的逆运算是很简单的。这让我们可以消除 Δz 通过观察 Δz=C1(wEΔy) 。得到
(11)[BEC1E]Δy=vEC1w .

其中
S=BEC1E

是H中C的Schur complement。它也被称为缩小的摄像机矩阵,因为参与(11)的唯一变量是与摄像机相对应的变量。 Spc×pc 是一个块结构的对称正定矩阵,块的大小是c×c。块 Sij 对应于一对图像i和j的值是非0的,当且仅当两张图像观察同一个点。

现在,(10)可以通过先S的形式,求解Δy,然后用Δy来求Δz的值。因此, n×n , n=pc+qs 线性系统的解被简化为块对角矩阵C的逆矩阵,几个矩阵与矩阵,矩阵与向量相乘,以及块稀疏 pc×pc 线性系统(11)的解。对于大多数问题,相机的数量要远小于点的数量, pq ,因此求解(11)比(10)简单的多,这就是 Schur complement的小把戏。

(11)仍待解决,对于对称正定系统的求解方法完全是通过Cholesky分解并取决于矩阵的结构,通常有两种选择,第一种,直接分解,我们存储和分解S当作一个稠密的矩阵。这个方法有 O(p2) 的空间复杂度和 O(p3) 的时间复杂度,只有在相机数目只有几百时是可行的。Ceres实现这个策略叫做 SPARSE_SCHUR。

但是,S是典型的相当稀疏的矩阵,因为大多数图片只能看到一小部分场景。这就让我们有了第二个选择:稀疏直接法。这些方法将S作为一个稀疏矩阵,使用行和列的重新排序算法来最大限度地增加Cholesky 分解的稀疏性,并将它们的计算工作重点放在分解的非零部分上。稀疏直接法,依赖于Schur complement的稀疏结构,允许BA调整算法将稠密分解显著的优化。Ceres实现这个策略叫做 SPARSE_SCHUR。


ITERATIVE_SCHUR

BA问题的另一个选择是应用Preconditioned Conjugate Gradients来减少相机矩阵S而不是H。这样做的一个原因是S要比H小的多,但是更重要的是,你可以看到 κ(S)κ(H) 。Ceres实现S 的Conjugate Gradients作为 ITERATIVE_SCHUR。当用户选择ITERATIVE_SCHUR作为线性求解方式,Ceres自动将精确求解转换为不精确求解。

使用Conjuagate Gradients的关键计算是 Sx 矩阵向量乘积x是任意向量。有两种方法可以求解对该乘积,并且可以通过 Solver::Options::use_explicit_schur_complement 来控制。根据手头的问题,这两种方法的性能差异可能相当大。

  1. 隐式的 这是默认的。隐式求值适用于大问题,即计算和存储S Schur Complement S的成本太高,因为PCG只需要通过乘以一个矢量来得到S,一个计算 Sx 的方法就是观察
    x1=ET

    x2=C1x1

    x3=Ex2

    x4=Bx

    (12)Sx=x4x3

因此,我们可以在S中运行PCG,与在H中每一次迭代PCG有一样的计算量。在享受更强大的预调节带来的好处的同时。事实上,我们甚至不需要计算H,(12)可以使用J的列来实现。

方程(12)与结构工程和偏微分方程中出现的大线性系统的Domain Decomposition 方法密切相关。在 Domain Decomposition中,BA问题中的每一个点都是一个域,摄像机在这些域之间形成了交互。Schur complement的迭代求解属于迭代子结构技术的子范畴。
2. 显式的 隐式矩阵向量乘积求值的复杂性与雅可比矩阵中的非零数有关。对于小到中型的问题,构建Schur complement的成本足够小,可以在内存中显式地构造它,并使用它来评估Sx乘积。

当用户选择迭代器作为线性解决程序时,Ceres自动地从精确的步骤算法切换到不精确的步骤算法。

Preconditioner

解(8)的Conjugate Gradients 的收敛速度取决于H的特征值的分布。一个有用的上界是 κ(H) κ(H) 是矩阵H的条件值。对于大多数BA问题, κ(H) 很大,直接应用Conjugate Gradients到(8)会导致非常糟糕的性能。

这个问题的解决方法是用一个预条件系统来替换(8)。给一个线性系统, Ax=b ,预条件M,预条件系统 M1Ax=M1b 。他得到的算法被称为Conjugate Gradients 算法(PCG),它最坏的情况现在取决于预条件矩阵 κ(M1A) 的条件数。

使用预调式M的计算成本是计算M和计算任意向量y的乘积 M1y 的成本,因此,要考虑两个相互竞争的因素:H的结构中有多少是由M占据的,以至于条件值 κ(HM1) 很小,以及构造和使用M的计算成本。理想的预调器是一个 κ(M1A)=1 M=A 实现了它,但这不是一个实际的选择,因为应用这个预调函数需要解一个线性方程组,等价于无预先条件的问题。通常情况下,M的信息越多,使用的成本就越高。例如,不完全的Cholesky分解预调的收敛性比雅可比预调要好得多,但也要复杂得多。

所有的预调器中最简单的是对角或雅可比的预调机。例如, M=diag(A) 。对于像H这样的块状结构矩阵,可以将其推广到Jacobi的预调节器。Ceres实现块Jacobi预调器叫做 JACOBI。当使用CGNR时,它指的是H的块对角线,当使用 ITERATIVE_SCHUR 时,它指的是B的块对角线。

ITERATIVE_SCHUR 的另一个明显的选择是,Schur complement 矩阵 S的块对角线。例如,S的块Jacobi预调器,Ceres实现了它,并把它称为 SCHUR_JACOBI 预调器。

对于图片集3维重建中出现的BA问题,可以通过分析和利用场景中的相机点可视性结构来构建更有效的预调器。Ceres实现了 Kushal & Agarwal 所描述的两种可见性的预调,CLUSTER_JACOBI 和 CLUSTER_TRIDIAGONAL。这些都是全新的预调器,Ceres的实现还处于早期阶段,并不像上面描述的其他预调机那样成熟。

Ordering

在线性求解器中消除变量的顺序对方法的效率和准确性有很大的影响。例如,当做稀疏的Cholesky 分解时,有一个矩阵,一个好的排序将会给出一个带有O(n)的存储,一个坏的排序将导致一个完全密集的因子。

Ceres允许用户向解决程序提供各种提示关于变量消除顺序。这可以从没有任何提示,求解程序可以根据用户的选择来决定最佳的排序,比如使用的线性解析器,到一个精确的变量应该被消除的顺序,以及在其间的各种可能性。

ParameterBlockOrdering 类的实例用于向Ceres传递这些信息。

形式上的排序是参数块的有序分割。每个参数块都属于一个组,每个组都有一个与之关联的惟一整数,这决定了组中的顺序。我们称这些组为消除组。

有了这样的排序,Ceres确保最低编号的消除组的参数块首先被消除,然后消除下一个最低编号的消除组,依次下去。在每个消除组中,Ceres可以自由地按照它所选择的参数块顺序来排序。例如,线性系统

x+y2x+3y=3=7

有两种方法可以解决这个问题。首先从两个方程中消去x,然后再用x代替解出y,或者先消除y,解出x,然后再用y替换。用户可以在这里构造三个排序。

  1. {0:x},{1:y} :先求解x
  2. {0:y},{1:x} :先求解y
  3. {0:x,y} :求解器决定消除顺序。

因此,为了让Ceres启发式自动确定排序,将所有的变量放在同一个消除组中。这个群体的身份并不重要。这和没有指定一个排序是一样的。为了控制每个变量的排序,为每个变量创建一个消处组,按需要的顺序排列它们。

如果用户使用一个Schur解决方案(DENSE_SCHUR、SPARSE_SCHUR、ITERATIVE_SCHUR),并选择指定一个排序,那么它必须具有一个重要的属性。最低编号的消除组必须在与Hessian相对应的图中形成一个独立的集合,或者换句话说,在第一个消除组中没有两个参数块应该在同一个残差块中。为了达到最佳的性能,这个消除组应该尽可能的大。对于标准BA问题,这对应于第一个消除组包含所有3d点,第二个包含所有摄像机参数块。

如果用户让Ceres选择,那么解析器使用近似最大的独立集算法来识别第一个消除组。

MarkDown出了点问题,这篇就到这了,Great Saturday.

你可能感兴趣的:(c++)