内存高效的 Python 批处理
Posted
技术标签:
【中文标题】内存高效的 Python 批处理【英文标题】:Memory efficient Python batch processing 【发布时间】:2012-11-04 01:36:29 【问题描述】:问题
我编写了一个小型 python 批处理器,它加载二进制数据、执行 numpy 操作并存储结果。它消耗的内存比它应该消耗的多得多。我查看了类似的stack-overflow discussions 并想寻求进一步的建议。
背景
我将光谱数据转换为 rgb。光谱数据存储在带线交错 (BIL) 图像文件中。这就是我逐行读取和处理数据的原因。我使用Spectral Python Library 读取数据,它返回一个numpy 数组。 hyp 是一个大型光谱文件的描述符:hyp.ncols=1600, hyp.nrows=3430, hyp.nbands=160
代码
import spectral
import numpy as np
import scipy
class CIE_converter (object):
def __init__(self, cie):
self.cie = cie
def interpolateBand_to_cie_range(self, hyp, hyp_line):
interp = scipy.interpolate.interp1d(hyp.bands.centers,hyp_line, kind='cubic',bounds_error=False, fill_value=0)
return interp(self.cie[:,0])
#@profile
def spectrum2xyz(self, hyp):
out = np.zeros((hyp.ncols,hyp.nrows,3))
spec_line = hyp.read_subregion((0,1), (0,hyp.ncols)).squeeze()
spec_line_int = self.interpolateBand_to_cie_range(hyp, spec_line)
for ii in xrange(hyp.nrows):
spec_line = hyp.read_subregion((ii,ii+1), (0,hyp.ncols)).squeeze()
spec_line_int = self.interpolateBand_to_cie_range(hyp,spec_line)
out[:,ii,:] = np.dot(spec_line_int,self.cie[:,1:4])
return out
内存消耗
所有大数据都在循环外初始化。我天真的解释是内存消耗不应该增加(我使用了太多的 Matlab 吗?)有人可以解释一下增加因子 10 吗?这不是线性的,因为 hyp.nrows = 3430。 有什么建议可以改善内存管理吗?
Line # Mem usage Increment Line Contents
================================================
76 @profile
77 60.53 MB 0.00 MB def spectrum2xyz(self, hyp):
78 186.14 MB 125.61 MB out = np.zeros((hyp.ncols,hyp.nrows,3))
79 186.64 MB 0.50 MB spec_line = hyp.read_subregion((0,1), (0,hyp.ncols)).squeeze()
80 199.50 MB 12.86 MB spec_line_int = self.interpolateBand_to_cie_range(hyp, spec_line)
81
82 2253.93 MB 2054.43 MB for ii in xrange(hyp.nrows):
83 2254.41 MB 0.49 MB spec_line = hyp.read_subregion((ii,ii+1), (0,hyp.ncols)).squeeze()
84 2255.64 MB 1.22 MB spec_line_int = self.interpolateBand_to_cie_range(hyp, spec_line)
85 2235.08 MB -20.55 MB out[:,ii,:] = np.dot(spec_line_int,self.cie[:,1:4])
86 2235.08 MB 0.00 MB return out
注释
我将 range 替换为 xrange 并没有大幅改进。我知道三次插值并不是最快的,但这与 CPU 消耗无关。
【问题讨论】:
spec_line_int
的形状和数据类型是什么?
您使用的是什么操作系统?在 Linux 上,您需要知道报告的内存是否包含 buffers and cache,否则这些数字可能会产生误导。
您可以通过在 numpy 函数(例如np.dot(spec_line_int, self.cie[:,1:4], out=out[:,ii,:])
)中使用输出参数来节省一些 CPU 时间并减少堆碎片,以避免临时 numpy 数组的分配/释放。 (我真的不相信它会解决你的问题,但谁知道呢?)
根据文档spectral
库在真正访问之前不会读取数据:spectralpython.sourceforge.net/…。这可以解释为什么在处理数据时内存消耗会增加。
【参考方案1】:
感谢 cmets。他们都帮助我改善了一点内存消耗。 但最终我弄清楚了内存消耗的主要原因是什么:
SpectralPython 图像包含一个 Numpy Memmap 对象。这与高光谱数据立方体的数据结构具有相同的格式。 (如果是 BIL 格式(nrows、nbands、ncols)) 调用时:
spec_line = hyp.read_subregion((ii,ii+1), (0,hyp.ncols)).squeeze()
图像不仅作为numpy数组返回值返回,而且还缓存在hyp.memmap中。第二次调用会更快,但在我的情况下,内存只会增加,直到操作系统抱怨。由于 memmap 实际上是一个很好的实现,我将在以后的工作中直接利用它。
【讨论】:
从spectral 0.15.0 版开始,所有文件读取方法(例如您问题中的read_subregion
)都接受可选的use_memmap
参数,可以将其设置为False 以避免使用内存映射接口。这将导致该方法改为直接读取文件,这通常会比 memmap 慢,但会减少内存消耗。以上是关于内存高效的 Python 批处理的主要内容,如果未能解决你的问题,请参考以下文章