Python 赛昂没有速度增益

Python 赛昂没有速度增益,python,optimization,numpy,cython,Python,Optimization,Numpy,Cython,我试图定义一个函数,它包含一个模拟积分的内循环 问题是速度。在我的机器上评估功能一次最多需要30秒。因为我的最终目标是最小化这个函数,所以一些额外的速度会很好 因此,我试图让Cython工作,但我一定是犯了一个严重的错误(可能是很多错误!)。在Cython文档之后,我尝试键入变量。这样做之后,代码就和纯Python一样慢了。这似乎很奇怪 这是我的密码: import numpy as np cimport cython cimport numpy as np import minuit da

我试图定义一个函数,它包含一个模拟积分的内循环

问题是速度。在我的机器上评估功能一次最多需要30秒。因为我的最终目标是最小化这个函数,所以一些额外的速度会很好

因此,我试图让Cython工作,但我一定是犯了一个严重的错误(可能是很多错误!)。在Cython文档之后,我尝试键入变量。这样做之后,代码就和纯Python一样慢了。这似乎很奇怪

这是我的密码:

import numpy as np 
cimport cython
cimport numpy as np
import minuit

data = np.genfromtxt('q6data.csv', usecols = np.arange(1, 24, 1), delimiter = ',')  

cdef int ns    = 1000                 # Number of simulation draws
cdef int K     = 5                    # Number of observed characteristics, including            constant
cdef int J     = len(data[:,1])       # Number of products, including outside
cdef double tol   = 0.0001            # Inner GMM loop tolerance
nu = np.random.normal(0, 1, (6, ns))  # ns random deviates

@cython.boundscheck(False)
@cython.wraparound(False)


def S(np.ndarray[double, ndim=1] delta, double s1, double s2, double s3, double s4,  double s5, double a):
    """Computes the simulated integrals, one for each good.
    Parameters: delta is an array of length J containing mean product specific utility levels
    Returns: Numpy array with length J."""
    cdef np.ndarray[double, ndim=2] mu_ij = np.dot((data[:,2:7]*np.array([s1, s2, s3, s4, s5])), nu[1:K+1,:])
    cdef np.ndarray[double, ndim=2] mu_y  = a * np.log(np.exp(data[:,21].reshape(J,1) +  data[:,22].reshape(J,1)*nu[0,:].reshape(1, ns)) - data[:,7].reshape(J,1))
    cdef np.ndarray[double, ndim=2] V = delta.reshape(J,1) + mu_ij + mu_y
    cdef np.ndarray[double, ndim=2] exp_vi = np.exp(V)
    cdef np.ndarray[double, ndim=2] P_i = (1.0 / np.sum(exp_vi[np.where(data[:,1] == 71)], 0)) * exp_vi[np.where(data[:,1] == 71)] 
    cdef int yrs = 19
    cdef int yr
    for yr in xrange(yrs):
        P_yr = (1.0 / np.sum(exp_vi[np.where(data[:,1]== (yr + 72))], 0)) *   exp_vi[np.where(data[:,1] == (yr + 72))]
        P_i  = np.concatenate((P_i, P_yr)) 
    cdef np.ndarray[double, ndim=1] S = np.zeros(dtype = "d", shape = J)
    cdef int j
    for j in xrange(ns):
        S += P_i[:,j]
    return (1.0 / ns) * S

def d_infty(np.ndarray[double, ndim=1] x, np.ndarray[double, ndim=1] y):
    """Sup norm."""
    return np.max(np.abs(x - y)) 

def T(np.ndarray[double, ndim=1] delta_exp, double s1, double s2, double s3, double s4,  double s5, double a):
    """The contraction operator.  This function takes the parameters and the exponential
    of the starting value of delta and returns the fixed point.""" 
    cdef int iter = 0
    cdef int maxiter = 200
    cdef int i
    for i in xrange(maxiter): 
        delta1_exp = delta_exp * (data[:, 8] / S(np.log(delta_exp), s1, s2, s3, s4, s5, a))                    
        print i
        if d_infty(delta_exp, delta1_exp) < tol:                                       
            break
        delta_exp = delta1_exp
    return np.log(delta1_exp)


def Q(double s1, double s2, double s3, double s4, double s5, double a):
    """GMM objective function."""  
    cdef np.ndarray[double, ndim=1] delta0_exp = np.exp(data[:,10])                                                     
    cdef np.ndarray[double, ndim=1] delta1 = T(delta0_exp, s1, s2, s3, s4, s5, a)
    delta1[np.where(data[:,10]==0)] = np.zeros(len(np.where(data[:,10]==0)))            
    cdef np.ndarray[double, ndim=1] xi =  delta1 - (np.dot(data[:,2:7],   np.linalg.lstsq(data[:,2:7], delta1)[0]))   
    cdef np.ndarray[double, ndim=2] g_J = xi.reshape(J,1) * data[:,11:21]
    cdef np.ndarray[double, ndim=1] G_J = (1.0 / J) * np.sum(g_J, 0) 
    return np.sqrt(np.dot(G_J, G_J))
将numpy导入为np
西姆波特赛顿酒店
cimport numpy作为np
输入minuit
data=np.genfromtxt('q6data.csv',usecols=np.arange(1,24,1),分隔符=',')
cdef int ns=1000#模拟绘图的数量
cdef int K=5#观察到的特征数量,包括常数
cdef int J=len(数据[:,1])#产品数量,包括外部
cdef双tol=0.0001#内GMM回路公差
nu=np.随机。正常(0,1,(6,ns))#ns随机偏差
@cython.boundscheck(错误)
@cython.wrapparound(假)
def S(np.ndarray[double,ndim=1]增量,双s1,双s2,双s3,双s4,双s5,双a):
“”“计算模拟积分,每种货物一个。
参数:delta是长度J的数组,包含特定于产品的平均效用水平
返回:长度为J的Numpy数组。“”
cdef np.ndarray[double,ndim=2]mu_ij=np.dot((数据[:,2:7]*np.array([s1,s2,s3,s4,s5])),nu[1:K+1,:])
cdef np.ndarray[double,ndim=2]mu_y=a*np.log(np.exp(数据[:,21]。重塑(J,1)+数据[:,22]。重塑(J,1)*nu[0,:]。重塑(1,ns))-数据[:,7]。重塑(J,1))
cdef np.ndarray[double,ndim=2]V=delta.重塑(J,1)+μij+μy
cdef np.ndarray[double,ndim=2]exp_vi=np.exp(V)
cdef np.ndarray[double,ndim=2]P_i=(1.0/np.sum(exp_vi[np.where(data[:,1]=71)],0))*exp_vi[np.where(data[:,1]=71)]
cdef int yrs=19
cdef国际年
对于X范围内的年数(年):
P_年=(1.0/np.和(经验六[np.式(数据[:,1]=(年+72))],0))*经验六[np.式(数据[:,1]=(年+72))]
P_i=np.连接((P_i,P_yr))
cdef np.ndarray[double,ndim=1]S=np.zeros(dtype=“d”,shape=J)
cdef int j
对于X范围内的j(ns):
S+=P_i[:,j]
返回值(1.0/ns)*S
定义信息(np.ndarray[double,ndim=1]x,np.ndarray[double,ndim=1]y):
“超级标准”
返回np.max(np.abs(x-y))
def T(np.ndarray[double,ndim=1]增量扩展,双s1,双s2,双s3,双s4,双s5,双a):
“”“收缩运算符。此函数接受参数和指数
,并返回固定点。”“”
cdef int iter=0
cdef int maxiter=200
cdef int i
对于X范围内的i(maxiter):
delta1_exp=delta_exp*(数据[:,8]/S(np.log(delta_exp),s1,s2,s3,s4,s5,a))
打印i
如果d_infty(delta_exp,delta1_exp)
我已经分析了代码,似乎是函数S,一个完整的模拟器,正在破坏性能。不管怎么说,我希望输入变量至少能提高一些速度。由于它没有产生任何收益,我被引导相信我正在犯一些根本性的错误

有没有人看到Cython代码中有一个明显的错误会导致这个结果


哦,因为我对编程非常陌生,所以肯定有很多不好的风格和事情会减慢代码的速度。如果你有时间的话,也可以直接告诉我这些问题

一个“基本错误”是,您期望Python的长循环具有良好的性能。它是一种解释语言,在实现和ctyping之间切换对这一点没有任何影响。有一些用于快速计算的数值Python库,大部分是用C编写的。例如,如果您已经对数组使用了
numpy
,为什么不进一步使用
scipy
进行高等数学呢?它将提高可读性和速度。

通过使用Numpy的更多功能,您肯定可以提高代码的速度

例如:

cdef np.ndarray[double, ndim=1] S = np.zeros(dtype = "d", shape = J)
cdef int j
for j in xrange(ns):
    S += P_i[:,j]
会更快,更清晰

S = P_i.sum(axis=1)
您还需要重复一些计算,因此所需的时间是所需时间的两倍。比如说

np.where(data[:,1]==(yr + 72))
只能计算一次,并存储在可重用的变量中


您还可以执行许多重塑和切片:从一开始就让变量的格式更简单可能会有所帮助。如果可能的话,您的代码会更加清晰,优化也会更加明显。

Cython不能自动提高性能,您必须了解其内部结构并检查生成的C代码

特别是,如果您想提高循环性能,必须避免在循环中调用Python函数,而在本例中,您恰好做了很多事情(所有的
np.
调用都是Python调用、切片,可能还有其他事情)

有关Cython性能优化的一般指南,请参阅(在优化时,-a开关非常方便)
python -O -m cProfile -o profile.out MYAPP.py
runsnake profile.out
cython -a MODULE.py
X = data[:, 2:7]
m_y = data[:, 21].reshape(J,1)
sigma_y = 1.0
price = data[:, 7].reshape(J, 1)
shares_data = data[:,8]
mu_ij = np.dot((X*np.array([s1, s2, s3, s4, s5])), nu[1:K+1,:])
mu_y  = a * np.log(np.exp(m_y + sigma_y*nu[0,:].reshape(1,ns)) - price)
V = delta.reshape(J,1) + mu_ij + mu_y
exp_vi = np.exp(V)
P_i = (1.0 / np.sum(exp_vi[np.where(data[:,1]==71)], 0)) *  exp_vi[np.where(data[:,1]==71)] 
for yr in xarange(19):
    P_yr = (1.0 / np.sum(exp_vi[np.where(data[:,1]==yr)], 0)) * exp_vi[np.where(data[:,1]==yr)]
P_i  = np.concatenate((P_i, P_yr))
import sys
import time
import numpy as np

def splitdata( data, n, start=1971 ):
    """ split data into n pieces, where col 1 == start .. start + n """
        # not fancy, fast enough for small n
    split = n * [None]
    for j in range(n):
        split[j] = data[ data[:,1] == start + j ]
    return split  # [ arrays: col1 0, col1 1 ... ]

#...........................................................................
N = 2237
ncol = 21
start = 1971
n = 20
seed = 1
exec "\n".join( sys.argv[1:] )  # run this.py N= ...
np.set_printoptions( 2, threshold=100, suppress=True )  # .2f
np.random.seed(seed)

print "N=%d  ncol=%d  n=%d" % (N, ncol, n)
data = np.random.uniform( start, start + n, (N,ncol) )
data[:,1] = data[:,1].round()
t0 = time.time()

split = splitdata( data, n, start )  # n pieces

print "time: %.0f us splitdata" % ((time.time() - t0) * 1e6)
for y, yeardata in enumerate(split):
    print "%d  %d  %g" % (start + y, len(yeardata), yeardata[:,0].sum())
time: 27632 us splitdata  # old mac ppc
1971  69  136638
1972  138  273292
...