微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

更快的 SciPy 优化

如何解决更快的 SciPy 优化

我需要找到具有数千个变量的成本函数的最小值。成本函数只是一个最小二乘计算,可以使用 numpy 向量化轻松快速地计算。尽管如此,优化仍然需要很长时间。我的猜测是运行缓慢发生在 SciPy 的最小化器中,而不是我的成本函数中。如何更改 SciPy 最小化器的参数以加快运行时速度?

示例代码

import numpy as np
from scipy.optimize import minimize

# random data
x = np.random.randn(100,75)

# initial weights guess
startingWeights = np.ones(shape=(100,75))

# random y vector 
y = np.random.randn(100)


def costFunction(weights):
   # reshapes flattened weights into 2d matrix
   weights = np.reshape(weights,newshape=(100,75))

   # weighted row-wise sum
   weighted = np.sum(x * weights,axis=1)

   # squared residuals
   residualsSquared = (y - weighted) ** 2

   return np.sum(residualsSquared)


result = minimize(costFunction,startingWeights.flatten())

解决方法

正如评论中已经指出的那样,强烈建议为具有 N = 100*75 = 7500 变量的大问题提供准确的客观梯度。如果没有提供梯度,它将通过有限差分和 approx_derivative 函数来近似。然而,有限差分容易出错且计算成本高,因为每次梯度评估都需要2*N对目标函数的评估(无缓存)。

这可以通过对目标和近似梯度进行计时来轻松说明:

In [7]: %timeit costFunction(startingWeights.flatten())
23.5 µs ± 2.03 µs per loop (mean ± std. dev. of 7 runs,10000 loops each)

In [8]: from scipy.optimize._numdiff import approx_derivative

In [9]: %timeit approx_derivative(costFunction,startingWeights.flatten())
633 ms ± 33.3 ms per loop (mean ± std. dev. of 7 runs,1 loop each)

因此,在我的机器上,每次梯度评估都需要超过半秒!评估梯度的更有效方法是算法微分。使用 JAX 库非常简单:

import jax.numpy as jnp
from jax import jit,value_and_grad

def costFunction(weights):
   # reshapes flattened weights into 2d matrix
   weights = jnp.reshape(weights,newshape=(100,75))

   # weighted row-wise sum
   weighted = jnp.sum(x * weights,axis=1)

   # squared residuals
   residualsSquared = (y - weighted) ** 2

   return jnp.sum(residualsSquared)

# create the derivatives
obj_and_grad = jit(value_and_grad(costFunction))

此处,value_and_grad 创建了一个评估目标的函数 和梯度并返回两者,即obj_value,grad_values = obj_and_grad(x0)。因此,让我们为这个函数计时:

In [12]: %timeit obj_and_grad(startingWeights.flatten())
132 µs ± 6.62 µs per loop (mean ± std. dev. of 7 runs,10000 loops each)

因此,我们现在评估目标和梯度的速度比以前快了近 5000 倍。最后,我们可以通过设置 minimize 告诉 jac=True 我们的目标函数返回目标和梯度。所以

minimize(obj_and_grad,x0=startingWeights.flatten(),jac=True)

应该会显着加快优化速度。

PS:您还可以尝试通过 cyipopt 包连接的最先进的 Ipopt 求解器。它还提供了一个类似于 scipy.optimize.minimize 的类似 scipy 的界面。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。