2016-03-04 148 views
6

我有一个由维数为(n,k)的n个矩阵和维数为(k,n)的矩阵V组成的张量U.与numpy tensordot的张量相乘

我想乘它们,使得结果返回尺寸(d,n)的,其中第j列是U的矩阵j和V.

的列j之间的矩阵乘法的结果的矩阵

enter image description here

一种可能的方式来获得,这是:

for j in range(n): 
    res[:,j] = U[:,:,j] * V[:,j] 

我想知道是否有使用numpy库更快的方法。特别是我正在考虑np.tensordot()函数。

这个小片段使我可以用一个标量乘以一个单一的矩阵,但是明显泛化到一个矢量并没有返回我所希望的。

a = np.array(range(1, 17)) 
a.shape = (4,4) 
b = np.array((1,2,3,4,5,6,7)) 
r1 = np.tensordot(b,a, axes=0) 

有什么建议吗?

+0

你用什么软件来绘制你的作品? – hlin117

+1

@ hlin117 - 我使用了主题演讲。 – Matteo

回答

6

有几种方法可以做到这一点。 ,想到的第一件事是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,jk是对应于UV的不同维度的下标。每个逗号分隔的分组对应于传递到np.einsum的其中一个操作数(在这种情况下,U的尺寸为ijkV的尺寸为jk)。 '->ik'部分指定输出数组的尺寸。任何不包含在输出字符串中的下标的尺寸都会加在一起。

np.einsum对于执行复杂的张量收缩非常有用,但它可能需要一段时间才能完全掌握它的工作原理。您应该查看文档中的示例(上面链接)。


一些其他选项:与broadcasting

  1. 逐元素乘法,然后求和:

    res3 = (U * V[None, ...]).sum(1) 
    
  2. 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 
+0

感谢您的回答!你能否添加关于函数如何工作的解释?例如,如果U而不是'(ni,nj,nk)'是'(nk,ni,nj)',函数调用会如何改变? – Matteo

+0

很好的回答!非常感谢! – Matteo