Улучшение производительности Numpy - PullRequest
18 голосов
/ 04 февраля 2010

Я хотел бы улучшить производительность свертки с использованием 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. 1010 *

Спасибо!

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

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

Ответы [ 5 ]

10 голосов
/ 05 февраля 2010

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

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

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, предложенные выше, а также преимущества более оптимизированных подпрограмм арифметики. По моим измерениям это в 1000 раз быстрее, чем функция convolve2d.

2 голосов
/ 04 февраля 2010

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

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

1 голос
/ 05 февраля 2010

Перед тем, как сказать 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 может потребоваться память для временных файлов размером A (поэтому оставьте 2 * sizeof (A) <размер кэша). <br> Если ваш C-код, тем не менее, выполняет текущее обновление на месте, это наполовину меньше, но другой алгоритм.

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

0 голосов
/ 08 июня 2018

Похоже, что с 2018 года комбо SciPy / Numpy значительно ускорилось.Это то, что я видел на своем ноутбуке (Dell Inspiron 13, i5).OpenCV сделал все возможное, но вы не имеете никакого контроля над режимами.

>>> img= np.random.rand(1000,1000)
>>> kernel = np.ones((3,3), dtype=np.float)/9.0
>>> t1= time.time();dst1 = cv2.filter2D(img,-1,kernel);print(time.time()-t1)
0.0235188007355
>>> t1= time.time();dst2 = signal.correlate(img,kernel,mode='valid',method='fft');print(time.time()-t1)
0.140458106995
>>> t1= time.time();dst3 = signal.convolve2d(img,kernel,mode='valid');print(time.time()-t1)
0.0548939704895
>>> t1= time.time();dst4 = signal.correlate2d(img,kernel,mode='valid');print(time.time()-t1)
0.0518119335175
>>> t1= time.time();dst5 = signal.fftconvolve(img,kernel,mode='valid');print(time.time()-t1)
0.13204407692
0 голосов
/ 04 февраля 2010

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

...