SciPy 优化:Newton-CG vs BFGS vs L-BFGS

Posted

技术标签:

【中文标题】SciPy 优化:Newton-CG vs BFGS vs L-BFGS【英文标题】:SciPy optimisation: Newton-CG vs BFGS vs L-BFGS 【发布时间】:2017-07-14 10:44:32 【问题描述】:

我正在使用 Scipy 做一个优化问题,我正在使用大小为 NNxNN 的顶点和键的平面网络,连接它的两侧(即,使其周期性),并最小化能量函数,这样它卷曲形成一个圆柱体。 (请参阅下面的链接。)

由于我有函数 energy(xyz-position) 并且它是渐变的,因此我决定使用 Scipy 手册中推荐的三种方法——Newton-CGBFGSL-BFGS-B——并比较它们的执行情况。

我调用优化函数如下,我只是根据大小写将'Newton-CG'替换为'BFGS''L-BFGS-B'

from scipy.optimize import minimize
res = minimize(energy, xyzInit, method='Newton-CG', jac = energy_der,  options='disp': True) 

我发现了以下一般行为(我给出了NN=9情况的输出数据,对应于3*9^2=243维参数空间)-

    BFGS 系统性地无法找到正确的最小值(对于低 NN),并且对于大 NN 根本无法收敛。最终结果见https://plot.ly/~apal90/162/。

     NN=9
     Method: BFGS
     Warning: Desired error not necessarily achieved due to precision loss.
     Current function value: 204.465912
     Iterations: 1239
     Function evaluations: 1520
     Gradient evaluations: 1508
     Time taken for minimisation: 340.728140116
    

    Newton-CG 为小的 NN (NN 会加剧这种行为。见https://plot.ly/~apal90/164/

     NN=9
     Method: Newton-CG
     Optimization terminated successfully.
     Current function value: 7.954412
     Iterations: 49
     Function evaluations: 58
     Gradient evaluations: 1654
     Hessian evaluations: 0
     Time taken for minimisation: 294.203114033
    

    对于我测试的所有NN(最多NN=14),L-BFGS-B 找到了正确的最小值,而且速度非常快。见https://plot.ly/~apal90/160/

     NN=9
     Method: L-BFGS-B
     Time taken for minimisation: 36.3749790192
    

问题:为什么L-BFGS-B 在这种情况下优于其他两种方法?特别是,为什么它比BFGS 优越得多,因为两者都应该是准牛顿方法(据我理解),以完全相同的方式工作。

我对这种情况的看法:所有三种方法都在每个点 x 处进行二次逼近。为此,它需要一个梯度和一个 Hessian。如果没有给出Hessian,则必须通过算法计算。在我们的例子中,只有梯度是明确给出的,这是由算法在每一步数值计算的。更具体地说,我们需要的是 Hessian 的倒数,这是一个非常昂贵的步骤,尤其是在更高维度上。现在,Newton-CG 显式地计算了这个逆 Hessian,因此需要更长的时间。像 BFGS 和 L-BFGS 这样的准牛顿方法基于梯度计算 Hessian 的近似值(即曲率),这在时间上更便宜,并且也被认为是对一个点的曲率的更好估计。因此,对于二次函数,Newton-CG 收敛得更快,而对于非二次函数,拟牛顿函数收敛得更好。 L-BFGS 是 BFGS 的低内存版本,每一步存储的内存远少于完整的 NxN 矩阵,因此它比 BFGS 快。

这个解释显示了 Newton-CG 和准牛顿方法之间的分歧。它没有解释的是算法无法找到真正的最小值,尤其是 BFGS 和 L-BFGS 之间的差异,它们都应该以相同的方式运行。

我对长收敛时间的一般预感是系统在最小值附近是非二次的(即平坦的),因此算法会随着收敛而振荡。除此之外,如果 BFGS 和 L-BFGS 真的以相同的方式工作,我相信 Scipy 算法的收敛容差水平之间肯定存在一些差异。否则,BFGS 和 L-BFGS 的工作方式并不相同,后者可能更准确地计算 Hessian。

参考--

http://www.scipy-lectures.org/advanced/mathematical_optimization/#newton-and-quasi-newton-methods

https://en.wikipedia.org/wiki/Newton%27s_method_in_optimization

https://en.wikipedia.org/wiki/Quasi-Newton_method

https://docs.scipy.org/doc/scipy-0.18.1/reference/optimize.minimize-bfgs.html#optimize-minimize-bfgs

https://docs.scipy.org/doc/scipy-0.18.1/reference/optimize.minimize-lbfgsb.html#optimize-minimize-lbfgsb

【问题讨论】:

您能展示一下您是如何调用算法的吗?从接口的角度来看,L-BFGS-B 与其他两种算法的主要区别在于它支持有界约束。你有指定吗?这些可能对收敛产生了积极影响。 @kazemakase 不,我根本没有为 L-BFGS-B 提供任何约束。我已更新帖子以显示我的代码行。所有算法都是一样的。 交叉发布:***.com/q/42424444/781723,cs.stackexchange.com/q/70780/755。请do not post the same question on multiple sites。每个社区都应该诚实地回答问题,而不会浪费任何人的时间。 energy() 是否足够小/足够独立,可以在 gist.github 上发布? (可扩展的测试用例对于测试其他优化器也很有用。) @denis 不,不幸的是,它还不够独立。涉及的输入变量太多。 【参考方案1】:

您的问题缺少两个重要信息:能量函数和初始猜测。能量函数可以是凸的/非凸的、平滑的/分段平滑的/不连续的。因此,很难在您的上下文中完全回答您的问题。不过,我可以解释一下 BFGS 和 L-BFGS-B 之间的一些关键区别。

这两种方法都是解决非线性优化问题的迭代方法。它们都通过在每次迭代中使用函数的 Hessian 逼近来逼近 Newton 方法。与 Newton 方法的主要区别在于,它们不是在特定点计算完整的 Hessian 矩阵,而是在先前的点上累积梯度并使用 BFGS 公式将它们放在一起作为 Hessian 矩阵的近似值。 Newton 和 BFGS 方法不能保证收敛,除非函数具有接近最优值的二次泰勒展开。

原始的 BFGS 方法会累积自给定初始猜测以来的所有梯度。这种方法有两个问题。首先,内存可以无限增加。其次,对于非线性问题,初始猜测处的 Hessian 矩阵通常不能代表解处的 Hessian 矩阵。因此,近似的 Hessian 将有偏差,直到在解决方案附近积累了足够的梯度。这可能会减慢收敛速度,但根据我的经验,对于具有单个局部最小值的能量函数,仍然应该使用良好的线搜索算法收敛。

L-BFGS 与 BFGS 相同,但内存有限,这意味着一段时间后,旧的梯度会被丢弃,以便为新计算的梯度留出更多空间。这样就解决了内存的问题,也避免了初始梯度的偏差。然而,根据保存在内存中的梯度数量,Hessian 可能永远不会被精确估计,并且可能是另一个偏差来源。这也可能会减慢收敛速度,但同样,对于具有单个局部最小值的能量函数,它仍应使用良好的线搜索算法收敛。

L-BFGS-B 与 L-BFGS 相同,但对输入变量有约束。 L-BFGS-B 将停止优化域边界上的变量。由于您没有指定任何约束,因此算法的这一方面不适用于您的问题。

我的假设是,您正在尝试使用与解决方案相去甚远的初始猜测来解决平滑但非凸的问题,并且您最终会处于局部最小值。既然你提到你从一个平面配置开始,我不会惊讶你从一个导致退化 Hessian 的奇异点开始,这可能会给其余的优化带来麻烦。在您的情况下,BFGS 和 L-BFGS 之间的唯一区别是每次迭代都会计算一个略有不同的梯度,并且 L-BFGS 方法最终将遵循一条导致全局最小值的路径。

【讨论】:

感谢您的长回答。最后,您是否认为 L-BFGS 的性能优于 BFGS 是因为纯粹的运气?还是因为它更快地忘记了初始平坦状态?另外,你有没有找到全局最小值的稳健算法? 没有能量函数本身很难确认,但我认为这纯粹是运气。 SciPy 有一种称为“Basin Shopping”的全局优化方法,它使用随机起点执行连续的局部优化(不保证收敛)。我从未亲自使用过它,所以我无法确认它的效率。但是,全局优化往往非常缓慢。我建议找到一个简单的算法,可以很好地近似解决方案,并使用局部最小化方法来改进它。您的解决方案似乎非常结构化,所以我认为应该不会太难。 知道了。但是你所说的“使用局部最小化方法来改进它”到底是什么意思? 我的意思是您使用的是您已经在使用的方法(例如 L-BFGS),但不是从平面配置开始,而是从近似解的结果开始。例如,您可以在第一次通过时像圆柱体一样包裹顶点,并将这些顶点位置提供给 L-BFGS。 在我的测试中,我遇到了相反的情况,即只有 newton-cg 收敛但 lbfgs 或 saga 等不收敛到全局最优,只需搜索为什么会这样,看看你的讨论,所以仍然存在混乱.

以上是关于SciPy 优化:Newton-CG vs BFGS vs L-BFGS的主要内容,如果未能解决你的问题,请参考以下文章

python 测试速度(cv2 vs scipy vs tensorflow)

Scipy randint vs numpy randint

将平面拟合到 3D 中的一组点:scipy.optimize.minimize vs scipy.linalg.lstsq

Scipy 中的优化

Scipy优化算法--scipy.optimize.fmin_tnc()/minimize()

markdown BFG - 从文件中清除repo git