如何加速 numpy 数组上的函数
How to speed up functions on numpy arrays
我正在编写代码以在 (5, 5) 形 numpy 数组中拟合二维函数。拟合是通过 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)
Objective 函数及其梯度,也需要一些优化:
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)
~50% 更好。
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% 更好。
这些只是微优化。一般来说,如果你的输入有这个大小,我认为 numba and/or cython 帮不上什么忙。也许最好检查可用的 optimization method 中哪个更适合您的特殊情况,并尝试以巧妙的方式对其进行初始化。
我正在编写代码以在 (5, 5) 形 numpy 数组中拟合二维函数。拟合是通过 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)
Objective 函数及其梯度,也需要一些优化:
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)
~50% 更好。
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% 更好。
这些只是微优化。一般来说,如果你的输入有这个大小,我认为 numba and/or cython 帮不上什么忙。也许最好检查可用的 optimization method 中哪个更适合您的特殊情况,并尝试以巧妙的方式对其进行初始化。