Python 用烧瓶和喷枪在生产中装入预训练手套
我有一个模型需要使用斯坦福大学的手套进行一些预处理。根据我的经验,手套至少需要20-30秒才能加载此代码:Python 用烧瓶和喷枪在生产中装入预训练手套,python,stanford-nlp,word2vec,fasttext,Python,Stanford Nlp,Word2vec,Fasttext,我有一个模型需要使用斯坦福大学的手套进行一些预处理。根据我的经验,手套至少需要20-30秒才能加载此代码: glove_pd = pd.read_csv(embed_path+'/glove.6B.300d.txt', sep=" ", quoting=3, header=None, index_col=0) glove = {key: val.values for key, val in glove_pd.T.items()} 我的问题是,在生产应用程序中处理此问题的最佳
glove_pd = pd.read_csv(embed_path+'/glove.6B.300d.txt', sep=" ", quoting=3, header=None, index_col=0)
glove = {key: val.values for key, val in glove_pd.T.items()}
我的问题是,在生产应用程序中处理此问题的最佳实践是什么?据我所知,每次重新启动服务器时,我都需要等待30秒,直到端点准备就绪
此外,在使用Gunicorn时,建议使用workers>1
,类似于以下内容:
ExecStart=/path/to/gunicorn --workers 3 --bind unix:app.sock -m 007 wsgi:app
这是否意味着gunicorn的每个实例都需要将相同的手套加载到内存中?这意味着服务器资源将相当大,请告诉我是否正确
一句话,我的问题是,如果您需要在内存中存储一个需要在生产服务器上进行预训练嵌入(glove/word2vec/fasttext)的模型,那么推荐的托管方法是什么?从磁盘将千兆字节以上的数据读入有用的RAM结构需要多长时间,然后是的——这就是流程准备使用该数据所需的时间。但是还有优化的空间 例如,将其作为第一个数据帧读取,然后将其转换为Python dict,比其他选项需要更多的步骤和更多的RAM。(在瞬时峰值,当
glood_pd
和glood
都被完全构造和引用时,内存中会有两个完整的副本,而且两者都没有理想的紧凑性,这可能会触发其他减速,特别是当膨胀使用任何虚拟内存触发时。)
正如您所担心的,如果3个gunicorn工作人员都运行相同的加载代码,那么将加载相同数据的3个单独副本,但有一种方法可以避免这种情况,如下所示
我建议首先将向量加载到一个实用类中,以便访问单词向量,比如Gensim库中的keyedvivers
接口。它将所有向量存储在一个紧凑的numpy
矩阵中,并带有一个类似dict的接口,该接口仍然为每个向量返回一个numpy
word2vec.c
code使用后调用word2vec_format
)。在gensim-3.8.3
(当前版本截至2020年8月)中,您可以执行以下操作:
从gensim.scripts.glove2word2vec导入glove2word2vec
glove2word2vec('glove.6B.300d.txt','glove.6B.300d.w2vtxt')
然后,实用程序类KeyedVectors
可以像这样加载它们:
从gensim.models导入关键向量
glove_kv=KeyedVectors.load_word2vec_格式('glove.6B.300d.w2vText',binary=False)
(从未来的gensim-4.0.0
版本开始,应该可以跳过转换&只需使用新的no_header
参数直接读取手套文本文件:globate_kv=KeyedVectors.load_word2vec_格式('globate.6B.300d.w2vtext',binary=False,no_header=True)
。但是这种无头格式会稍微慢一点,因为它需要对文件进行两次遍历—第一次遍历是为了了解文件的完整大小。)
只需将一次加载到KeyedVectors
中,应该比原来的通用两步过程更快、更紧凑。而且,在glove\u kv
实例上可以使用类似于您在前面的dict上所做的查找。(此外,还有许多其他方便的操作,如ranked。most_Simple()
查找,它们利用高效的数组库函数来提高速度。)
不过,您可以采取另一个步骤,最小化加载时的解析,推迟加载不需要的整个向量集范围,并在进程之间自动重用原始数组数据
额外的步骤是使用Gensim实例的.save()
函数重新保存向量,该函数将原始向量转储到一个单独的密集文件中,该文件适合在下次加载时进行内存映射。因此,首先:
glove_.kv.save('glove.6B.300d.gs'))
这将创建多个文件,如果重新定位,这些文件必须保存在一起–但是保存的.npy
文件将是内存映射所需的精确最小格式
然后,在以后需要时,加载为:
glove\u kv=KeyedVectors.load('glove.6B.300d.gs',mmap='r')
mmap
参数使用底层操作系统机制将相关矩阵地址空间简单地映射到磁盘上的(只读)文件,以便初始“加载”实际上是即时的,但任何访问矩阵范围的尝试都将使用虚拟内存来分页文件的正确范围。因此,它消除了对分隔符的任何扫描,并将IO延迟到绝对需要时。(如果有任何范围您永远无法访问?它们将永远不会被加载。)
内存映射的另一大好处是,如果多个进程(每个只读内存都映射相同的磁盘文件),那么操作系统就足够聪明,可以让它们共享任意范围内的公共分页。因此,比方说,使用3个完全独立的操作系统进程,每个mmap处理相同的文件,可以节省3倍的内存
(如果在所有这些更改之后,重新启动服务器进程的滞后仍然是一个问题——可能是因为服务器进程崩溃或需要经常重新启动),您甚至可以考虑使用其他长期稳定的进程来初始映射这些向量。ny在文件范围内分页,重新启动服务器进程可能会发现RAM中已经存在部分或全部相关数据。但一旦其他优化到位,这个额外角色的复杂性可能是多余的。)
还有一个额外的警告:如果你开始使用KeyedVectors
方法,比如。大多数类似的方法都可以