【问题标题】:Fast iteration over numpy array for squared residuals对残差平方的numpy数组进行快速迭代
【发布时间】:2019-01-30 18:54:35
【问题描述】:

我喜欢对具有许多已知信号形状的数据(一个 numpy 浮点数组)进行最小二乘匹配。我的代码可以运行,但对于我计划进行的多次运行来说太慢了:

import numpy
import time

samples = 50000
width_signal = 100
data = numpy.random.normal(0, 1, samples)
signal = numpy.random.normal(0, 1, width_signal)  # Placeholder

t0 = time.clock()
for i in range(samples - width_signal):
    data_chunk = data[i:i + width_signal]
    residuals = data_chunk - signal
    squared_residuals = residuals**2
    summed_residuals = numpy.sum(squared_residuals)
t1 = time.clock()
print('Time elapsed (sec)', t1-t0)

编辑:更正了一个错误:先将残差平方,然后将它们相加。

在我的机器上运行大约需要 0.2 秒。由于我有很多数据集和信号形状,这太慢了。我的具体问题不允许使用典型的 MCMC 方法,因为信号形状差异太大。它必须是蛮力。

典型的数据量为 50,000 个浮点数,信号为 100 个浮点数。这些值可能相差几倍。

我的测试表明:

  • 数据的总和numpy.sum(residuals) 占用了 90% 的时间。我尝试了 Python 的 sum(residuals),它对于小数组(~if 条件吗?
  • 我试过numpy.roll()而不是直接取数据,.roll()比较慢。

问题:

  • 对于加速有逻辑上的改进吗?
  • 有没有更快的方法来对数组求和?我不会 C,但如果它更快,我可以试试。
  • GPU 可以提供帮助吗?我有很多跑步要做。如果是这样,我在哪里可以找到代码 sn-p 来执行此操作?

【问题讨论】:

  • 您可能对pandas.rolling 有一些运气,但随后创建 DF 的开销可能会开始占主导地位
  • 您是否考虑过使用卷积而不是平方差之和?另外,平方必须在总和内吗?
  • 啊,当然,我犯了一个错误:必须先对残差求平方,然后再对这些平方残差求和。我在 Q 中更改了它。是否可以使用 np.lib.stride_tricks.as_strided 执行此操作?非常感谢。
  • 应该是for i in range(samples - width_signal + 1): 覆盖所有。
  • 关于 +1 的长度:在实践中,我将不得不翻过边缘。真实数据是相位折叠的,因此信号可以在数据末尾附近开始并在开头继续。

标签: python arrays performance numpy


【解决方案1】:

基于Compute mean squared, absolute deviation and custom similarity measure - Python/NumPy 中提出的各种方法,我们希望在这里解决我们的问题。

方法#1

我们可以利用基于scikit-image's view_as_windowsnp.lib.stride_tricks.as_strided 来获得滑动窗口,从而在这里获得我们的第一个解决方案,就像这样 -

from skimage.util import view_as_windows

d = view_as_windows(data,(width_signal))-signal # diffs
out = np.einsum('ij,ij->i',d,d)

More info on use of as_strided based view_as_windows.

方法 #2

再次基于该答案帖子中的矩阵乘法技巧,我们可以提高性能,就像这样 -

def MSD_strided(data, signal):
    w = view_as_windows(data,(width_signal))
    return (w**2).sum(1) + (signal**2).sum(0) - 2*w.dot(signal)

方法#3

我们将通过引入统一过滤和卷积来改进方法#2 -

from scipy.ndimage.filters import uniform_filter 

def MSD_uniffilt_conv(data, signal):
    hW = width_signal//2
    l = len(data)-len(signal)+1
    parte1 = uniform_filter(data**2,width_signal)[hW:hW+l]*width_signal
    parte3 = np.convolve(data, signal[::-1],'valid')    
    return parte1 + (signal**2).sum(0) - 2*parte3

基准测试

发布样本的时间 -

In [117]: %%timeit
     ...: for i in range(samples - width_signal + 1):
     ...:     data_chunk = data[i:i + width_signal]
     ...:     residuals = data_chunk - signal
     ...:     squared_residuals = residuals**2
     ...:     summed_residuals = numpy.sum(squared_residuals)
1 loop, best of 3: 239 ms per loop

In [118]: %%timeit
     ...: d = view_as_windows(data,(width_signal))-signal
     ...: np.einsum('ij,ij->i',d,d)
100 loops, best of 3: 11.1 ms per loop

In [209]: %timeit MSD_strided(data, signal)
10 loops, best of 3: 18.4 ms per loop

In [210]: %timeit MSD_uniffilt_conv(data, signal)
1000 loops, best of 3: 1.71 ms per loop

~140x 用第三个加速!

【讨论】:

  • 我在 numpy 中搜索了很多移动窗口,但这种方法没有出现在任何地方。好的,它不是 numpy 的一部分,但它比我发现的要好得多。我应该一直在搜索“numpy 移动窗口”以外的内容,还是应该将其添加为这些线程的答案?
  • @roganjosh 你会在 SO 上找到很多 np.lib.stride_tricks.as_stridedscikit-image's view_as_windows 是最近添加到 scikit-image 的,所以这可能就是为什么它不是很多,但我发现它与为 as_strided 修改步幅和形状 arg 相比非常整洁。因此,每当我需要使用滑动窗口时,我都会经常使用这个view_as_windows。至于术语 - “NumPy windows”可能是很好的关键字。
  • 是的,我确实看到了np.lib.stride_tricks.as_strided,但我同意这是一种很好的打包方式:)
  • 。 . .哇。您编辑中的最后一种方法非常棒。
【解决方案2】:

除了 Divakar 提供的版本之外,您还可以简单地使用 Numba 或 Cython 之类的编译器。

示例

import numba as nb
@nb.njit(fastmath=True,parallel=True)
def sq_residuals(data,signal):
  summed_residuals=np.empty(data.shape[0]+1-signal.shape[0],dtype=data.dtype)
  for i in nb.prange(data.shape[0] - signal.shape[0]+1):
      sum=0.
      for j in range(signal.shape[0]):
        sum+=(data[i+j]-signal[j])**2
      summed_residuals[i]=sum
  return summed_residuals

时间安排

Numba 0.4dev, Python 3.6, Quadcore i7
MSD_uniffilt_conv(Divakar): 2.4ms

after the first call which invokes some compilation overhead:
sq_residuals              : 1.7ms

【讨论】:

  • 谢谢!对于可以使用 Numba/Cython 的情况,这是一个有用的替代方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-17
  • 2015-12-12
  • 1970-01-01
  • 1970-01-01
  • 2010-11-01
  • 2021-04-26
  • 1970-01-01
相关资源
最近更新 更多