使用 optimize.fmin_l_bfgs_b 的错误收敛

Wrong convergence using optimize.fmin_l_bfgs_b

我正在使用 optimize.fmin_l_bfgs_b 优化一个用 Fortran 编写的函数。代码类似于:

def f(m, *args):
    # Optmization values
    Opt1 = m[0]
    Opt2 = m[1]
    # Rest of arguments:
    Var1 = args[0]
    Var2 = args[1]
    # Fortran Function
    r1, r2 = FortranFunction(Opt1, Opt2)
    # Evaluation of the result
    evaluation = sqrt((r1-Var1)**2 + (r2-Var2)**2)
    return evaluation

initial_values = numpy.array([1.09, 0.0025])
mybounds = [(1, 1.2), (0, 0.1)]
m = optimize.fmin_l_bfgs_b(f, x0 = initial_values, args=(x, file_vars), approx_grad = True, bounds = mybounds)

fortran 函数 returns 2 个值,用于使用两个所需结果(Var1 和 Var2)评估函数。我遇到的问题是算法没有优化第一个变量。如果我在每次迭代中打印它,这就是我得到的:

1.09
1.09
1.09000001
1.09
1.09
1.09
1.09000001
1.09
1.09
1.09
1.09000001
1.09
1.09
1.09
1.09000001
...

好像只是用1.09和1.09000001的差值来评估函数,这个值太低了,无法在Fortran函数中得到不同的值(这可能是它没有优化的原因)。第二个变量不存在这个问题。这是正常行为吗?是否有任何选项可以使算法使用更高的步骤?

根据 Fortran 函数的具体作用,有多种可能的修复方法。我从您的呼叫签名中看到您正在让最小化器以数字方式估计梯度。可以直接计算梯度吗?如果是这样,请尝试编写一个执行此操作的函数,将其作为 fprime 传递,并将 approx_grad 设置为 False。适当的梯度函数很有可能会产生更好的结果。

另一种可能性是尝试 epsilon 的不同值,它控制数值梯度近似的步长。但在我看来 fmin_l_bfgs_b 只接受 epsilon 的单个浮点值,这意味着你不能对不同的维度采用不同的步长。那可能不是问题;如果较大的 epsilon 可以为第一维提供更好的梯度估计,并且不会影响第二维的估计,那么您的问题可能会得到解决。您也可以尝试传递一个数组,每个维度都有一个 epsilon 值——它 可能 有效。最后,您可能可以做一些有点笨拙的事情,比如优化一个维度,然后调整 epsilon,然后优化另一个维度。如果我有时间,我稍后会做一些实验。

最后一种方法可能是使用不同的最小化函数。例如,您是否尝试过 fmin_cg?我发现它可以很好地处理我提出的大部分问题。但我以前从未在没有硬编码渐变函数的情况下使用过它。您也可以在 this list 上尝试其他函数——尤其是像 PowellAnneal 这样的函数,它们被设计为在不使用任何梯度信息的情况下工作。

这些方法中哪一种有效(如果有的话)在很大程度上取决于您要最小化的函数的具体性质。您可能需要做一些实验!