Есть несколько оптимизаций (использования памяти), которые вы можете сделать здесь ... Несколько уловок, о которых следует помнить:
- Большинство функций numpy принимают параметр
out
, который можно использовать дляукажите выходной массив вместо возврата копии.Например, np.sqrt(x, x)
возьмет квадратный корень массива на месте. x += 1
использует половину памяти, которую x = x + 1
делает, поскольку последняя создает временную копию.Когда это возможно, попробуйте разделить вычисления на *=
, +=
, /=
и т. Д. Если это не так, используйте Numberxpr, как предложено @eumiro.(Или просто использовал NumberxPr независимо от ... Во многих случаях это довольно удобно.)
Итак, во-первых, вот производительность вашей исходной функции с массивом случайных данных 10000x10000 и filtsize
из 3:
Профиль использования памяти исходной функции 
Что интересно, так это большие шипы на конце.Это происходит во время вашего numpy.select(...)
бита.Есть много мест, где вы случайно создаете дополнительные временные массивы, но они в основном не имеют значения, так как они перегружены тем, что происходит во время вызова select
.
в любомОцените, если мы заменим ваш оригинальный (чистый и компактный) код этой довольно многословной версией, вы можете значительно оптимизировать использование памяти:
import numpy
import scipy.ndimage
def main(x=None):
if x is None:
ni, nj = 10000, 10000
x = numpy.arange(ni*nj, dtype=numpy.float32).reshape(ni,nj)
filtsize = 3
nlooks = 10.0
dfactor = 10.0
x = enh_lee(x, filtsize, nlooks, dfactor)
return x
def moving_average(Ic, filtsize):
Im = numpy.empty(Ic.shape, dtype='Float32')
scipy.ndimage.filters.uniform_filter(Ic, filtsize, output=Im)
return Im
def moving_stddev(Ic, filtsize):
Im = numpy.empty(Ic.shape, dtype='Float32')
scipy.ndimage.filters.uniform_filter(Ic, filtsize, output=Im)
Im *= -1
Im += Ic
Im **= 2
scipy.ndimage.filters.uniform_filter(Im, filtsize, output=Im)
return numpy.sqrt(Im, Im)
def enh_lee(Ic, filtsize, nlooks, dfactor):
# Implementation based on PCI Geomatica's FELEE function documentation
Ci = moving_stddev(Ic, filtsize)
Im = moving_average(Ic, filtsize)
Ci /= Im
Cu = numpy.sqrt(1 / nlooks).astype(numpy.float32)
Cmax = numpy.sqrt(1 + (2 * nlooks)).astype(numpy.float32)
W = Ci.copy()
W -= Cu
W *= -dfactor
W /= Cmax - Ci
W = numpy.exp(W, W)
If = Im * W
W *= -1
W += 1
W *= Ic
If += W
del W
# Replace the call to numpy.select
out = If
filter = Ci <= Cu
numpy.putmask(out, filter, Im)
del Im
filter = Ci >= Cmax
numpy.putmask(out, filter, Ic)
return out
if __name__ == '__main__':
main()
Вот полученный профиль памяти для этого кода:
Профиль использования памяти оптимизированной версией на базе Numpy 
Итак, мы значительно сократили использование памяти, но код несколько менее читабелен (imo).
Однако эти последние три пика являются двумя numpy.where
вызовами ...
Если numpy.where
принял параметр out
, мы могли бы еще больше уменьшить пиковое использование памяти еще на ~ 300 МБили так.К сожалению, этого не происходит, и я не знаю более эффективного способа памяти ...
Мы можем использовать numpy.putmask
для замены вызова на numpy.select
ивыполнить операцию на месте (спасибо @eumiro за , упомянув это в совершенно другом вопросе .)
Если мы оптимизируем вещи с помощью Numberxpr, мы получим значительно более чистый код (по сравнению с чистым- версия выше, а не оригинал).Возможно, вы могли бы немного уменьшить использование памяти в этой версии ... Я не очень знаком с Numberxpr, за исключением того, что использовал его несколько раз.
import numpy
import scipy.ndimage
import numexpr as ne
def main(x=None):
if x is None:
ni, nj = 10000, 10000
x = numpy.arange(ni*nj, dtype=numpy.float32).reshape(ni,nj)
filtsize = 3
nlooks = 10.0
dfactor = 10.0
x = enh_lee(x, filtsize, nlooks, dfactor)
return x
def moving_average(Ic, filtsize):
Im = numpy.empty(Ic.shape, dtype='Float32')
scipy.ndimage.filters.uniform_filter(Ic, filtsize, output=Im)
return Im
def moving_stddev(Ic, filtsize):
Im = numpy.empty(Ic.shape, dtype='Float32')
scipy.ndimage.filters.uniform_filter(Ic, filtsize, output=Im)
Im = ne.evaluate('((Ic-Im) ** 2)')
scipy.ndimage.filters.uniform_filter(Im, filtsize, output=Im)
return ne.evaluate('sqrt(Im)')
def enh_lee(Ic, filtsize, nlooks, dfactor):
# Implementation based on PCI Geomatica's FELEE function documentation
Ci = moving_stddev(Ic, filtsize)
Im = moving_average(Ic, filtsize)
Ci /= Im
Cu = numpy.sqrt(1 / nlooks).astype(numpy.float32)
Cmax = numpy.sqrt(1 + (2 * nlooks)).astype(numpy.float32)
W = ne.evaluate('exp(-dfactor * (Ci - Cu) / (Cmax - Ci))')
If = ne.evaluate('Im * W + Ic * (1 - W)')
del W
out = ne.evaluate('where(Ci <= Cu, Im, If)')
del Im
del If
out = ne.evaluate('where(Ci >= Cmax, Ic, out)')
return out
if __name__ == '__main__':
main()
А вот профиль использования памяти для версии Numberxpr: (Обратите внимание, что время выполнения более чем в два раза по сравнению с оригиналом!)
Профиль использования памятиоптимизированной версии на основе Numexpr * 
Наибольшее использование памяти по-прежнему сохраняется во время вызовов на where
(вместо вызова на select
).Тем не менее, пиковое использование памяти было значительно сокращено.Самый простой способ еще больше уменьшить это - найти способ работы на одном из массивов select
.Это было бы довольно легко сделать с помощью Cython (вложенные циклы были бы довольно медленными в чистом Python, и любой тип логического индексирования в Numpy создаст дополнительную копию).Вам может быть лучше, просто разделив входной массив, как вы делали, хотя ...
Так же, как примечание, обновленные версии выдают тот же результат, что и исходный код.В оригинальном кодовом коде была опечатка ...