gensim Doc2Vec与tensorflow Doc2Vec的比较

55

我正在尝试比较我的Doc2Vec实现(通过tf)和gensim的实现。至少从视觉上看,gensim的表现更好。

我运行了以下代码以训练gensim模型,下面的代码用于tensorflow模型。我的问题如下:

  1. 我的Doc2Vec tf实现是否正确?基本上,它是否应该将单词向量和文档向量连接起来以预测特定上下文中的中间词?
  2. gensim中的window=5参数是否意味着我在使用两个相邻的单词来预测中间一个?还是一侧有5个单词?事实上,有很多文档长度小于10。
  3. 为什么Gensim表现更好?我的模型与他们的实现有何不同?
  4. 考虑到这实际上是一个矩阵分解问题,为什么TF模型会得到答案?由于它是一个秩缺乏的问题,因此存在无限个解。<-最后一个问题只是一个额外的奖励。

Gensim

model = Doc2Vec(dm=1, dm_concat=1, size=100, window=5, negative=10, hs=0, min_count=2, workers=cores)
model.build_vocab(corpus)
epochs = 100
for i in range(epochs):
    model.train(corpus)

TF

batch_size = 512
embedding_size = 100 # Dimension of the embedding vector.
num_sampled = 10 # Number of negative examples to sample.


graph = tf.Graph()

with graph.as_default(), tf.device('/cpu:0'):
    # Input data.
    train_word_dataset = tf.placeholder(tf.int32, shape=[batch_size])
    train_doc_dataset = tf.placeholder(tf.int32, shape=[batch_size/context_window])
    train_labels = tf.placeholder(tf.int32, shape=[batch_size/context_window, 1])

    # The variables   
    word_embeddings =  tf.Variable(tf.random_uniform([vocabulary_size,embedding_size],-1.0,1.0))
    doc_embeddings = tf.Variable(tf.random_uniform([len_docs,embedding_size],-1.0,1.0))
    softmax_weights = tf.Variable(tf.truncated_normal([vocabulary_size, (context_window+1)*embedding_size],
                             stddev=1.0 / np.sqrt(embedding_size)))
    softmax_biases = tf.Variable(tf.zeros([vocabulary_size]))

    ###########################
    # Model.
    ###########################
    # Look up embeddings for inputs and stack words side by side
    embed_words = tf.reshape(tf.nn.embedding_lookup(word_embeddings, train_word_dataset),
                            shape=[int(batch_size/context_window),-1])
    embed_docs = tf.nn.embedding_lookup(doc_embeddings, train_doc_dataset)
    embed = tf.concat(1,[embed_words, embed_docs])
    # Compute the softmax loss, using a sample of the negative labels each time.
    loss = tf.reduce_mean(tf.nn.sampled_softmax_loss(softmax_weights, softmax_biases, embed,
                                   train_labels, num_sampled, vocabulary_size))

    # Optimizer.
    optimizer = tf.train.AdagradOptimizer(1.0).minimize(loss)

更新:

请查看Jupyter笔记本此处(我在这里运行测试了两个模型)。在这个初步分析中,似乎gensim模型的表现更好。


3
这里可以找到有关此问题的适当讨论:https://groups.google.com/forum/#!topic/gensim/0GVxA055yOU - sachinruk
1
根据文档,“窗口是文档内用于预测的上下文单词和预测单词之间的最大距离”。因此,在每个侧面有5个单词。另外,您能告诉我“negative”或“num_sampled”的含义吗?我没太明白。 - Clock Slave
1
负采样方法在Mikolov的一篇论文中有所描述。据我所知,它可以减少每个学习步骤中更新的参数数量。 - patrick
1
请注意,dm_concat 模式会导致模型更大、训练速度更慢,可能需要比常用的 PV-DBOW 或 PV-DM-with-context-window-averaging 更多的数据(或训练次数)。我最初添加 dm_concat 模式到 gensim 中,是为了尝试紧密复现使用该模式的“段落向量”论文结果。(我无法做到;也没有其他人尝试过。)我个人没有发现任何数据集/评估,其中 dm_concat 值得额外的努力 - 但也许它们存在于真正大的文档语料库中。 - gojomo
1个回答

21

旧问题,但是未来的访问者可能会有用。以下是我的一些想法。

tensorflow 的实现存在一些问题:

  • window 是单边大小,因此window=5将是5*2+1=11个单词。
  • 请注意,在使用 doc2vec 的 PV-DM 版本时,batch_size应该是文档的数量。所以,train_word_dataset的形状为batch_size * context_window,而train_doc_datasettrain_labels的形状为batch_size
  • 更重要的是,sampled_softmax_loss不是negative_sampling_loss。它们是两种不同的 softmax 损失的近似值。

因此,针对 OP 提出的问题:

  1. 这种在 tensorflow 中的 doc2vec 实现方式是可行且正确的,但与 gensim 实现和论文不同。
  2. window是如上所述的单侧大小。如果文档大小小于上下文大小,则使用较小的那个。
  3. gensim 实现更快的原因有很多。首先,gensim 经过了大量的优化,所有操作都比简单的 Python 操作更快,特别是数据 I/O。其次,例如在 gensim 中的 min_count 过滤会减少数据集大小。更重要的是,gensim 使用的是negative_sampling_loss,比 sampled_softmax_loss 快得多,我想这是主要原因。
  4. 有很多东西时,找到某些东西更容易吗?只是开个玩笑;-)
在这个非凸优化问题中,确实存在许多解决方案,因此模型会找到一个局部最优解。有趣的是,在神经网络中,大多数局部最优解都“足够好”。观察发现,随机梯度下降似乎比更大的批量梯度下降找到了更好的局部最优解,尽管这仍然是当前研究中的一个谜题。

1
在神经网络中,大多数局部最优解都“足够好”。我认为更正确的说法是,在高维问题中,比如在神经网络中,大多数局部最小值实际上是鞍点,因此很容易跨越,特别是在使用更多随机步骤时。 - Ricardo Magalhães Cruz
1
没错,在高维问题中,大多数关键点都是鞍点,但随机动力学将解决方案驱向局部最优解而不是鞍点,除非是非常平坦和宽阔的鞍点。关键是,大多数找到的局部最优解已经足够好了,因为实证研究表明,不同的局部最优解通常具有几乎相同的泛化性能,这非常有趣。这个答案可能在于随机动力学也将解决方案驱向平坦和宽阔的局部最优解,而不是尖锐和狭窄的局部最优解。 - THN

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