我目前正在做一些内存密集型的文本处理,为此我必须构造float32s
的sparse matrix
,维度为~ (2M, 5M)
。在阅读5 M个文档的语料库时,我逐列构造这个矩阵。为此,我使用SciPy
的稀疏dok_matrix
数据结构。然而,当到达第500000个文档时,我的记忆已满(大约使用了30 GB),程序崩溃。我最终想做的是使用sklearn
对矩阵执行降维算法,但是,正如前面所说,在内存中保存和构造整个矩阵是不可能的。我已经研究了numpy.memmap
,因为sklearn支持这一点,并尝试对SciPy稀疏矩阵的一些底层numpy数据结构进行memmap
,但我无法成功。
我不可能以密集格式保存整个矩阵,因为这将需要40 TB的磁盘空间。所以我认为HDF5
和PyTables
对我来说是没有选择的(?)。
我现在的问题是:我如何在运行中构建一个稀疏矩阵,但直接写入磁盘而不是内存,这样我就可以在sklearn中使用它了?
谢谢你!
4条答案
按热度按时间j0pj023g1#
在单细胞基因组学数据处理磁盘上的大型稀疏数据集时,我们也遇到过类似的问题。我将向大家展示一个简单的小例子来说明我将如何处理这个问题。我的假设是,内存非常有限,可能无法同时将稀疏矩阵的多个副本放入内存。即使您无法将一个完整的副本放入内存,这个方法也会起作用。
我将在磁盘上逐列构建稀疏CSC矩阵。稀疏CSC矩阵使用3个底层数组:
data
:存储在矩阵中的值indices
:矩阵中每个值的行索引indptr
:长度为n_cols + 1
的数组,该数组将indices
和data
除以它们所属的列。作为说明性示例,列
i
的值存储在data
的范围indptr[i]:indptr[i+1]
中。类似地,这些值的行索引可以通过indices[indptr[i]:indptr[i+1]]
找到。为了模拟您的数据生成过程(我假设是解析文档),我将定义一个函数
process_document
,它返回相关文档的indices
和data
的值。现在,我将在hdf5文件中创建一个组,该文件将存储稀疏矩阵的组成数组。
最后是一个函数,用于阅读这个组作为稀疏矩阵(这个函数非常简单)。
现在,我们将创建磁盘上的稀疏矩阵,并一次向其中写入一列(我使用了较少的列,因为这可能会有点慢)。
同样,这也是考虑到内存非常有限的情况,在这种情况下,您可能无法在创建稀疏矩阵时将整个稀疏矩阵放入内存。如果您可以处理整个稀疏矩阵加上至少一个副本,则有一种更快的方法可以做到这一点,那就是不使用磁盘存储(类似于其他建议)。然而,对以下代码进行轻微修改应该会给予更好的性能:
这应该是相当快的,因为它只在你连接数组时复制数据。其他当前发布的解决方案在你处理数组时重新分配数组,复制许多大数组。
mbskvtky2#
如果你能提供一个最小的工作代码,那就太好了。我看不出你的矩阵是因为构造而变得太大了(1)还是因为你有太多的数据(2)。如果你真的不关心自己建立这个矩阵,你可以直接看我的备注2。
对于问题(1),在下面的示例代码中,我创建了一个 Package 器类来逐块构建csr_matrix。(行、列、数据)元组,直到达到缓冲区限制当达到限制时,它将减少内存中的数据,因为csr_matrix构造函数添加的数据具有相同(row,column)元组.这部分只允许你以快速的方式构造稀疏矩阵(比为每一行创建稀疏矩阵快得多),并避免当一个单词在文档中出现多次时,由于(row,column)的冗余而导致的内存错误。
对于问题(2),你可以很容易地扩展这个类,通过添加一个保存方法,在达到极限(或第二个极限)时将矩阵存储在磁盘上。这样,你将在磁盘上得到多个稀疏矩阵块。然后你将需要一个可以处理块矩阵的降维算法(见注解2)。
备注1:这里的缓冲区限制并没有真正定义好。最好检查numpy数组data_temp,col_indices_temp和row_indices_temp的实际大小,并与机器上可用的RAM进行比较(这很容易用python自动化)。
备注2:gensim是一个python库,它在使用分块文件构建NLP模型方面有很大的优势。因此,你可以用这个库构建一个字典,构造一个稀疏矩阵,并降低它的维数,而不需要太多的RAM。
r1zk6ea13#
我假设所有的数据都可以使用一种更适合内存的稀疏矩阵格式(如COO)放入内存。如果不能,那么即使使用
mmap
,也几乎没有希望继续使用sklearn
。实际上,sklearn
很可能会创建后续对象,其内存需求与输入的数量级相同。Scipy的
dok_matrix
实际上是vanilladict
的一个子类。它们使用单独的python对象和大量的指针来存储数据,因此内存效率不高。最紧凑的表示是coo_matrix
。您可以通过为坐标(行和列)和数据预分配数组来增量地构建创建COO矩阵所需的数据;如果最初的猜测是错误的,最终会增加这些缓冲区。你可以用随机生成的数据来测试它,如下所示:
有了COO矩阵后,您可能需要使用
coo.tocsr()
转换为CSR。CSR矩阵对于点积等常见运算进行了更优化。如果某些行最初为空,则需要更多内存。这是因为它存储所有行的指针,甚至是空行。jqjz2hbq4#
看看here,最后他解释了如何直接存储和读取稀疏矩阵到一个Hdf5文件。