在python中的进程之间共享连续numpy数组

在python中的进程之间共享连续numpy数组,python,numpy,multiprocessing,shared-memory,caffe,Python,Numpy,Multiprocessing,Shared Memory,Caffe,虽然我已经找到了许多类似于我的问题的答案,但我不认为这里已经直接提到了这个问题——我还有几个问题要问。共享连续numpy阵列的动机如下: 我使用一个在Caffe上运行的卷积神经网络对图像进行回归,得到一系列连续的值标签 这些图像需要特定的预处理和数据扩充 (1)标签的连续性(它们是浮动)和(2)数据扩充的约束意味着我在python中预处理数据,然后使用Caffe中的内存数据层将其作为连续的numpy数组提供 将训练数据加载到内存中相对较慢。我想将其并行化,以便: (1) 我正在编写的pyth

虽然我已经找到了许多类似于我的问题的答案,但我不认为这里已经直接提到了这个问题——我还有几个问题要问。共享连续numpy阵列的动机如下:

  • 我使用一个在Caffe上运行的卷积神经网络对图像进行回归,得到一系列连续的值标签
  • 这些图像需要特定的预处理和数据扩充
  • (1)标签的连续性(它们是浮动)和(2)数据扩充的约束意味着我在python中预处理数据,然后使用Caffe中的内存数据层将其作为连续的numpy数组提供
  • 将训练数据加载到内存中相对较慢。我想将其并行化,以便:
(1) 我正在编写的python创建了一个“数据处理程序”类,它实例化了两个连续的numpy数组。 (2) 工作进程在这些numpy阵列之间交替,从磁盘加载数据,执行预处理,并将数据插入numpy阵列。 (3) 同时,python Caffe包装器将数据从另一个阵列发送到GPU,以便通过网络运行

我有几个问题:

  • 是否可以在一个连续的numpy数组中分配内存,然后使用类似python的multiprocessing中的array类的东西将其包装到一个共享内存对象中(我不确定这里的“object”是否是正确的术语)

  • Numpy数组有一个.ctypes属性,我认为这对于从Array()实例化共享内存数组很有用,但似乎无法准确确定如何使用它们

  • 如果共享内存在没有numpy数组的情况下实例化,它是否保持连续?如果没有,是否有办法确保它保持连续

  • 是否可以执行以下操作:

    import numpy as np
    from multiprocessing import Array
    contArr = np.ascontiguousarray(np.zeros((n_images, n_channels, img_height, img_width)), dtype=np.float32)
    sm_contArr = Array(contArr.ctypes.?, contArr?)
    
    然后用

    p.append(Process(target=some_worker_function, args=(data_to_load, sm_contArr)))
    p.start()
    
    谢谢

    编辑:我知道有许多库在不同的维护状态下具有类似的功能。我更愿意将此限制为纯python和numpy,但如果不可能,我当然愿意使用一个

    将numpy的
    ndarray
    环绕多处理的
    RawArray()
    有多种方法可以跨进程共享内存中的numpy阵列。让我们来看看如何使用多处理模块来实现这一点

    第一个重要的观察结果是,numpy提供了
    np.frombuffer()
    函数来围绕支持缓冲协议的预先存在的对象(例如
    bytes()
    bytearray()
    array()
    等等)包装一个ndarray接口。这将从只读对象创建只读数组,从可写对象创建可写数组

    我们可以将其与多处理提供的共享内存
    RawArray()。请注意,
    Array()
    不能用于此目的,因为它是一个带有锁的代理对象,并且不会直接公开缓冲区接口。当然,这意味着我们需要自己为numpified数组提供适当的同步

    关于ndarray包装的rawArray,有一个复杂的问题:当多处理在进程之间发送这样一个数组时——实际上,它需要在创建数组后将数组发送给两个工作进程——它会对数组进行pickle,然后取消对它们的pickle。不幸的是,这导致它创建了Ndarray的副本,而不是在内存中共享它们

    解决方案虽然有点难看,但是保持原始数组的原样,直到它们被转移到工作进程,并且仅在每个工作进程启动后将它们包装在数据数组中

    此外,最好是通过
    多处理.Queue
    直接与数组通信,无论是普通的RawArray还是ndarray包装的数组,但这也不起作用。RawArray不能放在这样的队列中,而ndarray包装的队列将被pickle和unpickle,因此实际上是复制的

    解决方法是将所有预分配阵列的列表发送到工作进程,并通过队列将索引传递到该列表中。这非常类似于传递令牌(索引),任何持有令牌的人都可以在相关数组上操作

    主程序的结构可以如下所示:

    #/usr/bin/env蟒蛇3
    #-*-编码:utf-8-*-
    将numpy作为np导入
    导入队列
    从多处理导入冻结支持中,设置启动方法
    从多处理导入事件、进程、队列
    从multiprocessing.sharedTypes导入RawArray
    def创建共享数组(大小,dtype=np.int32,num=2):
    dtype=np.dtype(dtype)
    如果“bBhHiIlLfd”中的dtype.isbuiltin和dtype.char:
    typecode=dtype.char
    其他:
    类型代码,大小='B',大小*dtype.itemsize
    返回[范围(num)内的uu的RawArray(类型代码,大小)]
    def main():
    my_dtype=np.32
    #125000000(大小)*4(数据类型)*2(数量)~=1 GB内存使用率
    数组=创建共享数组(125000000,dtype=my\u dtype)
    q_free=队列()
    q_used=队列()
    bail=Event()
    对于范围内的arr_id(len(数组)):
    q_free.put(arr_id)#使用分配的数组索引预填充空闲队列
    pr1=MyDataLoader(数组、q_空闲、q_使用、bail、,
    数据类型=我的数据类型,步骤=1024)
    pr2=MyDataProcessor(阵列、无q_、使用q_、bail、,
    数据类型=我的数据类型,步骤=1024)
    pr1.start()
    pr2.start()
    pr2.join()
    打印(“\n{}joined.”格式(pr2.name))
    pr1.join()
    打印(“{}joined.”格式(pr1.name))
    如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
    冻结支持()
    #在Windows上,只有“生成”可用。
    #此外,这还测试了阵列的正确共享,而不存在“欺骗”。
    设置启动方法('spawn')
    main()
    
    这将准备一个包含两个arra的列表