Numpy 用Scipy计算两个矩阵行点积的矢量化方法

Numpy 用Scipy计算两个矩阵行点积的矢量化方法,numpy,scipy,vectorization,matrix-multiplication,dot-product,Numpy,Scipy,Vectorization,Matrix Multiplication,Dot Product,我想尽可能快地计算两个同维矩阵的行点积。我就是这样做的: import numpy as np a = np.array([[1,2,3], [3,4,5]]) b = np.array([[1,2,3], [1,2,3]]) result = np.array([]) for row1, row2 in a, b: result = np.append(result, np.dot(row1, row2)) print result 当然,输出是: [ 26. 14.] 您可以更

我想尽可能快地计算两个同维矩阵的行点积。我就是这样做的:

import numpy as np
a = np.array([[1,2,3], [3,4,5]])
b = np.array([[1,2,3], [1,2,3]])
result = np.array([])
for row1, row2 in a, b:
    result = np.append(result, np.dot(row1, row2))
print result
当然,输出是:

[ 26.  14.]

您可以更好地避免
追加
,但我想不出避免python循环的方法。也许是定制的Ufunc?我不认为numpy.vectorize能帮到你

import numpy as np
a=np.array([[1,2,3],[3,4,5]])
b=np.array([[1,2,3],[1,2,3]])
result=np.empty((2,))
for i in range(2):
    result[i] = np.dot(a[i],b[i]))
print result
编辑

基于此,如果现实问题中的向量是1D,则
inner1d
可能会起作用

from numpy.core.umath_tests import inner1d
inner1d(a,b)  # array([14, 26])
查看其他方法:

In [52]: a
Out[52]: 
array([[1, 2, 3],
       [3, 4, 5]])

In [53]: b
Out[53]: 
array([[1, 2, 3],
       [1, 2, 3]])

In [54]: einsum('ij,ij->i', a, b)
Out[54]: array([14, 26])
看起来
einsum
inner1d
快一点:

In [94]: %timeit inner1d(a,b)
1000000 loops, best of 3: 1.8 us per loop

In [95]: %timeit einsum('ij,ij->i', a, b)
1000000 loops, best of 3: 1.6 us per loop

In [96]: a = random.randn(10, 100)

In [97]: b = random.randn(10, 100)

In [98]: %timeit inner1d(a,b)
100000 loops, best of 3: 2.89 us per loop

In [99]: %timeit einsum('ij,ij->i', a, b)
100000 loops, best of 3: 2.03 us per loop

简单的方法是:

将numpy导入为np
a=np.数组([[1,2,3],[3,4,5]]
b=np.数组([[1,2,3],[1,2,3]]
np.和(a*b,轴=1)
这避免了python循环,在以下情况下速度更快:

def npsumdot(x,y):
返回np.和(x*y,轴=1)
def环点(x,y):
结果=np.空((x.shape[0]))
对于范围内的i(x.shape[0]):
结果[i]=np.dot(x[i],y[i])
返回结果
timeit npsumdot(np.random.rand(500000,50),np.random.rand(500000,50))
#1个回路,最佳3个:每个回路861毫秒
timeit环点(np.random.rand(500000,50),np.random.rand(500000,50))
#1圈,最佳3圈:每圈1.58秒

尝试了一下,发现
inner1d
最快。但是,该函数是内部函数,因此使用更健壮的方法

numpy.einsum(“ij,ij->i”,a,b)
更好的方法是调整你的记忆,使求和发生在第一维度,例如

a=numpy.random.rand(3,n)
b=numpy.random.rand(3,n)
neinsum(“ij,ij->j”,a,b)

对于
10**3我找到了这个答案,并用运行在Python 3.5中的Numpy 1.14.3重新验证了结果。尽管我发现对于非常大的矩阵(见下面的示例),除了一种方法外,所有方法都彼此非常接近,因此性能差异毫无意义,但在我的系统中,上述答案在很大程度上是正确的

对于较小的矩阵,我发现,
einsum
是最快的,有相当大的幅度,在某些情况下高达两倍

我的大型矩阵示例:

import numpy as np
from numpy.core.umath_tests import inner1d
a = np.random.randn(100, 1000000)  # 800 MB each
b = np.random.randn(100, 1000000)  # pretty big.

def loop_dot(a, b):
    result = np.empty((a.shape[1],))
    for i, (row1, row2) in enumerate(zip(a, b)):
        result[i] = np.dot(row1, row2)
%timeit inner1d(a, b)
# 128 ms ± 523 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.einsum('ij,ij->i', a, b)
# 121 ms ± 402 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.sum(a*b, axis=1)
# 411 ms ± 1.99 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit loop_dot(a, b)  # note the function call took negligible time
# 123 ms ± 342 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

因此,
einsum
在非常大的矩阵上仍然是最快的,但数量很少。但这似乎是一个统计上显著(微小)的数量

我非常喜欢einsum,它确实可以避免循环。但是,如果主要关注的是性能而不是代码样式,那么使用点和循环(取决于特定的数据和系统环境)可能会更好。与einsum相反,dot可以利用BLAS,并且通常会自动进行多线程处理@Warren,想详细解释一下神秘的下标符号。@PiQuer,推荐使用Python原生for循环?即使使用透明线程(我在这里不了解),循环仍然是Python的。老实说,我希望在Python中的上层循环准备好执行下一个点之前完成内部点。你能详细说明一下吗?我见过native for循环执行的速度比numpy操作慢几个数量级,在我相信使用native for是高性能的解决方案之前,我需要看一些证据和解释
einsum
似乎是一个很棒的工具,但我很高兴我终于被迫了解了它。不幸的是,
inner1d
被弃用(或者被删除,不是API的一部分)-请参阅Clear and readable