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

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

【问题讨论】:

    标签: python numpy machine-learning neural-network


    【解决方案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 是不必要的,因为您消除了所有负面组件。
          猜你喜欢
          • 2018-11-04
          • 2018-03-06
          • 1970-01-01
          • 2018-06-14
          • 2019-07-24
          • 1970-01-01
          • 2018-10-11
          • 2021-01-20
          • 2021-04-28
          相关资源
          最近更新 更多