Python 多个无回路的numpy点产品
有没有可能在没有循环的情况下计算几个点积? 假设你有以下几点:Python 多个无回路的numpy点产品,python,loops,numpy,matrix,Python,Loops,Numpy,Matrix,有没有可能在没有循环的情况下计算几个点积? 假设你有以下几点: a = randn(100, 3, 3) b = randn(100, 3, 3) 我想得到一个形状为(100,3,3)的数组z,这样对于所有I z[i, ...] == dot(a[i, ...], b[i, ...]) 换句话说,这验证了: for va, vb, vz in izip(a, b, z): assert (vq == dot(va, vb)).all() 简单的解决办法是: z = array([d
a = randn(100, 3, 3)
b = randn(100, 3, 3)
我想得到一个形状为(100,3,3)的数组z
,这样对于所有I
z[i, ...] == dot(a[i, ...], b[i, ...])
换句话说,这验证了:
for va, vb, vz in izip(a, b, z):
assert (vq == dot(va, vb)).all()
简单的解决办法是:
z = array([dot(va, vb) for va, vb in zip(a, b)])
它使用隐式循环(列表理解+数组)
有没有更有效的方法来计算z?此解决方案仍然使用循环,但速度更快,因为它通过使用
点的out
arg来避免不必要的临时数组创建:
def dotloop(a,b):
res = empty(a.shape)
for ai,bi,resi in zip(a,b,res):
np.dot(ai, bi, out = resi)
return res
%timeit dotloop(a,b)
1000 loops, best of 3: 453 us per loop
%timeit array([dot(va, vb) for va, vb in zip(a, b)])
1000 loops, best of 3: 843 us per loop
np.einsum
在这里很有用。尝试运行此复制+可粘贴代码:
import numpy as np
a = np.random.randn(100, 3, 3)
b = np.random.randn(100, 3, 3)
z = np.einsum("ijk, ikl -> ijl", a, b)
z2 = np.array([ai.dot(bi) for ai, bi in zip(a, b)])
assert (z == z2).all()
einsum
是一种编译代码,运行速度非常快,甚至比np.tensordot
还要快(这里并不完全适用,但通常是适用的)。以下是一些统计数据:
In [8]: %timeit z = np.einsum("ijk, ikl -> ijl", a, b)
10000 loops, best of 3: 105 us per loop
In [9]: %timeit z2 = np.array([ai.dot(bi) for ai, bi in zip(a, b)])
1000 loops, best of 3: 1.06 ms per loop
尝试用numpy进行爱因斯坦求和:
z = np.einsum('...ij,...jk->...ik', a, b)
它很优雅,不需要按照您的要求编写循环。
它使我的系统速度提高了4.8倍:
%timeit z = array([dot(va, vb) for va, vb in zip(a, b)])
1000 loops, best of 3: 454 µs per loop
%timeit z = np.einsum('...ij,...jk->...ik', a, b)
10000 loops, best of 3: 94.6 µs per loop
除了其他答案外,我想补充一点:
np.einsum("ijk, ijk -> ij", a, b)
适用于我遇到的一个相关案例,其中有两个3D数组,由2D向量(点或方向)的匹配2D字段组成。这将在这些2D向量之间提供一种“元素”点积
例如:
np.einsum("ijk, ijk -> ij", [[[1,2],[3,4]]], [[[5,6],[7,8]]])
# => array([[17, 53]])
其中:
np.dot([1,2],[5,6])
# => 17
np.dot([3,4],[7,8])
# => 53
在Python3.5中,它将是a@b
(除非此后情况发生了变化)。不幸的是,dot
不是这样工作的。我不知道为什么。tensordot在这里非常适用;简单地在轴上收缩=[[2],[1]]。虽然我怀疑einsum会更快,考虑到大量的小宫缩。我相信tensordot更适合大尺寸的收缩。Eelco,使用这些轴求和不会得到与einsum相同的结果。np。tensordot
会做一种外积,将a
中的每个矩阵与b
中的每个矩阵相乘,从而产生两个大小为100的轴。理想的结果是它的对角线,因此np.tensordot
计算的系数太多。