首页 > 解决方案 > 如何在 Gensim 中更快地加载 Word2Vec 预训练模型?

问题描述

我正在使用以下方法加载模型:

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

现在每次我在 Pycharm 中运行文件时,它都会再次加载模型。

model['king'] 那么,有没有一种方法可以加载一次并在我运行时可用 model.doesnt_match("house garage store dog".split())

因为每当我要检查相似性或不匹配的单词时,都会花费很多时间。当我运行model.most_similar('finance')时,它真的很慢,整个笔记本电脑冻结了 2 分钟。那么,有没有办法让事情变得更快,因为我想在我的项目中使用它,但我不能让用户等待这么久。

有什么建议么?

标签: pythonmachine-learningnlpgensimword2vec

解决方案


这是一组在磁盘上大约 3.6GB 的词向量,加载时稍大一些——因此仅磁盘 IO 就可能需要相当长的时间。

此外,至少在此之前(现在可作为 beta 预览版),Gensim 到 3.8.3 的版本需要在第一次使用or操作(及其他)gensim-4.0.0时对单位长度归一化向量进行额外的一次性预计算. 这一步也可能需要很长的时间,然后立即需要几 GB 额外的内存来完成一个完整的模型,例如- 在任何没有大约 8GB RAM 的机器上,使用较慢的虚拟内存甚至会因内存不足而崩溃-内存错误。(从 开始,一旦模型加载,第一个不需要任何额外的预计算/分配。).most_similar().doesnt_match()GoogleNewsgensim-4.0.0beta.most_similar()

避免这种烦人的延迟的主要方法是构建您的代码或服务,以便在每次计算之前不单独重新加载它。通常,这意味着保持一个已加载的交互式 Python 进程处于活动状态,为您的额外操作(或以后的用户请求,可能是 Web 部署服务的情况)做好准备。

听起来您可能正在开发单个 Python 脚本,例如mystuff.py,并通过 PyCharm 的 execute/debug/etc 实用程序运行它以启动 Python 文件。不幸的是,在每次完成执行时,这将使整个 Python 进程结束,完全释放任何加载的数据/对象。再次运行脚本必须再次执行所有加载/预计算。

如果您的主要兴趣是自己对一组词向量进行一些调查性检查和实验,那么一个很大的改进将是转移到一个交互式环境,该环境可以让单个 Python 运行并等待您的下一行代码。

例如,如果您ipython在命令行中运行解释器,在单独的 shell 中,您可以加载模型,执行一些查找/相似性操作以打印结果,然后让提示等待您的下一个代码。在您选择退出解释器之前,进程的完整加载状态仍然可用。

同样,如果您在 Web 浏览器中使用 Jupyter Notebook,您将在不断增长的可重新运行的可编辑代码和结果“单元”中获得相同的解释器体验。所有人都共享相同的后端解释器进程,并具有持久状态——除非您选择重新启动“内核”。

如果您为用户的调查工作提供脚本或库代码,他们也可以使用此类持久解释器。

但是,如果您正在构建 Web 服务或其他持续运行的工具,您同样希望确保模型在用户请求之间保持加载。(具体如何操作取决于您的部署细节,包括 Web 服务器软件,因此最好将其作为一个单独的问题询问/搜索,以便在您处于该步骤时提供更多详细信息。 )

还有另一个技巧可能有助于您不断重新启动的场景。Gensim 可以以自己的本机格式保存和加载,这可以利用“内存映射”。本质上,磁盘上的文件范围可以直接由操作系统的虚拟内存系统使用。然后,当许多进程都在自己的内存空间中将相同的文件指定为他们想要的东西的规范版本时,操作系统知道他们可以重用该文件中已经在内存中的任何部分。

这种技术在 `gensim-4.0.0beta' 和更高版本中更简单,所以我只描述那里所需的步骤。(如果您想在 Gensim 4.0 正式发布之前强制进行此预览安装,请参阅此消息。)

首先,加载原始格式的文件,然后以 Gensim 的格式重新保存它:

from gensim.models import KeyedVectors
kv_model = KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True) 
kv_model.save('GoogleNews-vectors-negative300.kv')

请注意,如果您将模型移动到其他位置,则会.npv创建一个额外的文件,必须将其保存在旁边。GoogleNews-vectors-negative300.kv只需执行一次即可创建新文件。

其次,当您以后需要模型时,使用 Gensim 的.load()选项mmap

kv_model = KeyedVectors.load('GoogleNews-vectors-negative300.kv', mmap='r')
# do your other operations

马上,.load()应该更快地完成。但是,当您第一次尝试访问任何单词(或 a 中的所有单词)时,.most_similar()仍然需要从磁盘读取,只是将延迟转移到以后。(如果您只进行单个单词查找或少量.doesnt_match()单词集,您可能不会注意到任何长时间的滞后。)

此外,根据您的操作系统和 RAM 量,您甚至可以在运行一次脚本时获得一些加速,让它完成,然后很快再次运行它。在某些情况下,即使操作系统已经结束了先前的进程,它的虚拟内存机制也可能会记住一些尚未清除的旧进程内存页面仍在 RAM 中,并且对应于内存映射文件。因此,下一个内存映射将重用它们。(我不确定这种效果,如果您处于记忆力低下的情况,那么从已完成内容中重复使用的机会可能会完全消失。

但是,您可以通过采取第三步来增加模型文件保持内存驻留的机会:启动一个单独的 Python 进程来预加载直到被终止才会退出的模型。为此,请制作另一个 Python 脚本,如preload.py

from gensim.models import KeyedVectors
from threading import Semaphore
model = KeyedVectors.load('GoogleNews-vectors-negative300.kv', mmap='r')
model.most_similar('stuff')  # any word will do: just to page all in
Semaphore(0).acquire()  # just hang until process killed

在单独的 shell 中运行此脚本:python preload.py. 它将模型映射到内存中,然后挂起直到您CTRL-C退出它。

现在,您在同一台机器上运行内存映射同一文件的任何其他代码将自动重用来自该单独进程的任何已加载的内存页面。(在内存不足的情况下,如果依赖任何其他虚拟内存,范围仍可能从 RAM 中清除。但如果您有充足的 RAM,这将确保每次引用同一文件时的磁盘 IO 最小。)

最后,可以与其中任何一种混合的另一个选项是仅加载完整的 300 万令牌、3.6GB 集的子GoogleNews集。不太常见的词在这个文件的末尾附近,跳过它们不会影响很多用途。因此,您可以使用limit参数load_word2vec_format()仅加载一个子集 - 加载速度更快,使用更少的内存,并更快地完成以后的完整搜索(如.most_similar())。例如,仅加载第一个 1,000,000 个字,从而节省大约 67% 的 RAM/加载时间/搜索时间:

from gensim.models import KeyedVectors
kv_model = KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin',  limit=1000000, binary=True) 

推荐阅读