Gensim word2vec / doc2vec 多线程并行查询

Gensim word2vec / doc2vec multi-threading parallel queries

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

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

我已尝试评估我的查询对。第一轮我花了大约 12 个小时。可能会有更多的回合到来。这就是我寻找线程解决方案的原因。我知道 Python 有 Global Interpreter Lock 个问题。

有什么建议吗?

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

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

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

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

Gensim v4.x.x 简化了@gojomo 上面描述的很多内容,正如他在他的另一个答案 中解释的那样。基于这些答案,这里有一个示例,说明如何以内存高效的方式进行多进程 most_similar,包括使用 tqdm 记录进度。换入您自己的 model/dataset 以了解其大规模运作方式。

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()