这是一组大约 3.6GB 磁盘上的词向量,加载时稍大一些 - 因此仅磁盘 IO 可能会花费大量时间。
另外,至少在 gensim-4.0.0(现在作为 beta 预览版提供)之前,Gensim 到 3.8.3 的版本需要在第一次使用.most_similar() 或 .doesnt_match() 操作(及其他)。这一步也可能需要很长的时间,然后立即需要几 GB 的内存用于完整模型,如 GoogleNews - 在任何没有大约 8GB RAM 的机器上,使用较慢的虚拟内存甚至崩溃的风险内存不足错误。 (从gensim-4.0.0beta 开始,一旦模型加载,第一个.most_similar() 将不需要任何额外的预计算/分配。)
避免这种烦人的延迟的主要方法是构建您的代码或服务以不在每次计算之前单独重新加载它。通常,这意味着保持一个已加载的交互式 Python 进程处于活动状态,为您的额外操作(或以后的用户请求,可能是网络部署服务的情况)做好准备。
听起来您可能正在开发单个 Python 脚本,例如 mystuff.py,并通过 PyCharm 的执行/调试/等实用程序运行它以启动 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 最小。)
最后,可以与其中任何一种混合的另一个选项是仅加载完整的 3 百万令牌、3.6GB GoogleNews 集合的子集。不太常见的词在这个文件的末尾附近,跳过它们不会影响很多用途。因此,您可以使用load_word2vec_format() 的limit 参数仅加载一个子集 - 加载速度更快,使用更少的内存,并更快地完成以后的全集搜索(如.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)