Gensim Doc2vec finalize_vocab内存错误

时间:2017-08-29 16:13:48

标签: python nlp gensim doc2vec

我正在尝试使用gensim培训Doc2Vec模型,该模型具有114M独特文档/标签和大约3M独特单词的词汇大小。我在Azure上有115GB Ram linux机器。 当我运行build_vocab时,迭代器会解析所有文件,然后抛出内存错误,如下所示。

    Traceback (most recent call last):
  File "doc_2_vec.py", line 63, in <module>
    model.build_vocab(sentences.to_array())
  File "/home/meghana/.local/lib/python2.7/site-packages/gensim/models/word2vec.py", line 579, in build_vocab
    self.finalize_vocab(update=update)  # build tables & arrays
  File "/home/meghana/.local/lib/python2.7/site-packages/gensim/models/word2vec.py", line 752, in finalize_vocab
    self.reset_weights()
  File "/home/meghana/.local/lib/python2.7/site-packages/gensim/models/doc2vec.py", line 662, in reset_weights
    self.docvecs.reset_weights(self)
  File "/home/meghana/.local/lib/python2.7/site-packages/gensim/models/doc2vec.py", line 390, in reset_weights
    self.doctag_syn0 = empty((length, model.vector_size), dtype=REAL)
MemoryError

我的代码 -

import parquet
import json
import collections
import multiprocessing


# gensim modules
from gensim import utils
from gensim.models.doc2vec import LabeledSentence
from gensim.models import Doc2Vec

class LabeledLineSentence(object):
    def __init__(self, sources):
        self.sources = sources   
        flipped = {}

    def __iter__(self):
        for src in self.sources:
            with open(src) as fo:
               for row in parquet.DictReader(fo, columns=['Id','tokens']):
                    yield LabeledSentence(utils.to_unicode(row['tokens']).split('\x01'), [row['Id']])

## list of files to be open ##
sources =  glob.glob("/data/meghana_home/data/*")
sentences = LabeledLineSentence(sources)

#pre = Doc2Vec(min_count=0)
#pre.scan_vocab(sentences)
"""
for num in range(0, 20):
    print('min_count: {}, size of vocab: '.format(num), pre.scale_vocab(min_count=num, dry_run=True)['memory']['vocab']/700)
    print("done")
"""

NUM_WORKERS = multiprocessing.cpu_count()
NUM_VECTORS = 300
model = Doc2Vec(alpha=0.025, min_alpha=0.0001,min_count=15, window=3, size=NUM_VECTORS, sample=1e-4, negative=10, workers=NUM_WORKERS) 
model.build_vocab(sentences)
print("built vocab.......")
model.train(sentences,total_examples=model.corpus_count, epochs=10)

按照顶部的内存使用情况 -

enter image description here

有人可以告诉我预期的内存是多少?什么是更好的选择 - 添加交换空间并减慢进程或增加更多内存,以便集群的成本最终可能相等。 gensim在内存中存储了哪些向量?我因为内存有效使用而遗漏的任何标志。

1 个答案:

答案 0 :(得分:1)

在训练期间,1.14亿个doctags至少需要114,000,000 doctags * 300 dimensions * 4 bytes/float = 136GB才能存储原始doctag-vectors。

(如果doctag键row['Id']是字符串,那么记住字符串到int-index映射字典会有额外的开销。如果doctag键是0到1.14亿的原始ints,这将避免填充该字典。如果doctag键是原始的int,但包含任何高于1.14亿的int,模型将尝试分配一个足够大的数组,以包含最大int的行 - 即使许多其​​他较低的int是未使用的。)

原始单词向量和模型输出层(model.syn1)将需要大约另外8GB,而词汇表字典则需要几GB。

因此,理想情况下,您需要更多可寻址的内存或更少的文档。

您提到的是&#39;群集&#39;,但gensim Doc2Vec不支持多机分发。

对于这些算法,使用交换空间通常是一个坏主意,这可能涉及大量的随机访问,因此在交换期间变得非常慢。但对于Doc2Vec的情况,可以使用Doc2Vec.__init__()可选参数docvecs_mapfile将其doctags数组设置为由内存映射文件提供。在每个文档具有单个标记的情况下,并且那些标记在每次重复扫描通过训练文本时以相同的升序出现时,性能可以是可接受的。

单独:

您对训练迭代的管理和alpha学习率是错误的。即使每个alpha调用正在尝试默认的5次传递,但您只需从非您获得一次迭代,您就会在数据上获得2次传递,train()值为0.025和0.023可重新启动的sentences.to_array()对象。

您应该以更少的代码行为目标,使用模型管理alpha从初始高到默认的最终微小min_alpha值。您只需拨打train()一次,除非您绝对确定需要在多次通话之间执行额外的步骤。 (这里没有任何显示要求。)

sentences对象设为真正的可迭代对象,可以多次迭代,将to_array()更改为__iter__(),然后单独传递sentences(而非sentences.to_array())模型。

然后使用此可乘法迭代对象调用train()一次,并让它执行指定的迭代次数,并使用从高到低的平滑alpha更新。 (继承自Word2Vec的默认值为5次迭代,但在已发布的Doc2Vec工作中更常使用10到20次。默认{0.}}的0.0001几乎不会被更改。)