我在尝试使用Fasttext提取僧伽罗语中的形态/相似词汇。但是FastText处理2.64个词需要1秒钟。如何在不改变模型大小的前提下提高速度呢?
我的代码如下所示:
import fasttextfasttext.util.download_model('si', if_exists='ignore') # Sinhalaft = 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_notebookfor i in tqdm_notebook(range(len(words))): word = words[i].strip() synon = ft.get_nearest_neighbors(word)[0][1] ### 这步花费很多时间 if is_strictly_sinhala_word(synon): synon_dict[word] = synonimport jsonwith open("out.json", "w", encoding='utf8') as f: json.dump(synon_dict, f, ensure_ascii=False)
回答:
要进行完全准确的get_nearest_neighbors()
类型计算本质上是相当费时的,需要对集合中每个词进行查找和计算。
看起来当只加载词向量时,该向量集的大小接近或超过2GB,这意味着扫描2GB可寻址内存可能是运行时间的主要因素。
可以尝试一些可能有帮助的方法:
- 确保有足够的RAM – 如果使用了’交换’/虚拟内存,会大大降低速度。
- 避免所有不必要的比较 – 例如,在执行昂贵的步骤之前进行
is_strictly_sinhala_word()
检查,这样如果对结果不感兴趣,就可以跳过昂贵的步骤。另外,可以考虑缩小完整的词向量集,排除那些不太可能作为响应的词。这可能涉及到删除已知不属于目标语言的词,或所有低频词。(如果你能在尝试get_nearest_neighbors()
之前排除一半的词作为可能的最近邻居,速度大约会提高一倍。)关于这些选项的更多信息见下文。 - 尝试其他词向量库,看看它们是否能提供任何改进。例如,Python的Gensim项目可以加载纯词向量集(例如
cc.si.300.vec
仅词文件)或FastText模型(.bin
文件),并提供一个.most_similar()
函数,该函数有一些额外的选项,并且在某些情况下可能提供不同的性能。(尽管,官方Facebook Fasttext的.get_nearest_neighbors()
可能已经相当不错了。) - 使用“近似最近邻居”库来预先构建词向量空间的索引,然后提供超快的最近邻居查找 – 尽管这会冒一些风险,无法找到完全正确的top-N邻居。有许多这样的库 – 请参阅这个基准测试项目,它比较了20多个库。但是,添加这一步会使事情复杂化,这种复杂性的权衡以及不完美的结果可能不值得付出努力和节省时间。所以,如果你的需求足够大,并且其他方法都不起作用,请记住这是一个可能性。
关于缩小搜索的向量集:
- Gensim的
KeyedVectors.load_word2vec_format()
函数,可以加载.vec
仅词文件,有一个limit
选项,只会读取指定数量的词。看起来你的数据集的.vec
文件有超过80万个词 – 但如果你选择只加载40万个,你的.most_similar()
计算速度将大约提高一倍。(而且,由于此类文件通常会先加载最常见的词,丢失那些非常罕见的词可能不是问题。) - 同样,即使你加载了所有向量,Gensim的
.most_similar()
函数也有一个restrict_vocab
选项,可以将搜索限制在该数量的前几个词,这也可以加速计算或有助于删除可能不太感兴趣的生僻词。 - 如果想要预先过滤词,例如删除非僧伽罗语词,
.vec
文件可能更容易处理。(注意:通常的.load_word2vec_format()
文本格式需要第一行声明词的数量和词维度,但你可以省略这一行,然后使用no_header=True
选项加载,该选项会对文件进行两次完整的扫描以获取数量。)