ReLU阈值中numpy.ceil和numpy.clip的缓慢性:瓶颈在哪里?

时间:2019-03-06 05:33:44

标签: python numpy neural-network performance-testing ceil

这是一个基于(或跟进)另一个问题Faster implementation of ReLU derivative的问题。

本着一种提出最快的计算导数的方式的精神,我写了一些解决方案,其中一种是:

In [35]: np.random.seed(0)       
In [36]: X = np.random.randn(3072,10000) 

# computing ReLU derivative
In [42]: np.ceil(np.clip(X, 0, 1))

在以other solutions of Divakar为基准进行测试时,我发现上述方法的运行速度非常慢( 30倍)。以下是时间安排(从最快到最慢)

In [43]: %timeit -n100 ne.evaluate('X>=0').view('i1')  
10.6 ms ± 203 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [44]: %timeit -n100 (X>=0).view('i1')
13.6 ms ± 77.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [45]: %timeit -n100 ne.evaluate('(X>=0)+0') 
22.1 ms ± 16.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# the super slowest one
In [46]: %timeit -n100 np.ceil(np.clip(X, 0, 1)) 
317 ms ± 2.14 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

什么是导致这种缓慢的因素?瓶颈在哪里?

1 个答案:

答案 0 :(得分:2)

首先,您只是在执行一种更复杂的操作序列。对于每个输入,您的ceil / clip物件都会执行以下操作:

  • 输入值是否小于0?如果是这样,请将中间值设置为0。
  • 否则,它大于1吗?如果是这样,请将中间值设置为1。
  • 否则,将中间值设置为输入值。
  • 计算中间值的上限,并将输出设置为该上限。

(这分为两个阶段,一个阶段完成所有裁剪,一个阶段完成所有天花板。)

您正在针对对每个输入执行以下操作的选项进行计时:

  • 在输入和0之间执行> =比较,并将输出设置为该值。

> =更快也就不足为奇了。


第二,您的ceil / clip东西正在写入的字节数是> =的16倍。 > =会为每个输入元素生成一个输出字节(view是一个视图,因此那里没有数据副本),而您的ceil / clip东西会生成一个中间类型数组和一个输出数组,均为dtype float64。


第三,分支预测变量在随机数组上的clip时间不好。不知道每次将采取哪个分支。更可预测的数组通过clip的速度更快:

In [21]: %timeit X.clip(0, 1)
1 loop, best of 5: 211 ms per loop

In [22]: A = np.full_like(X, 0.5)

In [23]: %timeit A.clip(0, 1)
10 loops, best of 5: 86.6 ms per loop

最后,至少在我测试过的机器和NumPy构建上,numpy.ceil令人惊讶地慢:

In [24]: %timeit np.ceil(X)
10 loops, best of 5: 166 ms per loop

我不确定它是否正在实现软件ceil实施或执行何种操作。在不同的版本上,这可能会有所不同。