有几种方法可以做到这一点。 ,想到的第一件事是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