【问题标题】:Gensim word2vec / doc2vec multi-threading parallel queriesGensim word2vec / doc2vec 多线程并行查询
【发布时间】:2017-11-28 14:18:29
【问题描述】:

我想在model 对象的同一副本上调用model.wv.most_similar_cosmul,在batches of input pairs 上使用multiple cores

multiprocessing 模块需要 model 的多个副本,这将需要太多 RAM,因为我的 model 有 30+ GB 的 RAM。

我已尝试评估我的查询对。第一轮我花了大约 12 个小时。可能会有更多轮次。这就是为什么我正在寻找线程解决方案。我知道 Python 有 Global Interpreter Lock 问题。

有什么建议吗?

【问题讨论】:

  • 哪个操作系统? multiprocessing 在 Linux 上使用 fork,因此数据应该被共享并且只在写访问时复制。
  • @BlackJack 这很奇怪。 Python 如何提前知道代码段是否需要写访问权限?我想如果它不知道,它必须在 fork 时为每个孩子复制对象。
  • Python 不知道,操作系统知道。这与 Python 对象或一般 Python 无关,而是与操作系统级别的进程和内存页面有关。

标签: python multithreading word2vec gensim doc2vec


【解决方案1】:

使用multiprocessing 分叉进程您的文本向量模型在内存中并且不变可能可以让许多进程共享相同的内存对象。

特别是,您需要确保自动生成单位范数向量(到syn0normdoctag_syn0norm)已经发生。它会在most_similar() 调用第一次需要它时自动触发,或者您可以使用相关对象上的init_sims() 方法强制它。如果您在单位规范向量之间进行最相似的查询,从不需要原始原始向量,请使用 init_sims(replace=True) 就地破坏原始混合大小 syn0 向量并从而节省大量可寻址内存。

Gensim 还可以选择使用内存映射文件作为模型巨型数组的来源,并且当多个进程使用同一个只读内存映射文件时,操作系统将足够智能,只将该文件映射到物理内存一次,提供两个进程指向共享数组的指针。

有关在类似但不相同的用例中使用此技术的棘手部分的更多讨论,请参阅我的回答:

How to speed up Gensim Word2vec model load time?

【讨论】:

    【解决方案2】:

    Gensim v4.x.x 简化了上面@gojomo 描述的很多内容,正如他在其他答案here 中所解释的那样。基于这些答案,这里有一个示例,说明如何以内存有效的方式对 most_similar 进行多处理,包括使用 tqdm 记录进度。换入您自己的模型/数据集,看看它是如何大规模工作的。

    import multiprocessing
    from functools import partial
    from typing import Dict, List, Tuple
    
    import tqdm
    from gensim.models.word2vec import Word2Vec
    from gensim.models.keyedvectors import KeyedVectors
    from gensim.test.utils import common_texts
    
    
    def get_most_similar(
        word: str, keyed_vectors: KeyedVectors, topn: int
    ) -> List[Tuple[str, float]]:
        try:
            return keyed_vectors.most_similar(word, topn=topn)
        except KeyError:
            return []
    
    
    def get_most_similar_batch(
        word_batch: List[str], word_vectors_path: str, topn: int
    ) -> Dict[str, List[Tuple[str, float]]]:
        # Load the keyedvectors with mmap, so memory isn't duplicated
        keyed_vectors = KeyedVectors.load(word_vectors_path, mmap="r")
        return {word: get_most_similar(word, keyed_vectors, topn) for word in word_batch}
    
    
    def create_batches_from_iterable(iterable, batch_size=1000):
        return [iterable[i : i + batch_size] for i in range(0, len(iterable), batch_size)]
    
    
    if __name__ == "__main__":
        model = Word2Vec(
            sentences=common_texts, vector_size=100, window=5, min_count=1, workers=4
        )
    
        # Save wv, so it can be reloaded with mmap later
        word_vectors_path = "word2vec.wordvectors"
        model.wv.save(word_vectors_path)
    
        # Dummy set of words to find most similar words for
        words_to_match = list(model.wv.key_to_index.keys())
    
        # Multiprocess
        batches = create_batches_from_iterable(words_to_match, batch_size=2)
        partial_func = partial(
            get_most_similar_batch,
            word_vectors_path=word_vectors_path,
            topn=5,
        )
    
        words_most_similar = dict()
        num_workers = multiprocessing.cpu_count()
        with multiprocessing.Pool(num_workers) as pool:
            max_ = len(batches)
            with tqdm.tqdm(total=max_) as pbar:
                # imap required for tqdm to function properly
                for result in pool.imap(partial_func, batches):
                    words_most_similar.update(result)
                    pbar.update()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-28
      • 2017-02-12
      • 1970-01-01
      • 2018-07-21
      • 1970-01-01
      • 2016-06-07
      • 1970-01-01
      相关资源
      最近更新 更多