ScipyOptimizeDriver中的SLSQP只执行一次迭代,需要很长时间,然后退出

Bee*_*per 1 optimization gradient-descent openmdao

我正在尝试使用 SLSQP 来优化机翼的迎角,以将驻点放置在所需的位置。这纯粹是作为一个测试用例,以检查我计算停滞位置的部分的方法是否有效。

当使用 COBYLA 运行时,优化在 47 次迭代后收敛到正确的 alpha (6.04144912)。当使用 SLSQP 运行时,它完成一次迭代,然后挂起长时间(10、20 分钟或更长时间,我没有准确计时),并以错误值退出。输出是:

Driver debug print for iter coord: rank0:ScipyOptimize_SLSQP|0
--------------------------------------------------------------
Design Vars
{'alpha': array([0.5])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'obj_cmp.obj': array([0.00023868])}
Driver debug print for iter coord: rank0:ScipyOptimize_SLSQP|1
--------------------------------------------------------------
Design Vars
{'alpha': array([0.5])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'obj_cmp.obj': array([0.00023868])}
Optimization terminated successfully.    (Exit mode 0)
            Current function value: 0.0002386835700364719
            Iterations: 1
            Function evaluations: 1
            Gradient evaluations: 1
Optimization Complete
-----------------------------------
Finished optimisation
Run Code Online (Sandbox Code Playgroud)

为什么 SLSQP 会出现这样的不当行为?据我所知,当我查看 check_partials() 时,没有不正确的分析导数。

代码比较长,所以我把它放在Pastebin这里:

Jus*_*ray 7

您问了两个问题,两个问题的答案最终彼此无关:

  1. 为什么使用 SLSQP 时模型很慢,但使用 COBYLA 时模型却很快
  2. 为什么 SLSQP 在一次迭代后停止?

1)为什么SLSQP这么慢?
COBYLA 是一种无梯度方法。SLSQP 使用梯度。因此,可靠的赌注是,当 SLSQP 要求提供衍生品时,速度就会变慢(COBYLA 从未这样做过)。那就是我首先去看的地方。计算导数分两步进行:a) 计算每个分量的偏项;b) 使用这些偏项求解线性系统以计算总计。减速必须是这两个步骤之一。

由于您可以check_partials轻松运行,因此步骤 (a) 不太可能是罪魁祸首。因此,这意味着步骤 (b) 可能是我们需要加快速度的地方。

我在您的模型上运行了摘要实用程序 ( openmdao summary core.py) 并看到了以下内容:

============== Problem Summary ============
Groups:               9
Components:          36
Max tree depth:       4
Design variables:            1   Total size:        1
Nonlinear Constraints:       0   Total size:        0
    equality:                0                      0
    inequality:              0                      0
Linear Constraints:          0   Total size:        0
    equality:                0                      0
    inequality:              0                      0
Objectives:                  1   Total size:        1
Input variables:            87   Total size:  1661820
Output variables:           44   Total size:  1169614
Total connections: 87   Total transfer data size: 1661820
Run Code Online (Sandbox Code Playgroud)

然后我生成了你的模型的 N2 并看到了这个: 在此输入图像描述

因此,我们有一个长度为 1169614 个元素的输出向量,这意味着您的线性系统是一个大约 1e6x1e6 的矩阵。这是相当大的,并且您正在使用 DirectSolver 来尝试计算/存储它的因式分解。这就是减速的根源。使用 DirectSolvers 非常适合较小的模型(根据经验,输出向量应少于 10000 个元素)。对于较大的,您需要更加小心并使用更先进的线性求解器。

在您的情况下,我们可以从 N2 看到模型中的任何位置都没有耦合(N2 的下三角形中没有任何耦合)。像这样的纯前馈模型可以使用更简单、更快的 LinearRunOnce 求解器(如果您没有设置其他任何东西,这是默认值)。所以我关闭了模型中的所有 DirectSolvers,导数变得有效即时。让你的 N2 看起来像这样: 在此输入图像描述

最佳线性求解器的选择很大程度上依赖于模型。要考虑的一个因素是计算成本,另一个因素是数值鲁棒性。OpenMDAO 论文的第 5.3 节详细介绍了这个问题,我不会在这里介绍所有内容。但这里非常简要地总结了关键考虑因素。

刚开始使用 OpenMDAO 时,使用 DirectSolver 是最简单且通常也是最快的选择。它很简单,因为它不需要考虑模型结构;它很快,因为对于小型模型,OpenMDAO 可以将雅可比矩阵组装成稠密或稀疏矩阵,并提供直接因式分解。然而,对于较大的模型(或具有非常大的输出向量的模型),计算因式分解的成本非常高。在这种情况下,您需要更有意地分解求解器结构,并使用其他线性求解器(有时与直接求解器结合使用——请参阅 OpenMDAO 论文的第 5.3 节以及此 OpenMDAO 文档)。

您表示您希望使用 DirectSolver 来利用稀疏雅可比存储。这是一个很好的直觉,但 OpenMDAO 的构建方式无论如何都不是问题。我们现在已经陷入困境了,但既然你问了,我会给出一个简短的总结解释。从 OpenMDAO 3.7 开始,只有 DirectSolver 才需要组装的雅可比行列式(事实上,线性求解器本身决定了它所连接的任何系统的这一点)。所有其他 LinearSolvers 都使用 DictionaryJacobian(它存储键控到 [of-var, wrt-var] 对的每个 sub-jac)。每个 sub-jac 都可以存储为密集或稀疏(取决于您如何声明特定的偏导数)。字典雅可比行列式实际上是稀疏矩阵的一种形式,尽管不是传统的矩阵。这里的关键要点是,如果您使用 LinearRunOnce(或任何其他求解器),那么无论如何您都会获得内存高效的数据存储。只有 DirectSolver 会转换为实际矩阵对象的更传统的组装。

关于内存分配的问题。我从 openmdao 文档借用了这张图片

2)为什么SLSQP在一次迭代后停止?

基于梯度的优化对缩放非常敏感。我在允许的设计空间内绘制了您的目标函数并得到了以下结果: 在此输入图像描述 所以我们可以看到最小值约为 6 度,但目标值很小(约 1e-4)。作为一般经验法则,将目标控制在数量级 1 左右是一个好主意(我们有一个扩展报告功能可以帮助实现这一点)。我添加了一个关于您的目标数量级的参考:

p.model.add_objective('obj', ref=1e-4)
Run Code Online (Sandbox Code Playgroud)

然后我得到了一个很好的结果:

Optimization terminated successfully    (Exit mode 0)
            Current function value: [3.02197589e-11]
            Iterations: 7
            Function evaluations: 9
            Gradient evaluations: 7
Optimization Complete
-----------------------------------
Finished optimization
alpha =  [6.04143334]
time:  2.1188600063323975 seconds
Run Code Online (Sandbox Code Playgroud)

不幸的是,基于梯度的优化很难进行扩展。从将目标/约束缩放到 1 阶开始是一个不错的经验法则,但通常情况下,您需要对更复杂的问题进行调整。

  • Justin,这是一个很好的答案,可以按原样放入文档页面中,以获取有关如何在 OpenMDAO 中调试优化的真实示例。该纲要可能还不存在,但这绝对应该是其中的一部分! (2认同)