openCV 代码已打开,因此请检查模块。 openCV 的 cheatsheet 具有 Levenberg Marquardt 公式,请参阅第 1 页第 3 列。您可以使用 grep 搜索该备忘单公式或仅搜索方法名称:
grep -rnw . -e "Levenberg"
例如,您可以在 modules/legacy 文件夹中发现很多 levmar*.c 文件。使用 RANSAC 求解线性方程组后使用 LMA 的原因是因为前者的线性方程优化了错误的度量 - Homography 参数中的代数误差。更好的度量标准是点坐标的残差平方和。在这个指标中,解决方案是非线性的,必须迭代地找到。线性解用于初始化非线性算法并增加其收敛到全局最小值的机会。
然而,查看代码并不总是理解复杂概念的最简单方法。以下是优化算法的“进化”系列,希望能帮助您理解:
- 始终可以将优化视为成本或残差的最小化
-
如果函数是凸函数(碗形),它只有一个全局最小值,可以通过向下滑动找到。由于梯度向上,我们用减号表示:
param[t+1]=param[t]-k*cost',其中 t-iteration、k-step 和 ' 代表梯度或 w.r.t 的导数。参数
-
到目前为止一切都很好,但在某些情况下(想象一个看起来像滑雪板管的成本函数)沿着坡度行驶会使您从山谷的一侧到另一侧过冲和曲折,而只会慢慢下降(到全局最小值)。解决方案 - 使用更好的函数逼近(泰勒展开到二阶导数)来达到这个
param[t+1]=param[t]-(k/cost'') *cost',其中 '' 是二阶导数
直观的快速函数意味着较大的二阶导数,因此我们通过减少步长(将其除以一个大数)来减慢收敛速度,反之亦然。
4. 对于通常表示残差平方和或一些距离的二次误差,我们可以提出二阶导数的近似值作为一阶导数的乘积(称为雅可比矩阵,表示为 J,通常是一个矩阵);二阶导数或 Hessian = JTJ,所以优化看起来像
param[t+1]=param[t]-(JTJ)-1 *cost' - 称为高斯-牛顿
为了简单起见,这里我们删除了 k;比较这个和以前的公式,我们差不多了!另请注意,在维基百科和其他网站中,您可能会看到他们使用残差 yi-f(x, param) 而不是 k,但您现在也可以忽略它。仅当您对最后一个公式感到满意时,才能走得更远。
5. Levenberg 来了,他说 - 它很棒,但可能不稳定。我们对那些二阶导数做了太多假设,最好抛弃它们并使事情看起来像以前一样的一阶导数。为此,我们添加了一个倾倒因子,可以使优化看起来像
param[t+1]=param[t]-(JTJ + lambda*I)-1 *cost' ,其中 I 是单位矩阵,lambda 是标量
请注意,当 lambda 很大时,您可以丢弃 JTJ,剩下的是标准梯度下降。他们在收敛速度减慢时这样做,否则他们使用小的 lambda,这使得算法看起来更像 Gauss-Newton。注意 J = d_z/d_param, cost’=d_f/d_param and f=zT*z
Marquardt 来了,他说,太好了,但我喜欢我们的梯度下降速度更快 - 对于小对角线 (JTJ),我想要更大的步长,因此我们将最终公式除以 small当梯度很慢时,对角元素导致更快的收敛:
param[t+1]=param[t]-(JTJ + lambda*diagonal(JTJ))- 1 *成本'
这里是使用速降滑雪类比的快速总结,其中我们展示了参数变化 param[t+1]-param[t] 的表达式:
1. 梯度 J 指向上,但你想滑下来,所以逆梯度 -kJ;
2. 如果在“雪管”中的锯齿形以与二阶导数(H)相反的步长移动;希望你能走得更快:-H-1J
3. 用一阶导数的乘积逼近二阶导数 -(JTJ)-1J ;
4. 如果它太多就转储它并返回一阶导数 -(JTJ + lambdaI)-1 J;
5.不想卡在平坦的斜坡上?将您的步长反向缩放到近似 Hessian 的对角线:-(JTJ + lambdadiag(JTJ))-1 日
这是一个很大的谜,为什么所有这些工作,因为我的直觉在一段时间后放弃了。也许现在是从哲学角度看待这个问题并应用奥卡姆剃刀原则的好时机——我们假设越少越好,但如果假设太少,我们可能不会走得太远。我们所做的只是预测(假设)函数在本地查看它的全局行为(想象在一起 1 周后结婚)。使用 Hessian 就像在其众多参数方面有更多假设,而不是更保守的雅可比矩阵,它有几个参数(假设)。最后,我们可能希望在保守或冒险方面保持灵活,这就是 Levenberg-Marquardt 方法试图实现的目标。