Doc2Vec 和 PySpark:基于 DeepDist 的 Gensim Doc2vec

Doc2Vec and PySpark: Gensim Doc2vec over DeepDist

我正在查看 DeepDist (link) 模块并考虑将其与 GensimDoc2Vec API 结合起来训练段落向量在 PySpark 上。 link 实际上提供了以下干净示例,说明如何为 GensimWord2Vec 模型执行此操作:

from deepdist import DeepDist
from gensim.models.word2vec import Word2Vec
from pyspark import SparkContext

sc = SparkContext()
corpus = sc.textFile('enwiki').map(lambda s: s.split())

def gradient(model, sentences):  # executes on workers
    syn0, syn1 = model.syn0.copy(), model.syn1.copy()   # previous weights
    model.train(sentences)
    return {'syn0': model.syn0 - syn0, 'syn1': model.syn1 - syn1}

def descent(model, update):      # executes on master
    model.syn0 += update['syn0']
    model.syn1 += update['syn1']

with DeepDist(Word2Vec(corpus.collect()) as dd:
    dd.train(corpus, gradient, descent)
    print dd.model.most_similar(positive=['woman', 'king'], negative=['man']) 

据我了解,DeepDist是将梯度下降的工作分批分配给worker,在master重新组合更新。如果我用 Doc2Vec 替换 Word2Vec,应该有正在用词向量训练的文档向量。

所以我查看了 gensim.models.doc2vec (link) 的源代码。 Doc2Vec模型实例中有以下字段:

  1. model.syn0
  2. model.syn0_lockf
  3. model.docvecs.doctag_syn0
  4. model.docvecs.doctag_syn0_lockf

gensim.models.word2vec (link) 的源代码相比,Doc2Vec 模型中缺少以下字段:

  1. model.syn1
  2. model.syn1neg

我想我不会触及 lockf 向量,因为它们似乎是在训练完成后新数据点进来时使用的。因此我的代码应该类似于

from deepdist import DeepDist
from gensim.models.doc2vec import Doc2Vec, LabeledSentence
from pyspark import SparkContext

sc = SparkContext()

# assume my dataset is in format 10-char-id followed by doc content
# 1 line per doc
corpus = sc.textFile('data_set').map(
    lambda s: LabeledSentence(words=s[10:].split(),labels=s[:10])
)

def gradient(model, sentence):  # executes on workers
    syn0, doctag_syn0 = model.syn0.copy(), model.docvecs.doctag_syn0.copy()   # previous weights
    model.train(sentence)
    return {'syn0': model.syn0 - syn0, 'doctag_syn0': model.docvecs.doctag_syn0 - doctag_syn0}

def descent(model, update):      # executes on master
    model.syn0 += update['syn0']
    model.docvecs.doctag_syn0 += update['doctag_syn0']

with DeepDist(Doc2Vec(corpus.collect()) as dd:
    dd.train(corpus, gradient, descent)
    print dd.model.most_similar(positive=['woman', 'king'], negative=['man']) 

我是不是漏掉了什么重要的东西?例如:

  1. 我应该关心 model.syn1 吗?它们到底是什么意思?
  2. 我说的对吗 model.*_lockf 是训练后的锁定矩阵?
  3. 我可以使用 lambda s: LabeledSentence(words=s[10:].split(),labels=s[:10] 来解析我的数据集吗,假设我将每个文档放在一行中,并以 0 填充的 10 位数字 ID 作为前缀?

非常感谢任何suggestion/contribution。我将写一篇博客 post 来总结结果,在这里提到贡献者,可能会帮助其他人在扩展的分布式系统上训练 Doc2Vec 模型,而无需花费太多开发时间来尝试解决我现在正在解决的问题。

谢谢


2018 年 6 月 13 日更新

很抱歉,我没有实现它。但是现在有更好的选择,而且 DeepDist 已经有一段时间没有维护了。请阅读下面的评论。

如果您现在坚持要尝试我的想法,请注意,您要自行承担风险。另外,如果有人知道 DeepDist 仍然有效,请在评论中反馈。它会帮助其他读者。

为避免此问题显示为未解决,以下是提问者解决问题的方式:

I did not get to implement this, until it's too late that I didn't think it would work. DeepDist uses Flask app in backend to interact with Spark web interface. Since it wasn't maintained anymore, Spark's update very likely broke it already. If you are looking for Doc2Vec training in Spark, just go for Deeplearning4J(deeplearning4j.org/doc2vec#)