Je veux calculer le produit ponctuel par rangée de deux matrices de même dimension aussi rapidement que possible. C'est comme ça que je le fais:
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
et bien sûr le résultat est:
[ 26. 14.]
Découvrez numpy.einsum pour une autre méthode:
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])
On dirait que einsum
est un peu plus rapide que 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
Joué avec cela et trouvé inner1d
le plus rapide:
La parcelle a été créée avec perfplot (un de mes petits projets)
import numpy
from numpy.core.umath_tests import inner1d
import perfplot
perfplot.show(
setup=lambda n: (numpy.random.Rand(n, 3), numpy.random.Rand(n, 3)),
n_range=[2**k for k in range(1, 18)],
kernels=[
lambda data: numpy.sum(data[0] * data[1], axis=1),
lambda data: numpy.einsum('ij, ij->i', data[0], data[1]),
lambda data: inner1d(data[0], data[1])
],
labels=['np.sum(a*b, axis=1)', 'einsum', 'inner1d'],
logx=True,
logy=True,
xlabel='len(a), len(b)'
)
Une façon simple de le faire est:
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)
ce qui évite la boucle python et est plus rapide dans les cas suivants:
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
Vous ferez mieux d'éviter la variable append
, mais je ne vois pas comment éviter la boucle python. Une coutume d'Ufunc peut-être? Je ne pense pas que numpy.vectorize vous aidera ici.
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
MODIFIER
D'après cette réponse , il semble que inner1d
puisse fonctionner si les vecteurs de votre problème réel sont 1D.
from numpy.core.umath_tests import inner1d
inner1d(a,b) # array([14, 26])
Je suis tombé sur cette réponse et j'ai revérifié les résultats avec Numpy 1.14.3 exécuté en Python 3.5. La plupart des réponses ci-dessus sont vraies sur mon système, bien que j'ai constaté que pour les très grandes matrices (voir exemple ci-dessous), toutes les méthodes sauf une sont si proches les unes des autres que la différence de performances n'a pas de sens.
Pour les matrices plus petites, j’ai trouvé que einsum
était le plus rapide de loin, jusqu’à un facteur deux dans certains cas.
Mon grand exemple de matrice:
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)
Donc, einsum
est toujours le plus rapide sur de très grandes matrices, mais par une quantité infime. Cela semble être une quantité statistiquement significative (minuscule) cependant!