Ich bekomme einige Effizienz Testergebnisse, die ich nicht erklären kann.Warum ist B = numpy.dot (A, x) so viel langsamer durch B [i,:,:] = numpy.dot (A [i,:,:], x))?
Ich möchte eine Matrix B zusammenstellen, deren i-te Einträge B [i,:,:] = A [i,:,:] Punkt (x), wo jedes A [i,:,:] ist eine 2D-Matrix, und so ist x.
Ich kann dies drei Möglichkeiten, um die Leistung zu testen Ich mache zufällige (numpy.random.randn
) Matrizen A = (10,1000,1000), x = (1000,1200). Ich erhalte die folgenden Zeitergebnisse:
(1) Einzel mehrdimensionalen Skalarprodukt
B = A.dot(x)
total time: 102.361 s
(2) bis i Looping und Durchführung 2D-Punktprodukte
# initialize B = np.zeros([dim1, dim2, dim3])
for i in range(A.shape[0]):
B[i,:,:] = A[i,:,:].dot(x)
total time: 0.826 s
(3) numpy.einsum
So ist Option (2) bei weitem die schnellste. Betrachtet man nur (1) und (2), sehe ich nicht den großen Unterschied zwischen ihnen. Wie kann das Durchschleifen und Ausführen von 2D-Punktprodukten ~ 124 mal schneller sein? Beide benutzen numpy.dot. Irgendwelche Einsichten?
I enthalten den Code für die obigen Ergebnisse verwendet knapp unter:
import numpy as np
import numpy.random as npr
import time
dim1, dim2, dim3 = 10, 1000, 1200
A = npr.randn(dim1, dim2, dim2)
x = npr.randn(dim2, dim3)
# consider three ways of assembling the same matrix B: B1, B2, B3
t = time.time()
B1 = np.dot(A,x)
td1 = time.time() - t
print "a single dot product of A [shape = (%d, %d, %d)] with x [shape = (%d, %d)] completes in %.3f s" \
% (A.shape[0], A.shape[1], A.shape[2], x.shape[0], x.shape[1], td1)
B2 = np.zeros([A.shape[0], x.shape[0], x.shape[1]])
t = time.time()
for i in range(A.shape[0]):
B2[i,:,:] = np.dot(A[i,:,:], x)
td2 = time.time() - t
print "taking %d dot products of 2D dot products A[i,:,:] [shape = (%d, %d)] with x [shape = (%d, %d)] completes in %.3f s" \
% (A.shape[0], A.shape[1], A.shape[2], x.shape[0], x.shape[1], td2)
t = time.time()
B3 = np.einsum("ijk, kl -> ijl", A, x)
td3 = time.time() - t
print "using np.einsum, it completes in %.3f s" % td3
'dot' macht viele Dinge unter der Haube, es ist offensichtlich, dass 'np.dot (A, x)' BLAS nicht aufruft und irgendwie in die interne GEMM-Routine von numpy übergeht. In Ihrem Umformungsbeispiel wird die Schleifenmechanik umgangen und direkt zu einem herkömmlichen 2D-GEMM-Aufruf übergegangen, ohne dass Daten kopiert werden. Bei einem guten BLAS ist dies immer die schnellste Lösung für Probleme von vernünftiger Größe. Auf meinem Laptop mit MKL ist es ~ 50 mal schneller als einsum bei Problemen der Originalgröße. – Daniel
'Tensordot' macht die gleiche Umformung. – hpaulj
Nun Tensordot erzwingt eine Kopie der Daten intern, ich würde diese Option nicht empfehlen. – Daniel