web-dev-qa-db-fra.com

Méthode vectorisée de calcul de deux matrices produit par points avec Scipy

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.]
24
Cupitor

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
23
Warren Weckesser

Joué avec cela et trouvé inner1d le plus rapide:

 enter image description here

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)'
    )
19
Nico Schlömer

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
19
sim

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

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!

0
Engineero