gensim 当使用build_vocab_from_freq()时,Doc2vec无法训练,

e3bfsja2  于 23天前  发布在  其他
关注(0)|答案(3)|浏览(24)
描述

我使用 build_vocab_from_file() 函数训练了一个 Doc2Vec 模型。这样我可以在索引为 0 的位置手动添加一个 <PAD> 标记。这个标记在原始数据集中没有出现,但在我的程序的后续部分需要使用。

重现步骤/代码/语料库

下面是一个简单的示例,展示了我想要实现的目标:

import collections, sys

import gensim
from gensim import models
from gensim.models.doc2vec import TaggedDocument

lines = [u'It is a truth universally acknowledged',
        u'This was invitation enough.', 
        u'An invitation to dinner was soon afterwards dispatched']
words = [line.split() for line in lines]
doc_labels = [u'text0', u'tex1', u'text2']
word_freq = collections.Counter([w for line in words for w in line])
word_freq['<PAD>'] = sys.maxint # this ensure that the pad token has index 0 in gensim's vocabulary

class DocIterator(object):
    def __init__(self, docs, labels):
        self.docs = docs
        self.labels = labels
    def __iter__(self):
        for idx, doc in enumerate(self.docs):
            yield TaggedDocument(words=doc, tags=[self.labels[idx]])
            
doc_it = DocIterator(words, doc_labels)
model = gensim.models.Doc2Vec(vector_size=100, min_count=0)
model.build_vocab_from_freq(word_freq)
model.train(doc_it, total_examples=len(lines), epochs=10)
预期结果

预期 model.docvecs.count 的大小为 3(而不是 0)。

实际结果

实际 model.docvecs.count 的大小为 0
print(model.docvecs.count) -> 0

版本

Linux-3.19.0-82-generic-x86_64-with-Ubuntu-15.04-vivid
('Python', '2.7.9 (default, Apr 2 2015, 15:33:21)
[GCC 4.9.2]')
('NumPy', '1.14.3')
('SciPy', '1.1.0')
('gensim', '3.4.0')
('FAST_VERSION', 1)
现在我的问题是:

  • 如何正确使用 build_vocab_from_freq() 以获得有效的模型?
  • 如果无法实现这一点,如何在特定索引值强制 gensim 在词汇表中包含一个未见过的标记?
nimxete2

nimxete21#

Doc2Vec 需要在预训练阶段发现所有语料库 tags 并分配/初始化它们的向量。但是,这个(新的,从共享超类继承的方法)build_vocab_from_freq() 并没有做 Doc2Vec 需要的所有事情,只做了 Word2Vec 需要的事情。它需要被重写或者在 Doc2Vec 中标记为不支持,或者添加一个辅助方法来帮助设置 Doc2Vec 状态。(@manneshiva?)

在完成这些之前,你可以查看 build_vocab()Doc2Vec 情况下做了哪些额外的事情,并手动将其应用到你的模型中。但是,既然你有一个完整的语料库迭代器,你是否可以直接使用标准的 build_vocab() 作为解决方法呢?

ajsxfq5m

ajsxfq5m2#

谢谢。在这种情况下,我将使用 build_vocab():

  • 如果 <PAD> 令牌不在原始数据集中,我无法稍后使用 build_vocab(..., update=True) 添加它。这会导致段错误,因为 Doc2Vec 不支持词汇扩展(参见 Segmentation fault using build_vocab(..., update=True) for Doc2Vec #1019)。
  • 如果 <PAD> 令牌作为附加文档(以免干扰正常计算文档向量),我无法轻松保证 <PAD> 令牌将被分配索引0。由于词汇表中的索引是按频率顺序分配的,我必须创建一个充满 <PAD> 令牌的整个文档,其计数高于数据集中任何其他单词。这是非常不希望看到的,因为:(i) 我事先不知道单词频率;(ii) 它会无故占用内存。

我调查过的另一个选项是使用 null_word。然而,build_vocab() 会覆盖任何现有的词汇表:

doc_it = DocIterator(lines, doc_labels)
model = gensim.models.Doc2Vec(vector_size=100, min_count=0)

model.vocabulary.add_null_word(model.wv) #add null word to vocabulary
assert(model.wv.vocab['\0'].index == 0)

model.build_vocab(doc_it)

model.wv.vocab['\0'] <-- 'KeyError: '\x00''
vom3gejh

vom3gejh3#

感谢alexandry-augustin的报告,对我来说这看起来像是一个bug,有几个修复版本。

  • 禁用此方法对于doc2vec(也许对于fasttext也一样,需要测试所有这些情况)
  • 使此方法与build_vocab相似(即端到端)
  • 修复d2v的方法(通过测试覆盖+检查它如何适用于其他*2vec)

相关问题