用numpy乘以高阶矩阵

Multiply high order matrices with numpy

我创造了这个反映我更大问题的玩具问题:

import numpy as np

ind = np.ones((3,2,4)) # shape=(3L, 2L, 4L)
dist = np.array([[0.1,0.3],[1,2],[0,1]]) # shape=(3L, 2L)

ans = np.array([np.dot(dist[i],ind[i]) for i in xrange(dist.shape[0])]) # shape=(3L, 4L)
print ans

""" prints:
   [[ 0.4  0.4  0.4  0.4]
    [ 3.   3.   3.   3. ]
    [ 1.   1.   1.   1. ]]
"""

我想尽快完成,所以使用 numpy 的函数来计算 ans 应该是最好的方法,因为这个操作很重而且我的矩阵很大。

我看了this post, but the shapes are different and I cannot understand which axes I should use for this problem. However, I'm certain that tensordot应该有答案。有什么建议吗?

编辑:我接受了,但也请阅读我自己的回答,它可能对其他人有帮助...

您可以使用 np.einsum 进行操作,因为它允许非常小心地控制哪些轴相乘,哪些相加:

>>> np.einsum('ijk,ij->ik', ind, dist)
array([[ 0.4,  0.4,  0.4,  0.4],
       [ 3. ,  3. ,  3. ,  3. ],
       [ 1. ,  1. ,  1. ,  1. ]])

函数将ind第一轴的条目乘以dist第一轴的条目(下标'i')。每个数组的第二个轴同上(下标 'j')。我们不返回 3D 数组,而是告诉 einsum 通过从输出下标中省略轴 'j' 对值求和,从而返回 2D 数组。


np.tensordot这个问题比较难应用。它会自动对轴的乘积求和。然而,我们想要 two 组产品,但只对其中的 one 求和。

写入 np.tensordot(ind, dist, axes=[1, 1])(如您链接到的答案)会为您计算正确的值,但是 returns 一个形状为 (3, 4, 3) 的 3D 数组。如果你能负担得起更大数组的内存成本,你可以使用:

np.tensordot(ind, dist, axes=[1, 1])[0].T

这给了你正确的结果,但是因为 tensordot 首先创建了一个比需要的大得多的数组,所以 einsum 似乎是一个更好的选择。

按照,我想确定哪种方法最快,所以我使用了timeit

import timeit

setup_code = """
import numpy as np
i,j,k = (300,200,400)
ind = np.ones((i,j,k)) #shape=(3L, 2L, 4L)
dist = np.random.rand(i,j) #shape=(3L, 2L)
"""

basic ="np.array([np.dot(dist[l],ind[l]) for l in xrange(dist.shape[0])])"
einsum = "np.einsum('ijk,ij->ik', ind, dist)"
tensor= "np.tensordot(ind, dist, axes=[1, 1])[0].T"

print "tensor - total time:", min(timeit.repeat(stmt=tensor,setup=setup_code,number=10,repeat=3))
print "basic - total time:", min(timeit.repeat(stmt=basic,setup=setup_code,number=10,repeat=3))
print "einsum - total time:", min(timeit.repeat(stmt=einsum,setup=setup_code,number=10,repeat=3))

令人惊讶的结果是:

tensor - total time: 6.59519493952
basic - total time: 0.159871203461
einsum - total time: 0.263569731028

所以显然使用 tensordot 是错误的方法(更不用说 memory error 在更大的例子中,正如@ajcr 所说)。

由于这个例子很小,我将矩阵大小更改为 i,j,k = (3000,200,400),翻转顺序以确保它没有效果并设置另一个具有更高重复次数的测试:

print "einsum - total time:", min(timeit.repeat(stmt=einsum,setup=setup_code,number=50,repeat=3))
print "basic - total time:", min(timeit.repeat(stmt=basic,setup=setup_code,number=50,repeat=3))

结果与第一个一致运行:

einsum - total time: 13.3184077671
basic - total time: 8.44810031351

然而,测试另一种类型的尺寸增长 - i,j,k = (30000,20,40) 导致以下结果:

einsum - total time: 0.325594117768
basic - total time: 0.926416766397

查看评论以了解这些结果的解释。

寓意是,在寻找特定问题的最快解决方案时,尽量生成在类型和形状方面与原始数据尽可能相似的数据。在我的例子中,ij,k 小得多,所以我继续使用丑陋的版本,这在这种情况下也是最快的。