如何将 gensim 的 KeyedVectors 对象存储在 Redis 队列工作程序中的全局变量中

How to store gensim's KeyedVectors object in a global variable inside a Redis Queue worker

我正在尝试将数据存储在 Redis 队列 (RQ) 工作程序中的全局变量中,以便此数据保持预加载状态,即不需要为每个 RQ 作业加载它。

具体来说,我正在使用 Word2Vec 向量并使用 gensim 的 KeyedVectors 加载它们。

我的应用程序在 Python Flask 中,运行 在 Linux 服务器上,使用 Docker 容器化。

我的目标是通过始终将少量大型矢量文件加载到内存中来减少处理时间。

我首先尝试将它们存储在 Flask 的全局变量中,但随后我的 8 个 gunicorn worker 中的 每个 都加载了向量,这会占用大量 RAM。

我只需要 一个 工作人员来存储特定的矢量文件。

有人告诉我,一种解决方案是让一定数量的 RQ worker 将向量保存在一个全局变量中,这样我就可以控制哪些 worker 加载哪些向量文件。

这是我目前的情况:

RQ_worker.py

from rq import Worker, Connection
from gensim.models.keyedvectors import KeyedVectors
from my_common_methods import get_redis

W2V = KeyedVectors.load_word2vec_format('some_path/vectors.bin', binary=True)

def rq_task(some_args):
    # use some_args and W2V to do some processing, e.g.:
    with open(some_args_filename, 'w') as f_out:
        f_out.write(str(W2V['word']))

if __name__ == '__main__':
    with Connection(get_redis()):
        worker = Worker(['default'])
        worker.work()

app.py

from rq import Queue, Connection
from RQ_worker import rq_task

@app.route("/someroute", methods=['POST'])
def some_route():
    # test Redis Queue
    with Connection(get_redis()):
        q = Queue()
        task = q.enqueue(rq_task, some_args)

docker-stack.yml

version: '3.7'

services:
  nginx:
    image: nginx:mainline-alpine
    deploy: ...
    configs: ...
    networks: ...

  flask:
    image: ...
    deploy: ...
    environment: ...
    networks: ...
    volumes: ...

  worker:
    image: ...
    command: python2.7 RQ_worker.py
    deploy:
      replicas: 1
    networks: ...
    volumes:
      - /some_path/data:/some_path/data

configs:
  nginx.conf:
    external: true
    name: nginx.conf

networks:
  external:
    external: true
  database:
    external: true

(我从 Docker 中编辑了一堆内容,但如果相关,可以提供更多详细信息。)

以上通常有效,除了 RQ worker 似乎在每次获得新工作时从头开始 加载 W2V ,这打败了整个目的。它应该将向量存储在 W2V 中作为全局变量,因此它们不需要每次都重新加载。

我错过了什么吗?我应该以不同的方式设置它吗?

有人告诉我可以使用 mmap 将向量文件加载到 RQ worker 所在的全局变量中,但我不确定这将如何与 KeyedVectors 一起工作。

如有任何建议,我们将不胜感激!

如果您使用 load_word2vec_format(),代码将始终解析(非本机到 gensim-or-Python)词向量格式,并分配新的 objects/memory 来存储结果。

您可以改为使用 gensim 的本机 .save() 以更友好的格式存储,以便以后本机 .load() 操作。大型矢量数组将存储在单独的内存映射就绪文件中。然后,当您 .load(..., mmap='r') 这些文件时,即使来自同一容器内的不同线程或进程多次,它们也会共享相同的 RAM。

(请注意,这甚至不需要任何共享全局变量。OS 会注意到每个进程都在请求相同的只读内存映射文件,并自动共享这些 RAM 页。唯一重复将是多余的 Python dicts 帮助每个单独的 .load() 知道共享数组中的索引。)

在模型需要重复单位范数的向量上进行相似性操作时,需要考虑一些额外的皱纹 - 有关如何解决该问题的更多详细信息,请参阅这个较旧的答案:

(请注意,syn0syn0_norm 在最近的 gensim 版本中已重命名为 vectorsvectors_norm,但旧名称可能仍然存在使用弃用警告一段时间。)