np.dot沿指定轴的两个3D矩阵之间的乘积

时间:2018-03-14 15:16:58

标签: python numpy matrix-multiplication tensor numpy-ndarray

我有两个3D矩阵:

a = np.random.normal(size=[3,2,5])
b = np.random.normal(size=[5,2,3])

我想分别沿着2轴和0轴的每个切片的点积:

c = np.zeros([3,3,5]) # c.size is 45
c[:,:,0] = a[:,:,0].dot(b[0,:,:])
c[:,:,1] = a[:,:,1].dot(b[1,:,:])
...

我想使用np.tensordot(为了效率和速度)这样做 我试过了:

c = np.tensordot(a, b, axes=[2,0])

但我得到一个包含36个元素(而不是45个)的4D数组。 c.shape,c.size =((3L,2L,2L,3L),36)。我在这里找到了一个类似的问题(Numpy tensor: Tensordot over frontal slices of tensor),但这并不是我想要的,我无法推断出我的问题的解决方案。 总结一下,我可以使用np.tensordot来计算上面的c数组吗?

更新#1

@hpaulj的回答是我想要的,但是在我的系统(python 2.7和np 1.13.3)中,这些aproach非常慢:

n = 3000
a = np.random.normal(size=[n, 20, 5])
b = np.random.normal(size=[5, 20, n])


t = time.clock()
c_slice = a[:,:,0].dot(b[0,:,:])
print('one slice_x_5: {:.3f} seconds'.format( (time.clock()-t)*5 ))

t = time.clock()
c = np.zeros([n, n, 5])
for i in range(5):
     c[:,:,i] = a[:,:,i].dot(b[i,:,:])
print('for loop: {:.3f} seconds'.format(time.clock()-t))

t = time.clock()
d = np.einsum('abi,ibd->adi', a, b)
print('einsum: {:.3f} seconds'.format(time.clock()-t))

t = time.clock()
e = np.tensordot(a,b,[1,1])
e1 = e.transpose(0,3,1,2)[:,:,np.arange(5),np.arange(5)]
print('tensordot: {:.3f} seconds'.format(time.clock()-t))

a = a.transpose(2,0,1)
t = time.clock()
f = np.matmul(a,b)
print('matmul: {:.3f} seconds'.format(time.clock()-t))

1 个答案:

答案 0 :(得分:1)

使用einsum比使用tensordot更容易。让我们从那里开始:

In [469]: a = np.random.normal(size=[3,2,5])
     ...: b = np.random.normal(size=[5,2,3])
     ...: 
In [470]: c = np.zeros([3,3,5]) # c.size is 45
In [471]: for i in range(5):
     ...:     c[:,:,i] = a[:,:,i].dot(b[i,:,:])
     ...:     

In [472]: d = np.einsum('abi,ibd->iad', a, b)
In [473]: d.shape
Out[473]: (5, 3, 3)
In [474]: d = np.einsum('abi,ibd->adi', a, b)
In [475]: d.shape
Out[475]: (3, 3, 5)
In [476]: np.allclose(c,d)
Out[476]: True

我不得不考虑与尺寸相匹配。它有助于将焦点放在a[:,:,i]上为2d,对b[i,:,:]也是如此。因此dot总和超过了两个数组的中间维度(大小为2)。

在测试想法时,如果c的前两个维度不同,它可能会有所帮助。将它们混合的可能性较小。

dot中指定tensordot求和轴(轴)很容易,但更难约束其他维度的处理。这就是你获得4d阵列的原因。

我可以使用转置工作,然后选择对角线:

In [477]: e = np.tensordot(a,b,[1,1])
In [478]: e.shape
Out[478]: (3, 5, 5, 3)
In [479]: e1 = e.transpose(0,3,1,2)[:,:,np.arange(5),np.arange(5)]
In [480]: e1.shape
Out[480]: (3, 3, 5)
In [481]: np.allclose(c,e1)
Out[481]: True

我计算出的价值远远超过了所需的价值,并把大部分价值扔掉了。

matmul进行一些转置可能会更好。

In [482]: f = a.transpose(2,0,1)@b
In [483]: f.shape
Out[483]: (5, 3, 3)
In [484]: np.allclose(c, f.transpose(1,2,0))
Out[484]: True

我认为5维度为“顺其自然”。这就是你的循环所做的。在einsumi在所有部分都是相同的。