Scipy 最小化与调节

问题描述 投票:0回答:1

我有一个包含 3 个变量的函数,我想优化它。不幸的是,这些变量具有不同的数量级,这意味着该问题是非常病态的。我通过将优化变量乘以条件矩阵来处理这个问题。 (在下面的示例中,我的条件矩阵是对角的,因此我将其实现为向量。非对角条件矩阵可能有应用。)

import numpy as np
from scipy.optimize import minimize

def penalty(x):
    # do stuff
    return # result

conditioner = np.array([1000, 1, 0.1])
x0 = np.array([0.003, 1.415, 9.265]) * conditioner 
optim_result = minimize(penalty, x0)
bestfit = optim_result.x / conditioner 

有没有一种方法可以做到这一点,不需要手动应用

conditioner
或要求我跟踪哪些数据经过调节(例如,
bestfit
)和哪些数据没有经过调节(例如,
optim_result
)?

我想我可以将一些算术放在惩罚函数中,如下所示:

def penalty(x):
    x_cond = x * conditioner
    # do stuff
    return # result

但这只解决了一半问题。

python optimization scipy
1个回答
0
投票

代码

一个简单的方法是为

minimize()
编写一个包装器,它处理转换到这个新坐标系和从这个新坐标系转换出来的任务。

这个包装器至少需要做三件事:

  • 将初始x0点更改到坐标系中。
  • 每次调用函数时,更改坐标系之外的 x 值。
  • 优化完成后将 x 值更改出坐标系。

可以这样做:

import scipy


def minimize_with_parameter_scaling(f, x0, typical_x_value=None, *args, **kwargs):
    def wrapped_f(x, *args, **kwargs):
        return f(x * typical_x_value, *args, **kwargs)
    x0 = np.asarray(x0)
    if typical_x_value is None:
        typical_x_value = np.ones_like(x0)
    typical_x_value = np.asarray(typical_x_value)
    assert (typical_x_value != 0).all(), "parameter scale cannot be zero"
    x0_scaled = x0 / typical_x_value
    result = scipy.optimize.minimize(wrapped_f, x0_scaled, *args, **kwargs)
    result.x = result.x * typical_x_value
    return result

警告:在您的示例中,

typical_x_value
具有与
conditioner
相反的含义,而是代表典型x值的比例。

我们还可以在一个示例问题上尝试一下,看看调节会有所帮助的假设是否正确。

示例问题

我选择的示例问题是 rosenbrock 函数,这是一个旨在测试非线性优化器的函数。它是一个具有全局最小值的非线性函数,其导数会误导优化器,这就是为什么它是一个难题。

为了解决比例尺不合适的问题,我使其中一个坐标的比例尺与其他坐标不同。

def wacky_rosenbrock(x):
    # Code copied from scipy.optimize.rosen
    x = np.asarray(x)
    x[1] *= 100  # Second coordinate is multiplied by 100
    r = np.sum(100.0 * (x[1:] - x[:-1]**2.0)**2.0 + (1 - x[:-1])**2.0,
                  axis=0)
    return r

结果

然后我尝试在三个维度上解决

wacky_rosenbrock()
。我尝试在不缩放的情况下解决它,并使用
[1, 0.01, 1]
的缩放。 nfev 是获得解决方案所需的函数调用次数。该误差是优化器找到的解决方案与实际最佳解决方案的百分比误差。最后一列是在没有缩放的情况下需要多少次函数调用。我还尝试了各种 SciPy 求解器。

方法 错误(无缩放) nfev(无缩放) 错误(缩放) nfev(缩放) 函数调用计数的改进
内尔德米德 0.001% 305 0.003% 289 1.05536
鲍威尔 0.0% 862 0.0% 886 0.972912
CG 0.239% 1772 0.0% 248 7.14516
bfgs 0.035% 340 0.001% 148 2.2973
l-bfgs-b 0.023% 268 0.0% 140 1.91429
tnc 37.086% 404 0.015% 400 1.01
科比拉 98.729% 39 11.214% 4087 0.00954245
科比卡 0.008% 1406 0.0% 157 8.95541
slsqp 0.042% 128 0.049% 112 1.14286
信任建设 0.038% 264 0.001% 232 1.13793

讨论

在这些结果中,我想指出以下几点:

  • 对于许多求解器,例如 BFGS、L-BFGS-B 和 COBYQA,重新缩放可以更快地解决问题。
  • 对于某些求解器来说,这没有什么区别。特别是,无导数求解器 Nelder-Mead 和 Powell 并不关心。
  • 求解器的选择比缩放的使用更重要。例如,您可以通过使用缩放将 L-BFGS-B 调用从 280 次优化到 140 次。但您可以改用 SLSQP,这比我在本答案中描述的扩展工作量更少,并且仅使用 128 次调用。
© www.soinside.com 2019 - 2024. All rights reserved.