2013-05-23 4 views
5

Свертка в Matlab в два раза быстрее свертки в Numpy.Свертка медленнее в Numpy, чем в Matlab?

код Python (занимает 19 секунд на моей машине):

import numpy as np 
from scipy import ndimage 
import time 

img = np.ones((512,512,512)) 
kernel = np.ones((5,5,5))/125 

start_time = time.time() 
ndimage.convolve(img,kernel,mode='constant') 
print "Numpy execution took ", (time.time() - start_time), "seconds" 

Matlab код (занимает 8,7 секунды на моей машине):

img = ones(512,512,512); 
kernel = ones(5,5,5)/125; 
tic 
convn(img, kernel, 'same'); 
toc 

Оба дают одинаковые результаты.

Есть ли способ улучшить Numpy, чтобы соответствовать или превосходить производительность Matlab здесь?

Интересно, что этот коэффициент или ~ 2 разницы во время работы согласован во многих размерах ввода.

+0

На самом деле это не игра Python, вы здесь, но NumPy/SciPy. Можете ли вы улучшить производительность этих модулей? Конечно, но не написав код Python. – kindall

+0

Отредактировано (с/Python/Numpy /). – naroom

+2

Вы можете посмотреть, какие библиотеки numpy построены против vs matlab. По личному опыту я знаю, что, когда numpy создается против библиотеки Intel MKL, я получаю намного лучшую производительность для некоторых операций, чем с настройками по умолчанию. – JoshAdel

ответ

3

Не anwer; просто комментарий:

Прежде чем сравнивать производительность, вам необходимо убедиться, что обе функции возвращают один и тот же результат:

Если CONVN MatLab возвращает тот же результат, как CONVN Октава, то convn отличается ndimage.convolve:

octave> convn(ones(3,3), ones(2,2)) 
ans = 

    1 2 2 1 
    2 4 4 2 
    2 4 4 2 
    1 2 2 1 

In [99]: ndimage.convolve(np.ones((3,3)), np.ones((2,2))) 
Out[99]: 
array([[ 4., 4., 4.], 
     [ 4., 4., 4.], 
     [ 4., 4., 4.]]) 

ndimage.convolve имеет другие режимы, 'reflect','constant','nearest','mirror', 'wrap', но ни один из ("полного") поведения этого матча convn «s по умолчанию.


Для 2D массивов, scipy.signal.convolve2d быстрее, чем scipy.signal.convolve.

Для 3D-массивов, scipy.signal.convolve, кажется, имеет такое же поведение, как convn(A,B):

octave> x = convn(ones(3,3,3), ones(2,2,2)) 
x = 

ans(:,:,1) = 

    1 2 2 1 
    2 4 4 2 
    2 4 4 2 
    1 2 2 1 

ans(:,:,2) = 

    2 4 4 2 
    4 8 8 4 
    4 8 8 4 
    2 4 4 2 

ans(:,:,3) = 

    2 4 4 2 
    4 8 8 4 
    4 8 8 4 
    2 4 4 2 

ans(:,:,4) = 

    1 2 2 1 
    2 4 4 2 
    2 4 4 2 
    1 2 2 1 

In [109]: signal.convolve(np.ones((3,3,3), dtype='uint8'), np.ones((2,2,2), dtype='uint8')) 
Out[109]: 
array([[[1, 2, 2, 1], 
     [2, 4, 4, 2], 
     [2, 4, 4, 2], 
     [1, 2, 2, 1]], 

     [[2, 4, 4, 2], 
     [4, 8, 8, 4], 
     [4, 8, 8, 4], 
     [2, 4, 4, 2]], 

     [[2, 4, 4, 2], 
     [4, 8, 8, 4], 
     [4, 8, 8, 4], 
     [2, 4, 4, 2]], 

     [[1, 2, 2, 1], 
     [2, 4, 4, 2], 
     [2, 4, 4, 2], 
     [1, 2, 2, 1]]], dtype=uint8) 

Обратите внимание, что np.ones((n,m,p)) создает поплавка массива по умолчанию. Кажется, что Matlab ones(n,m,p) создает массив ints. Чтобы сделать хорошее сравнение, вы должны попытаться сопоставить dtype массивов numpy с типом Matlab-матриц.

+1

Как раз на стороне примечания, 'scipy.signal.convolve2d' по умолчанию имеет такое же поведение, как' conv' matlab/octave. В 'scipy.signal'' mode' kwarg управляет размером результата (аналогично 'conv'), а в' ndimage', 'mode' kwarg (в основном) управляет граничными условиями. Это соглашение об именах, которое приводит к большой путанице при сравнении вещей ... –

+0

Matlab's() делает матрицу двойников, поэтому типы действительно совпадают. – naroom

+1

@naroom - Тем не менее, если вам не нужны поплавки, использование ints даст в этом случае ускорение ~ 20x. –

8

Что именно вы делаете? Существует ряд оптимизаций, которые ndimage обеспечивает, если вам не нужна общая свертка N-d.

Например, текущая операция:

img = np.ones((512,512,512)) 
kernel = np.ones((5,5,5))/125 
result = ndimage.convolve(img, kernel) 

эквивалентно:

img = np.ones((512,512,512)) 
result = ndimage.uniform_filter(img, 5) 

Однако, есть большая разница в скорости выполнения:

In [22]: %timeit ndimage.convolve(img, kernel) 
1 loops, best of 3: 25.9 s per loop 

In [23]: %timeit ndimage.uniform_filter(img, 5) 
1 loops, best of 3: 8.69 s per loop 

Разница обусловлена по uniform_filter, образуя 1-мерную свертку вдоль каждой оси, вместо общего 3 D свертки.

В случаях, когда ядро ​​является симметричным, вы можете сделать эти упрощения и значительно ускориться.

Я не уверен в convn, но часто функции Matlab делают такие оптимизации за кадром, если ваши входные данные соответствуют определенным критериям. Scipy чаще использует один алгоритм для каждой функции и ожидает, что пользователь узнает, какой из них выбрать в этом случае.


Вы упомянули фильтр «Лапласиан гауссова». Здесь я всегда смущаюсь терминологией.

Я думаю, что вы хотите с точки зрения ndimage функций либо scipy.ndimage.gaussian_laplace или scipy.ndimage.gaussian_filter с order=2 (который фильтрует второй производной от гауссовой).

Во всяком случае, оба упрощают операцию до 1-й свертки по каждой оси, что должно дать значительное (2-3x) ускорение.

+2

Хорошая мысль! Я работаю над проблемой сегментации 3D, поэтому фильтр, который мне действительно нужно запустить, представляет собой 3D-лапласиан гауссова (AKA Mexican Hat). Замена ядра Matlab на 'kernel = rand (5,5,5);' не изменил время его выполнения, поэтому, я не думаю, что Matlab здесь обманывает :) – naroom

+0

А, да, я думаю, что Matlab не «обманывать», тогда! :) –

+0

Я думаю, что вы хотите 'scipy.ndimage.gaussian_laplace', но я не уверен на 100% ... Я всегда немного путаюсь с различиями между различными краевыми фильтрами. Надеюсь, это предложение поможет, во всяком случае! –

Смежные вопросы