web-dev-qa-db-ja.com

結果の対角要素のみを計算する、numpy / scipyドット積はありますか?

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個の対角要素のみを保持していますが、対角要素のみが保持される中間(n x n)配列が計算されています。

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

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

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

このような既成の機能はありますか?これは、中間(n x n)配列を割り当てる必要なしに効率的に実行できますか?

32
user2051916

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

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

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

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

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

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

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

私が間違っている場合は私を訂正してください、しかしこれはそれであるはずです...

31
user2051916

あなたが今まで夢見ていたほとんどすべてのものを手に入れることができます 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])

[〜#〜]編集[〜#〜]それだけではあまり理解させたくないのですが... 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
27
Jaime

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

result=np.empty([n,], dtype=A.dtype )
for i in xrange(n):
    result[i]=A[i,:].dot(B).dot(A[i,:])
2
Dave