如何使用预训练的词向量来使用Gensim doc2vec?

46

我最近了解到Gensim中的doc2vec功能。如何使用预训练的单词向量(例如在word2vec原始网站上找到的向量)与doc2vec一起使用?

还是说doc2vec从用于段落向量训练的相同句子中获取单词向量?

谢谢。

4个回答

25
注意,“DBOW”(dm=0)训练模式不需要或者说甚至没有创建单词向量作为训练的一部分。它只是学习好的文档向量,以此来预测每个单词(类似于word2vec中的skip-gram训练模式)。
在gensim 0.12.0之前,有一个参数train_words被提到,一些文档建议用这个参数来共同训练单词。但是,我不认为这真正起作用了。从gensim 0.12.0开始,有一个名为dbow_words的参数,可以与DBOW doc向量同时使用skip-gram训练单词。请注意,这会使训练时间变长——与窗口相关的因素。因此,如果您不需要单词向量,可以将其关闭。
在“DM”训练方法(dm=1)中,单词向量在过程中固有地进行训练,并且可能也会影响doc向量的质量。理论上,可以从先前的数据中预初始化单词向量。但是,我不知道有任何强有力的理论或实验依据可以确信这会改善doc向量。
我进行的一个不完整的实验表明,在这些方面进行的doc向量训练开始得更快——在前几次通过后具有更好的预测性能——但是随着更多次通过,这种优势就会消失。无论您将单词向量保持不变还是让它们继续与新的训练一起调整,这也可能是一个重要的考虑因素——但是哪种选择更好可能取决于您的目标、数据集以及预先存在的单词向量的质量/相关性。
您可以使用gensim 0.12.0中提供的intersect_word2vec_format()方法重复我的实验,并尝试使用syn0_lockf值使预加载的向量对新训练具有抵抗力。但请记住,这是实验领域:基本的doc2vec结果并不依赖于或者说并不必然会改善重复使用的单词向量。

我认为,预训练的词向量可以改善使用DM=1得到的结果,特别是在小数据集上,因为相似单词之间已经有强关联。所以如果我的训练数据中有句子“I like hot chocolate”,但单词“warm”从未出现过,那么如果没有预初始化向量,我的模型就不知道该给“I like warm chocolate”分配什么向量,对吧?但是它会受益于word2vec初始化,并将类似的向量分配给这两个句子。这只是一个猜测,我不知道是否正确。 - Simon Hessner
我也不知道doc2vec如何处理未知的单词。所以我假设在训练中有一些单词X,然后我会为文档推断向量,这些文档也包含未知的单词Y。这些单词会被分配随机向量还是被忽略?如果使用word2vec进行预初始化,行为会有所不同,因为语料库中有更多的单词?还是我只能预初始化那些在训练doc2vec模型中也会遇到的单词向量? - Simon Hessner
4
在训练/推断过程中,原始语料库中不存在的词语(或其他词汇修剪方式所不保留的词语,例如“min_count”)将被忽略 - 因此将它们传递给“infer_vector()”没有任何效果。(如果你传递一个包含所有未知单词的文本,则相当于传递了一个空列表。)没有支持的方法可以使用其他单词向量对“Doc2Vec”进行预初始化;即使是我在上面三年前提到的实验性“intersect_word2vec_format()”方法:(1) 在最近的“Doc2Vec”版本中出现了错误;(2) 只在提供的初始化语料库中包含单词的情况下才会保留词汇表。 - gojomo
嗨@gojomo,尽管您提示了一些信息,但我仍然不理解DBOW模式下词向量和段落向量的协同训练在概念上是如何实现的。我已经为此创建了一个问题。您能看一下吗:https://stackoverflow.com/questions/55592142/how-are-word-vectors-co-trained-with-paragraph-vectors-in-doc2vec-dbow - Antoine

12

最近我也在使用Doc2Vec。我正在考虑使用LDA结果作为单词向量,并固定这些单词向量以获得文档向量。结果并不是很有趣,可能是因为我的数据集不太好。 代码如下。Doc2Vec将单词向量和文档向量一起保存在字典doc2vecmodel.syn0中。您可以直接更改向量值。唯一的问题可能是您需要找出syn0中哪个位置代表哪个单词或文档。向量以随机顺序存储在字典syn0中。

import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
from gensim import corpora, models, similarities
import gensim
from sklearn import svm, metrics
import numpy

#Read in texts into div_texts(for LDA and Doc2Vec)
div_texts = []
f = open("clean_ad_nonad.txt")
lines = f.readlines()
f.close()
for line in lines:
    div_texts.append(line.strip().split(" "))

#Set up dictionary and MMcorpus
dictionary = corpora.Dictionary(div_texts)
dictionary.save("ad_nonad_lda_deeplearning.dict")
#dictionary = corpora.Dictionary.load("ad_nonad_lda_deeplearning.dict")
print dictionary.token2id["junk"]
corpus = [dictionary.doc2bow(text) for text in div_texts]
corpora.MmCorpus.serialize("ad_nonad_lda_deeplearning.mm", corpus)

#LDA training
id2token = {}
token2id = dictionary.token2id
for onemap in dictionary.token2id:
    id2token[token2id[onemap]] = onemap
#ldamodel = models.LdaModel(corpus, num_topics = 100, passes = 1000, id2word = id2token)
#ldamodel.save("ldamodel1000pass.lda")
#ldamodel = models.LdaModel(corpus, num_topics = 100, id2word = id2token)
ldamodel = models.LdaModel.load("ldamodel1000pass.lda")
ldatopics = ldamodel.show_topics(num_topics = 100, num_words = len(dictionary), formatted = False)
print ldatopics[10][1]
print ldatopics[10][1][1]
ldawordindex = {}
for i in range(len(dictionary)):
    ldawordindex[ldatopics[0][i][1]] = i

#Doc2Vec initialize
sentences = []
for i in range(len(div_texts)):
    string = "SENT_" + str(i)
    sentence = models.doc2vec.LabeledSentence(div_texts[i], labels = [string])
    sentences.append(sentence)
doc2vecmodel = models.Doc2Vec(sentences, size = 100, window = 5, min_count = 0, dm = 1)
print "Initial word vector for word junk:"
print doc2vecmodel["junk"]

#Replace the word vector with word vectors from LDA
print len(doc2vecmodel.syn0)
index2wordcollection = doc2vecmodel.index2word
print index2wordcollection
for i in range(len(doc2vecmodel.syn0)):
    if index2wordcollection[i].startswith("SENT_"):
        continue
    wordindex = ldawordindex[index2wordcollection[i]]
    wordvectorfromlda = [ldatopics[j][wordindex][0] for j in range(100)]
    doc2vecmodel.syn0[i] = wordvectorfromlda
#print doc2vecmodel.index2word[26841]
#doc2vecmodel.syn0[0] = [0 for i in range(100)]
print "Changed word vector for word junk:"
print doc2vecmodel["junk"]

#Train Doc2Vec
doc2vecmodel.train_words = False 
print "Initial doc vector for 1st document"
print doc2vecmodel["SENT_0"]
for i in range(50):
    print "Round: " + str(i)
    doc2vecmodel.train(sentences)
print "Trained doc vector for 1st document"
print doc2vecmodel["SENT_0"]

#Using SVM to do classification
resultlist = []
for i in range(4143):
    string = "SENT_" + str(i)
    resultlist.append(doc2vecmodel[string])
svm_x_train = []
for i in range(1000):
    svm_x_train.append(resultlist[i])
for i in range(2210,3210):
    svm_x_train.append(resultlist[i])
print len(svm_x_train)

svm_x_test = []
for i in range(1000,2210):
    svm_x_test.append(resultlist[i])
for i in range(3210,4143):
    svm_x_test.append(resultlist[i])
print len(svm_x_test)

svm_y_train = numpy.array([0 for i in range(2000)])
for i in range(1000,2000):
    svm_y_train[i] = 1
print svm_y_train

svm_y_test = numpy.array([0 for i in range(2143)])
for i in range(1210,2143):
    svm_y_test[i] = 1
print svm_y_test


svc = svm.SVC(kernel='linear')
svc.fit(svm_x_train, svm_y_train)

expected = svm_y_test
predicted = svc.predict(svm_x_test)

print("Classification report for classifier %s:\n%s\n"
      % (svc, metrics.classification_report(expected, predicted)))
print("Confusion matrix:\n%s" % metrics.confusion_matrix(expected, predicted))

print doc2vecmodel["junk"]

12
这个gensim的分叉版本允许加载预先训练好的词向量来训练doc2vec。这里有一个使用它的例子。词向量必须采用C-word2vec工具文本格式:每个词向量占一行,首先是表示单词的字符串,然后是以空格分隔的浮点值,每个嵌入维度一个。
这项工作属于一篇论文, 论文中声称使用预先训练的词嵌入实际上有助于构建文档向量。但是,无论我是否加载预先训练的嵌入,我都会得到几乎相同的结果。 编辑: 实际上,在我的实验中有一个显着的差异。当我加载预先训练的嵌入时,我要训练doc2vec一半的迭代次数才能获得几乎相同的结果(比这更长的训练时间会在我的任务中产生更差的结果)。

2

1
谢谢Aaron。确实是一个及时的问题 :) 这在教程中有所说明: “……如果您只想学习标签的表示并保留单词表示不变,则该模型还具有train_words=False标志”。。我知道你可以使用预训练的word2vec向量..问题是,我如何使用这些预训练的向量调用doc2vec? - Stergios
1
@Stergios:也许我误解了问题(我自己还在摸索中)。但是看起来推理仍然没有真正实现-请参见https://groups.google.com/forum/#!topic/gensim/EFy1f0QwkKI。值得庆幸的是,至少有几个人正在积极研究它。我猜测序列将是以下步骤:1)加载预训练向量;2)使用新标签为未知句子创建向量;3)调用most_similar(“NEW_LABEL”)。或者,为多个未知句子创建向量,并计算这些向量之间的距离。但这只是一个猜测。 - AaronD
我知道这有点陈旧,但是你是否成功解决了如何让GloVe和doc2vec一起工作的问题? - RinW

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