我目前想要使用Python中的余弦相似度和Tfidf功能来计算所有对文档的相似度。我的基本方法如下:
from sklearn.feature_extraction.text import TfidfVectorizer
#c = [doc1, doc2, ..., docn]
vec = TfidfVectorizer()
X = vec.fit_transform(c)
del vec
Y = X * X.T
工作完全正常,但不幸的是,不适用于我非常大的数据集。 X的维度为(350363, 2526183)
,因此输出矩阵Y应为(350363, 350363)
。由于tfidf功能,X非常稀疏,因此很容易适合内存(仅约2GB)。然而,在运行一段时间后,乘法会给我一个内存错误(即使内存不满但我认为scipy是如此聪明以至于期望内存使用)。
我已经尝试过使用dtypes而没有任何成功。我还确保numpy和scipy将他们的BLAS库链接起来 - 虽然这对csr_matrix点功能没有影响,因为它在C中实现。我想过可能使用像memmap这样的东西,但我不确定这一点。
有没有人知道如何最好地接近这个?
答案 0 :(得分:6)
即使X
稀疏,X * X.T
可能也不会注意到它只需要一对给定行中的一个非零公共元素。你正在使用NLP任务,所以我很确定在几乎所有文档中都会出现大量的单词(如前所述 - 不一定是一个单词所有对,但每对一个(可能不同)。因此,您获得了350363^2
元素的矩阵,其中包含大约122,000,000,000个元素,如果您不这样做的话。 t有200GB的ram,它看起来不可计算。尝试对单词进行更强烈的过滤,以强制X * X.T
稀疏(删除许多常用单词)
一般来说,你无法计算大数据的Gram矩阵,除非你强制执行X * X.T
的稀疏性,所以你的大多数向量都是如此。对(文档)有0"相似性"。它可以通过多种方式完成,最简单的方法是设置一些阈值T
,您可以将<a,b>
视为0
并自行计算点积,并在其中创建一个条目得到稀疏矩阵iff <a,b> > T
答案 1 :(得分:5)
您可能希望查看scikit-learn中的random_projection
模块。 Johnson-Lindenstrauss lemma表示随机投影矩阵可以保证成对距离达到某个容差eta
,当您计算所需的随机投影数时,这是一个超参数。
简而言之,scikit-learn课程SparseRandomProjection
seen here是一个为你做这件事的转换器。如果您在vec.fit_transform
之后在X上运行它,您应该会看到功能大小相当大的减少。
sklearn.random_projection.johnson_lindenstrauss_min_dim
中的公式表明,为了保持最高10%的容差,您只需要johnson_lindenstrauss_min_dim(350363, .1)
10942个功能。这是一个上限,所以你可以用更少的东西逃脱。即使1%的容差也只需要johnson_lindenstrauss_min_dim(350363, .01)
1028192个功能,这个功能仍然比现在少得多。
编辑: 简单的尝试 - 如果您的数据是dtype =&#39; float64&#39;,请尝试使用&#39; float32&#39;。仅此一项就可以节省大量空间,特别是如果你不需要双倍精度的话。
如果问题是您无法存储&#34;最终矩阵&#34;在内存中,我建议使用HDF5Store中的数据(如使用pytables的pandas中所见)。 This link有一些很好的入门代码,您可以迭代计算点积的块并写入磁盘。我最近在45GB数据集项目中广泛使用了这个,如果你决定走这条路,可以提供更多的帮助。
答案 2 :(得分:1)
你可以做的是切一行和一列X,乘以那些并将得到的行保存到文件中。然后移动到下一行和列。
它仍然是相同数量的计算工作,但你不会耗尽内存。
如果您使用multiprocessing.Pool.map()
来读取映射函数中的矩阵,则使用multiprocessing.Pool.map_async()
或numpy.memmap()
您可以加速迁移速度。并且您可能必须将每个计算的行写入单独的文件以便稍后合并它们。如果要从映射函数返回行,则必须将其传输回原始进程。这将占用大量内存和IPC带宽。