在dask中的单独进程中调用scheduler.multiprocessing.get

时间:2018-06-26 19:01:50

标签: python-multiprocessing dask

我正在训练带有大型文本语料库的神经网络。每个文本都会生成一个很大的矩阵,因为我使用的是卷积模型。由于我的数据将无法存储在仍然很大的内存中,因此我尝试对其进行流传输并使用keras.models fit_generator

要养护喀拉拉邦,我有一个由不同的预处理步骤组成的管道,该管道由一个装有许多隔板的轻便袋子组成。轻快的袋子会读取磁盘上的文件。

即使是愚蠢的,也不是以一种聪明的方式来处理迭代(它只是compute()并在结果上进行迭代,在我的情况下,这会消耗内存),我将使用以下方式:

def compute_partition_iter(collection, **kwargs):
    """A utility to compute a collection items after items
    """
    get = kwargs.pop("get", None) or _globals['get']
    if get is None:
        get = collection.__dask_scheduler__
    postcompute_func, postcompute_args = collection.__dask_postcompute__()
    dsk = collection.__dask_graph__()
    for key in collection.__dask_keys__():
        yield from f([partition], *args)

此计算一个接一个地划分分区并返回项目,当我们越过分区边界时计算下一个分区。

这种方法有一个问题:只有当我们击中分区中的最后一项时,我们才会推动下一个元素的计算,导致到下一个元素的延迟。在这段时间里,喀拉拉邦陷入停滞,我们失去了宝贵的时间!

因此,我想想要归功于compute_partition_iter在单独的进程中运行上述multiprocessing.Pool,将Queue中的分区馈入具有2个插槽的分区,因此在生成器中,我不会总是再准备一个分区。

但是dask.bag似乎不支持此功能。我没有对代码进行足够的深入研究,但是似乎使用了一些异步方法,或者我不知道是什么。

这是该问题的可复制代码。

首先使用一个简单的范围即可工作的代码。

import multiprocessing
import time


def put_q(n, q):
    for i in range(n):
        print(i, "<-")
        q.put(i)
    q.put(None)

q = multiprocessing.Queue(2)
with multiprocessing.Pool(1, put_q, (4, q)) as pool:
    i = True
    while i is not None:
        print("zzz")
        time.sleep(.5)
        i = q.get()
        if i is None:
            break
        print("-> ", i)

此输出

0 <-
1 <-
2 <-
zzz
3 <-
->  0
zzz
->  1
zzz
->  2
zzz
->  3
zzz

您可以看到,按预期方式计算元素,一切正常。

现在让我们用dask.bag替换范围:

import multiprocessing
import time

import dask.bag


def put_q(n, q):
    for i in dask.bag.from_sequence(range(n), npartitions=2):
        print(i, "<-")
        q.put(i)
    q.put(None)

q = multiprocessing.Queue(5)
with multiprocessing.Pool(1, put_q, (4, q)) as pool:
    i = True
    while i is not None:
        print("zzz")
        time.sleep(.5)
        i = q.get()
        if i is None:
            break
        print("-> ", i)

在jupyter笔记本中,它会无限期地升高:

Process ForkPoolWorker-71:
Traceback (most recent call last):
  File "/usr/lib/python3.5/multiprocessing/process.py", line 249, in _bootstrap
    self.run()
  File "/usr/lib/python3.5/multiprocessing/process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/lib/python3.5/multiprocessing/pool.py", line 103, in worker
    initializer(*initargs)
  File "<ipython-input-3-e1e9ef9354a0>", line 8, in put_q
    for i in dask.bag.from_sequence(range(n), npartitions=2):
  File "/usr/local/lib/python3.5/dist-packages/dask/bag/core.py", line 1190, in __iter__
    return iter(self.compute())
  File "/usr/local/lib/python3.5/dist-packages/dask/base.py", line 154, in compute
    (result,) = compute(self, traverse=False, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/dask/base.py", line 407, in compute
    results = get(dsk, keys, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/dask/multiprocessing.py", line 152, in get
    initializer=initialize_worker_process)
  File "/usr/lib/python3.5/multiprocessing/context.py", line 118, in Pool
    context=self.get_context())
  File "/usr/lib/python3.5/multiprocessing/pool.py", line 168, in __init__
    self._repopulate_pool()
  File "/usr/lib/python3.5/multiprocessing/pool.py", line 233, in _repopulate_pool
    w.start()
  File "/usr/lib/python3.5/multiprocessing/process.py", line 103, in start
    'daemonic processes are not allowed to have children'
AssertionError: daemonic processes are not allowed to have children

当主进程停止时,等待队列中的元素。

我也尝试使用ipyparallel群集,但是在这种情况下,主要进程只是停滞了(没有异常的踪迹)。

有人知道这样做的正确方法吗?

有什么办法可以与我的主代码并行运行scheduler.get?

1 个答案:

答案 0 :(得分:0)

最后,我应该仔细看看该异常!

Stackoverflow给了我解决方案:Python Process Pool non-daemonic?

事实上,由于bag Scheduler使用Pool,因此不能在Pool产生的进程内部调用它。我的解决方案是仅使用线程。 (请注意,该错误及其解决方案取决于您使用的调度程序。)

因此,我已将multiprocessing.Pool替换为multiprocessing.pool.ThreadPool,无论是在普通笔记本中还是在using ipyparallel时,它的工作方式都像是一种魅力。

它是这样的:

import queue
from multiprocessing.pool import ThreadPool
import time

import dask.bag


def put_q(n, q):
    b = dask.bag.from_sequence(range(n), npartitions=3)
    for i in b:
        print(i, "<-")
        q.put(i)
    q.put(None)

q = queue.Queue(2)
with ThreadPool(1, put_q, (6, q)) as pool:
    i = True
    while i is not None:
        print("zzz")
        time.sleep(.5)
        i = q.get()
        if i is None:
            break
        print("-> ", i)

哪个输出:

zzz
0 <-
1 <-
2 <-
->  0
zzz
3 <-
->  1
zzz
4 <-
-> 5 <-
 2
zzz
->  3
zzz
->  4
zzz
->  5
zzz