【问题标题】:Numpy stride tricks complaining "array too big", why?Numpy stride 技巧抱怨“数组太大”,为什么?
【发布时间】:2013-11-14 21:13:22
【问题描述】:

在 numpy (1.8) 中,我想将此计算从 Python 循环中移出,以实现更好的性能:

(width, height) = base.shape
(toolw, toolh) = tool.shape
for i in range(0, width-toolw):
    for j in range(0, height-toolh):
        zdiff[i,j] = (tool - base[i:i+toolw, j:j+toolh]).min()

base 是一个 ~2000x2000 数组,tool 是一个 25x25 数组。 (背景:base 和 tool 是高度图,我正在尝试找出最接近的工具在整个 base 上移动的方法。)

我正在尝试使用跨步技巧,从以下开始:

base_view = np.lib.stride_tricks.as_strided(base, shape=(2000, 2000, 25, 25), 
                                            strides=(base.strides * 2))

这将使base_view[10,20] 成为一个 25x25 的值数组,从 (10, 20) 的左上角开始。

但是,由于“数组太大”而失败。从值测试来看,当数组的潜在大小(例如 2000*2000*25*25*8)超过 2^32-ish 并且它触发了将所有维度相乘的溢出检查时,它似乎会报告此问题。 (我安装的是 32 位 Python)。

我觉得我错过了一些东西——为什么当步幅值明显有效时,它不能让我创建这个“步幅视图”?有没有办法强制这样做?

更一般地说,有没有办法优化我上面的循环?

更新:确切错误:

ValueError                                Traceback (most recent call last)
<ipython-input-14-313b3d6c74fa> in <module>()
----> 1 newa = np.lib.stride_tricks.as_strided(base, shape=(1000, 1000, 25, 25), strides=(base.strides * 2))

C:\Python27\lib\site-packages\numpy\lib\stride_tricks.pyc in as_strided(x, shape, strides)
     28     if strides is not None:
     29         interface['strides'] = tuple(strides)
---> 30     array = np.asarray(DummyArray(interface, base=x))
     31     # Make sure dtype is correct in case of custom dtype
     32     array.dtype = x.dtype

C:\Python27\lib\site-packages\numpy\core\numeric.pyc in asarray(a, dtype, order)
    458 
    459     """
--> 460     return array(a, dtype, copy=False, order=order)
    461 
    462 def asanyarray(a, dtype=None, order=None):

ValueError: array is too big.

【问题讨论】:

  • 确切的错误是什么?顺便说一句,这是scipy.ndimage.generic_filter 的一个很好的候选者,尽管这可能比你的大步方法慢。
  • 我查看了 scipy.ndimage 通用过滤器。过滤器是一个 Python 函数,它不会将循环移动到 numpy 中,这是(我怀疑)真正加速的地方。
  • @alko:正是那个帖子让我走上了这条路。但是,如果维度相乘太大,这种方法就会失效。
  • 您看到的错误正在引发here。它正在计算数组的总大小,它会溢出 np.intp,在 32 位系统上是 int32。看起来你的阵列的虚拟大小不应该是一个问题。看来您只需要将当前检查替换为基于步幅乘积与其相应尺寸之和的检查。如果你切换到 64 位版本的 Numpy,你所有的问题都会消失......
  • @JoeKington 不,as_strided 不会检查这一点,如果您做错数学并开始超出实际数组边界进行写入或读取,您可能会搞砸很多事情。

标签: python arrays numpy stride


【解决方案1】:

我无法真正帮助您使用 strides 方法,但确实有一种方法应该比您的原始代码更快。它循环遍历tool 数组而不是base 数组,这意味着,尽管没有完全矢量化,更多的工作被推到了numpy。

请注意,在您的原始代码中,我更改了范围并切换了宽度和高度,因为我认为这就是您的意图..

import numpy as np

height, width = 500, 500
toolh, toolw = 6, 6

base = np.random.rand(height, width)
tool = np.random.rand(toolh, toolw)

m, n = height-toolh+1, width-toolw+1

def height_diff_old(base, tool):
    zdiff = np.empty((m, n))
    for i in range(m):
        for j in range(n):
            zdiff[i, j] = (tool - base[i:i+toolh, j:j+toolw]).min()
    return zdiff

def height_diff_new(base, tool):
    zdiff = np.empty((m, n))
    zdiff.fill(np.inf)
    for i in range(toolh):
        for j in range(toolw):
            diff_ij = tool[i, j] - base[i:i+m, j:j+n]
            np.minimum(zdiff, diff_ij, out=zdiff)
    return zdiff

当然,您希望在实际函数中计算高度和宽度,但为了测试,将它们作为全局变量更容易。

对于给定的数组大小,原始代码在 7.38 秒内运行,而新代码在我的系统上仅需 206 毫秒。我假设新代码对于您的数组大小也更快,但我不确定它的扩展性如何:)

您可能感兴趣也可能不感兴趣的其他替代方法是使用 Numba 或 Cython,在许多情况下,它们应该比您想到的任何“矢量化”numpy 代码都要快..

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-13
    • 2015-06-09
    • 2020-08-12
    • 2016-02-27
    • 1970-01-01
    • 2018-08-08
    • 1970-01-01
    • 2018-11-17
    相关资源
    最近更新 更多