50

2 つの numpy 配列があるとします。

> A, A.shape = (n,p)
> B, B.shape = (p,p)

通常、p は小さい数値 (p <= 200) ですが、n は任意に大きくすることができます。

私は次のことをしています:

result = np.diag(A.dot(B).dot(A.T))

ご覧のとおり、n 個の対角エントリのみを保持していますが、対角エントリのみが保持される計算された中間 (nxn) 配列があります。

結果の対角エントリのみを計算し、完全なメモリを割り当てない diag_dot() のような関数が必要です。

結果は次のようになります。

> result = diag_dot(A.dot(B), A.T)

このような事前に作成された機能はありますか?これは、中間 (nxn) 配列を割り当てる必要なく効率的に実行できますか?

4

3 に答える 3

59

私は自分でそれを手に入れたと思いますが、それでも解決策を共有します:

行列乗算の対角線のみを取得するため

> Z = N.diag(X.dot(Y))

は、X の行と Y の列のスカラー積の個々の合計と同等であり、前のステートメントは次と同等です。

> Z = (X * Y.T).sum(-1)

元の変数の場合、これは次のことを意味します。

> result = (A.dot(B) * A).sum(-1)

私が間違っている場合は修正してくださいが、これで終わりです...

于 2013-02-07T19:22:38.687 に答える
38

で夢見ていたものはほとんど何でも手に入れることができますnumpy.einsum。コツをつかむまでは、基本的に黒いブードゥー教のようです...

>>> a = np.arange(15).reshape(5, 3)
>>> b = np.arange(9).reshape(3, 3)

>>> np.diag(np.dot(np.dot(a, b), a.T))
array([  60,  672, 1932, 3840, 6396])
>>> np.einsum('ij,ji->i', np.dot(a, b), a.T)
array([  60,  672, 1932, 3840, 6396])
>>> np.einsum('ij,ij->i', np.dot(a, b), a)
array([  60,  672, 1932, 3840, 6396])

編集実際には、すべてを 1 回のショットで取得できます。ばかげています...

>>> np.einsum('ij,jk,ki->i', a, b, a.T)
array([  60,  672, 1932, 3840, 6396])
>>> np.einsum('ij,jk,ik->i', a, b, a)
array([  60,  672, 1932, 3840, 6396])

EDITただし、それ自体をあまり理解したくありません...比較のために、OPの回答を独自の質問に追加しました。

n, p = 10000, 200
a = np.random.rand(n, p)
b = np.random.rand(p, p)

In [2]: %timeit np.einsum('ij,jk,ki->i', a, b, a.T)
1 loops, best of 3: 1.3 s per loop

In [3]: %timeit np.einsum('ij,ij->i', np.dot(a, b), a)
10 loops, best of 3: 105 ms per loop

In [4]: %timeit np.diag(np.dot(np.dot(a, b), a.T))
1 loops, best of 3: 5.73 s per loop

In [5]: %timeit (a.dot(b) * a).sum(-1)
10 loops, best of 3: 115 ms per loop
于 2013-02-07T19:26:51.077 に答える
2

大きな中間アレイの構築を回避する歩行者の答えは次のとおりです。

result=np.empty([n,], dtype=A.dtype )
for i in xrange(n):
    result[i]=A[i,:].dot(B).dot(A[i,:])
于 2013-02-07T20:09:20.823 に答える