np.dot 沿指定轴的两个 3D 矩阵之间的乘积
np.dot product between two 3D matrices along specified axis
我有两个 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).我在这里发现了一个类似的问题 (),但这并不是我想要的,而且我无法推断出我的问题的解决方案。
总而言之,我可以使用 np.tensordot 来计算上面显示的 c 数组吗?
更新 #1
@hpaulj 的回答是我想要的,但是在我的系统中(python 2.7 和 np 1.13.3)这些方法非常慢:
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))
使用 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
的前两个维度不同,可能会有所帮助。把它们混在一起的机会就少了。
在tensordot
中指定dot
求和轴(axes)很容易,但很难约束其他维度的处理。这就是为什么你得到一个 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
维度是 'going-along-for-ride'。这就是你的循环所做的。在 einsum
中 i
在所有部分都是相同的。
我有两个 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).我在这里发现了一个类似的问题 (
更新 #1
@hpaulj 的回答是我想要的,但是在我的系统中(python 2.7 和 np 1.13.3)这些方法非常慢:
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))
使用 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
的前两个维度不同,可能会有所帮助。把它们混在一起的机会就少了。
在tensordot
中指定dot
求和轴(axes)很容易,但很难约束其他维度的处理。这就是为什么你得到一个 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
维度是 'going-along-for-ride'。这就是你的循环所做的。在 einsum
中 i
在所有部分都是相同的。