如何将 Gensim doc2vec 与预训练的词向量一起使用?

Posted

技术标签:

【中文标题】如何将 Gensim doc2vec 与预训练的词向量一起使用?【英文标题】:How to use Gensim doc2vec with pre-trained word vectors? 【发布时间】:2015-02-12 18:13:45 【问题描述】:

我最近在 Gensim 中发现了 doc2vec 附加功能。如何在 doc2vec 中使用预训练的词向量(例如在 word2vec 原始网站中找到的)?

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

谢谢。

【问题讨论】:

【参考方案1】:

Radim 刚刚在 gensim 的 doc2vec 功能上发布了 tutorial(我相信昨天 - 你的问题是及时的!)。

Gensim 支持从the C implementation 加载预训练向量,如the gensim models.word2vec API documentation 中所述。

【讨论】:

感谢 Aaron .. 确实及时的问题 :) 这写在教程中:“...如果您只想学习标签的表示并保持单词表示固定,该模型也有标志train_words=False".. 我知道您可以将预训练的向量用于 word2vec.. 问题是,如何使用这些预训练的向量调用 doc2vec? @Stergios:也许我误解了这个问题(我自己仍然在磕磕绊绊)。但看起来推理仍然没有真正实现 - 请参阅groups.google.com/forum/#!topic/gensim/EFy1f0QwkKI。值得庆幸的是,至少有几个人在积极地工作。我猜这个序列将类似于 1) 加载预训练的向量; 2)为你看不见的句子创建一个带有新标签的向量; 3) 调用 most_similar("NEW_LABEL")。或者,为多个看不见的句子创建向量并计算这些向量之间的距离。但这只是猜测。 我知道这有点老了,但是您是否设法弄清楚如何让 GloVe 和 doc2vec 一起工作?【参考方案2】:

嗯,我最近也在使用 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"]

【讨论】:

【参考方案3】:

请注意,“DBOW”(dm=0) 训练模式不需要甚至创建词向量作为训练的一部分。它只是学习擅长依次预测每个单词的文档向量(很像 word2vec skip-gram 训练模式)。

(在 gensim 0.12.0 之前,在另一条评论中提到了参数 train_words,一些文档建议该参数将共同训练单词。但是,我不相信这实际上有效。从 gensim 0.12.0 开始,有参数dbow_words,它可以与DBOW doc-vectors同时进行skip-gram训练单词。请注意,这会使训练花费更长的时间——与window相关的一个因素。所以如果你不需要word-向量,您仍然可以不使用它。)

在“DM”训练方法(dm=1)中,词向量在此过程中固有地与文档向量一起训练,并且可能还会影响文档向量的质量。理论上可以从先前的数据中预初始化词向量。但我不知道有任何强有力的理论或实验理由来确信这会改善 doc-vectors。

我按照这些思路进行的一个零碎实验表明,文档向量训练开始得更快——在最初的几次通过后预测质量更好——但这种优势随着更多的通过而消失。无论您是保持单词向量不变还是让它们随着新的训练继续调整也可能是一个重要的考虑因素……但是哪个选择更好可能取决于您的目标、数据集以及预先存在的质量/相关性词向量。

(您可以使用 gensim 0.12.0 中可用的 intersect_word2vec_format() 方法重复我的实验,并尝试通过 syn0_lockf 值使预加载的向量抵抗新训练的不同级别。但请记住这是实验领域:基本的 doc2vec 结果不依赖于,甚至不一定会改进,重用的词向量。)

【讨论】:

我认为预训练的词向量可以改善使用 DM=1 获得的结果,尤其是在小型数据集上,因为相似词之间已经存在很强的关系。因此,如果我的训练数据中有“我喜欢热巧克力”这句话,但“温暖”这个词从未出现在那里,那么如果我不预初始化,我的模型将不知道分配什么向量“我喜欢热巧克力”向量,对吧?但它会受益于 word2vec 初始化,将相似的向量分配给两个句子。这只是猜测,不知道是不是真的。 我也不知道 doc2vec 如何处理看不见的单词。因此,假设我在训练中看到了一些单词 X,然后我推断出包含未见过单词 Y 的文档的向量。这些会被分配随机向量还是被忽略?当我使用 word2vec 进行预初始化时,行为是否会有所不同,因为语料库中有更多单词?或者我可以只预初始化在训练 doc2vec 模型期间也会遇到的词向量吗? 原始语料库中不存在的单词(或无法通过其他词汇修剪,例如min_count)在训练/推理期间被忽略 - 因此将它们传递给infer_vector() 无效. (如果你传递一个包含所有未知单词的文本,这就像传递一个空列表。)没有支持的方法来使用其他词向量预初始化Doc2Vec;甚至我在上述 3 年前的答案中提到的实验性 intersect_word2vec_format() 方法:(1)在 Doc2Vec 的最新版本中已损坏; (2) 只在你提供的任何初始化语料库中留下单词。 嗨@gojomo 尽管有你的提示,但我从概念上不明白如何在 DBOW 模式下协同训练单词和段落向量。我为此创建了一个问题。请您看一下:***.com/questions/55592142/…【参考方案4】:

This forked version of gensim 允许加载预训练的词向量来训练 doc2vec。 Here 你有一个如何使用它的例子。词向量必须采用 C-word2vec 工具文本格式:每个词向量一行,其中首先是表示单词的字符串,然后是空格分隔的浮点值,每个嵌入维度都有一个。

这项工作属于paper,他们声称使用预训练的词嵌入实际上有助于构建文档向量。但是,无论我是否加载预训练的嵌入,我都会得到几乎相同的结果。

编辑:实际上我的实验有一个显着的不同。当我加载预训练嵌入时,我对 doc2vec 进行了一半的迭代训练,以获得几乎相同的结果(训练时间比我的任务产生的结果更差)。

【讨论】:

以上是关于如何将 Gensim doc2vec 与预训练的词向量一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

Gensim Doc2vec 已训练,但未保存

gensim doc2vec 从预训练模型中训练更多文档

Doc2vec:gensim doc2vec模型中只有10个docvecs?

gensim Doc2Vec vs tensorflow Doc2Vec

gensim简介

机器学习使用gensim 的 doc2vec 实现文本相似度检测