如何在Numpy中实现ReLU功能


Answers:


128

有两种方法。

>>> x = np.random.random((3, 2)) - 0.5
>>> x
array([[-0.00590765,  0.18932873],
       [-0.32396051,  0.25586596],
       [ 0.22358098,  0.02217555]])
>>> np.maximum(x, 0)
array([[ 0.        ,  0.18932873],
       [ 0.        ,  0.25586596],
       [ 0.22358098,  0.02217555]])
>>> x * (x > 0)
array([[-0.        ,  0.18932873],
       [-0.        ,  0.25586596],
       [ 0.22358098,  0.02217555]])
>>> (abs(x) + x) / 2
array([[ 0.        ,  0.18932873],
       [ 0.        ,  0.25586596],
       [ 0.22358098,  0.02217555]])

如果使用以下代码计时结果:

import numpy as np

x = np.random.random((5000, 5000)) - 0.5
print("max method:")
%timeit -n10 np.maximum(x, 0)

print("multiplication method:")
%timeit -n10 x * (x > 0)

print("abs method:")
%timeit -n10 (abs(x) + x) / 2

我们得到:

max method:
10 loops, best of 3: 239 ms per loop
multiplication method:
10 loops, best of 3: 145 ms per loop
abs method:
10 loops, best of 3: 288 ms per loop

因此乘法似乎是最快的。


19
+1。我冒昧地将一些时间结果添加到您的答案中。请随时对其进行编辑,或根据需要还原编辑。
IVlad 2015年

9
np.maximum(x,0,x)在这里运行最快。
Daniel S.

1
@DanielS。对于将来的读者:最后一个xmaximum(x, 0, x)意思是“请x适当改变而不是分配新的矩阵”。(来源
ynn

1
@DanielS。如果可以选择就地操作,那么Tobias的回应中指出,就地操作速度更快。
Sid

43

由于其他问题和评论中提出的观点,我将完全修改我的原始答案。这是新的基准脚本:

import time
import numpy as np


def fancy_index_relu(m):
    m[m < 0] = 0


relus = {
    "max": lambda x: np.maximum(x, 0),
    "in-place max": lambda x: np.maximum(x, 0, x),
    "mul": lambda x: x * (x > 0),
    "abs": lambda x: (abs(x) + x) / 2,
    "fancy index": fancy_index_relu,
}

for name, relu in relus.items():
    n_iter = 20
    x = np.random.random((n_iter, 5000, 5000)) - 0.5

    t1 = time.time()
    for i in range(n_iter):
        relu(x[i])
    t2 = time.time()

    print("{:>12s}  {:3.0f} ms".format(name, (t2 - t1) / n_iter * 1000))

在每次实现和迭代时都应使用不同的ndarray。结果如下:

         max  126 ms
in-place max  107 ms
         mul  136 ms
         abs   86 ms
 fancy index  132 ms

6
与np.maximum(0,x)相比,np.maximum(x,0,x)如何花费更少的时间?
pikachuchameleon

5
还值得注意的是,这将修改x
Andrea

8
@pikachuchameleon更快,因为它就位。的返回值将np.maximum(x, 0, x)被忽略,结果将直接写入x
Lenar Hoyt

2
如果可以选择就地操作,那么Tobias的回应中指出,就地操作速度更快。
Sid

31

编辑正如jirassimok在下面提到的那样,我的函数将在适当的位置更改数据,之后它的运行速度会更快。这导致良好的结果。这是一种作弊行为。不便之处,敬请原谅。

我发现使用numpy的ReLU更快的方法。您也可以使用numpy的花式索引功能。

花式指数:

每个循环20.3 ms±272 µs(平均±标准偏差,共运行7次,每个循环10个)

>>> x = np.random.random((5,5)) - 0.5 
>>> x
array([[-0.21444316, -0.05676216,  0.43956365, -0.30788116, -0.19952038],
       [-0.43062223,  0.12144647, -0.05698369, -0.32187085,  0.24901568],
       [ 0.06785385, -0.43476031, -0.0735933 ,  0.3736868 ,  0.24832288],
       [ 0.47085262, -0.06379623,  0.46904916, -0.29421609, -0.15091168],
       [ 0.08381359, -0.25068492, -0.25733763, -0.1852205 , -0.42816953]])
>>> x[x<0]=0
>>> x
array([[ 0.        ,  0.        ,  0.43956365,  0.        ,  0.        ],
       [ 0.        ,  0.12144647,  0.        ,  0.        ,  0.24901568],
       [ 0.06785385,  0.        ,  0.        ,  0.3736868 ,  0.24832288],
       [ 0.47085262,  0.        ,  0.46904916,  0.        ,  0.        ],
       [ 0.08381359,  0.        ,  0.        ,  0.        ,  0.        ]])

这是我的基准:

import numpy as np
x = np.random.random((5000, 5000)) - 0.5
print("max method:")
%timeit -n10 np.maximum(x, 0)
print("max inplace method:")
%timeit -n10 np.maximum(x, 0,x)
print("multiplication method:")
%timeit -n10 x * (x > 0)
print("abs method:")
%timeit -n10 (abs(x) + x) / 2
print("fancy index:")
%timeit -n10 x[x<0] =0

max method:
241 ms ± 3.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
max inplace method:
38.5 ms ± 4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
multiplication method:
162 ms ± 3.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
abs method:
181 ms ± 4.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
fancy index:
20.3 ms ± 272 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

2
(+1)您喜欢的方法是我以前实际看到的唯一使用过的方法!我认为,它不仅高效,而且可以完美描述ReLU操作。
n1k31t4

2
当数组没有负数时,此方法仅比其他方法快。您的测试似乎很快,因为timeit修改了数组,因此在第一个循环之后,就没有负值了,并且运行速度更快。在每次都重新生成数组的测试中,逻辑索引分配(a[a < 0] = 0)执行的方法最差,np.maximum执行效果最佳。
jirassimok

@jirassimok,你是对的。我的职能将就地修改数据。而且运行一遍后,速度会快很多。我将更改其职位
Tobias

27

您可以用更简单的方式做到这一点:

def ReLU(x):
    return x * (x > 0)

def dReLU(x):
    return 1. * (x > 0)

谢谢。我发现这比花哨的索引方法要快。@Shital Shah能否请您进一步解释此语法或共享一些链接?
Sreeragh AR

1
这只是广播和逐元素乘法。该0会自动转向到相同大小的张量x。该bool结果将上交至0或1,然后将得到倍增的elementwise。没有魔法:)。
Shital Shah

7

理查德·莫恩(RichardMöhn)的比较 并不公平。
正如Andrea Di Biagio的评论,就地方法np.maximum(x, 0, x)将在第一个循环处修改x。

所以这是我的基准:

import numpy as np

def baseline():
    x = np.random.random((5000, 5000)) - 0.5
    return x

def relu_mul():
    x = np.random.random((5000, 5000)) - 0.5
    out = x * (x > 0)
    return out

def relu_max():
    x = np.random.random((5000, 5000)) - 0.5
    out = np.maximum(x, 0)
    return out

def relu_max_inplace():
    x = np.random.random((5000, 5000)) - 0.5
    np.maximum(x, 0, x)
    return x 

计时:

print("baseline:")
%timeit -n10 baseline()
print("multiplication method:")
%timeit -n10 relu_mul()
print("max method:")
%timeit -n10 relu_max()
print("max inplace method:")
%timeit -n10 relu_max_inplace()

得到结果:

baseline:
10 loops, best of 3: 425 ms per loop
multiplication method:
10 loops, best of 3: 596 ms per loop
max method:
10 loops, best of 3: 682 ms per loop
max inplace method:
10 loops, best of 3: 602 ms per loop

就地最大方法仅比最大方法快一点,这可能是因为它省略了“ out”的变量分配。而且它仍然比乘法方法慢。
并且由于您正在实现ReLU函数。您可能必须通过relu将'x'保存为反向传播。例如:

def relu_backward(dout, cache):
    x = cache
    dx = np.where(x > 0, dout, 0)
    return dx

所以我建议您使用乘法方法。


为什么您的基准测试显示relu_mul最快,但是您说relu_max_inplace稍快?另外,为什么您要初始化每个函数中的测试矩阵,而对于每种方法在刚开始时不初始化一次?现在,您的计时包括创建具有5000 * 5000 = 25000000个元素的矩阵所需的时间-如果使用默认值,float64则大小约为200 Mb 。%timeit np.random.random((5000, 5000)) - 0.5273 ms ± 7.95 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)。这超过了您发布的实际时间的三分之一。
n1k31t4 '18年

@ n1k31t4首先,我说relu_max_inplace比稍快一些relu_max,但最推荐的方法是relu_mul
ivanpp '18

1
np.random.random()故意添加了初始化函数,因为如果我不这样做,relu_max_inplace方法似乎会很快,就像@RichardMöhn的结果一样。@RichardMöhn的结果表明relu_max_inplacevsrelu_max是38.4ms vs每个循环238ms。只是因为in_place方法只会被执行一次。并且在每个循环中初始化矩阵将避免这种情况。比较将是公平的。
ivanpp

@ivanpp我不确定在时序结果中包含随机代op是否完全公平。
席德

1

如果我们有3个(t0, a0, a1)Relu参数,那就是我们要实现

if x > t0:
    x = x * a1
else:
    x = x * a0

我们可以使用以下代码:

X = X * (X > t0) * a1 +  X * (X < t0) * a0

X 有一个矩阵。


1

numpy没有relu的功能,但是您可以自己定义如下:

def relu(x):
    return np.maximum(0, x)

例如:

arr = np.array([[-1,2,3],[1,2,3]])

ret = relu(arr)
print(ret) # print [[0 2 3] [1 2 3]]


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.