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
分叉进程 在 你的文本向量模型在内存中并且不变 可能 工作让许多进程共享相同的内存中对象。
特别是,您需要确保自动生成单位归一化向量(生成 syn0norm
或 doctag_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()
我想在 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
分叉进程 在 你的文本向量模型在内存中并且不变 可能 工作让许多进程共享相同的内存中对象。
特别是,您需要确保自动生成单位归一化向量(生成 syn0norm
或 doctag_syn0norm
)已经发生。它会在 most_similar()
调用第一次需要时自动触发,或者您可以使用相关对象上的 init_sims()
方法强制触发。如果您 仅 在单位规范向量之间进行最相似的查询,从不需要原始原始向量,请使用 init_sims(replace=True)
破坏原始混合幅度 syn0
就地向量,从而节省大量可寻址内存。
Gensim 还可以选择使用内存映射文件作为模型巨型数组的来源,当多个进程使用同一个只读内存映射文件时,OS 将足够聪明,只将该文件映射到物理内存一次,为两个进程提供指向共享数组的指针。
有关在相似但不相同的用例中使用此技术的棘手部分的更多讨论,请参阅我的回答:
Gensim v4.x.x 简化了@gojomo 上面描述的很多内容,正如他在他的另一个答案
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()