Вот векторизация с использованием broadcasting
-
def gaussian_filter_vectorized(mode, size, cutoff):
cutoffTerm = 2 * (cutoff ** 2)
v = np.asarray([size[0] // 2, size[1] // 2])
I,J = np.ogrid[:size[0],:size[1]]
p,q = I-v[0],J-v[1]
Dsq = p**2 + q**2
d = -1 * Dsq
R = np.power(np.e,d/cutoffTerm)
if mode == 'low':
return R
elif mode == 'high':
return 1-R
Сроки на больших size
х
In [80]: N = 100
...: %timeit gaussian_filter(mode='low', size=(N,N), cutoff=N)
...: %timeit gaussian_filter_vectorized(mode='low', size=(N,N), cutoff=N)
10 loops, best of 3: 65.2 ms per loop
1000 loops, best of 3: 225 µs per loop
In [81]: N = 1000
...: %timeit gaussian_filter(mode='low', size=(N,N), cutoff=N)
...: %timeit gaussian_filter_vectorized(mode='low', size=(N,N), cutoff=N)
1 loop, best of 3: 6.5 s per loop
10 loops, best of 3: 29.8 ms per loop
200x+
ускорения!
Использование numexpr
для больших вычислений данных для дальнейшей работы. увеличить
При работе с большими данными мы также можем использовать numexpr
модуль , который поддерживает многоядерную обработку, если предполагаемые операции могут быть выражены как арифметические. Чтобы решить наш случай, мы можем заменить шаги в Dsq = p**2 + q**2
и R = np.power(np.e,d/cutoffTerm)
эквивалентными numexpr
, используя функцию numexpr.evaluate
.
Итак, мы бы получили что-то вроде этого -
import numexpr as ne
def gaussian_filter_vectorized_numexpr(mode, size, cutoff):
cutoffTerm = 2 * (cutoff ** 2)
I,J = np.ogrid[:size[0],:size[1]]
v0,v1 = size[0] // 2, size[1] // 2
p,q = I-v0,J-v1
E = np.e
if mode == 'low':
return ne.evaluate('E**(-1*(p**2+q**2)/cutoffTerm)')
elif mode == 'high':
return ne.evaluate('1-E**(-1*(p**2+q**2)/cutoffTerm)')
Время на 1920x1080
изображении размера -
In [2]: M,N=1920,1080
...: %timeit gaussian_filter(mode='low', size=(M,N), cutoff=N)
...: %timeit gaussian_filter_vectorized(mode='low', size=(M,N), cutoff=N)
...: %timeit gaussian_filter_vectorized_numexpr(mode='low',size=(M,N),cutoff=N)
1 loop, best of 3: 13.9 s per loop
10 loops, best of 3: 63.3 ms per loop
100 loops, best of 3: 9.48 ms per loop
Близко к 1500x
ускорение здесь!
Это было с 8
темами. Таким образом, с большим количеством потоков, доступных для вычислений, это должно улучшиться дальше. Related post
о том, как управлять многоядерными функциями.