大数据集的TFIDF

47
我有一个语料库,其中包含大约800万篇新闻文章,我需要将它们作为稀疏矩阵获取TFIDF表示。我已经能够使用scikit-learn对相对较少数量的样本执行此操作,但我认为它不能用于这么庞大的数据集,因为它首先会将输入矩阵加载到内存中,这是一个昂贵的过程。
有人知道如何提取大型数据集的TFIDF向量吗?

5
你看过这个链接链接吗? - EdChum
3
这种方法使用了HashingVectorizer(http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.HashingVectorizer.html#sklearn.feature_extraction.text.HashingVectorizer)进行特征提取,确实适用于大型数据集并具有可扩展性,但是您可以在缺点部分中看到,它指出无法用于计算IDF权重。 - apurva.nandan
如果无法全部放入内存或存储在DB/pytables中,则必须进行迭代处理,并执行查询以计算tfidf并将其存储为另一个表。 - EdChum
我需要获取一个csr矩阵,我认为迭代不会保留矩阵的形状,因为我们每次都会提供新的数据批次并获得不同形状的矩阵,如果我错了请纠正我。另外,如何考虑idf权重,它们是全局权重。使用Python生成器是否有帮助,您认为先通过CountVectorizer计算TF,然后转换为IDF权重? - apurva.nandan
1
我相信您可以使用HashingVectorizer从文本数据中获取一个较小的car_matrix,然后在其上使用TfidfTransformer。存储一个由800万行和数万列组成的稀疏矩阵并不是什么大问题。 - mbatchkarov
你考虑过使用Mahout吗?它适用于大型数据集,并且可以轻松地与Lucene一起使用。- https://mahout.apache.org/ - nbz
4个回答

31

Gensim拥有高效的tf-idf模型,不需要一次性将所有内容存储在内存中。

你的语料库只需要是可迭代的,因此无需一次性将整个语料库存储在内存中。

make_wiki脚本根据评论显示,在笔记本电脑上运行维基百科大约需要50分钟。


4
实际上使用可迭代对象是正确的方法。我最终使用了TfidfVectorizer和一个可迭代对象作为语料库。 - apurva.nandan
“iterable”是什么意思?你能解释一下吗? - Ivan Bilan
@ivan_bilan 请查找Python生成器,那应该可以解释清楚。 - apurva.nandan
gensim需要使用词袋来进行tf-idf操作吗?https://radimrehurek.com/gensim/tut2.html#available-transformations - InsParbo
1
@apurva.nandan,您能否解释一下如何使用可迭代对象在pandas数据框上执行tfidf? - James
@James 你可以使用 df.iterrows() 来遍历 df 的行。 - Matt

15

我相信你可以使用 HashingVectorizer 来从文本数据中获取一个较小的 csr_matrix ,然后在其上使用 TfidfTransformer。存储 800 万行和数万列的稀疏矩阵并不是什么大问题。另一种选择是根本不使用 TF-IDF,也许在没有它的情况下,你的系统可以运行得相当好。

在实践中,您可能需要对数据集进行子采样-有时候,只学习所有可用数据的10%就足够了。这是一个经验性问题,在事先无法确定哪种策略最适合您的任务。在我确信需要800万个文档之前(即在我看到学习曲线上升明显的情况下),我不会担心扩展到800万个文档。

以下是今天早上我正在处理的示例。您可以看到系统的性能随着增加文档而改善,但已经处于一个似乎几乎没有区别的阶段。考虑到训练所需的时间,我认为在500个文件上训练它不值得我的时间。


我想计算所有文章(网页文本,因此大小相当巨大)的最近邻居。您认为我应该仍然忽略IDF计数,只考虑标记计数吗? 顺便问一下,拆分数据并运行并行作业怎么样?但我猜合并结果(矩阵及其不同的形状)会很麻烦。 我正在对它们进行可视化,所以之后也许可以检查何时停止以及需要多少数据。 - apurva.nandan
2
PS 我提到保持稀疏的术语-文档矩阵可能不是问题。Jonathan的回答说了同样的话 - 保存所有网页的文本表示是困难的。然而,您可以通过流式传输(使用生成器)进入向量化器来绕过这个问题。这就是 gensim 即使处理非常大的语料库时也能实现如此小的内存占用的方式。在此处查看教程: http://radimrehurek.com/gensim/tut1.html - mbatchkarov
是的,确切地说,在gensim话题出现之前我就开始使用这种方法了,它通过惰性求值和特征哈希法节省了大量内存。现在问题是存储,如何存储/导出这样一个庞大的csr矩阵,我遇到了内存错误,有什么线索吗? - apurva.nandan
如果你可以将它保存在内存中,那么将它保存到磁盘上不应该有问题。你使用的是什么格式?是否正在进行密集转换? - mbatchkarov
不,这只是一个csr矩阵问题在于我同时使用了bigrams和trigrams,导致内存使用量很大。而pickle无法转储如此大的矩阵。因此,我开始使用pytables中的HDF5格式,现在一切都很好 :) - apurva.nandan

6

我使用sklearn和pandas解决了这个问题。

使用pandas iterator迭代数据集一次,创建一个单词集合,然后在CountVectorizer词汇中使用它。通过这样做,Count Vectorizer将生成一个具有相同形状的稀疏矩阵列表。现在只需要使用vstack进行分组即可。结果得到的稀疏矩阵与CountVectorizer对象具有相同的信息(但单词顺序不同),并且已适用于所有数据。

如果考虑时间复杂度,这种解决方案并不是最好的选择,但对于内存复杂度而言很不错。我在一个20GB +的数据集上使用了它。

我编写了一个Python代码(不是完整的解决方案),显示了属性,编写了一个生成器或使用pandas块来迭代您的数据集。

from sklearn.feature_extraction.text import CountVectorizer
from scipy.sparse import vstack


# each string is a sample
text_test = [
    'good people beauty wrong',
    'wrong smile people wrong',
    'idea beauty good good',
]

# scikit-learn basic usage

vectorizer = CountVectorizer()

result1 = vectorizer.fit_transform(text_test)
print(vectorizer.inverse_transform(result1))
print(f"First approach:\n {result1}")

# Another solution is

vocabulary = set()

for text in text_test:
    for word in text.split():
        vocabulary.add(word)

vectorizer = CountVectorizer(vocabulary=vocabulary)

outputs = [] 
for text in text_test: # use a generator
    outputs.append(vectorizer.fit_transform([text]))


result2 = vstack(outputs)
print(vectorizer.inverse_transform(result2))

print(f"Second approach:\n {result2}")

最后,使用TfidfTransformer

-2

文档长度 公共词项数 词项是否常见或不常见 每个词项出现的次数


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接