2010-02-04 15 views
18

Me gustaría mejorar el rendimiento de la convolución usando python, y esperaba obtener una idea de cómo mejorar el rendimiento.Mejorando el rendimiento de Numpy

Actualmente estoy usando scipy para realizar la convolución, utilizando el código de algo así como el fragmento a continuación:

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) 

estoy de procesamiento de datos de imagen, usando la escala de grises (valores enteros entre 0 y 255), y en la actualidad consigo aproximadamente un cuarto de segundo por convolución. Mi pensamiento era hacer una de las siguientes:

Usar corepy, preferiblemente con algunas optimizaciones Recompilar numpy con icc & ikml. Utilice python-cuda.

Me preguntaba si alguien tenía alguna experiencia con cualquiera de estos enfoques (qué tipo de ganancia sería típica, y si vale la pena el tiempo), o si alguien conoce una mejor biblioteca para realizar convolución con Numpy.

Gracias!

EDIT:

velocidad de aproximadamente 10 veces por el bucle pitón re-escrito en C sobre el uso de Numpy.

Respuesta

10

El código en scipy para hacer convoluciones 2d es un poco desordenado y no optimizado. Consulte http://svn.scipy.org/svn/scipy/trunk/scipy/signal/firfilter.c si desea echar un vistazo al funcionamiento de bajo nivel de scipy.

Si lo que quieres es para procesar con un pequeño núcleo, constante como el que mostró, una función como esta podría funcionar:

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 

Esta función se aprovecha de la capacidad de separación del núcleo, como darenw sugirió más arriba, así como aprovechar las rutinas aritméticas numpy más optimizadas. Es más de 1000 veces más rápido que la función convolve2d según mis mediciones.

+0

Gracias por señalar eso, no había considerado que la convolvente scipy pudiera ser tan ineficiente. Parece que, aunque no lo he comprobado tan de cerca, esa convoluta coincidencia está haciendo bastantes operaciones de manipulación de memoria y tiene varias declaraciones if que ralentizan las cosas. Publicaré los resultados y les agradeceré a todos sus comentarios. – Bear

+1

Sí, convolve2d es bastante ineficiente, ya que se trata del caso general (se trata de objetos arbitrarios, por ejemplo, debe poder convolucionar con una matriz de objetos decimales). Creo que podría acelerarse considerablemente utilizando rutas de codificación especiales para el caso común (en particular para evitar la llamada al puntero de función dentro del lazo triple, que es muy probable que sea uno de los hostpot. –

0

Una optimización típica para convolución es usar la FFT de su señal. La razón es: la convolución en el espacio real es un producto en el espacio FFT. A menudo es más rápido calcular la FFT, luego el producto y la iFFT del resultado que convolucionar de la forma habitual.

+0

y hacer esto con CUDA, y será realmente muy rápido. Si cuda funciona en el entorno objetivo, es probable que obtenga el máximo rendimiento ... Las GPU son muy rápidas. La única forma de que cuda no gane es si la transferencia de datos a la GPU y viceversa comienza a dominar el tiempo. –

+0

¡Deseo que la transferencia de datos entre la tarjeta de video sea el problema! ¿Alguna sugerencia para bibliotecas preexistentes? – Bear

+2

El truco de Fourier es bueno para los núcleos de convolución grandes, pero para el ejemplo que se muestra, es solo 3x3. La forma simple es probablemente más rápida, pero si la FFT usa CUDA mientras que la manera simple no lo hace, no digamos sin medir. – DarenW

2

para el núcleo ejemplo 3x3 particular, me gustaría observo que

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 

y que el primero de estos es factorizable - puede ser enrevesado mediante la convolución de (1 1 1) para cada fila, y luego de nuevo para cada columna Luego reste nueve veces los datos originales. Esto puede o no ser más rápido, dependiendo de si los programadores desordenados lo hicieron lo suficientemente inteligente como para hacer esto automáticamente. (No lo he comprobado en mucho tiempo)

Es probable que desee hacer convoluciones más interesantes, donde el factoring puede o no ser posible.

1

Antes de decir C con ctypes, sugiero ejecutar una convolve independiente en C, para ver dónde está el límite.
Del mismo modo para CUDA, Cython, scipy.weave ...

Añadido 7feb: convolve33 datos de 8 bits con el recorte tarda unos 20 ciclos de reloj por punto, 2 ciclos de reloj por el acceso mem, en mi Mac G4 con PCC gcc 4.2.Su kilometraje variará.

Un par de sutilezas:

  • Le gustan los recortes correcta a 0..255? np.clip() es lento, cython etc. no lo sé.
  • Numpy/scipy puede necesitar memoria para temps del tamaño de A (así que mantenga 2 * sizeof (A) < tamaño de caché).
    Sin embargo, si su código C ejecuta una actualización en ejecución, esa es la mitad de la memoria pero un algoritmo diferente.

Por cierto, Google theano convolución => "Un op convolución que deben imitar scipy.signal.convolve2d, pero más rápido! En el desarrollo"

Cuestiones relacionadas