如何更快地在 Gensim 中加载 Word2Vec 预训练模型?

如何解决如何更快地在 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 分钟。那么,有没有办法让事情变得更快,因为我想在我的项目中使用它,但我不能让用户等这么久。

有什么建议吗?

解决方法

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

此外,至少在 gensim-4.0.0(现在可作为 beta 预览版)之前,Gensim 到 3.8.3 的版本在第一次使用时需要额外的一次性预计算单位长度归一化向量.most_similar().doesnt_match() 操作(及其他)。这一步也可能需要一段时间,然后立即需要一些额外的 GB 内存用于完整模型,例如 GoogleNews - 在任何机器上,它的可用内存少于约 8GB 的​​风险使用较慢的虚拟内存甚至崩溃出现内存不足错误。 (从 gensim-4.0.0beta 开始,一旦模型加载,第一个 .most_similar() 将不需要任何额外的预计算/分配。)

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

听起来您可能正在开发一个 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 正式发布之前强制安装此预览版,请参阅 this message。)

首先,加载原始格式的文件,然后将其重新保存为 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() 应该完成得更快。但是,当您第一次尝试访问任何单词或 .most_similar() 中的所有单词时,仍然需要从磁盘读取,只是将延迟转移到以后。 (如果您只进行单个字词查找或小组 .doesnt_match() 字词,您可能不会注意到任何长时间的滞后。)

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

但是,您可以通过采取第三步来增加模型文件保持内存驻留的机会:启动一个单独的 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 集的一个子集。不太常用的词在这个文件的末尾,跳过它们不会影响很多用途。因此,您可以使用 limitload_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) 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?