当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何加快GensimWord2vec模型加载时间?》经验,为你挑选了1个好方法。

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

我正在使用谷歌300万字的预训练模型(GoogleNews-vectors-negative300).

所以我使用Gensim加载模型:

import gensim
model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True)

问题是加载模型大约需要2分钟.我不能让用户等那么久.

那么我该怎么做才能加快加载时间呢?

我想过将300万个单词中的每一个及其相应的向量放入MongoDB数据库中.这肯定会加快速度,但直觉告诉我这不是一个好主意.



1> gojomo..:

在最近的gensim版本中,您可以使用可选limit参数来加载从文件前面开始的子集load_word2vec_format().(GoogleNews向量似乎是大致从最常见到最低频率的顺序,所以前N个通常是你想要的N大小子集.所以limit=500000用来获得最频繁的500,000个单词的向量 - 仍然相当大词汇 - 节省内存/加载时间的5/6.)

所以这可能会有所帮助.但是,如果您为每个Web请求重新加载,您仍然会受到加载IO绑定速度以及存储每个重新加载的冗余内存开销的影响.

您可以结合使用一些技巧来提供帮助.

请注意,在以原始word2vec.c格式加载此类向量后,您可以使用gensim的native来重新保存它们save().如果您将它们保存为未压缩,并且后备阵列足够大(并且GoogleNews设置肯定足够大),则后备阵列将以原始二进制格式转储到单独的文件中.该文件以后可以使用gensim的native [load(filename, mmap='r')][1]选项从磁盘进行内存映射.

最初,这将使负载看起来很快 - 而不是从磁盘读取所有数组,操作系统只会将虚拟地址区域映射到磁盘数据,因此一段时间后,当代码访问这些内存位置时,将读取必要的范围 - 从盘.到现在为止还挺好!

但是,如果你正在做类似的典型操作most_similar(),那么你还是会面临很大的滞后.那是因为这个操作需要对所有向量进行初始扫描和计算(在第一次调用时,为每个单词创建单位长度归一化向量),然后对所有赋值向量进行另一次扫描和计算(在每次通话,找到N个最相似的向量).这些全扫描访问将整个阵列分页到RAM中 - 再次耗费几分钟的磁盘IO.

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

首先,加载word2vec.c格式向量load_word2vec_format().然后,使用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的进程将被指示在该位置重新使用该数据.).

所以这个网络reqeust load(),以及任何后续访问,都可以重复使用,在现有工艺已进入地址空间和主动存储器中的数据.需要对每个向量进行相似性计算的操作仍然需要时间来访问多GB的RAM,并进行计算/排序,但不再需要额外的磁盘IO和冗余的重新规范化.

如果系统面临其他内存压力,阵列的范围可能会从内存中消失,直到下一次读取它们的状态为止.如果机器缺少RAM来完全加载矢量,那么每次扫描都需要混合分页进出,无论什么,性能都会令人沮丧.(在这种情况下:获得更多RAM或使用更小的向量集.)

但是如果你有足够的内存,那么最终会使原始/自然的加载和使用直接代码"正常工作",而不需要额外的Web服务接口,因为机器的共享文件映射内存函数作为服务接口.

推荐阅读
个性2402852463
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有