Python 减少使用numpy的代码行的内存使用
我正在使用Python 减少使用numpy的代码行的内存使用,python,numpy,memory,Python,Numpy,Memory,我正在使用python库: 用于规范化我的数据集(scRNAseq)和库文件utils.py中的最后一行: 使用太多内存: np.percentile(matrix[np.any(matrix > 0, axis=1)], p, axis=0) 有没有重写这行代码以提高内存使用率的好方法?我的意思是,我可以在集群上访问200GbRAM,使用20Gb之类的matrix,这行代码不起作用,但我相信应该有办法让它起作用。我将此作为一个答案,因为注释中包含的内容可能不完整。有两件可疑的事情-
python
库:
用于规范化我的数据集(scRNAseq
)和库文件utils.py
中的最后一行:
使用太多内存:
np.percentile(matrix[np.any(matrix > 0, axis=1)], p, axis=0)
有没有重写这行代码以提高内存使用率的好方法?我的意思是,我可以在集群上访问
200Gb
RAM
,使用20Gb
之类的matrix
,这行代码不起作用,但我相信应该有办法让它起作用。我将此作为一个答案,因为注释中包含的内容可能不完整。有两件可疑的事情-如果您的机器有200Gb的可用ram,那么第一个百分比在20Gb矩阵上应该可以正常运行。这是一个很大的内存,所以开始看看还有什么可能在使用它。从top
开始-是否有其他进程,或者您的python程序是否使用了所有这些
第二个可疑之处是utils.percentile
的文档与它的实际行为不匹配。以下是您链接到的代码中的相关位:
def percentile(matrix, p):
"""
Estimation of percentile without zeros.
....
Returns
-------
float
Calculated percentile.
"""
return np.percentile(matrix[np.any(matrix > 0, axis=1)], p, axis=0)
它实际做的是返回为不全为零的行计算的(按列)百分位数编辑至少包含一个正元素的行。如果值是非负的,这是同一回事,但通常情况下,这将是一个非常不同的结果
np.any(矩阵>0,轴=1)
返回一个布尔数组来索引不全为零的行。比如说
>>> np.any(array([[3, 4], [0, 0]]) > 0, axis=1)
array([ True, False])
>>> np.any(array([[3, 4], [1, 0]]) > 0, axis=1)
array([ True, True])
>>> np.any(array([[3, 0], [1, 0]]) > 0, axis=1)
array([ True, True])
该数组用于索引矩阵
,它只选择不全为零的行并返回这些行。
如果你不熟悉这种索引方法,你应该仔细阅读
需要大量内存的计算-matrix>0
创建一个与matrix维度相同的布尔数组,然后索引创建一个可能包含大多数行的matrix
副本。因此,布尔数组可能为2-4Gb,副本可能接近20Gb 可以减少,
## Find rows with all zeros, one row at a time to reduce memory
mask = [np.any(r > 0) for r in matrix]
## Find percentile for each column, excluding rows with all zeros
perc = [np.percentile(c[mask], p) for c in matrix.T]
但是,如前所述,这与函数文档不匹配
这种逻辑可能有其原因,但它很奇怪。
如果您不知道原因,可以直接调用np.percentile
——只需检查它是否为较小的数据子集返回一个接近值。
还有nanpercentile
,可以以相同的方式使用,但忽略nan
值。您可以使用布尔索引来替换不希望包含在
nan
中的值(即matrix[matrix<0]=np.nan
),然后调用该值。如果matrix
的所有元素都>=0,则可以执行以下操作:
np.percentile(matrix[np.any(matrix, axis = 1)], p, axis = 0)
这使用了一个事实,即当作为布尔值查看时,0
以外的任何浮点或整数都被解释为True
(这是np.any
内部执行的)。避免了单独构建大型布尔矩阵
由于您在矩阵[…]中进行布尔索引
,因此您正在创建一个临时副本,而您并不真正关心它是否在百分位数
过程中被覆盖。因此,您可以使用overwrite\u input=True
来节省更多内存
mat = matrix.copy()
perc = np.percentile(matrix[np.any(matrix, axis = 1)], p, axis = 0, overwrite_input = True)
np.array_equals(mat, matrix) # is `matrix` still the same?
True
最后,根据您的其他架构,我建议您考虑制作
matrix
某种风格的scipy.sparse
,这将再次显著降低内存使用率(尽管根据您使用的类型会有一些缺点)。我最近回答的一个问题与此类似(使用np.median
,它在内部使用百分位)。基本上,您需要采用每列百分位来防止其占用内存,类似于“[np.percentile(c,p)for c in cmatrix[np.any(矩阵>0,轴=1)]].T”()好的,那么什么是cmatrix
?仅仅是matrix.T
?还是仅仅是一个打字错误?我实际上将行更改为[np.百分位(c,p)表示矩阵中的c[np.any(矩阵>0,轴=1)]].T
但它没有解决内存问题。可能应该进一步改进。是的,这是一个打字错误。您如何测量内存使用率?我还需要将其更改为np.array([np.percentile(c,p)表示矩阵中的c[np.any(矩阵>0,轴=1)]).T
因为列表
没有.T
功能。但它仍然不工作:我收到内存错误。仍然无法修复。错误仍然存在。我尝试使用sacct--format="JobID,expead,MaxVMSize
,它为作业提供了205Gb
的内存。对,所以这一行可能不是导致内存问题的那一行。它只是因为进程的某个部分占用内存而导致最终失败的那一行。您运行了top
吗?它显示了什么?我运行了,但它是SLURM集群,确实如此不显示任何相关内容我认为答案不错,但我不确定为什么要比较mat
和matrix
。即使它们对某些输入比较为真,但在使用overwrite\u input=true
@user2699时也不能保证。你能给出一个不起作用的示例吗?我找不到任何示例,以及matrix
应该是一个副本,而不是原始matrix
的视图。因此,如果它被更改,它不应该影响matrix
本身。你是对的。因为它在副本上运行,所以原始版本不会更改,我错过了。