60

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

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.]
4

5 回答 5

56

直接的方法是:

import numpy as np
a=np.array([[1,2,3],[3,4,5]])
b=np.array([[1,2,3],[1,2,3]])
np.sum(a*b, axis=1)

这避免了 python 循环,并且在以下情况下更快:

def npsumdot(x, y):
    return np.sum(x*y, axis=1)

def loopdot(x, y):
    result = np.empty((x.shape[0]))
    for i in range(x.shape[0]):
        result[i] = np.dot(x[i], y[i])
    return result

timeit npsumdot(np.random.rand(500000,50),np.random.rand(500000,50))
# 1 loops, best of 3: 861 ms per loop
timeit loopdot(np.random.rand(500000,50),np.random.rand(500000,50))
# 1 loops, best of 3: 1.58 s per loop
于 2015-06-24T08:44:18.837 回答
45

查看numpy.einsum以了解另一种方法:

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])

看起来einsuminner1d

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 在不断发展和改进;多年来,上述功能的相对性能可能发生了变化。如果性能对您很重要,请使用您将使用的 NumPy 版本运行您自己的测试。

于 2013-03-25T19:17:02.147 回答
29

玩弄这个并找到inner1d最快的。然而,该功能是内部的,因此更强大的方法是使用

numpy.einsum("ij,ij->i", a, b)

更好的是对齐你的记忆,使总和发生在第一维,例如,

a = numpy.random.rand(3, n)
b = numpy.random.rand(3, n)

numpy.einsum("ij,ij->j", a, b)

对于10 ** 3 <= n <= 10 ** 6,这是最快的方法,速度是未转置等效方法的两倍。最大值出现在二级缓存被最大化时,大约为2 * 10 ** 4.

还要注意,转置后的转换sum比其未转置的等价物快得多。

在此处输入图像描述

在此处输入图像描述

该情节是用perfplot创建的(我的一个小项目)

import numpy
from numpy.core.umath_tests import inner1d
import perfplot


def setup(n):
    a = numpy.random.rand(n, 3)
    b = numpy.random.rand(n, 3)
    aT = numpy.ascontiguousarray(a.T)
    bT = numpy.ascontiguousarray(b.T)
    return (a, b), (aT, bT)


b = perfplot.bench(
    setup=setup,
    n_range=[2 ** k for k in range(1, 25)],
    kernels=[
        lambda data: numpy.sum(data[0][0] * data[0][1], axis=1),
        lambda data: numpy.einsum("ij, ij->i", data[0][0], data[0][1]),
        lambda data: numpy.sum(data[1][0] * data[1][1], axis=0),
        lambda data: numpy.einsum("ij, ij->j", data[1][0], data[1][1]),
        lambda data: inner1d(data[0][0], data[0][1]),
    ],
    labels=["sum", "einsum", "sum.T", "einsum.T", "inner1d"],
    xlabel="len(a), len(b)",
)

b.save("out1.png")
b.save("out2.png", relative_to=3)
于 2016-09-23T09:49:47.960 回答
5

你会更好地避免append,但我想不出一种方法来避免 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

编辑

根据这个答案inner1d,如果您的实际问题中的向量是一维的,它看起来可能会起作用。

from numpy.core.umath_tests import inner1d
inner1d(a,b)  # array([14, 26])
于 2013-03-25T14:26:39.663 回答
0

我遇到了这个答案,并使用在 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在非常大的矩阵上仍然是最快的,但数量很少。不过,这似乎是一个具有统计学意义的(微小)数量!

于 2018-09-24T20:38:16.117 回答