Улучшение производительности

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

В настоящее время я использую 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.

Спасибо!

EDIT:

Ускорьтесь примерно на 10 раз, перезаписав цикл питона в C с помощью Numpy.

Ответы

Ответ 1

Код в 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 по моим измерениям.

Ответ 2

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

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 программисты достаточно умными, чтобы автоматически это делать. (Я не проверял через некоторое время.)

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

Ответ 3

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

Добавлено 7feb: convolve33 8-битные данные с отсечением составляют ~ 20 тактов за точку, 2 тактовых цикла на доступ к памяти, на моем mac g4 pcc с gcc 4.2. Ваш пробег будет отличаться.

Несколько тонкостей:

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

Кстати, google theano convolve = > "Операция свертки, которая должна имитировать scipy.signal.convolve2d, но быстрее! В разработке"

Ответ 4

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