如何 运行 Fasttext get_nearest_neighbors() 更快?

How to run Fasttext get_nearest_neighbors() faster?

我正在尝试使用 Fasttext 提取 morphs/similar 僧伽罗语单词。 但是 FastText 需要 1 秒来处理 2.64 个单词。如何在不改变模型大小的情况下提高速度?

我的代码如下所示:

import fasttext
fasttext.util.download_model('si', if_exists='ignore')  # Sinhala
ft = fasttext.load_model('cc.si.300.bin')
words_file = open(r'/Datasets/si_words_filtered.txt')
words = words_file.readlines()
words = words[0:300]
synon_dict = dict()
from tqdm import tqdm_notebook
for i in tqdm_notebook(range(len(words))):
    word = words[i].strip()
    synon = ft.get_nearest_neighbors(word)[0][1] ### takes a lot of time
    if is_strictly_sinhala_word(synon):
        synon_dict[word] = synon
import json
with open("out.json", "w", encoding='utf8') as f:
    json.dump(synon_dict, f, ensure_ascii=False)

要进行完全准确的 get_nearest_neighbors() 类型的计算本质上是相当昂贵的,需要针对集合中的 每个 个单词,每个新单词进行查找和计算.

看起来这组向量的大小接近或超过 2GB,当仅加载词向量时,这意味着扫描 2GB 的可寻址内存可能是运行时的主导因素。

尝试一些可能有帮助的事情:

  • 确保您有足够的 RAM - 如果使用了 'swap'/虚拟内存,那会使运行速度变慢。
  • 避免所有不必要的比较 - 例如,在 昂贵的步骤之前执行 is_strictly_sinhala_word() 检查 ,这样如果对结果不感兴趣,您可以跳过昂贵的步骤。此外,您可以考虑缩小整组词向量以消除那些您不太可能希望作为响应的词向量。这可能涉及丢弃您知道不属于感兴趣语言的词,或所有低频词。 (如果你甚至在尝试 get_nearest_neighbors() 之前就可以将最近邻词的一半扔掉,它的速度大约会提高一倍。)下面有更多关于这些选项的信息。
  • 尝试其他词向量库,看看它们是否提供任何改进。例如,Python Gensim 项目可以加载普通的全词向量集(例如,cc.si.300.vec words-only 文件)或 FastText 模型(.bin 文件),并提供具有一些额外选项的 .most_similar() 函数和 可能 ,在某些情况下,提供不同的性能。 (虽然,官方 Facebook Fasttext .get_nearest_neighbors() 可能相当不错。)
  • 使用“近似最近邻”库预先构建词向量索引 space,然后可以提供超快速的最近邻查找 - 虽然存在找不到确切的风险右前 N 个邻居。有很多这样的库——请参阅这个 benchmarking project,它比较了其中的 20 多个。但是,添加此步骤会使事情变得复杂,这种复杂性和不完美结果的权衡可能不值得付出努力和节省时间。所以,请记住,如果您的需求足够大并且没有其他帮助,那是有可能的。

关于精简搜索的向量集:

  • Gensim KeyedVectors.load_word2vec_format() 函数可以加载 .vec 只包含单词的文件,它有一个选项 limit 可以只从文件中读取指定数量的单词。看起来您的数据集的 .vec 文件有超过 800k 个单词 - 但如果您选择仅加载 400k 个,您的 .most_similar() 计算速度将快两倍。 (而且,由于此类文件通常会预先加载最常用词的文件,因此丢失很少见的词可能不是问题。)
  • 同样,即使加载所有向量,Gensim .most_similar() 函数也有一个 restrict_vocab 选项,可以将 searches 限制为仅第一个单词那个计数,这也可以加快速度或有助于删除可能不那么有趣的晦涩词。
  • 如果您想预先过滤单词,例如消除非僧伽罗语单词,.vec 文件可能更容易使用。 (注意:通常的 .load_word2vec_format() 文本格式需要第一行声明单词数和单词维度,但您可以将其关闭,然后使用 no_header=True 选项加载,而不是使用 2 个完整传递文件以获取计数。)