使用Doc2Vec计算句子列表之间的余弦相似度

3
我是一名NLP新手,但我试图在Python中根据语义相似性将一个句子列表匹配到另一个句子列表。举个例子:
list1 = ['what they ate for lunch', 'height in inches', 'subjectid']
list2 = ['food eaten two days ago', 'height in centimeters', 'id']

基于之前的帖子和先前的知识,似乎创建每个句子的文档向量并计算列表之间的余弦相似度得分是最佳方法。我找到的其他与Doc2Vec相关的帖子以及教程似乎都集中在预测方面。 这篇帖子手动计算了余弦相似度,但我认为 Doc2Vec 已经可以做到这一点了。 我正在使用的代码是:

import gensim
from gensim.models.doc2vec import Doc2Vec, TaggedDocument

def build_model(train_docs, test_docs, comp_docs):
    '''
    Parameters
    -----------
    train_docs: list of lists - combination of known both sentence list
    test_docs: list of lists - one of the sentence lists
    comp_docs: list of lists - combined sentence lists to match the index to the sentence 
    '''
    # Train model
    model = Doc2Vec(dm = 0, dbow_words = 1, window = 2, alpha = 0.2)#, min_alpha = 0.025)
    model.build_vocab(train_docs)
    for epoch in range(10):
        model.train(train_docs, total_examples = model.corpus_count, epochs = epoch)
        #model.alpha -= 0.002
        #model.min_alpha = model.alpha


    scores = []

    for doc in test_docs:
        dd = {}
        # Calculate the cosine similarity and return top 40 matches
        score = model.docvecs.most_similar([model.infer_vector(doc)],topn=40)
        key = " ".join(doc)
        for i in range(len(score)):
            # Get index and score
            x, y = score[i]
            #print(x)
            # Match sentence from other list
            nkey = ' '.join(comp_docs[x])
            dd[nkey] = y
        scores.append({key: dd})

    return scores

这里有一个计算相似度得分的方法,但问题在于我需要对两个列表中的所有句子进行模型训练,然后再进行匹配。是否有一种方法可以使用Doc2Vec仅获取向量,然后计算余弦相似度?明确一下,我正在尝试找到两个列表之间最相似的句子。我期望的输出是:

scores = []
for s1 in list1:
    for s2 in list2:
        scores.append((s1, s2, similarity(s1, s2)))

print(scores)
[('what they ate for lunch', 'food eaten two days ago', 0.23567),
 ('what they ate for lunch', 'height in centimeters', 0.120),
 ('what they ate for lunch', 'id', 0.01023),
 ('height in inches', 'food eaten two days ago', 0.123),
 ('height in inches', 'height in centimeters', 0.8456),
 ('height in inches', 'id', 0.145),
 ('subjectid', 'food eaten two days ago', 0.156),
 ('subjectid', 'height in centimeters', 0.1345),
 ('subjectid', 'id', 0.9567)]

你能澄清一下你所说的“列表之间最相似的句子”是什么意思吗?你可以提供一些输入示例“列表”和期望的输出吗?(另外:您使用了一个非常大的“alpha”,然后自己多次调用“train()”,使得“alpha”异常大。更好的做法是将默认的“alpha”保留在原位,并仅调用一次“train()”,并设置您所需的“epochs”计数,让代码为您完成正确的操作。) - gojomo
谢谢,@gojomo,我添加了一个例子。我发现在第一次训练模型时,“alpha”效果最好,但这很有用,所以我会尝试其他值。 - m13op22
所以你想计算list1中每个项目与list2之间的成对相似度?看起来你已经有了可用的代码,那么还有什么需要解决的问题吗?(或者,你只是还没有一个“similarity()`函数吗?) - gojomo
请注意,您当前展示的代码在alphatrain()管理方面非常不典型,甚至有些荒谬。它进行了10次train(),但第一次没有进行任何传递,最后一次进行了9次传递,并且每次train()都会将有效的alpha从0.2降至0.0001 - 这是一个上升下降的锯齿形模式。这是不合适的SGD,会导致早期文本始终以高alpha进行训练,而晚期文本始终以低alpha进行训练。使用更多默认/合理的做法可以获得更好的模型。 - gojomo
1
问题中的代码仍然在循环中显示train(),每个train()执行更多的内部epochs。也许500对于短语料库(也许是小的单个文档?)有所帮助,并且通过给予单词向量(通过dbow_words=1)更多的训练可以继续帮助-这远远超出了更大的语料库和已发表作品的典型范围。 - gojomo
显示剩余8条评论
2个回答

1

如果您担心训练模型并在运行时获得结果是耗时的任务。那么考虑保存模型。您可以在单独的文件中训练模型并将其保存到磁盘中。

在训练完成后

model.save("similar_sentence.model")

创建一个新文件并像下面这样加载模型, model = Doc2Vec.load("similar_sentence.model") 模型文件将保存您训练的句子的向量。
模型对象可以在代码中的任何位置保存和加载。 使用您的数据集-NLP实现语义“相似句子”

-1

如果您提供要生成向量的单词,Doc2vec可以生成向量,但需要存在doc2vec模型。然而,此模型不一定需要在您尝试比较的句子上进行训练。我不知道是否存在预生成的doc2vec模型,但我知道您可以导入具有预训练向量的word2vec模型。是否要这样做取决于您要比较的句子类型-通常,word2vec模型是在像维基百科或20newsgroup这样的语料库上进行训练的。因此,它们可能没有这些文章中不经常出现的单词的向量(或向量较差),例如,如果您尝试比较具有许多科学术语的句子,则可能不希望使用预训练模型。但是,在训练模型之前,您将无法生成向量(我认为这是您的核心问题)。


此外,我相信gensim有预先训练好的模型。但你可能需要下载它们。 - Evan Mata
我会研究一下可能存在的模型。我的句子更科学,可能不包含在典型的nltk语料库中,这就是为什么我试图在不训练模型的情况下完成它的原因。使用Doc2vec生成我的句子向量,这样做我是否正确? - m13op22
我个人对word2vec比doc2vec更熟悉,但你的方法似乎也不错。我不太确定你想要训练什么样的语料库,但你可以考虑使用WOS(科学网)数据集。话虽如此,对于具有特定词汇的语料库,通常不要使用预生成的向量,而是在你的语料库上进行训练。基本上,你总是需要训练一个嵌入模型,但你希望你正在训练的模型具有类似于你正在使用的上下文。除非它太小,否则在你的语料库上进行训练没有太多的缺点。 - Evan Mata

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