Python 如何加速numpy阵列上的函数

Python 如何加速numpy阵列上的函数,python,arrays,numpy,cython,numba,Python,Arrays,Numpy,Cython,Numba,我正在编写一个代码,以便在(5,5)形状的numpy数组中安装2D函数。拟合是通过使用scipy.optimize.minimize进行最大化来完成的,因此许多函数在迭代过程中会被调用大量次 以下是第一个cProfile结果: Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1392579 60.849 0.000 60.849 0.

我正在编写一个代码,以便在(5,5)形状的numpy数组中安装2D函数。拟合是通过使用
scipy.optimize.minimize
进行最大化来完成的,因此许多函数在迭代过程中会被调用大量次

以下是第一个cProfile结果:

Ordered by: internal time

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1392579   60.849    0.000   60.849    0.000 maxima.py:288(derfs)
1392578   34.243    0.000   34.243    0.000 maxima.py:279(dexp)
696289   31.186    0.000  152.278    0.000 maxima.py:322(ll_jac)
1392578   26.670    0.000   26.670    0.000 maxima.py:283(derf)
3639673   23.772    0.000   23.772    0.000 {method 'reduce' of 'numpy.ufunc' objects}
696290   10.440    0.000   53.965    0.000 maxima.py:308(logll)
1392579    8.882    0.000   69.731    0.000 maxima.py:295(lambda_g)
13202    5.216    0.000  215.145    0.016 lbfgsb.py:198(_minimize_lbfgsb)
3494647    4.468    0.000   32.576    0.000 fromnumeric.py:1621(sum)
这些就是功能

import numpy as np
from scipy.special import erf

def dexp(x0, sigma, x=np.arange(5)):
    xx1 = (x + 1 - x0)/sigma
    xx = (x - x0)/sigma
    return np.exp(-xx1*xx1) - np.exp(-xx*xx)

def derf(x0, sigma, x=np.arange(5)):
    return erf((x + 1 - x0) / sigma) - erf((x - x0) / sigma)

def derfs(x0, y0, sigma, xy=np.arange(5)):
    i = erf((xy + 1 - x0) / sigma) - erf((xy - x0) / sigma)
    j = erf((xy + 1 - y0) / sigma) - erf((xy - y0) / sigma)
    return i[:, np.newaxis] * j

def lambda_g(x0, y0, fwhm, factor=0.09*np.pi, f2=0.6):
    return factor * fwhm**2 * derfs(x0, y0, fwhm * f2)
目标函数及其梯度,也需要一些优化:

def logll(parameters, *args):
    """ Log-likelihood function
    """
    A, x0, y0, bkg = parameters
    fwhm, area = args

    lambda_p = A * lambda_g(x0, y0, fwhm) + bkg
    return -np.sum(area * np.log(lambda_p) - lambda_p)

def ll_jac(parameters, *args):
    """ This is the Jacobian of the log-likelihood function
    """
    A, x0, y0, bkg = parameters
    fwhm, area = args

    derfx = derf(x0, fwhm*0.6)
    derfy = derf(y0, fwhm*0.6)
    lambda1 = lambda_g(x0, y0, fwhm)
    factor = 1 - area/(A * lambda1 + bkg)

    jac = np.zeros(4)
    # dL/d(A)
    # The derivative of lambda_g is lambda_g(A=1)
    jac[0] = np.sum(factor*lambda1)
    # dL/d(x0) and dL/d(y0)
    jac12 = -0.3*A*fwhm*np.sqrt(np.pi)
    jac[1] = jac12*np.sum(dexp(x0, fwhm * 0.6)[:, np.newaxis] * derfy*factor)
    jac[2] = jac12*np.sum(dexp(y0, fwhm * 0.6)[:, np.newaxis] * derfx*factor)
    # dL/d(bkg)
    jac[3] = np.sum(factor)

    return jac
在我的电脑上

%timeit dexp(2.3, 2.)
10000 loops, best of 3: 16 µs per loop

%timeit derf(2.3, 2.)
100000 loops, best of 3: 14 µs per loop

%timeit derfs(2.3, 2.2, 2.)
10000 loops, best of 3: 32.2 µs per loop

有没有可能加快速度?你建议我做什么?cython?numba?在进入这些模块之前,我还有什么可以尝试的吗

您可以尝试执行较少的操作。例如,您可以执行以下操作:

def dexp2(x0, sigma, x=np.arange(5)):
    a = (x - x0) / sigma
    return np.exp(-(a + 1/sigma)**2) - np.exp(-(a*a))
约35%-40%的改善

def derf2(x0, sigma, x=np.arange(5)):
    a = (x - x0) / sigma
    return erf(a + 1 / sigma) - erf(a)
def derfs2(x0, y0, sigma, xy=np.arange(5)):
    a = (xy - x0) / sigma
    b = (xy - y0) / sigma
    i = erf(a + 1 / sigma) - erf(a)
    j = erf(b + 1 / sigma) - erf(b)
    return i[:, np.newaxis] * j
大约好50%

def derf2(x0, sigma, x=np.arange(5)):
    a = (x - x0) / sigma
    return erf(a + 1 / sigma) - erf(a)
def derfs2(x0, y0, sigma, xy=np.arange(5)):
    a = (xy - x0) / sigma
    b = (xy - y0) / sigma
    i = erf(a + 1 / sigma) - erf(a)
    j = erf(b + 1 / sigma) - erf(b)
    return i[:, np.newaxis] * j
大约好50%

def derf2(x0, sigma, x=np.arange(5)):
    a = (x - x0) / sigma
    return erf(a + 1 / sigma) - erf(a)
def derfs2(x0, y0, sigma, xy=np.arange(5)):
    a = (xy - x0) / sigma
    b = (xy - y0) / sigma
    i = erf(a + 1 / sigma) - erf(a)
    j = erf(b + 1 / sigma) - erf(b)
    return i[:, np.newaxis] * j

这些只是微观优化。一般来说,如果你的输入有这样的大小,我认为numba和/或cython帮不了你多少忙。也许最好检查一下哪些可用的更适合您的特殊情况,并尝试以一种巧妙的方式初始化它。

对于初学者,我会将**2更改为x*x(请参见)您也可以发布您的目标函数吗?另外,我也不担心用x*x替换正方形,因为它是由numpy处理的,我猜它是非常优化的。谢谢你们,我正在更新这个问题,考虑到你们的建议。我认为cython或numba不会有帮助,因为你们没有在这些函数中进行迭代。他们正在调用已经编译好的
np.exp
erf
。我同意,但是
np.exp
s和
erf
s的总和没有编译。。。正确的?这会有什么不同吗?根据你的建议,将优化方法改为
Newton CG
I将计算时间减少了10倍!伟大的不久前,我也遇到了类似的优化问题。