转载计算机视觉life的非线性优化GN/LM
尊重别人的劳动成果就是对自己的尊重——声明至上:转载来源计算机视觉life的知乎:https://zhuanlan.zhihu.com/p/35392121
从零开始学习「张氏相机标定法」(四)优化算法前传
前一篇说到LM优化算法。在介绍LM优化算法前,得先说说它是怎么一步步发展来的。因此,优化算法部分的介绍顺序是:梯度下降、牛顿法、高斯牛顿法、LM法。
一般待优化的目标函数都是由误差的平方和组成,也就是我们常见的最小二乘问题:
这里的 f 一般是非线性函数。我们在微积分课程里学过,如果想要求目标函数的极值(可能是极大值、极小值或者鞍点处的值),可以通过令目标函数的导数(如果可导)为零,求解析解得到。但是实际应用中函数 f 一般是非常复杂的非线性函数,没有解析解。所以一般最常用的优化方法就是迭代求解。
如何迭代呢?
迭代求解
以迭代求解极小值为例(如果求极大值,在前面加个负号转化为求极小值问题)。通用的方法就是先给定一个初值 θ0,对应目标函数值为 f(θ0),这个初值可以是经验估计的或者随机指定(随机的话收敛会慢一些),然后我们改变(增大或减少) θ0 的值,得到一个新的值 θ1,如果 f(θ1)<f(θ0),那么说明我们迭代的方向是朝着目标函数值减小的方向,离我们期待的极小值更近了一步,继续朝着这个方向改变 θ1的值,否则朝着相反的方向改变 θ1的值。如此循环迭代多次,直到达到终止条件结束迭代过程。这个终止条件可以是:相邻几次迭代的目标函数值差别在某个阈值范围内,也可以是达到了最大迭代次数等。我们认为迭代终止时的目标函数值就是极小值。
因此迭代的一般过程如下:
1、对于目标函数 f(x),给定自变量某个初始值x0。这个初值可以是经验估计的或者随机指定。
2、根据采用的具体方法(梯度下降、牛顿、高斯牛顿、LM等)确定一个增量△xk
3、计算目标函数添加了增量后的值如下
4、如果达到迭代终止条件(达到最大迭代次数或函数值/自变量变化非常小),则迭代结束,可以认为此时对应的目标函数值就是最小值。
5、如果没有达到迭代终止条件,按如下方式更新自变量,并返回第2步。
因此,不同的优化算法的不同主要体现在增量的更新方式上。如果采用不合适的更新方式,其实很容易陷入局部最小值。这里给出一个关于局部最小值和全局最小值的直观的理解。如下图所示:
通常我们希望迭代得到的是全局最小值(只有一个)而不是局部最小值(可能有多个)。下图表示的是不同初始值对迭代结果的影响。如果我们初值设的是红色的θ0,最后找到的极小值位于 θ∞,在这个例子中它是全局最小值。但是如果我们初值设的是蓝色的 θ'0,最后我们找到的极小值位于 θ'∞,显然只是局部最小值,不是全局最小值。
实际上按照上述迭代方法,很多时候我们找到的所谓「全局最小值」其实只是局部最小值,这并不是我们期望的。有没有保证局部最小值一定是全局最小值的情况呢?
答案是有,当然这就需要对目标函数有一定的限制。如果我们的目标函数是凸函数,那么可以保证最终找到的极小值就是最小值。很多人可能忘记了什么是凸函数,下面简单介绍一下。
凸函数的概念
凸函数(convex function)在优化算法中是一个非常重要的概念,其定义如下:
如果上面定义没有看懂也没关系,我们可以发现,凸函数其实就是一个「碗」状曲线,碗口向上。下图列举了几种凸函数和非凸函数的例子。如果一个函数是凸函数,那么它只有一个极小值点,也就是最小值点,从下图中也可以看出来。下图从左到右从上到下分别展示了:只有一个极小值的凸函数、只有一个极小值的非凸函数、有多个极值的非凸函数、加了噪音的非凸函数。
如何判断一个函数是否是凸函数?
对于一元二次可微函数,如果它的二阶导数是正的,那么该函数就是严格凸函数。对于多元二次可微函数,当它的Hessian矩阵(不懂没关系,后面会讲)是正定的时候,就是严格凸函数。好了,凸函数就介绍这么些,对凸函数做个总结吧。
1、如果一个函数是凸函数,那么它有且只有一个极值点,且这个极值点是最值点。
2、几个非负凸函数的和仍然是凸函数。
3、如果一个函数不是凸函数,那么它可能只有一个极值点,也可能有多个极值点(局部极值点和全局极值点)
凸函数讲完了,我们继续说说迭代求解最小值法。从最简单易懂的梯度下降法说起。
梯度下降(Gradient Descent)法
先来假设一个场景:一个人去探险被困在一座山上的某个位置,他必须赶在太阳下山之前回到山底的营地,否则会有危险。但是他对这座山不熟悉,而且视野有限无法看清哪里是山底,时间不多了,因此他必须采取比较激进的最快下山路线。由于可视距离有限,比如说10米。那么他需要以当前位置(坐标x0)为中心,以10米为半径寻找周围最陡峭的路(假设没有障碍),盯着这条10米长的路终点(坐标x1)走,当走到坐标为x1位置时,需要重新观察以当前位置为中心,以可视距离10米为半径区域重新寻找最陡峭的路,盯着这条路的终点(坐标x2)走,如此往复,那么最终他能够走到山底吗?
上述过程就是梯度下降法的实例。梯度下降法是求解无约束优化问题最简单和最古老的方法之一,虽然现在已经不具有实用性,但是许多有效算法都是以它为基础进行改进和修正而得到的。
梯度下降法,其本质是一阶优化算法,有时候也称为最速下降法(Steepest Descent)。与其对应的是梯度上升法(Gradient ascent),用来求函数的极大值,两种方法原理一样,只是计算的过程中正负号不同而已。本文后续都是以梯度下降法为例进行介绍。
在微积分里,对函数求导数(如果是多元函数则求偏导)得到的就是梯度。具体来说,对于函数 f(x,y), 在点 (x0,y0),沿着梯度向量的方向就是(∂f/∂x0, ∂f/∂y0),它的方向是 f(x,y) 增加最快的方向。或者说,沿着梯度向量的方向,更加容易找到函数的极大值。反过来说,沿着梯度向量相反的方向,也就是 -(∂f/∂x0, ∂f/∂y0) 的方向,梯度减少最快,也就是更加容易找到函数的极小值。
以一元函数为例,假设目标函数为F(x),梯度的符号为∇,那么在梯度下降中,自变量x每次按照如下方式迭代更新:
其中 λ 称为步长或者学习率。按照上述方式迭代更新自变量,直到满足迭代终止条件,此时认为自变量更新到函数最小值点。下图是梯度下降法的示意图。
梯度下降法看起来很好,但是在具体使用过程中,会存在不少问题。
问题1:初始值选取
如下图所示,是二维函数优化的一个例子,不同颜色表示不同高度。我们初始值在蓝色的加号位置,最小值位于绿色的加号位置。图中展示了从不同的初始值开始迭代所需要的迭代次数,可以看到不同初始值会产生非常大的差异。(a) 图只经过5次迭代就收敛了, (b) 图经过了22次迭代才 收敛。
那么 (b)图里究竟为什么会多出那么多次的迭代呢?将局部细节放大后如下图所示,这部分「锯齿」状的震荡式下降是因为它经历了一个高度逐渐下降的「山谷」,而每次下降方向都与上一次垂直。换句话说,这种下降方式太「短视」,只关注了一个极小范围内的变化,而忽略了长远的趋势(牛顿法就是根据这点进行的改进,具体见后介绍),并不是一种「平滑」的下降。
既然初始值影响这么大,那么一般采用随机多次选取初始值进行多次迭代,然后将多次迭代结果进行比较,找出其中最小的那个作为最小值。这样会导致计算量急剧变大,最终确定的值也不能保证最小,并且因为随机选取,会导致每次最小值都不同,结果不稳定。
问题2:步长选取
步长,就是每一步走多远,这个参数如果设置的太大,那么很容易就在最小值附近徘徊;相反,如果设置的太小,则会导致收敛速度过慢。所以步长的选取也是一个非常关键的因素。
总结一下梯度下降法:
1、实际使用中使用梯度下降法找到的通常是局部极小值,而不是全局最小值。
2、具体找到的是哪个极小值和初始点位置有很大关系。
3、如果函数是凸函数,那么梯度下降法可以保证收敛到全局最优解(最小值)
4、梯度下降法收敛速度通常比较慢
5、梯度下降法中搜索步长 λ 很关键,步长太长会导致找不到极值点甚至震荡发散,步长太小收敛非常慢。
因此,前面的例子中,迷路的探险者可能花费了大量时间找到的却是一个假的山脚(山腰的某个低洼处,局部最小值)。
从零开始学习「张氏相机标定法」(五)优化算法正传
前一篇《从零开始学习「张氏相机标定法」(四)优化算法前传》花了不少篇幅介绍了最简单的求解无约束优化问题的梯度下降法,这篇我们从算法的演进方向分别介绍牛顿法、高斯牛顿法、Levenberg-Marquardt(LM)法。
牛顿法
介绍牛顿法之前,我们先来看看它到底有什么优势?能够解决什么问题?这样才有兴趣去了解它。
前面文章中介绍了梯度下降法在山谷中容易出现锯齿状的迭代,如下图所示。从不同的初始值开始迭代所需要的迭代次数会产生非常大的差异。
这种锯齿状的迭代,可以通过牛顿法来避免。对于同样的目标函数,使用牛顿法在不同的初始值位置(下图中蓝色十字),只需要3、4次迭代即可找到最小值(下图中绿色十字)。
知道了牛顿法相较于梯度下降法的优势了,那么牛顿法是怎么来的?原理是什么?
首先回顾一下,泰勒展开的通式,如下:
待优化的目标函数和前面一样:
我们将目标函数在x处进行二阶泰勒展开。对照上述通式,目标函数泰勒展开如下:
其中 J(x) 是 ||f(x)||2 关于x的Jacobian矩阵(一阶导数),H(x)则是Hessian矩阵(二阶导数)。
什么是Jacobian矩阵和Hessian矩阵呢?
Jacobian矩阵
假设函数 f = {f1(x1,...,xn), .., fm((x1,...,xn))} 有m维空间组成,对应n个自变量。那么函数 f 的一阶偏导数(如果存在)可以组成一个m行n列的矩阵,称为Jacobian(译为雅克比)矩阵
Hessian矩阵
假设有一个实数函数 f(x1,...,xn), 如果函数 f 的所有二阶偏导数存在,那么称如下矩阵为Hessian矩阵,它是一个n x n 的方阵。
而梯度下降法可以认为是泰勒展开式中只取到了一阶项,二阶项后面的部分丢弃。而在牛顿法中,泰勒展开中取到二阶导数,我们的目标函数变为:
对其关于△x求导,并令其为0,可以得到步长
类似于梯度下降法,我们用上述步长来迭代优化,最终收敛到极值点。这个过程就称为牛顿法。
牛顿法相比于梯度下降法的优点是什么呢? 为什么会有这样的优点? 我们来直观的理解一下。
如下图所示。红点和蓝点的梯度(一阶导数)是一样的,但是红点出的二阶导数比蓝点大,也就是说在红点处梯度变化比蓝点处更快,那么最小值可能就在附近,因此步长就应该变小;而蓝点处梯度变化比较缓慢,也就是说这里相对平坦,多走一点也没事,那么可以大踏步往前,因此步长可以变大一些。所以我们看到牛顿法迭代优化时既利用了梯度,又利用了梯度变化的速度(二阶导数)的信息。
从几何上说,牛顿法就是用一个二次曲面去拟合你当前所处位置的局部曲面,而梯度下降法是用一个平面去拟合当前的局部曲面,通常情况下,二次曲面的拟合会比平面更好,所以牛顿法选择的下降路径会更符合真实的最优下降路径。
从本质上去看,牛顿法是二阶收敛,梯度下降是一阶收敛,所以牛顿法收敛更快。比如你想找一条最短的路径走到一个盆地的最底部,梯度下降法每次只从你当前所处位置选一个坡度最大的方向走一步,牛顿法在选择方向时,不仅会考虑坡度是否够大,还会考虑你走了一步之后,坡度是否会变得更大。所以,可以说牛顿法比梯度下降法看得更远一点,能更快地走到最底部。也就是说梯度下降法更贪心,只考虑了眼前,反而容易走出锯齿形状走了弯路;而牛顿法目光更加长远,所以少走弯路。
有人会问了,牛顿法既然那么好,为啥还有后续的高斯牛顿法、LM法?牛顿法肯定有什么缺点吧?
是的,牛顿法确实有比较明显的缺点。前面推导过程,可以看到:牛顿法中包含了Hessian 矩阵的计算。而在高维度下计算Hessian 矩阵需要消耗很大的计算量,很多时候甚至无法计算。
下面介绍高斯牛顿法,看看它是怎么改进牛顿法这个缺点的。
高斯牛顿法
高斯牛顿(Gauss-Newton)法是对牛顿法的一种改进,它用雅克比矩阵的乘积近似代替牛顿法中的二阶Hessian 矩阵,从而省略了求二阶Hessian 矩阵的计算。下面来看看高斯牛顿法是怎么一步步推导来的。
牛顿法中我们是将目标函数 f(x+△x)^2 进行泰勒展开,而在高斯牛顿法中,我们对 f(x+△x) 进行一阶泰勒展开,如下:
前面我们说过,我们要确定一个步长,使得目标函数
达到最小,将一阶泰勒展开代入,问题转化为如下最小二乘问题:
展开后可得:
对△x求导并令导数为0,可得:
则有:
其中我们把J^TJ作为牛顿法中Hessian矩阵的近似,从而用求一阶雅克比矩阵代替了直接求二阶Hessian矩阵的过程。上述就是高斯牛顿法中步长的推导过程。
这个结果完美吗?No!
我们知道牛顿法中,要求Hessian矩阵是可逆的并且正定。但在高斯牛顿法中,用来近似Hessian矩阵的J^TJ可能是奇异矩阵或者病态的,此时会导致稳定性很差,算法不收敛。
另外一个不可忽视的问题是,由于前面我们采用二阶泰勒展开来进行的推导,而泰勒展开只是在一个较小的范围内的近似,因此如果高斯牛顿法计算得到的步长较大的话,上述的近似将不再准确,也会导致算法不收敛。
肿么办?LM算法可以修正上述问题。
LM法
列文伯格-马夸尔特Levenberg-Marquardt(LM)法在一定程度上修正了高斯牛顿法的缺点,因此它比高斯牛顿法更加鲁棒,不过这是以牺牲一定的收敛速度为代价的--它的收敛速度比高斯牛顿法慢。
下面来看看LM算法到底怎么修正高斯牛顿法的缺点的。
LM算法中定义的步长为:
其中I是单位矩阵,u是一个非负数。如果 u 取值较大时,uI 占主要地位,此时的LM算法更接近一阶梯度下降法,说明此时距离最终解还比较远,用一阶近似更合适。反之,如果 u 取值较小时,H 占主要地位,说明此时距离最终解距离较近,用二阶近似模型比较合适,可以避免梯度下降的“震荡”,容易快速收敛到极值点。因此参数 u 不仅影响到迭代的方向还影响到迭代步长的大小。
设x初值为x0,根据经验可以设置u的初值u0为:
其中,τ可以自己指定,aii表示矩阵A对角线元素。
LM采用的搜索方法是信赖域(Trust Region)方法,和梯度下降、牛顿法、高斯牛顿法采用的线性搜索(Line Search)方法不同。
为什么要用信赖域呢?这是因为高斯牛顿法中采用近似二阶泰勒函数只在展开点附近有较好的近似效果,如果步长太大近似就不准确,因此我们应该给步长加个信赖区域,在信赖区域里,我们认为近似是有效的,出了这个区域,近似会出问题。
那么如何确定信赖区域的范围呢?一个比较好的方法是根据我们的近似模型跟实际函数之间的差异来确定。使用如下因子来判断泰勒近似是否足够好:
其中,分子是实际函数迭代下降的值,分母是近似模型下降的值。如果 ρ 接近于1,认为近似比较准确,可以扩大信赖范围;如果 ρ 远小于1,说明实际减小的值和近似减少的值差别很大,也就是说近似比较差,需要缩小信赖范围。
下面给出LM算法优化的一个实例流程。
1、给定初始点 x0 ,计算初始信赖域半径 u0
2、对于第 k 次迭代,根据前面式子求出步长 △xk,并计算得到 ρ
3、若 ρ ≤0.25 ,说明步子迈得太大了,应缩小信赖域半径,令
4、若 ρ ≥0.75 ,说明这一步近似比较准确,可以尝试扩大信赖域半径,令
5、若 0.25<ρ<0.75 ,说明这一步迈出去之后,处于“可信赖”和“不可信赖”之间,可以维持当前的信赖域半径,令
6、若 ρ≤0 ,说明函数值是向着上升而非下降的趋势变化了(与最优化的目标相反),这说明这一步迈得错得“离谱”了,这时不应该走到下一点,而应“原地踏步”,即
并且和上面 ρ≤0.25的情况一样缩小信赖域。反之,在 ρ>0 的情况下,都可以走到下一点,即
上面优化过程中的阈值参数只是作为示例使用的经验值,也可以自己指定。
LM算法可以一定程度避免系数矩阵的非奇异和病态问题,可以提供更鲁棒、更准确的步长。因此LM算法在相机标定、视觉SLAM等领域中应用非常广泛。