28

積分をシミュレートするための内部ループを含む関数を定義しようとしています。

問題は速度です。私のマシンでは、関数を 1 回評価するのに最大 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))

コードのプロファイリングを行ったところ、パフォーマンスを低下させているのは関数 S (積分シミュレーター) のようです。とにかく、変数を入力することで、少なくともある程度の速度向上が期待できました。何の利益ももたらさなかったので、私はいくつかの根本的な間違いを犯していると信じるようになりました.

この結果につながる可能性のある Cython コードの明らかなエラーに気付いた人はいますか?

ああ、私はプログラミングに非常に慣れていないので、確かに多くの悪いスタイルとコードの速度を低下させるものがあります. 時間があれば、これらの点についても率直に教えてください。

4

7 に答える 7

34

Cython は、これを支援する html ファイルを生成できます。

cython -a MODULE.py

これは、ソース コードの各行が白から黄色のさまざまな色合いに着色されていることを示しています。黄色が濃いほど、より動的な Python の動作がその行でまだ実行されています。黄色を含む行ごとに、静的な型宣言を追加する必要があります。

これを行っているときは、ソース コードで問題が発生している部分を、式または演算子ごとに 1 行ずつ、多くの個別の行に分割して、最も詳細なビューを取得するのが好きです。

これがないと、変数、関数呼び出し、または演算子のいくつかの静的型宣言を見落としがちです。(たとえば、インデックス演算子 x[y] は、特に宣言しない限り、依然として完全に動的な Python 操作です)

于 2010-11-24T10:00:38.190 に答える
17

Cython は自動パフォーマンス向上を提供しません。その内部を知り、生成された C コードをチェックする必要があります。

特に、ループのパフォーマンスを向上させたい場合は、ループ内で Python 関数を呼び出さないようにする必要があります。この場合、たまたま多くのことを行います (すべてのnp.呼び出しは、Python 呼び出し、スライス、およびおそらくその他のものです)。

Cython を使用したパフォーマンスの最適化に関する一般的なガイドラインについては、このページを参照してください(-a スイッチは、最適化の際に非常に便利です) 。

于 2010-11-24T08:57:31.073 に答える
11

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)

また、いくつかの計算を繰り返すため、必要以上に 2 倍の時間がかかります。例えば

np.where(data[:,1]==(yr + 72))

一度だけ計算して、再利用できる変数に格納できます。

また、多くの再形成とスライスを実行します。最初から変数をより単純な形式にすることが役立つ場合があります。可能であれば、コードがより明確になり、最適化がより明確になる可能性があります。

于 2010-11-24T08:32:10.600 に答える
6

プロファイラーは、どの部分が遅いかを判断するのに役立ちますか?標準ライブラリプロファイラーを使用してプログラムを実行するのが好きです。

python -O -m cProfile -o profile.out MYAPP.py

次に、「RunSnakeRun」GUIでその出力を表示します。

runsnake profile.out

RunSnakeRunはここからインストールできます: http ://www.vrplumber.com/programming/runsnakerun/

RunSnakeRunのスクリーンショット

于 2010-11-24T09:55:13.713 に答える
1

11 月 28 日のコメントの後、データのみを分割します。

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
...
于 2010-11-29T17:15:25.723 に答える
1

Taking the advice given here, I have spent more time profiling the above code. To hopefully clean things up a bit I defined

I have profiled the code a bit more and have a better idea of which pieces are the slowest. I additionally defined

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]

Then it is the following lines that are eating up most of the total time.

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))

I get the impression this is an overly cumbersome way to achieve my goal. I was hoping somebody might be able to provide some advice on how to speed these lines up. Maybe there are Numpy capabilities I am missing? If this problem is not sufficiently well specified for you to be helpful, I would be happy to provide more details on the context of my problem. Thanks!

于 2010-11-24T18:20:24.987 に答える
-7

「根本的な間違い」は、Python に長いループで優れたパフォーマンスを期待することです。これはインタープリター言語であり、実装と ctyping を切り替えても、これには何の影響もありません。高速計算用の数値 Python ライブラリがいくつかあり、ほとんどが C で記述されています。たとえば、numpy配列に既に使用している場合は、さらにscipy進んで高度な数学に使用してみませんか? 読みやすさと速度の両方が向上します。

于 2010-11-24T08:04:50.413 に答える