Python 重新计算给定新文档的相似度矩阵

Python 重新计算给定新文档的相似度矩阵,python,scikit-learn,cosine-similarity,tfidfvectorizer,Python,Scikit Learn,Cosine Similarity,Tfidfvectorizer,我正在运行一个实验,其中包括我需要计算所有文本之间的(余弦)相似性矩阵(用于另一个计算)的文本文档。为此,我使用sklearn的: 问题是,在我的实验的每次迭代中,我都会发现需要添加到相似性矩阵中的新文档,而且考虑到我正在处理的文档数量(数万个或更多),这非常耗时 我希望找到一种方法,只计算新一批文档与现有文档之间的相似性,而不需要对整个数据集进行再次计算 请注意,我使用的是术语频率(tf)表示法,而没有使用反向文档频率(idf),因此理论上我不需要每次都重新计算整个矩阵。好的,我知道了。 正如

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

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

我希望找到一种方法,只计算新一批文档与现有文档之间的相似性,而不需要对整个数据集进行再次计算

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

好的,我知道了。 正如我所说,这个想法是只计算新批文件和现有文件之间的相似性,它们的相似性是不变的。问题是要用新出现的术语更新TFIDFvectorier的词汇表

该解决方案有两个步骤:

  • 更新词汇表和tf矩阵
  • 矩阵乘法和叠加
  • 以下是整个脚本-我们首先获得原始语料库以及经过训练和计算的对象和矩阵:

    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
    
    我们可以通过比较计算出的
    相似度矩阵
    与我们在联合语料库上训练
    tfidf向量器
    时得到的矩阵来检查这一点:
    corpus+new\u docs\u corpus


    正如评论中所讨论的,我们之所以能够做到这一切,是因为我们没有使用idf(反向文档频率)元素,这将改变给定新文档的现有文档的表示形式。

    我认为如果您不在每次发现新文档时重新计算,您会遇到一个问题,那就是您的TfidfVectorizer,有了它,这些文档中的生词就不适合使用词汇表了。这可能意味着,即使您向其中添加了一个新文档,其中的大部分可能都不可用,如果TfidfVectorizer没有使用其中的单词调整其词汇表。@KimTang这是问题的一部分-我希望将新术语添加到相同的termsXdocs矩阵中,然后再将其与自身相乘以获得新的docsXdocs相似性矩阵。@KimTang对于这一部分,我看到了这个问题的答案,并使用了“部分拟合”的相关部分我可以用新出现的术语更新词汇表,但这对我的相似性矩阵部分没有帮助:@KimTang我刚刚发布了一个解决这部分问题的答案-你可以查看它谢谢更新!我现在也删除了我的答案。
    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