我原以为scipy
的稀疏矩阵使用的内存要比朴素的列表表示少得多,但实验证明我错了。在下面的代码片段中,我构建了一个随机二进制矩阵,其中大约75%为零,然后将内存使用情况与scipy.sparse
(简化的IPython会话)中的每个可用表示进行比较:
# %load_ext memory_profiler, from scipy import sparse, etc.
# ...
%memit M = random_binary_matrix(5000, 5000) # contains ints
peak memory: 250.36 MiB, increment: 191.77 MiB
In : sum(line.count(0) for line in M) / (len(M) * len(M[0]))
Out: 0.75004468
%memit X_1 = sparse.bsr_matrix(M)
peak memory: 640.49 MiB, increment: 353.76 MiB
%memit X_2 = sparse.coo_matrix(M)
peak memory: 640.71 MiB, increment: 286.09 MiB
%memit X_3 = sparse.csc_matrix(M)
peak memory: 807.51 MiB, increment: 357.53 MiB
%memit X_4 = sparse.csr_matrix(M)
peak memory: 840.04 MiB, increment: 270.91 MiB
%memit X_5 = sparse.dia_matrix(M)
peak memory: 1075.20 MiB, increment: 386.87 MiB
%memit X_6 = sparse.dok_matrix(M)
peak memory: 3059.86 MiB, increment: 1990.62 MiB
%memit X_7 = sparse.lil_matrix(M)
peak memory: 2774.67 MiB, increment: 385.39 MiB
字符串
我做错什么了吗?我是否错过了什么(包括这些替代表示的要点)?
还是memory_profiler
,或者我对它缺乏理解,应该受到责备?特别是,“峰值记忆”和“增量”之间的关系有时似乎是可疑的:初始化X_2
会使内存使用量增加286.09 MiB,但峰值内存使用量仅略高于执行该行之前的水平。
如果重要的话:我正在运行Debian 12、Python 3.11.2、IPython 8.5.0、scipy 1.10.1、memory_profiler 0.61.0
2条答案
按热度按时间6ljaweal1#
从密集矩阵创建稀疏矩阵会导致完全填充的稀疏矩阵(包括显式零)。这是因为scipy不知道您希望用于排除接近零的值的容差。可以说,它应该附带一个工厂方法来为您完成这项工作,但我们总是可以手动构建一个。
稀疏矩阵带有许多不同的构造函数参数组合。看起来您只想排除显式的零。对于这个,像这样的东西应该工作得很好:
字符串
对于其他公差,您可以使用以下内容:
型
您也可以先创建一个完全填充的稀疏矩阵,然后在其上调用
eliminate_zeros()
,但这会导致暂时更高的内存使用率。iyzzxitl2#
在不添加
memit
的情况下,使用更小的维度,下面是一个coo矩阵:字符串
它的稠密等价物:
型
主数组的内存使用-数据和坐标:
型
转换为csr可以通过将
M.row
替换为indptr
来减少内存使用。我说可以,因为这取决于形状。CSC也一样。型
对于“random”矩阵,
bsr
没有帮助:型
lil的内存更难估计,因为它将信息存储在对象dtype数组中,如列表:
型
dok
是一个字典:型
这些都没有考虑转换期间的内存使用。从dense到coo需要一个
nonzero
调用来获取所有非零坐标。如果这些数组是正确的类型,则从data/row/col输入进行coo可能不会增加任何内存使用。从coo中生成企业社会责任可能需要对坐标进行词法排序。大多数数学都是以CSR格式完成的,因此格式之间可能有很多隐藏的转换。lil和dok对于迭代输入是好的,但是在其他方面是慢的并且是更存储密集的。