numpyスライスを使用してこの複雑な式を表現する方法


14

Pythonで次の式を実装したい: ここでとはサイズ、 numpy配列サイズ numpy配列です。サイズは最大で約10000で、関数は何度も評価される内部ループの一部であるため、速度が重要です。

バツ=j=11kjjajaj
バツynkn×nn

理想的には、forループを完全に回避したいのですが、もしそれがあれば世界の終わりではないと思います。問題は、ネストされたループをいくつか持たずにそれを行う方法を理解するのに問題があることです。

誰もが効率的で、できれば読みやすい方法でnumpyを使用して上記の式を表現する方法を見ることができますか?より一般的には、この種のものにアプローチする最良の方法は何ですか?


数日前に同様の質問がありました。私はstackoverflowでそれを尋ねました。この投稿をご覧ください。cythonの代わりにscipy.weaveを使用します。これにより、(かなりの)パフォーマンスの違いが生じるかどうかは誰にもわかりますか?
seb

回答:


17

これがNumbaソリューションです。私のマシンでは、Numbaバージョンはデコレータなしのpythonバージョンよりも1000倍以上高速です(200x200マトリックス、「k」および200長のベクトル「a」の場合)。また、同じコードが複数のタイプで機能するように、呼び出しごとに約10マイクロ秒を追加する@autojitデコレーターを使用することもできます。

from numba import jit, autojit

@jit('f8[:](f8[:,:],f8[:])')
#@autojit
def looped_ver(k, a):
    x = np.empty_like(a)
    for i in range(x.size):
        sm = 0.0
        for j in range(0, i+1):
            sm += k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

開示:私はNumba開発者の一人です。


おかげで、それは非常に簡単に見えます。numbaのことすら知りませんでした!Cython、PyPy、Numba ...それは混乱する世界です。
ナサニエル

3
Travis、非常にクール、あなたは答えの一番下にあなたがnumba開発者の1人であるという開示を追加してもいいですか?
アロンアーマディア

1
では、Cython版もはるかに高速相対ループのPythonへ(〜700X、私のために)です。マトリックスが大きくなるとこのパフォーマンスがどのように変化するか、また同じ(メモリ?)ボトルネックが発生するかどうかに興味があります。n=200
ナットウィルソン

@NatWilson-これをscicompの質問として聞けば、喜んで試してみてください:)
アロン

4

ここから始まります。まず、間違いをおmyびします。

私はいくつかの異なるアプローチを試しました。上限があるべきである-私は、合計に対する制限で混乱ビットがあったのではなく、?1

編集:いいえ、質問で提供されているとおり、上限は正しいものでした。別の答えが同じコードを使用するようになったため、ここにそのまま残しましたが、修正は簡単です。

最初にループバージョン:

def looped_ver(k, a):
    x = np.empty_like(a)
    for i in range(x.size):
        sm = 0
        for j in range(0, i+1):
            sm += k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

私はそれをnumpyスライスで単一ループにしました:

def vectorized_ver(k, a):
    ktr = zeros_like(k)
    ar = zeros_like(k)
    sz = len(a)
    for i in range(sz):
        ktr[i,:i+1] = k[::-1].diagonal(-sz+i+1)
        a_ = a[:i+1]
        ar[i,:i+1] = a_[::-1] * a_
    return np.sum(ktr * ar, 1)

場合、明示的なループが1つあるnumpyバージョンは、コンピューター上で約25倍高速です。n=5000

次に、Cythonバージョンの(読みやすい)ループコードを作成しました。

import numpy as np
import cython
cimport numpy as np

@cython.boundscheck(False)
@cython.wraparound(False)
def cyth_ver(double [:, ::1] k not None,
              double [:] a not None):
    cdef double[:] x = np.empty_like(a)
    cdef double sm
    cdef int i, j

    for i in range(len(a)):
        sm = 0.0
        for j in range(i+1):
            sm = sm + k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

私のラップトップでは、これはループバージョンよりも約200倍高速です(1ループベクトル化バージョンよりも8倍高速)。他の人がもっとうまくできると確信しています。

私はジュリアバージョンでプレイしましたが、(適切にタイミングを合わせれば)Cythonコードに匹敵するように見えました。


バツ01

ああ、分かった。私は元の総計からそれを集めましたが、それが意図であるかどうかはわかりませんでした。
ナットウィルソン

1

あなたが望むものは畳み込みのようです。それを達成する最も早い方法はnumpy.convolve機能だと思います。

正確なニーズに応じてインデックスを修正する必要があるかもしれませんが、次のようなものを試してみたいと思います。

import numpy as np
a = [1, 2, 3, 4, 5]
k = [2, 4, 6, 8, 10]

result = np.convolve(a, k*a[::-1])
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.