有幾種方法可以做到這一點。 ,想到的第一件事是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
的其中一個操作數(在這種情況下,U
的尺寸爲ijk
和V
的尺寸爲jk
)。 '->ik'
部分指定輸出數組的尺寸。任何不包含在輸出字符串中的下標的尺寸都會加在一起。
np.einsum
對於執行復雜的張量收縮非常有用,但它可能需要一段時間才能完全掌握它的工作原理。您應該查看文檔中的示例(上面鏈接)。
一些其他選項:與broadcasting
逐元素乘法,然後求和:
res3 = (U * V[None, ...]).sum(1)
inner1d
與轉置的負載:
from numpy.core.umath_tests import inner1d
res4 = inner1d(U.transpose(0, 2, 1), V.T)
項
一些性能測試:
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