2010-02-04 1 views
18

Я хотел бы улучшить производительность свертки с помощью python и надеялся на некоторое понимание того, как лучше всего улучшить производительность.Улучшение производительности Numpy

настоящее время я использую SciPy выполнить свертку, используя код несколько как ниже фрагмент кода:

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) 

Я обработки данных изображения, используя оттенки серого (целые значения от 0 до 255), и я в настоящее время получают около четверти секунды за свертку. Мое мышление состояло в том, чтобы сделать одно из следующего:

Использовать corepy, желательно с некоторой оптимизацией Перекомпилировать numpy с icc & ikml. Используйте python-cuda.

Мне было интересно, есть ли у кого-нибудь опыт с любым из этих подходов (какой тип выигрыша будет типичным, и если он того стоит), или если кто-то знает о лучшей библиотеке для выполнения свертки с помощью Numpy.

Спасибо!

РЕДАКТИРОВАТЬ:

Скорость до приблизительно в 10 раз путем повторного написания цикла питона в C по сравнению с использованием Numpy.

ответ

10

Код в scipy для выполнения 2d сверток немного беспорядочен и неоптимизирован. См. http://svn.scipy.org/svn/scipy/trunk/scipy/signal/firfilter.c, если вы хотите взглянуть на низкоуровневое функционирование scipy.

Если все, что вы хотите, чтобы обработать с небольшим постоянным ядром, как тот, который вы показали, функция, как это может работать:

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 

Эта функция использует отделимости ядра, как DarenW предложил выше, а также использовать более оптимизированные арифметические операции numpy. Мои измерения были более чем в 1000 раз быстрее, чем функция convolve2d.

+0

Спасибо, что указали, что я не считал, что scipy convolve может быть неэффективным. Похоже, хотя я не слишком внимательно проверял, что scipy convolve выполняет довольно немного операций манипуляции с памятью и имеет ряд утверждений if, замедляющих работу. Я отправлю результаты и благодарю всех за ваши комментарии. – Bear

+1

Да, convolve2d довольно неэффективен, так как он имеет дело с общим случаем (он имеет дело с произвольными объектами - вы должны быть способны свернуться с массивом десятичных объектов, например). Я думаю, что его можно значительно ускорить, используя специальные кодеки для общего случая (в частности, чтобы избежать вызова указателя функции внутри тройного цикла, который, скорее всего, будет одним из хостов. –

0

Типичной оптимизацией для свертки является использование БПФ вашего сигнала. Причина в том, что свертка в реальном пространстве является продуктом в пространстве FFT. Часто быстрее вычислять БПФ, затем продукт и iFFT результата, а не свертывать обычным способом.

+0

И сделать это с помощью CUDA, и это будет действительно очень быстро. Если cuda работает в целевой среде, он, скорее всего, получит максимальную производительность ... Графические процессоры очень быстрые. Единственный способ, с помощью которого cuda не выиграть, - это то, что передача данных на GPU и обратно начинает доминировать над временем. –

+0

Я хочу, чтобы передача данных взад и вперед между видеокартой была проблемой! Любые предложения для уже существующих библиотек? – Bear

+2

Трюк Fourier хорош для больших ядер свертки, но для показанного примера это всего лишь 3x3. Простой способ, вероятно, быстрее - но если FFT использует CUDA, а простой способ - нет, не сообщая без измерения. – DarenW

2

Для конкретного примера 3х3 ядра, я бы заметить, что

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 

и что первый из них является факторизуема - он может быть свернут путем свертки (1 1 1) для каждой строки, а затем снова для каждого столбца. Затем вычитайте в девять раз исходные данные. Это может быть или не быть быстрее, в зависимости от того, сделали ли scipy программисты достаточно умными, чтобы автоматически это делать. (Я не проверял через некоторое время.)

Возможно, вы захотите сделать более интересные свертки, где факторинг может быть или может быть невозможным.

1

Перед тем, как сказать C с помощью ctypes, я предлагаю запустить автономный сверток на C, чтобы увидеть, где предел.
Аналогично для CUDA, Cython, scipy.weave ...

Добавлено 7feb: convolve33 8-битные данные с вырезку занимает ~ 20 тактов на точку, 2 тактов на доступ MEM, на мой макинтош g4 ОКК gcc 4.2.Ваш пробег будет различны.

пар тонкостей:

  • вы заботитесь о правильной вырезке к 0..255? np.clip() медленный, cython и т. д. не знаю.
  • Возможно, для работы с Numpy/scipy вам потребуется память для темпов размером A (так что держите 2 * sizeof (A) < размер кеша).
    Если ваш C-код, однако, выполняет текущее обновление inplace, это половина mem, но отличается от другого.

Кстати, Google theano скручивать => «Свертка ор, который должен имитировать scipy.signal.convolve2d, но быстрее! В развитии»