Gensim的Doc2Vec——如何使用预训练的word2vec(词相似度)

Gensim's Doc2Vec - How to use pre-trained word2vec (word similarities)

我没有大量的数据来训练单词相似性,例如'hot' 比 'cold' 更类似于 'warm'。但是,我喜欢在相对较小的语料库 ~100 文档上训练 doc2vec,以便它可以对我的领域特定文档进行分类。

为了详细说明,让我使用这个玩具示例。假设我只有 4 个训练文档,由 4 个句子给出 - "I love hot chocolate."、"I hate hot chocolate."、"I love hot tea." 和 "I love hot cake."。 给定一个测试文档 "I adore hot chocolate",我预计 doc2vec 将总是 return "I love hot chocolate." 作为最接近的文档。如果 word2vec 已经提供了 "adore" 与 "love" 非常相似的知识,那么这种期望就会成立。但是,我得到的文档与 "I hate hot chocolate" 最相似——这很奇怪!!

关于如何规避这一点的任何建议,即能够使用预训练的词嵌入,这样我就不需要冒险进行训练 "adore" 接近 "love","hate"接近于"detest",以此类推。

代码(Jupyter Nodebook。Python 3.7.Jensim 3.8.1)

from gensim.models.doc2vec import Doc2Vec, TaggedDocument
from nltk.tokenize import word_tokenize
data = ["I love hot chocolate.",
        "I hate hot chocolate",
       "I love hot tea.",
       "I love hot cake."]

tagged_data = [TaggedDocument(words=word_tokenize(_d.lower()), tags=[str(i)]) for i, _d in enumerate(data)]
print(tagged_data)
#Train and save
max_epochs = 10
vec_size = 5
alpha = 0.025


model = Doc2Vec(vector_size=vec_size, #it was size earlier
                alpha=alpha, 
                min_alpha=0.00025,
                min_count=1,
                dm =1)

model.build_vocab(tagged_data)

for epoch in range(max_epochs):
    if epoch % 10 == 0:
        print('iteration {0}'.format(epoch))
    model.train(tagged_data,
                total_examples=model.corpus_count,
                epochs=model.epochs) #It was model.iter earlier
    # decrease the learning rate
    model.alpha -= 0.0002
    # fix the learning rate, no decay
    model.min_alpha = model.alpha

print("Model Ready")

test_sentence="I adore hot chocolate"
test_data = word_tokenize(test_sentence.lower())
v1 = model.infer_vector(test_data)
#print("V1_infer", v1)

# to find most similar doc using tags
sims = model.docvecs.most_similar([v1])
print("\nTest: %s\n" %(test_sentence))
for indx, score in sims:
    print("\t(score: %.4f) %s" %(score, data[int(indx)]))

只有 ~100 个文档太小,无法有意义地训练 Doc2Vec(或 Word2Vec)模型。已发布的 Doc2Vec 作品往往使用数万到数百万的文档。

就您可能能够从较小的数据集获得稍微有意义的结果而言,您通常需要将向量大小减少很多——远小于 words/examples 的数量——然后增加训练时期。 (您的玩具数据有 4 个文本和 6 个独特的单词。即使要获得 5 维向量,您可能需要类似 5^2 的约束文档。)

此外,gensim 的 Doc2Vec 不提供任何从其他地方导入词向量的官方选项。内部 Doc2Vec 训练不是先训练词向量,然后计算文档向量的过程。相反,doc-vectors 和 word-vectors 在同步过程中进行训练,一起逐渐改进。 (某些模式,例如可以通过 dm=0 启用的快速且通常非常有效的 DBOW,根本不创建或使用词向量。)

你的 4 句结果并没有什么奇怪的,当我们把数据看成 Doc2VecWord2Vec 算法时,它们没有关于单词的先验知识,只有什么在训练数据中。在您的训练数据中,标记 'love' 和标记 'hate' 的使用方式几乎完全相同,周围的词也相同。只有看到单词的许多微妙变化的替代用法,以及周围许多对比鲜明的单词,这些 "dense embedding" 模型才能将单词向量移动到有用的相对位置,使它们更接近相关单词而远离其他单词。 (而且,由于您没有提供带有标记 'adore' 的训练数据,模型对该词一无所知——如果它在测试文档中提供,就好像模型的 infer_vector() 方法一样,它将被忽略。所以测试文件它'sees'只是已知的词['i', 'hot', 'chocolate']。)

而且,即使您确实设法在更大的数据集上进行训练,或者以某种方式从其他词向量中注入知识 'love''adore' 有些相似,请务必注意反义词在词向量集中也通常非常相似——因为它们在相同的上下文中使用,并且通常在句法上可以互换,并且属于相同的一般类别。这些模型通常 不是 非常擅长通过将单词与其反义词交换(或插入单个 'not' 或其他反向意图词)。

最终,如果你想使用 gensim 的 Doc2Vec,你应该用更多的数据训练它。 (如果你愿意获取一些其他预训练的词向量,为什么不获取一些其他类似的大量句子的来源?使用与你的实际问题不完全相同的数据的效果将是相似的,无论你是否利用它通过批量文本或预训练模型获取外部数据。)

最后:在您自己的循环中多次调用 train() 并进行您自己的 alpha 调整是一种糟糕且容易出错的模式。您只需调用一次,使用正确的 epochs 次数,模型将执行多次训练并在正确的时期数内平稳地管理内部 alpha