Curve_fit to apply_along_axis。如何加快速度?

时间:2016-07-04 00:16:37

标签: python performance python-2.7 numpy scipy

我有一些大数据集,我希望能够适应单指数时间衰减。

数据由在不同时间获取的多个4D数据集组成,因此拟合应沿第5维(通过数据集)运行。

我目前使用的代码如下:

import numpy as np
import scipy.optimize as opt

[... load 4D datasets ....]
data = (dataset1, dataset2, dataset3)
times = (10, 20, 30)

def monoexponential(t, M0, t_const):
    return M0*np.exp(-t/t_const)

# Starting guesses to initiate  descent.
M0_init = 80.0
t_const_init = 50.0
init_guess = (M0_init, t_const_init)

def fit(vector):
    try:
        nlfit, nlpcov = opt.curve_fit(monoexponential, times, vector,
                                      p0=init_guess,
                                      sigma=None,
                                      check_finite=False,
                                      maxfev=100, ftol=0.5, xtol=1,
                                      bounds=([0, 2000], [0, 800]))
        M0, t_const = nlfit
    except:
        t_const = 0

    return t_const

# Concatenate datasets in data into a single 5D array.
concat5D = np.concatenate([block[..., np.newaxis] for block in data],
                     axis=len(data[0].shape))

# And apply the curve fitting along the last dimension.
decay_map = np.apply_along_axis(fit, len(concat5D.shape) - 1, concat5D)

代码工作正常,但需要永久(例如,dataset1.shape == (100,100,50,500))。我已经阅读了一些其他主题,提到apply_along_axis非常慢,所以我猜这是罪魁祸首。不幸的是,我真的不知道在这里可以使用什么(除了可能是一个明确的for循环?)。

有没有人知道我可以做些什么来避免apply_along_axis并加速多次调用curve_fit?

2 个答案:

答案 0 :(得分:1)

所以你将fit操作100 * 100 * 50 * 500次应用到1d数组(示例中为3个值,现实生活中更多?)?

apply_along_axis会迭代输入数组的所有维度,除了一个。没有在多个轴上同时编译或执行此fit

没有apply_along_axis,最简单的方法是将数组重新整形为2d,将(100,100,50,500)压缩到一个(250 ...,)维度,然后对其进行迭代。然后重塑结果。

我认为在最后一个轴上连接datasets可能比在第一个轴上连接慢,但是时间建议不然。

np.stackconcatenate的新版本,可以轻松地在任何位置添加新轴。

In [319]: x=np.ones((2,3,4,5),int)
In [320]: d=[x,x,x,x,x,x]

In [321]: np.stack(d,axis=0).shape   # same as np.array(d)
Out[321]: (6, 2, 3, 4, 5)

In [322]: np.stack(d,axis=-1).shape
Out[322]: (2, 3, 4, 5, 6)

获取更大的列表(使用简单的sum函数):

In [295]: d1=[x]*1000       # make a big list

In [296]: timeit np.apply_along_axis(sum,-1,np.stack(d1,-1)).shape
10 loops, best of 3: 39.7 ms per loop

In [297]: timeit np.apply_along_axis(sum,0,np.stack(d1,0)).shape
10 loops, best of 3: 39.2 ms per loop

显式循环使用数组重塑时间大致相同

In [312]: %%timeit 
   .....: d2=np.stack(d1,-1)
   .....: d2=d2.reshape(-1,1000)
   .....: res=np.stack([sum(i) for i in d2],0).reshape(d1[0].shape)
   .....: 
10 loops, best of 3: 39.1 ms per loop

但像sum之类的函数可以在整个数组上运行,并且可以更快地完成

In [315]: timeit np.stack(d1,-1).sum(-1).shape
100 loops, best of 3: 3.52 ms per loop

因此,改变堆叠和迭代方法并没有对速度产生太大影响。但改变“适应性”#39;所以它可以在多个维度上工作可以是一个很大的帮助。我不太了解optimize.fit以了解是否可能。

====================

我只是挖掘了apply_along_axis的代码。它基本上构造一个看起来像ind=(0,1,slice(None),2,1)的索引,然后做func(arr[ind]),然后递增它,就像使用carry的长算术一样。因此,它只是系统地逐步执行所有元素,同时保持一个轴为:切片。

答案 1 :(得分:0)

在这种特殊情况下,如果您需要使用单个指数,那么您最好能够记录数据。然后拟合变为线性, 比非线性最小二乘法快得多,并且很可能被矢量化,因为它几乎成为线性代数问题。

(当然,如果您对如何改进least_squares有所了解,那可能会被scipy开发者所欣赏。)