0

非常に大きなデータ マトリックス (約 15,000 x 15,000、double 型) を作成および操作するコードのセクションを高速化しようとしています。今のところ、行列のサイズはそれほど重要ではないと思います。なぜなら、小さな 10 x 10 の行列でもスピードアップが見られないからです (実際、コンパイルされた cython コードは、小さな行列の場合、純粋な python よりも遅くなりますが、時間は大きな行列については、cython と python の間でほぼ同じです)。私は 1 週間だけ Python をコーディングしており (Matlab から新たに変換されました)、私は謙虚な化学エンジニアに過ぎないので、しばらくお待ちください。

コードの目的は、入力として 1D 配列 (長さ L) を取得することです。次に例を示します。

[ 16.66  16.85  16.93  16.98  17.08  17.03  17.09  16.76  16.67  16.72]

出力として行列 (高さ L、幅 L-1) を生成します。

[[ 16.66  16.85  16.93  16.98  17.08  17.03  17.09  16.76  16.67]
 [ 16.85  16.93  16.98  17.08  17.03  17.09  16.76  16.67  16.72]
 [ 16.93  16.98  17.08  17.03  17.09  16.76  16.67  16.72   0.  ]
 [ 16.98  17.08  17.03  17.09  16.76  16.67  16.72   0.     0.  ]
 [ 17.08  17.03  17.09  16.76  16.67  16.72   0.     0.     0.  ]
 [ 17.03  17.09  16.76  16.67  16.72   0.     0.     0.     0.  ]
 [ 17.09  16.76  16.67  16.72   0.     0.     0.     0.     0.  ]
 [ 16.76  16.67  16.72   0.     0.     0.     0.     0.     0.  ]
 [ 16.67  16.72   0.     0.     0.     0.     0.     0.     0.  ]
 [ 16.72   0.     0.     0.     0.     0.     0.     0.     0.  ]]

上記の例と以下のコードから、私が達成しようとしていることは明らかです。アルゴリズムは非常に大きな行列にスケーリングする必要がありますが、現在はエラーなしで実行されていますが、単に遅いだけです!

これが私のcythonコードです:

from scipy.sparse import spdiags
import numpy as np
cimport numpy as np
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
def sfmat(np.ndarray[double, ndim=1] data):
    cdef int h = data.shape[0]   
    cdef np.ndarray[double, ndim=2] m = np.zeros([h, h-1])
    m = np.flipud(spdiags(np.tril(np.tile(data,[h-1,1]).T,0),range(1-h,1), h, h-1).todense())
    return m

読みやすいかもしれない、より詳細なコードも試しました。

from scipy.sparse import spdiags
import numpy as np
cimport numpy as np
cimport cython

DTYPE = np.float
ctypedef np.float_t DTYPE_t

@cython.boundscheck(False)
@cython.wraparound(False)
def sfmat(np.ndarray[DTYPE_t, ndim=1] data):
    assert data.dtype == DTYPE
    cdef int h = data.shape[0]   
    cdef np.ndarray[DTYPE_t, ndim=2] m = np.zeros([h, h-1], dtype=DTYPE)
    cdef np.ndarray[DTYPE_t, ndim=2] s1 = np.zeros([h, h-1], dtype=DTYPE)
    cdef np.ndarray[DTYPE_t, ndim=2] s2 = np.zeros([h, h-1], dtype=DTYPE)
    cdef np.ndarray[DTYPE_t, ndim=2] s3 = np.zeros([h, h-1], dtype=DTYPE)

    s1 = np.tile(data,[h-1,1]).T
    s2 = np.tril(s1,0)
    s3 = spdiags(s2,range(1-h,1), h, h-1).todense()
    m = np.flipud(s3)
    return m

cython の実装に関するヘルプをいただければ幸いです。このアルゴリズムを高速化する他の方法があれば、それも役立ちます。助けてくれてありがとう!

私はこれに慣れていないので、ここに詳細があります。私は 64 ビットの Windows 7 Pro を実行しており、Windows SDK C/C++ コンパイラを使用して cython コードを正常にコンパイルしています。(私はgithub hereの指示に従って成功しました)。単純な「hello world」の cython の例は、64 ビット モードで正常にコンパイルおよび実行されます。また、上記のコードもエラーなしでコンパイルおよび実行されます。15,000 x 15,000 マトリックス全体を操作するには、64 ビット アーキテクチャが必要です。少なくとも私はそう信じています。32 ビット用にコンパイルした後にコードを実行すると、メモリ エラーが発生するからです。この質問では、マトリックスを小さなチャンクに分割することは不可能であると仮定してください。この質問に答えるために他に必要な情報があれば教えてください。

乾杯、科学者R

アップデート

for ループを回避するのが最善の方法だと思いましたが、spdiags が主なボトルネックです。したがって、新しいアルゴリズムはよりうまく機能します (私のコンピューターでは 4 倍の改善):

import numpy as np
cimport numpy as np
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
def sfmat(np.ndarray[double, ndim=1] data):
     cdef int i
     cdef np.ndarray[double, ndim=2] m = np.zeros([data.shape[0], data.shape[0]-1])
     for i in range(data.shape[0]-1):
         m[:,i] = np.roll(data,-i);
     return m

しかし、Cython は純粋な Python よりも改善されていません。助けてください。コメンテーターが指摘しているように、アルゴリズムをより最適化する以外に、これを改善する方法はないかもしれませんが、私は期待しています。ありがとう!また、より高速なアルゴリズム、cython または python はありますか?

4

2 に答える 2

0

これは少し古い質問かもしれませんが、どの質問も未回答のままにしておくべきではありません:)np.roll単純な for ループ (Cython では実際に高速) を使用して、7000 の配列サイズで Cython コードを約 8 倍高速化することができました。(!)、しかし、私はその機能を使用してタイミングを比較しました。

Typed Memoryviews を使用するようにコードを編集し、np.empty代わりにnp.zeros

def sfmat(double[:] data):
     cdef int n = data.shape[0]
     cdef np.ndarray[double, ndim=2] out = np.empty((n, n-1))
     cdef double [:, :] out_v = out  # "typed memoryview"

     cdef int i, j
     for i in range(n-1):
        out_v[0, i] = data[i]

     for i in range(1, n):
        for j in range(n-i):
            out_v[i, j] = data[i+j]
        for j in range(n-i, n-1):
            out_v[i, j] = 0.
     return out

残念ながら、Cython の作業は、通常の Python セッションで次のコードを実行するよりも 1.2 倍しか高速ではありません。

def sfmat(data):
    n = len(data)
    out = np.empty((n, n-1))
    out[0, :] = data[:n-1]
    for i in xrange(1, n):
        out[i, :n-i] = data[i:]
        out[i, n-i:] = 0
    return out

ただし、コメントで既に説明したように、元のかなり小さな行列をこのように爆破することは、実際の全体的な問題に取り組む最も効率的な方法ではないでしょう。最初に for ループの使用を避けたかっただけなら、Cython ではその必要はまったくありません!

于 2013-11-01T11:41:33.667 に答える