2010-02-04 12 views
18

Ich möchte die Leistung von Convolution mit Python verbessern und hoffe auf einige Einblicke, wie man am besten die Leistung verbessern kann.Verbessern der Leistung von Numpy

ich derzeit scipy bin mit der Faltung auszuführen, Code etwas wie das Snippet unten:

import numpy 
import scipy 
import scipy.signal 
import timeit 

a=numpy.array ([ range(1000000) ]) 
a.reshape(1000,1000) 
filt=numpy.array([ [ 1, 1, 1 ], [1, -8, 1], [1,1,1] ]) 

def convolve(): 
    global a, filt 
    scipy.signal.convolve2d (a, filt, mode="same") 

t=timeit.Timer("convolve()", "from __main__ import convolve") 
print "%.2f sec/pass" % (10 * t.timeit(number=10)/100) 

I-Bilddaten am Verarbeitung, Graustufen (ganzzahlige Werte zwischen 0 und 255) verwendet wird, und ich zur Zeit bekommen etwa eine viertel Sekunde pro Faltung. Mein Gedanke war, einen der folgenden zu tun:

Verwenden Sie corpy, vorzugsweise mit einigen Optimierungen Kompilieren Sie numpy mit icc & ikml. Verwenden Sie Python-Cuda.

Ich fragte mich, ob irgendjemand irgendwelche Erfahrung mit irgendeinem dieser Ansätze hatte (welche Art von Gewinn wäre typisch, und wenn es die Zeit wert ist), oder wenn jemand eine bessere Bibliothek zur Durchführung von Faltung mit Numpy kennt.

Danke!

EDIT:

Geschwindigkeit aus etwa 10-fach durch Umschreiben Python Schleife in C über Numpy verwenden.

Antwort

10

Der Code in scipy für 2d Faltungen ist ein bisschen chaotisch und nicht optimiert. Sehen Sie http://svn.scipy.org/svn/scipy/trunk/scipy/signal/firfilter.c, wenn Sie einen Einblick in die Low-Level-Funktionen von scipy wollen.

Wenn alles, was Sie wollen, ist mit einem kleinen, konstanten Kern wie das verarbeiten Sie zeigten, eine Funktion wie dies funktionieren könnte:

def specialconvolve(a): 
    # sorry, you must pad the input yourself 
    rowconvol = a[1:-1,:] + a[:-2,:] + a[2:,:] 
    colconvol = rowconvol[:,1:-1] + rowconvol[:,:-2] + rowconvol[:,2:] - 9*a[1:-1,1:-1] 
    return colconvol 

Diese Funktion nutzt die Trennbarkeit des Kernels wie darenw vorgeschlagen oben, sowie die Vorteile der optimierten numpy arithmetischen Routinen zu nutzen. Es ist über 1000 mal schneller als die convolve2d Funktion durch meine Messungen.

+0

Danke, dass Sie darauf hingewiesen haben, ich hätte nicht gedacht, dass die Scipy Convolve so ineffizient sein könnte. Es sieht so aus, obwohl ich das nicht genau überprüft habe, dass Scipy Convolve ziemlich viele Speichermanipulationsoperationen durchführt und eine Anzahl von if-Anweisungen die Dinge verlangsamt. Ich werde die Ergebnisse posten und danke Ihnen allen für Ihre Kommentare. – Bear

+1

Ja, convolve2d ist ziemlich ineffizient, da es sich um den allgemeinen Fall handelt (es handelt sich um willkürliche Objekte - Sie sollten zum Beispiel mit einem Array von Dezimal-Objekten falten können). Ich denke, dass es erheblich beschleunigt werden könnte, indem spezielle Codepaths für den allgemeinen Fall verwendet werden (insbesondere, um den Funktionszeiger-Aufruf innerhalb der Dreifachschleife zu vermeiden, der sehr wahrscheinlich einer der Hauptrollen ist. –

0

Eine typische Optimierung für Faltung ist die Verwendung der FFT Ihres Signals. Der Grund ist: Die Faltung im realen Raum ist ein Produkt im FFT-Raum. Es ist oft schneller, die FFT, dann das Produkt und die iFFT des Ergebnisses zu berechnen, als den üblichen Weg zu falten.

+0

Und tut dies mit CUDA, und es wird wirklich extrem sein schnell. Wenn Cuda in der Zielumgebung funktioniert, wird es wahrscheinlich die meiste Leistung erzielen ... GPUs sind in der Tat sehr schnell. Der einzige Weg, wie Cuda nicht gewinnen würde, ist, wenn die Datenübertragung zur GPU und zurück beginnt, die Zeit zu dominieren. –

+0

Ich wünschte, Datenübertragung zwischen der Grafikkarte wäre das Problem! Irgendwelche Vorschläge für bereits existierende Bibliotheken? – Bear

+2

Der Fourier-Trick ist gut für große Faltungs-Kernel, aber für das gezeigte Beispiel ist es nur 3x3. Der einfache Weg ist wahrscheinlich schneller - aber wenn die FFT CUDA verwendet, während der einfache Weg nicht geht, keine Aussage ohne Messung. – DarenW

2

Für das spezielle Beispiel 3x3 kernel, würde ich feststellen, dass

1 1 1 
1 -8 1 
1 1 1 

    1 1 1  0 0 0 
= 1 1 1 + 0 -9 0 
    1 1 1  0 0 0 

und dass die erste von diesen ist factor - es kann durch Falten (1 1 1) für jede Zeile, und dann wieder gefaltet werden für jede Spalte. Dann subtrahiere das Neunfache der ursprünglichen Daten. Dies kann oder kann nicht schneller sein, abhängig davon, ob die schlauen Programmierer es intelligent genug gemacht haben, dies automatisch zu tun. (Ich habe eine Weile nicht eingecheckt.)

Sie möchten wahrscheinlich mehr interessante Faltungen machen, wo Factoring möglich oder nicht möglich ist.

1

Bevor ich C mit Ctypes sage, würde ich vorschlagen, eine eigenständige Faltung in C auszuführen, um zu sehen, wo das Limit liegt.
Ebenso für CUDA, cython, scipy.weave ...

Hinzugefügt 7feb: convolve33 8-Bit-Daten mit Clipping nimmt ~ 20 Taktzyklen pro Punkt, 2 Taktzyklen pro mem-Zugang, auf meinem Mac g4 pcc mit gcc 4.2.Ihr Kilometerstand wird variieren.

Ein paar Feinheit:

  • kümmern Sie 0..255 korrekten Schnitt über? np.clip() ist langsam, Cython usw. weiß es nicht.
  • Numpy/scipy benötigen möglicherweise Speicher für temps die Größe von A (also behalten Sie 2 * sizeof (A) < Cache-Größe).
    Wenn Ihr C-Code jedoch ein laufendes Update inplace ausführt, ist das die Hälfte des mem, aber ein anderer Algorithmus.

By the way, google theano convolve => "Eine Faltung op, die scipy.signal.convolve2d nachahmen sollte, aber schneller! In Entwicklung"

Verwandte Themen