14

Ich möchte das zeilenweise Skalarprodukt zweier Matrizen der gleichen Dimension so schnell wie möglich berechnen. Dies ist die Art, wie ich es tue:Vektorisierte Methode zur Berechnung von zeilenweisen Skalarprodukten zwei Matrizen mit 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 

und natürlich die Ausgabe lautet:

[ 26. 14.] 
+3

Ist Ihr Python-Code, was Sie wirklich wollen? Sie nehmen das Skalarprodukt der ersten und zweiten Zeile von "a" und das Skalarprodukt der ersten und zweiten Zeile von "b", nicht das Skalarprodukt jeder i-ten Zeile von "a" und "b" '. – jorgeca

+0

Wie Jorgeca sagte, ist das für die Indizierung falsch: in diesem Codeausschnitt machst du: dot (a [0,:], a [1 ,:]), dot (b [0 ,:], b [1 ,: ]), siehe http://stackoverflow.com/questions/1663807/how-can-i-iterate-through-two-lists-in-parallel-in-python – lib

+0

Danke für die Erklärung, aber nein, ich war wirklich auf der Suche nach was ich geschrieben, dh zwei Zeilen mit dem gleichen Index multiplizieren. – Cupitor

Antwort

18

Check out numpy.einsum für eine andere Methode:

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

wie einsum Sieht ein bisschen schneller als 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 
+1

Ich mag einsum sehr und es ist wahr, dass man damit Schleifen vermeiden kann. Wenn Sie jedoch primär auf die Leistung und nicht auf den Code-Stil achten, sind Sie mit dot und der Schleife (abhängig von Ihrer spezifischen Daten- und Systemumgebung) immer noch besser dran. Im Gegensatz zu einsum kann dot BLAS nutzen und wird oft automatisch multithread. http://mail.scipy.org/pipermail/numpy-discussion/2012-October/064277.html – PiQuer

4

Sie werden besser tun, um die append zu vermeiden, aber ich kann so nicht denken um die Pythonschleife zu vermeiden. Eine benutzerdefinierte Ufunc vielleicht? Ich denke numpy.vectorize wird dir hier nicht helfen.

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 

EDIT

Basierend auf this answer, sieht es aus wie inner1d funktionieren kann, wenn die Vektoren in der realen Welt Problem 1D sind.

from numpy.core.umath_tests import inner1d 
inner1d(a,b) # array([14, 26]) 
12

einfache Art und Weise zu tun, ist:

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) 

, die die Python-Schleife und ist schneller in solchen Fällen vermeidet:

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 
8

rumgespielt mit diesem und gefunden inner1d die schnellste:

enter image description here

Das Grundstück erstellt wurde mit perfplot (ein kleines Projekt Mine)

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, 17)], 
     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)' 
     ) 
Verwandte Themen