在矩阵上使用scipy.fsolve

时间:2019-01-20 15:13:15

标签: python python-2.7 scipy numeric numerical-methods

在得到here的帮助之后,我一直试图在脚本中实现它,但是我无法聪明地运行它。

我需要对4072x3080图像的每个像素使用此算法,整个过程大约需要1h30,因此我尝试以某种方式强制执行此操作,但出现此错误:

startgame

这是我一直在尝试的代码:

ValueError                                Traceback (most recent call last)
<ipython-input-12-99c1f41dbba7> in <module>()
----> 1 res = scipy.optimize.fsolve(func, x0=np.ones((K.shape[0], K.shape[1])), args=(f[:,None], g[:,None], K))

/*/python2.7/site-packages/scipy/optimize/minpack.pyc in fsolve(func, x0, args, fprime, full_output, col_deriv, xtol, maxfev, band, epsfcn, factor, diag)
    146                'diag': diag}
    147 
--> 148     res = _root_hybr(func, x0, args, jac=fprime, **options)
    149     if full_output:
    150         x = res['x']

/*/python2.7/site-packages/scipy/optimize/minpack.pyc in _root_hybr(func, x0, args, jac, col_deriv, xtol, maxfev, band, eps, factor, diag, **unknown_options)
    212     if not isinstance(args, tuple):
    213         args = (args,)
--> 214     shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n,))
    215     if epsfcn is None:
    216         epsfcn = finfo(dtype).eps

/*/python2.7/site-packages/scipy/optimize/minpack.pyc in _check_func(checker, argname, thefunc, x0, args, numinputs, output_shape)
     25 def _check_func(checker, argname, thefunc, x0, args, numinputs,
     26                 output_shape=None):
---> 27     res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
     28     if (output_shape is not None) and (shape(res) != output_shape):
     29         if (output_shape[0] != 1):

<ipython-input-7-911c817cb57d> in func(x, f, g, K)
      1 def func(x, f, g, K):
----> 2     return np.sum(f * np.exp(-g*x), axis=0) - K
      3 
      4 
      5 def derivative(x, f, g, K):

ValueError: operands could not be broadcast together with shapes (13551616,) (4072,3328) 

+

def func(x, f, g, K):
    return np.sum(f * np.exp(-g*x), axis=0) - K


def derivative(x, f, g, K):
    return np.sum(-g*f * np.exp(-g*x), axis=0)

res = scipy.optimize.fsolve(func, x0=np.ones((K.shape[0], K.shape[1])), args=(f[:,None], g[:,None], K)) f都是g数组,其中(47,)K图像

否则,缓慢的过程会以这种方式下降:(但无论如何,此过程会失败,并衍生出来。

(4072, 3328)

如果我尝试将慢速方法与导数一起使用

,这将是错误
res = np.ones((mbn.shape[0],mbn.shape[1]))
for i_x in range(0,mbn.shape[0]):
    if i_x%10 == 0:
        print i_x/4070 
    for i_y in range(0,mbn.shape[1]):
        res[i_x,i_y] = scipy.optimize.fsolve(func, x0=1, args=(f[:], g[:], K[i_x,i_y]) )

3 个答案:

答案 0 :(得分:0)

optimize.fsolve中的func可以接受一维向量,但不能接受二维数组。 因此,即使Kx是二维的,对于此计算,我们也应该将它们重塑为一维数组。

Kshape = K.shape
K = K.ravel()

然后在调用optimize.fsolve之后,您可以将结果重塑为再次2D:

res = optimize.fsolve(func, x0=np.ones(K.shape).ravel(), args=(f, g, K))
res = res.reshape(Kshape)

然后您可以通过以下方式编写计算来避免出现两次循环:

import numpy as np
import scipy.optimize as optimize

np.random.seed(123)

def func(x, f, g, K):
    return np.sum(f * np.exp(-g*x[:, None]), axis=-1) - K


def derivative(x, f, g, K):
    return np.sum(-g*f * np.exp(-g*x[:, None]), axis=-1)


f = np.random.uniform(size=(47,))
g = np.random.uniform(size=f.shape)
K = np.random.uniform(size=(4072,3080))
Kshape = K.shape
K = K.ravel()

res = optimize.fsolve(func, x0=np.ones(K.shape).ravel(), args=(f, g, K))
res = res.reshape(Kshape)
print(res)

请注意,g*x[:, None]使用broadcasting来生成形状为(4072*3080, 47)的二维数组。 2D数组f * np.exp(-g*x[:, None]), 然后将形状也为(4072*3080, 47)的元素(axis=-1)加到最后一个轴上。 这留下了形状为(4072*3080,)的一维数组。 fsolve求解x并返回形状为(4072*3080,)的一维数组。

res = res.reshape(Kshape)将解决方案重塑为形状(4072, 3080)

答案 1 :(得分:0)

n标量寻根问题的解决方案转换为向量化n维维寻根问题通常是一个坏主意。标量求解器做出的奇异决策通常在所有输入变量上都不统一,并且系统的任何通用求解器都会尝试计算或近似雅可比行列式。虽然我们知道此设置与它成对角线,但将其传达给求解器可能会很复杂。此外,如前所述,对于矢量问题,统一确定步长,行搜索等决策几乎可以保证对于每个单个标量问题都是次优的。

最好的策略是减少要解决的标量问题的数量,或者至少使用一维性质来获得反函数的良好近似值,然后只需很少的迭代即可完善求解。

K中少量整数值的情况

您想在紧密相关的输入上多次调用一个复杂的函数(迭代求逆),甚至可能只使用有限数量的值。

进行更快计算的第一个想法是首先确定K的值范围,为每个实际存在的值计算x的值,然后将这些值分布回图像数组。例如,如果K仅采用从0255的整数值,则产生一个数组x_vals,其中func(x_vals[k],f,g,0)=k允许将结果数组作为{{ 1}} la

x_vals[K]

x_vals = np.array([0.1,0.2,0.3,0.4])
K = np.array([[1,2],[0,2],[3,3]]);
x=x_vals[K]; print x

K中非整数值范围相对较小的情况

如果array([[ 0.2, 0.3], [ 0.1, 0.3], [ 0.4, 0.4]]) 在相对较小的范围内(相对于K而言包含较大数量的(非整数)值,则为某些问题计算解仍可能会带来很大的改进。对f进行采样,以便func(x_samples [k],f,g,0)= K_samples [k])`

然后使用插值获得解决方案或至少非常好的近似值进行进一步的迭代优化

K_samples = np.linspace(np.amin(K), np.amax(K), 256)

使用func的正向评估

如果向量x = np.interp(K, K_samples, x_samples) f均为正值,则指数和为单调下降函数,其反函数可通过简单的查找表和它。对于指数和函数的值,可以得到

g

以便人们可以将sum(f)*exp(-max(g)*x) <= K <= sum(f)*exp(-min(g)*x) 的范围计算为

x

使用这些上限和下限生成数组- log(max(K)/sum(f)) / max(g) <= x <= - log(min(K)/sum(f)) / min(g) ,可以通过对x_samples进行前向求值来获得相应的K_samples。线性插值得到逆函数的近似值,如上所述。

答案 2 :(得分:0)

您可以通过创建一个接受N个输入和N个输出的函数来向量化问题,其中N是像素数。这涉及平整输入图像并将其视为一维数组。在此设置中,输入与输出无关,因此,雅可比是对角线的。由于fsolve计算出了jacobian的完全近似值,因此最终会耗尽内存(MemoryError)。相反,您可以将scipy.optimize.rootmethod='diagbroyden'一起使用,它仅通过跟踪对角雅可比而使用近似值:

import numpy as np
import scipy.optimize as optimize

def func(x, f, g, K):
    return np.sum(f * np.exp(-g*x[:, None]), axis=1) - K

np.random.seed(123)

f = np.random.uniform(size=(47,))
g = np.random.uniform(size=f.shape)
img = np.random.uniform(size=(4072, 3328)).ravel()
K = func(img, f, g, 0)

res = optimize.root(func, method='diagbroyden', x0=0.5*np.ones(img.size), args=(f, g, K))
print('Success:', res.success)
print('Message:', res.message)
assert np.allclose(img, res.x)

但是,使用这种方法,您无法利用可以为您的特定函数计算的解析导数。