Doc2Vec:编码文档和未见文档之间的相似性

Doc2Vec: Similarity Between Coded Documents and Unseen Documents

我有大约 60,000 个文件的样本。我们已经将其中的 700 个手动编码为具有特定类型的内容。现在我们想找到 "most similar" 文档到我们已经手工编码的 700 个。我们正在使用 gensim doc2vec,但我想不出最好的方法。

我的代码如下所示:

cores = multiprocessing.cpu_count()

model = Doc2Vec(dm=0, vector_size=100, negative=5, hs=0, min_count=2, sample=0, 
        epochs=10, workers=cores, dbow_words=1, train_lbls=False)

all_docs = load_all_files() # this function returns a named tuple
random.shuffle(all_docs)
print("Docs loaded!")
model.build_vocab(all_docs)
model.train(all_docs, total_examples=model.corpus_count, epochs=5)

我想不出前进的正确方法。这是 doc2vec 可以做的吗?最后,我想要一个包含 60,000 个文档的排名列表,其中第一个是 "most similar" 文档。

感谢您提供的任何帮助!我花了很多时间阅读 gensim 帮助文档和各种教程,但一直无法弄清楚。

编辑:我可以使用此代码来获取与短句最相似的文档:

token = "words associated with my research questions".split()
new_vector = model.infer_vector(token)
sims = model.docvecs.most_similar([new_vector])
for x in sims:
    print(' '.join(all_docs[x[0]][0]))

如果有办法修改它以获取与 700 个编码文档最相似的文档,我很想学习如何做!

n_similarity 看起来像你想要的功能,但它似乎只适用于训练集中的样本。

由于您只有 700 个文档需要交叉检查,因此使用 sklearn 应该不会 post 性能问题。只需获取 700 个文档的向量并使用 sklearn.metrics.pairwise.cosine_similarity,然后找到最接近的匹配项。然后你可以找到相似度最高的那些(例如使用 `np.argmax)。一些未经测试的代码来说明:

from sklearn.metrics.pairwise import cosine_similarity

reference_vectors = ... # your vectors to the 700 documents
new_vector = ... # inferred as per your last example
similarity_matrix = cosine_similarity([new_vector], reference_vectors)
most_similar_indices = np.argmax(similarity_matrix, axis=-1)

也可以修改它以实现类似 n_similarity 的方法,用于许多看不见的文档。

您的总体做法是合理的。关于您的设置的一些注意事项:

  • 您必须在 train() 调用中指定 epochs=10 才能真正获得 10 个训练通行证 – 10 个或更多在已发表的作品中最为常见
  • sample-controlled downsampling 有助于加快训练速度,通常还可以提高矢量质量,并且随着数据集的增大,该值会变得更积极(更小)
  • train_lbls 不是任何最近 gensim 版本 Doc2Vec 的参数

有几种可能的方法来解释和追求您 "find the 'most similar' documents to the 700 we already hand-coded" 的目标。例如,对于一个候选文档,它与 set-of-700 的相似性应该如何定义——作为与完整集合的一个摘要 'centroid' 向量的相似性?或者它与任何一份文件的相似性?

有几种方法可以获得集合的单个汇总向量:

  • 将他们的 700 个向量平均在一起

  • 将他们所有的词组合成一个合成复合文档,然后 infer_vector() 在该文档上。 (但请注意:提供给 gensim 的优化 word2vec/doc2vec 例程的文本面临 10,000 个标记的内部实施限制 - 多余的单词将被静默忽略。)

事实上,most_similar() 方法可以将多个向量的列表作为其 'positive' 目标,并在返回结果之前自动将它们平均在一起。因此,如果 700 个文档 ID(训练期间使用的标签)在列表 ref_docs 中,您可以尝试...

sims = model.docvecs.most_similar(positive=ref_docs, topn=0)

...并根据它们与所有这些 positive 示例的平均值的相似性,返回所有其他模型内文档的排名列表。

但是,另一种解释可能更适合您的目的,即文档与参考集的相似性是其与集合中任何一个文档的最高相似性。如果参考集本身在许多主题上各不相同,并且因此不能用单个平均向量很好地总结,情况尤其如此。

您必须使用自己的循环来计算这些相似性。例如,大致为:

sim_to_ref_set = {}
for doc_id in all_doc_ids:
    sim_to_ref_set[doc_id] = max([model.docvecs.similarity(doc_id, ref_id) for ref_id in ref_docs])
sims_ranked = sorted(sim_to_ref_set.items(), key=lambda it:it[1], reverse=True)

sims_ranked 中排名靠前的项目将是那些与参考集中的任何项目最相似的项目。 (假设参考集 id 也在 all_doc_ids 中,前 700 个结果将再次成为所选文档,所有结果都具有 1.0 的自相似性。)

我认为您可以使用 TaggedDocument 做您想做的事。基本用例是为每个文档添加一个唯一标签(文档 ID),但在这里您需要为所有 700 个手动选择的文档添加一个特殊标签。随便你怎么称呼它,在这种情况下我称它为TARGET。将该标签仅添加到您的 700 个手动标记的文档中,将其他 59,300 个忽略。

TaggedDocument(words=gensim.utils.simple_preprocess(document),tags=['TARGET',document_id])

现在,训练你的 Doc2Vec。

接下来,您可以使用 model.docvecs.similarity 对未标记文档与自定义标记之间的相似性进行评分。

model.docvecs.similarity(document_id,'TARGET')

然后对它进行排序。 n_similaritymost_similar 我认为不适合你想做的事情。

60,000 个文档对于 Doc2Vec 来说不算多,但也许你会有好运。