我正在使用Python中的multiprocessing
模块,同时将keras
对象和Pool(processes = 4)
与imap
一起训练神经网络。在每个“周期”(即每4个进程)之后,它会稳定地使用越来越多的内存,直到最终崩溃。
我使用了memory_profiler
模块来跟踪我的内存使用情况,并训练了12个网络。这是使用香草imap
:
如果我将maxtasksperchild = 1
放在Pool
中:
如果我使用imap(chunksize = 3)
:
在后一种情况下,一切正常,我只是向池中的每个进程发送一个批处理,因此似乎问题在于这些进程携带有关先前批处理的信息。如果是这样,我可以强迫游泳池不这样做吗?
即使块解决方案似乎可以工作,我还是不想使用它,因为
tqdm
模块跟踪进度,在大块的情况下,它只会在每个大块之后更新,这实际上意味着它不会真正跟踪任何东西,因为所有大块都完成了同时(在此示例中)这是香草盒中的代码段。在其他两种情况下,我只是更改了maxtasksperchild
中的Pool
参数和chunksize
中的imap
参数:
def train_network(network):
(...)
return score
pool = Pool(processes = 4)
scores = pool.imap(train_network, networks)
scores = tqdm(scores, total = networks.size)
for (network, score) in zip(networks, scores):
network.score = score
pool.close()
pool.join()
答案 0 :(得分:1)
不幸的是,Python中的multiprocessing
模块带来了巨大的开销。数据通常在流程之间不共享,需要复制。从python 3.8开始,这将发生变化。
https://docs.python.org/3.8/library/multiprocessing.shared_memory.html
尽管python 3.8的正式发布时间为2019年10月21日,但您已经可以在github上下载它了
答案 1 :(得分:1)
我想出了一个可行的解决方案。我抛弃了游泳池,制作了自己的简单排队系统。除了不增加(虽然确实增加了一点,但我认为这是我将一些字典存储为日志)之外,它甚至比上面的块解决方案消耗的更少内存:
我不知道为什么会这样。也许Pool
对象只是占用大量内存?无论如何,这是我的代码:
def train_network(network):
(...)
return score
# Define queues to organise the parallelising
todo = mp.Queue(size = networks.size + 4)
done = mp.Queue(size = networks.size)
# Populate the todo queue
for idx in range(networks.size):
todo.put(idx)
# Add -1's which will be an effective way of checking
# if all todo's are finished
for _ in range(4):
todo.put(-1)
def worker(todo, done):
''' Network scoring worker. '''
from queue import Empty
while True:
try:
# Fetch the next todo
idx = todo.get(timeout = 1)
except Empty:
# The queue is never empty, so the silly worker has to go
# back and try again
continue
# If we have reached a -1 then stop
if idx == -1:
break
else:
# Score the network and store it in the done queue
score = train_network(networks[idx])
done.put((idx, score))
# Construct our four processes
processes = [mp.Process(target = worker,
args = (todo, done)) for _ in range(4)]
# Daemonise the processes, which closes them when
# they finish, and start them
for p in processes:
p.daemon = True
p.start()
# Set up the iterable with all the scores, and set
# up a progress bar
idx_scores = (done.get() for _ in networks)
pbar = tqdm(idx_scores, total = networks.size)
# Compute all the scores in parallel
for (idx, score) in pbar:
networks[idx].score = score
# Join up the processes and close the progress bar
for p in processes:
p.join()
pbar.close()