【问题标题】:Re-calculate similarity matrix given new documents给定新文档重新计算相似度矩阵
【发布时间】:2021-02-03 03:18:00
【问题描述】:

我正在运行一个包含文本文档的实验,我需要计算所有文本文档之间的(余弦)相似度矩阵(用于另一个计算)。为此,我使用 sklearn 的 TfidfVectorizer:

corpus = [doc1, doc2, doc3, doc4]
vect = TfidfVectorizer(min_df=1, stop_words="english", use_idf=False) 
tfidf = vect.fit_transform(corpus)
similarities = tfidf * tfidf.T
pairwise_similarity_matrix = similarities.A

问题在于,在我的实验的每次迭代中,我都会发现需要添加到相似度矩阵中的新文档,并且考虑到我正在处理的文档数量(数万和更多) - 这非常耗时。

我希望找到一种方法来仅计算新批次文档与现有文档之间的相似度,而无需重新计算整个数据集。

请注意,我使用的是词频 (tf) 表示,没有使用逆文档频率 (idf),因此理论上我不需要每次都重新计算整个矩阵。

【问题讨论】:

  • 我认为如果您每次发现新文档时不重新计算,您会遇到的一个问题是您的 TfidfVectorizer 以及随之而来的词汇表将不适合这些文件。这可能意味着,即使您向其中添加了一个新文档,如果 TfidfVectorizer 没有根据其中的单词调整其词汇表,它的大部分内容也可能无法使用。
  • @KimTang 这是问题的一部分——我希望将新术语添加到相同的 termsXdocs 矩阵中,然后再将其与自身相乘以获得新的 docsXdocs 相似度矩阵。
  • @KimTang 对于那部分,我看到了这个问题的答案,并使用“partial_fit”的相关部分,我可以设法用新看到的术语更新词汇表,但这对我没有帮助相似度矩阵部分:stackoverflow.com/questions/39109743/…
  • @KimTang 我刚刚发布了一个解决这部分问题的答案 - 你可以查看一下
  • 感谢更新!我现在也刚刚删除了我的答案。

标签: python scikit-learn cosine-similarity tfidfvectorizer


【解决方案1】:

好的,我明白了。 正如我所说,这个想法是只计算新批次文件和现有文件之间的相似度,它们的相似度没有变化。问题是要使用新出现的术语来更新 TfidfVectorizer 的词汇表。

解决方案有两个步骤:

  1. 更新词汇表和 tf 矩阵。
  2. 矩阵乘法和堆叠。

这是整个脚本 - 我们首先得到原始语料库以及经过训练和计算的对象和矩阵:

corpus = [doc1, doc2, doc3]
# Build for the first time:
vect = TfidfVectorizer(min_df=1, stop_words="english", use_idf=False) 
tf_matrix = vect.fit_transform(corpus)
similarities = tf_matrix * tf_matrix.T
similarities_matrix = similarities.A # just for printing

现在,给定新文件:

new_docs_corpus = [docx, docy, docz] # New documents
# Building new vectorizer to create the parsed vocabulary of the new documents:
new_vect = TfidfVectorizer(min_df=1, stop_words="english", use_idf=False) 
new_vect.fit(new_docs_corpus)

# Merging old and new vocabs:
new_terms_count = 0
for k, v in new_vect.vocabulary_.items():
    if k in vect.vocabulary_.keys():
        continue
    vect.vocabulary_[k] = np.int64(len(vect.vocabulary_)) # important not to assign a simple int
    new_terms_count = new_terms_count + 1
new_vect.vocabulary_ = vect.vocabulary_

# Build new docs represantation using the merged vocabulary:
new_tf_matrix = new_vect.transform(new_docs_corpus)
new_similarities = new_tf_matrix * new_tf_matrix.T

# Get the old tf-matrix with the same dimentions:
if new_terms_count:
    zero_matrix = csr_matrix((tfidf.shape[0],new_terms_count))
    tf_matrix = hstack([tf_matrix, zero_matrix])
# tf_matrix = vect.transform(corpus) # Instead, we just append 0's for the new terms and stack the tf_matrix over the new one, to save time
cross_similarities = new_tf_matrix * tf_matrix.T # Calculate cross-similarities
tf_matrix = vstack([tf_matrix, new_tfidf])
# Stack it all together:
similarities = vstack([hstack([similarities, cross_similarities.T]), hstack([cross_similarities, new_similarities])])
similarities_matrix = similarities.A

# Updating the corpus with the new documents:
corpus = corpus + new_docs_corpus

我们可以通过将计算得到的similarities_matrix 与我们在联合语料库上训练TfidfVectorizer 时得到的corpus + new_docs_corpus 进行比较来检查这一点。

正如 cmets 中所讨论的,我们可以做到这一切只是因为我们没有使用 idf(逆文档频率)元素,这将改变现有文档的新文档的表示。

【讨论】:

    猜你喜欢
    • 2017-11-11
    • 1970-01-01
    • 1970-01-01
    • 2017-11-07
    • 2023-03-24
    • 1970-01-01
    • 2016-02-15
    • 2014-03-25
    • 2016-10-22
    相关资源
    最近更新 更多