用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
查看评论以了解这些结果的解释。
寓意是,在寻找特定问题的最快解决方案时,尽量生成在类型和形状方面与原始数据尽可能相似的数据。在我的例子中,i
比 j,k
小得多,所以我继续使用丑陋的版本,这在这种情况下也是最快的。
我创造了这个反映我更大问题的玩具问题:
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
查看评论以了解这些结果的解释。
寓意是,在寻找特定问题的最快解决方案时,尽量生成在类型和形状方面与原始数据尽可能相似的数据。在我的例子中,i
比 j,k
小得多,所以我继续使用丑陋的版本,这在这种情况下也是最快的。