これを行うにはいくつかの方法があります。心に来る最初の事はnp.einsum
です:
# some fake data
gen = np.random.RandomState(0)
ni, nj, nk = 10, 20, 100
U = gen.randn(ni, nj, nk)
V = gen.randn(nj, nk)
res1 = np.zeros((ni, nk))
for k in range(nk):
res1[:,k] = U[:,:,k].dot(V[:,k])
res2 = np.einsum('ijk,jk->ik', U, V)
print(np.allclose(res1, res2))
# True
np.einsum
はテンソル収縮を表現するためにEinstein notationを使用しています。上記式'ijk,jk->ik'
の式において、i
,j
およびk
は、U
およびV
の異なる寸法に対応する下付き文字である。カンマで区切られた各グループは、np.einsum
に渡されるオペランドの1つに対応します(この場合、のサイズはijk
で、V
のサイズはjk
です)。 '->ik'
の部分には、出力配列の寸法を指定します。出力文字列に存在しない下付き文字を持つ次元は合計されます。
np.einsum
は、複雑なテンソル収縮を実行するのに非常に便利ですが、動作の仕方を頭の中で完全に包むにはしばらく時間がかかることがあります。ドキュメントの例を見てください(上にリンクされています)。
いくつかの他のオプション:broadcastingと
要素ごとの乗算、合計が続く:
from numpy.core.umath_tests import inner1d
res4 = inner1d(U.transpose(0, 2, 1), V.T)
:転置の荷重で
res3 = (U * V[None, ...]).sum(1)
の
いくつかのベンチマーク:
In [1]: ni, nj, nk = 100, 200, 1000
In [2]: %%timeit U = gen.randn(ni, nj, nk); V = gen.randn(nj, nk)
....: np.einsum('ijk,jk->ik', U, V)
....:
10 loops, best of 3: 23.4 ms per loop
In [3]: %%timeit U = gen.randn(ni, nj, nk); V = gen.randn(nj, nk)
(U * V[None, ...]).sum(1)
....:
10 loops, best of 3: 59.7 ms per loop
In [4]: %%timeit U = gen.randn(ni, nj, nk); V = gen.randn(nj, nk)
inner1d(U.transpose(0, 2, 1), V.T)
....:
10 loops, best of 3: 45.9 ms per loop
あなたがあなたのイメージを描画するために使用しているどのようなソフトウェア? – hlin117
@ hlin117 - キーノートを使用しました。 – Matteo