Python 带numpy-tensordot的张量乘法

Python 带numpy-tensordot的张量乘法,python,performance,numpy,matrix,linear-algebra,Python,Performance,Numpy,Matrix,Linear Algebra,我有一个由n维矩阵(d,k)和一个维矩阵V(k,n)组成的张量U 我想将它们相乘,这样结果会返回一个维数为(d,n)的矩阵,其中j列是U的矩阵j和V的j列之间矩阵相乘的结果 一种可能的方法是: for j in range(n): res[:,j] = U[:,:,j] * V[:,j] 我想知道是否有使用numpy库的更快方法。我特别想到了函数 这个小片段允许我将一个矩阵乘以一个标量,但对向量的明显概括并没有返回我所希望的结果 a = np.array(range(1, 17))

我有一个由n维矩阵(d,k)和一个维矩阵V(k,n)组成的张量U

我想将它们相乘,这样结果会返回一个维数为(d,n)的矩阵,其中j列是U的矩阵j和V的j列之间矩阵相乘的结果

一种可能的方法是:

for j in range(n):
    res[:,j] = U[:,:,j] * V[:,j]
我想知道是否有使用
numpy
库的更快方法。我特别想到了函数

这个小片段允许我将一个矩阵乘以一个标量,但对向量的明显概括并没有返回我所希望的结果

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)

有什么建议吗?

有几种方法可以做到这一点。首先想到的是:

np.einsum
用于表示张量收缩。在上面的表达式中,
i
j
k
是对应于
U
V
的不同维度的下标。每个逗号分隔的分组对应于传递给
np.einsum
(在本例中,
U
具有维度
ijk
V
具有维度
jk
)。
'->ik'
部分指定输出数组的尺寸。输出字符串中不存在的任何带有下标的维度都将累加

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


其他一些选择:

  • 元素相乘,然后求和:

    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
    

    谢谢你的回答!你能补充一下这个功能是如何工作的吗?例如,如果U不是
    (ni,nj,nk)
    而是
    (nk,ni,nj)
    ,函数调用会发生什么变化?回答得好!谢谢!看着op的问题,我注意到他想要(i,j,k)与(k,i)相乘,在公认的答案中是ijk,jk->ik,但它应该是ijk,ik->ij。@wedran op想要在
    U
    的第二个轴和
    V
    的第一个轴上进行缩减,在我的示例中是下标
    j
    。按照他的原始符号,它将是
    dkn,kn->dn
    (相当于
    ijk,jk->ik
    )。您使用什么软件来绘制图像?@hlin117-我使用了keynote。
    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