如何在 Numpy 中实现 ReLU 函数

Posted

技术标签:

【中文标题】如何在 Numpy 中实现 ReLU 函数【英文标题】:How to implement the ReLU function in Numpy 【发布时间】:2015-11-13 13:38:55 【问题描述】:

我想制作一个使用 ReLU 函数的简单神经网络。有人可以告诉我如何使用 numpy 实现该功能。

【问题讨论】:

【参考方案1】:

有几种方法。

>>> 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

所以乘法似乎是最快的。

【讨论】:

+1。我冒昧地在您的答案中添加了一些 timeit 结果。如果您愿意,请随时编辑它们或恢复编辑。 np.maximum(x, 0, x) 在这里跑得最快。 @DanielS。对于未来的读者:maximum(x, 0, x) 中的最后一个 x 表示“请原地更改 x 而不是分配新矩阵”。 (source) @DanielS。如果就地操作是一个选项,那么正如Tobias's response 中指出的那样,有更快的就地操作。【参考方案2】:

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

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

【讨论】:

np.maximum(x,0,x) 与 np.maximum(0,x) 相比如何花费更少的时间? 另外值得注意的是这会修改x @pikachuchameleon 它更快,因为它是就地的。 np.maximum(x, 0, x)的返回值被忽略,结果直接写入x 如果就地操作是一种选择,那么正如Tobias's response 中所指出的,有更快的就地操作。【参考方案3】:

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

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

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

【讨论】:

谢谢。我发现这比花哨的索引方法要快。 @Shital Shah 你能解释一下这个语法或分享一些链接吗? 它只是广播和元素乘法。 0 将自动变成与张量 x 相同的大小。 bool 结果将变为 0 或 1,然后逐元素相乘。没有魔法:)。【参考方案4】:

编辑 正如 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)

【讨论】:

(+1) 你花哨的方法是我之前真正见过的唯一方法!在我看来,它不仅高效,而且完美地描述了 ReLU 操作。 这个方法只有在数组没有负数的情况下才比其他方法快;您的测试似乎很快,因为 timeit 修改了数组,所以在第一个循环之后,没有剩下的负数,它运行得更快。在每次重新生成数组的测试中,逻辑索引分配 (a[a &lt; 0] = 0) 执行的方法最差,np.maximum 表现最好。 @jirassimok 你是对的。我的函数将修改数据。并且在一次运行之后它会快很多。我会改变我的帖子【参考方案5】:

Richard Möhn's comparison 不公平。 作为Andrea Di Biagio's comment,就地方法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 首先,我说relu_max_inplacerelu_max 稍快,但最推荐的方法是relu_mul 我故意添加了初始化函数np.random.random(),因为如果我不这样做,relu_max_inplace 方法似乎会非常快,就像@Richard Möhn 的结果一样。 @Richard Möhn 的结果显示 relu_max_inplacerelu_max 的每个循环分别为 38.4 毫秒与 238 毫秒。只是因为 in_place 方法只会被执行一次。并且在每个循环中初始化矩阵将避免这种情况。比较将是公平的。 @ivanpp 我不确定在时序结果中包含随机生成操作是否公平。【参考方案6】:

如果我们有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有一个矩阵。

【讨论】:

【参考方案7】:

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]]

【讨论】:

【参考方案8】:

ReLU(x) 也等于 (x+abs(x))/2

【讨论】:

【参考方案9】:

这是更精确的实现:

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

【讨论】:

为什么? abs 是不必要的,因为您消除了所有负面组件。

以上是关于如何在 Numpy 中实现 ReLU 函数的主要内容,如果未能解决你的问题,请参考以下文章

如何从零开始在 Keras 中实现 Leaky ReLU? [关闭]

如何在 numpy 中实现定点二进制支持

在 numpy 中实现零均值和单位方差

用条件在keras中实现自定义丢失函数

如何在tensorflow中实现sklearn的PolynomialFeatures?

如何在 Python 3 和 PyQt5 中实现多核处理?