Deep learning 如何加快Gensim Word2vec模型加载时间?

Deep learning 如何加快Gensim Word2vec模型加载时间?,deep-learning,gensim,word2vec,Deep Learning,Gensim,Word2vec,我正在构建一个聊天机器人,所以我需要使用Word2Vec对用户的输入进行矢量化 我使用的是一个由谷歌(GoogleNews-vectors-negative300)预先训练的300万字的模型 因此,我使用Gensim加载模型: import gensim model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True) 问题是加载模型大约需要2分钟

我正在构建一个聊天机器人,所以我需要使用Word2Vec对用户的输入进行矢量化

我使用的是一个由谷歌(GoogleNews-vectors-negative300)预先训练的300万字的模型

因此,我使用Gensim加载模型:

import gensim
model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True)
问题是加载模型大约需要2分钟。我不能让用户等那么久

那么我能做些什么来加快加载时间呢


我考虑将300万个单词中的每一个单词及其对应的向量放入MongoDB数据库。这当然会加快速度,但直觉告诉我这不是一个好主意。

每当我使用谷歌新闻数据集时,我都会遇到这个问题。问题是数据集中的单词比你需要的要多得多。有大量的打字错误等等。我所做的是扫描我正在处理的数据,建立一个包含50k个最常见单词的字典,用Gensim获取向量并保存字典。加载此词典需要半秒钟而不是2分钟

如果没有特定的数据集,可以使用大型数据集中的50或100k最常见的单词,例如WMT中的

其他选项是始终保持Gensim运行。您可以为运行Gensim的脚本创建脚本。该脚本的作用类似于“服务器”,可以读取“客户端”写入的文件,监视向量请求

我认为最优雅的解决方案是运行一个提供单词嵌入的web服务。请查看下面的示例。安装后,获取“餐厅”的嵌入非常简单:

curl http://127.0.0.1:5000/word2vec/model?word=restaurant

在最近的gensim版本中,您可以使用可选的
limit
参数从文件的前面加载子集到
load\u word2vec\u format()
。(GoogleNews向量的频率从高到低,因此前N个通常是您想要的N个大小的子集。因此,使用
limit=500000
获取最频繁的500000个单词的向量-仍然是一个相当大的词汇表-节省5/6的内存/加载时间。)

所以这可能有点帮助。但是,如果您正在为每个web请求加载,那么加载的IO绑定速度和存储每次重新加载的冗余内存开销仍然会对您造成伤害

有一些技巧,你可以结合使用,以帮助

请注意,以原始word2vec.c格式加载这些向量后,可以使用gensim的本机
save()
重新保存它们。如果您未压缩保存它们,并且备份数组足够大(GoogleNews集合肯定足够大),那么备份数组将以原始二进制格式转储到单独的文件中。该文件稍后可以使用gensim的本机
[load(filename,mmap='r')][1]
选项从磁盘进行内存映射

最初,这将使加载看起来很快–操作系统不会从磁盘读取所有阵列,而是将虚拟地址区域映射到磁盘数据,以便稍后,当代码访问这些内存位置时,将从磁盘读取必要的范围。到目前为止还不错

但是,如果您正在执行典型的操作,如
most\u similar()
,您仍然会面临很大的延迟,只是稍晚一点。这是因为此操作需要对所有向量进行初始扫描和计算(第一次调用时,为每个字创建单位长度的规范化向量),然后对所有规范化向量进行另一次扫描和计算(每次调用时,查找N个最相似的向量)。这些全扫描访问将分页到整个阵列的RAM中,同样需要花费几分钟的磁盘IO时间

您想要的是避免冗余地进行单元规范化,并且只需支付一次IO成本。这需要将向量保存在内存中,以供所有后续web请求(甚至多个并行web请求)重用。幸运的是,内存映射在这方面也有帮助,尽管需要一些额外的准备步骤

首先,使用
load\u word2vec\u format()
加载word2vec.c格式向量。然后,使用
model.init_sims(replace=True)
强制单元标准化,破坏性地在适当的位置(关闭非标准化向量)

然后,将模型保存到一个新的文件名前缀:model.save('GoogleNews-vectors-gensim-normed.bin')。(请注意,这实际上会在磁盘上创建多个文件,这些文件需要保存在一起,以便重新加载模型。)

现在,我们将制作一个简短的Python程序,用于内存映射和加载向量,并将整个数组强制放入内存。我们还希望该程序挂起,直到外部终止(保持映射活动),并小心不要重新计算已赋范的向量。这需要另一个技巧,因为加载的KeyedVectors实际上不知道向量已赋范。(通常只保存原始向量,并在需要时重新计算赋范版本。)

大致来说,以下几点应该有效:

from gensim.models import KeyedVectors
from threading import Semaphore
model = KeyedVectors.load('GoogleNews-vectors-gensim-normed.bin', mmap='r')
model.syn0norm = model.syn0  # prevent recalc of normed vectors
model.most_similar('stuff')  # any word will do: just to page all in
Semaphore(0).acquire()  # just hang until process killed
这仍然需要一段时间,但只需要在任何web请求之前/之外执行一次。当进程处于活动状态时,向量将保持映射到内存中。此外,除非/直到出现其他虚拟内存压力,否则向量应保持在内存中加载。这对于下一步的操作非常重要

最后,在web请求处理代码中,您现在只需执行以下操作:

model = KeyedVectors.load('GoogleNews-vectors-gensim-normed.bin', mmap='r')
model.syn0norm = model.syn0  # prevent recalc of normed vectors
# … plus whatever else you wanted to do with the model
多个进程可以共享只读内存映射文件。(也就是说,一旦操作系统知道文件X位于RAM中的某个位置,同样需要只读映射版本的X的每个其他进程都将被引导在该位置重新使用该数据。)

所以这个web请求
load()
,以及任何后续访问,都可以重新使用先前进程已经带入地址空间和活动内存的数据。需要对每个向量进行相似性计算的操作仍然需要时间来访问多GB的RAM,并进行计算/排序,但不再需要额外的磁盘IO和冗余re norma欢迎光临。

model = Word2Vec.load_word2vec_format('wikipedia-pubmed-and-PMC-w2v.bin',binary=True) model.init_sims(replace=True) model.save('bio_word')
Word2Vec.load('bio_word',mmap='r')