【问题标题】:Python tile based processing of numpy array基于 Python tile 的 numpy 数组处理
【发布时间】:2019-11-15 02:30:11
【问题描述】:

我有一个非常大的 numpy 数组,大小为 [256,256,256,256],它占用了大约 8GB 的​​内存。

我想使用 multiprocessing 快速处理数据,类似于基于 tile 的渲染软件(例如 blender)使用的方法。

我想将我的数据分成更小的块,为它们创建一个进程列表,当一个完成后,开始处理下一个。我当前的方法使用 V 拆分,然后循环执行 hsplit 并获取块。下面是一个小得多的数据集的代码示例(9 x 9 分为 3 x 3 块):

import numpy as np

data = np.array(np.arange(0,81)).reshape((9,9))

print(data.shape)
print(data)
print("#"*30)
data = np.array([np.vsplit(set,3) for set in np.hsplit(data,3)])
print(data.shape)
print(data)
print("#"*30)

这并不完全符合我的要求,它创建了 9 个 3 x 3 的块,但这是一个小问题。主要问题是我不认为通过这样的数组循环来应用 vsplit 非常有效,但我不知道有一个内置函数可以自动执行此操作而无需循环。

我尝试使用 map 来查看它是否以更有效的方式自动应用 vsplit,但计时结果非常相似,所以我认为不是这样。有关如何优化此问题的任何建议?

【问题讨论】:

  • 你可能想看看numexpr AFAIK 实现的东西与你的策略非常相似。
  • @Paul Panzer 我仍然看不出它如何帮助将数组分成更小的块。这就是我大部分性能提升的来源。能够充分利用所有 CPU 内核比对处理算法本身的小幅改进重要得多。
  • "NumExpr 将表达式解析为它自己的操作码,然后由集成计算虚拟机使用。数组操作数被分成小块,很容易放入 CPU 的缓存中,并且传递给虚拟机。然后虚拟机对每个块应用操作。值得注意的是,表达式中的所有临时变量和常量也是块化的。块分布在 CPU 的可用内核中,从而实现高度并行的代码执行。 " 这和你尝试的不一样?

标签: python arrays numpy indexing


【解决方案1】:

您可以使用SciKit-Image's skimage.util.view_as_blocks(),它可以在不移动或复制任何数据的情况下解决您的阻塞问题。

skimage.util.view_as_blocks(data, (3, 3))

【讨论】:

    【解决方案2】:

    您想要基于as_strided 的东西。我推荐my recipe here。使用这个你可以做到:

    patches = window_nd(data, window = 3, steps = 3)
    

    要传递到多处理,您需要一个生成器。比如:

    def patch_gen(data, window, **kwargs):
        dims = len(data.shape)
        patches = window_nd(data, window, **kwargs)
        patches = patches.reshape((-1,) + patches.shape[-dims:])
        for i in range(patches.shape[0]):
            yield patches[i]
    

    您也可以使用来自@NilsWerner 的view_as_blocks

    def patch_gen(data, window_shape):
        dims = len(data.shape)
        patches = skimage.util.view_as_blocks(data, window_shape)
        patches = patches.reshape((-1,) + patches.shape[-dims:])
        for i in range(patches.shape[0]):
            yield patches[i]
    

    然后你可以这样做:

    with Pool(processors) as p:
        out = p.map(func, patch_gen(data, window = 3, steps = 3))
    

    这种方法应该只使用数组的原始内存,而不是在任何时候复制(我认为)。这样您就不会浪费时间和内存来复制原始数据,所有结果都只是指向原始数据的指针。

    警告:不要写入补丁!至少,in general it's a bad idea to write to as_strided results. 如果你没有重叠的窗口,你可能没问题,但是一旦你的窗口重叠(即stepswindow)你就会一团糟你的手。

    【讨论】:

    • 您还可以使用outlist = Truewindow_nd 输出窗口列表,但我正在考虑将其更改为生成器,因为它最终可能会抛出MemoryErrors 以​​供更大的使用案例。
    • 谢谢,这正是我想要的。但是我不确定如何在不复制数据的情况下将数据堆叠回原始形状。最终输出将是所有已处理块的列表。这是我写的一个例子:repl.it/repls/PlaintiveJointDrawing
    • @OM222O 您是要就地更改数据还是创建新的输出?
    • 如果你绝对确定你总是会有不相交的块,你应该能够就地写回生成的patches。如果你的patches 重叠,就会出现问题(你会遇到比赛条件和其他一些奇怪的事情,我还不足以让一个 CS 人解开)。谨慎使用!
    • 或者您可以使用下面的@NilsWerner 的答案并像我一样为您的p.map() 构建生成器。 view_as_blocks 的内置护栏比我的食谱多,以后不太可能绊倒你。
    猜你喜欢
    • 2018-01-31
    • 2020-08-23
    • 1970-01-01
    • 2021-06-24
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    相关资源
    最近更新 更多