Python 多处理-将进程用作服务/api 背景
我有一个生成一串单词的应用程序,它由keras模型进行评估。每个字符串都由NN进行处理、评估,并根据模型进行更新。这些字符串被更新了数千次。我必须用几百根弦来做这个 我想做什么 我想通过在自己的进程中运行每个字符串进程(给定足够的内核)来实现并行化。问题是,我无法轻松地将模型传递给每个进程(即使可以,也可能会耗尽内存)。我也无法在每个过程中轻松加载模型 我想做什么 是否有一种方法可以让我将每个字符串操作作为一个单独的进程运行,但将其调用到一个加载了模型的进程,该进程可以接受数据并将输出结果(如api调用),这样字符串进程就可以继续处理。我见过其他人在REST场景中使用keras,但这更像是一种Web服务。在字符串生成过程和神经网络过程之间进行通信的最佳方式是什么?在这个过程中使用队列之类的东西并对输入进行排队是否更好?或者asyncio 编辑 好的:看起来我们可以生成进程并将消息传递给keras模型的单个实例(无需传递keras对象或调用进程中的库)。我们可以继续检查字典是空的还是没有(是的,不是最优雅的)Python 多处理-将进程用作服务/api 背景,python,keras,multiprocessing,Python,Keras,Multiprocessing,我有一个生成一串单词的应用程序,它由keras模型进行评估。每个字符串都由NN进行处理、评估,并根据模型进行更新。这些字符串被更新了数千次。我必须用几百根弦来做这个 我想做什么 我想通过在自己的进程中运行每个字符串进程(给定足够的内核)来实现并行化。问题是,我无法轻松地将模型传递给每个进程(即使可以,也可能会耗尽内存)。我也无法在每个过程中轻松加载模型 我想做什么 是否有一种方法可以让我将每个字符串操作作为一个单独的进程运行,但将其调用到一个加载了模型的进程,该进程可以接受数据并将输出结果(如a
按照您的建议,最简单的方法可能是使用队列 一种可能的配置: 定义三个队列:
- 输入字符串:等待处理的字符串
- 已处理字符串:已处理但未计算的字符串
- 已评估字符串:已处理和评估的字符串(即结果)
.get()
,直到一个项目可用为止),对其进行处理,并将输出放入已处理的字符串中(使用.put()
,并无限期地重复此操作(或直到输入队列耗尽)
您的求值方法还将采用两个队列作为参数——已处理字符串队列和已求值字符串队列,其操作与字符串处理方法类似(但将调用Keras stuff)
然后,您可以让另一个进程处理求值字符串队列,或者可以等待输入字符串队列为空并在主线程中处理它
当然,这里有很多细节没有提及,但是假设您在一台机器上操作,并且不想扩展到多个处理节点(使用芹菜这样的任务队列),这是一种简单的方法,可能需要对现有代码进行最少的修改。下面是一个示例,展示了它的工作原理:
import multiprocessing
import multiprocessing.managers
import time
import random
import os
def string_stuff(input_queue, output_queue):
# you should maybe use a signal here, or check remaining count in queue
while True:
item = input_queue.get()
processed = item.split() # do the processing
# simulate some long running thing
time.sleep(random.random() * 2)
output_queue.put(processed)
print(f"[string_stuff:{os.getpid()}] Putting: {processed}")
def nn_stuff(input_queue, output_queue):
while True:
item = input_queue.get()
evaluated = ':'.join(item)
# simulate some less long running thing
time.sleep(random.random()/2)
output_queue.put(evaluated)
print(f"[nn_stuff:{os.getpid()}] Putting: {evaluated}")
def generate_random_word(length):
return ''.join([chr(ord('A') + random.randint(0, 25)) for _ in range(length)])
def fix_broken_multiprocessing():
# this is a really horrible bug in Python that for some reason
# has gone unfixed for years. Fix came from: https://stackoverflow.com/a/50878600/492759
# Backup original AutoProxy function
backup_autoproxy = multiprocessing.managers.AutoProxy
# Defining a new AutoProxy that handles unwanted key argument 'manager_owned'
def redefined_autoproxy(token, serializer, manager=None, authkey=None,
exposed=None, incref=True, manager_owned=True):
# Calling original AutoProxy without the unwanted key argument
return backup_autoproxy(token, serializer, manager, authkey, exposed, incref)
# Updating AutoProxy definition in multiprocessing.managers package
multiprocessing.managers.AutoProxy = redefined_autoproxy
if __name__ == "__main__":
fix_broken_multiprocessing()
n_words = 1000
words = [generate_random_word(random.randint(0, 10)) for _ in range(n_words)]
m = multiprocessing.Manager()
inputs = m.Queue()
processed = m.Queue()
evaluated = m.Queue()
# populate our fake queue
for word in words:
inputs.put(word)
string_pool = multiprocessing.Pool(4, string_stuff, (inputs, processed)) # 4 workers
nn_process = multiprocessing.Process(target=nn_stuff, args=(processed, evaluated))
nn_process.start()
while True:
result = evaluated.get()
print(f"Just got: {result} back from the pipeline")
这里的瓶颈在哪里?字符串处理是否比NN处理慢得多?更多的上下文也会很有用。您想在同一台机器上运行所有这些任务,还是想将其扩展到多台机器上运行?@JosephRedfern是的字符串处理是瓶颈(在我的NN预测之后,我在此字符串上运行单独的模拟)@JosephRedfern环境是一个计算集群,在这里我可以请求内核,也可以请求虚拟机,尽管请求新作业时会有开销,而且我有数百个字符串,所以我想在同一台机器上运行它。我喜欢你的解决方案,但我不确定为什么你不能从顶部函数导入keras,然后调用models predict函数。似乎您必须在流程中实例化一个模型。这并不是很有用,因为我们必须加载模型(和keras库)在进行评估的过程中,它会挂起。@Kevin当然,您可以在nn_内容中导入Keras,而不是全局导入。如果这是有益的,您甚至可以完全跳过Keras过程,只在主过程中进行。
import multiprocessing
import multiprocessing.managers
import time
import random
import os
def string_stuff(input_queue, output_queue):
# you should maybe use a signal here, or check remaining count in queue
while True:
item = input_queue.get()
processed = item.split() # do the processing
# simulate some long running thing
time.sleep(random.random() * 2)
output_queue.put(processed)
print(f"[string_stuff:{os.getpid()}] Putting: {processed}")
def nn_stuff(input_queue, output_queue):
while True:
item = input_queue.get()
evaluated = ':'.join(item)
# simulate some less long running thing
time.sleep(random.random()/2)
output_queue.put(evaluated)
print(f"[nn_stuff:{os.getpid()}] Putting: {evaluated}")
def generate_random_word(length):
return ''.join([chr(ord('A') + random.randint(0, 25)) for _ in range(length)])
def fix_broken_multiprocessing():
# this is a really horrible bug in Python that for some reason
# has gone unfixed for years. Fix came from: https://stackoverflow.com/a/50878600/492759
# Backup original AutoProxy function
backup_autoproxy = multiprocessing.managers.AutoProxy
# Defining a new AutoProxy that handles unwanted key argument 'manager_owned'
def redefined_autoproxy(token, serializer, manager=None, authkey=None,
exposed=None, incref=True, manager_owned=True):
# Calling original AutoProxy without the unwanted key argument
return backup_autoproxy(token, serializer, manager, authkey, exposed, incref)
# Updating AutoProxy definition in multiprocessing.managers package
multiprocessing.managers.AutoProxy = redefined_autoproxy
if __name__ == "__main__":
fix_broken_multiprocessing()
n_words = 1000
words = [generate_random_word(random.randint(0, 10)) for _ in range(n_words)]
m = multiprocessing.Manager()
inputs = m.Queue()
processed = m.Queue()
evaluated = m.Queue()
# populate our fake queue
for word in words:
inputs.put(word)
string_pool = multiprocessing.Pool(4, string_stuff, (inputs, processed)) # 4 workers
nn_process = multiprocessing.Process(target=nn_stuff, args=(processed, evaluated))
nn_process.start()
while True:
result = evaluated.get()
print(f"Just got: {result} back from the pipeline")