Векторизованный способ вычисления строки-точки продукта с двумя матрицами с Scipy
Я хочу как можно быстрее вычислить произведение точек в виде строки двух матриц того же размера. Так я это делаю:
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.]
Ответы
Ответ 1
Зайдите 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])
Похоже, что 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
Ответ 2
Простой способ сделать это:
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
Ответ 3
Поиграл с этим и нашел 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 ** 5
это самый быстрый метод.
![enter image description here]()
Сюжет был создан с помощью 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)
perfplot.show(
setup=setup,
n_range=[2 ** k for k in range(1, 20)],
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"],
logx=True,
logy=True,
xlabel="len(a), len(b)",
)
Ответ 4
Лучше избегайте 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
может работать, если векторы в вашей реальной проблеме - 1D.
from numpy.core.umath_tests import inner1d
inner1d(a,b) # array([14, 26])
Ответ 5
Я наткнулся на этот ответ и еще раз проверил результаты, запустив Numpy 1.14.3 в Python 3.5. По большей части приведенные выше ответы верны в моей системе, хотя я обнаружил, что для очень больших матриц (см. пример ниже) все методы, кроме одного, настолько близки друг к другу, что разница в производительности не имеет смысла.
Для небольших матриц я обнаружил, что 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
по-прежнему самый быстрый на очень больших матрицах, но незначительно. Это кажется статистически значимым (крошечным) количеством, хотя!