【问题标题】:Neural network backpropagation with RELU使用 RELU 的神经网络反向传播
【发布时间】:2015-12-09 08:18:30
【问题描述】:

我正在尝试使用 RELU 实现神经网络。

输入层 -> 1 个隐藏层 -> relu -> 输出层 -> softmax 层

以上是我的神经网络的架构。 我对这个 relu 的反向传播感到困惑。 对于 RELU 的导数,如果 x 0,则输出为 1。 所以当你计算梯度时,这是否意味着如果 x

有人可以“逐步”解释我的神经网络架构的反向传播吗?

【问题讨论】:

    标签: neural-network backpropagation


    【解决方案1】:

    如果 x 0,则输出为 1

    ReLU 函数定义为:对于 x > 0,输出为 x,即 f(x) = max(0,x)

    所以对于导数 f '(x) 它实际上是:

    如果 x 0,则输出为 1。

    导数 f'(0) 没有定义。所以它通常设置为 0 或者你将激活函数修改为 f(x) = max(e,x) 以获得较小的 e。

    通常:ReLU 是使用整流器激活函数的单元。这意味着它的工作原理与任何其他隐藏层完全相同,但除了 tanh(x)、sigmoid(x) 或您使用的任何激活之外,您将改为使用 f(x) = max(0,x)。

    如果您已经为具有 sigmoid 激活功能的多层网络编写了代码,那么这实际上是 1 行更改。前向或反向传播在算法上没有任何变化。如果您还没有使用更简单的模型,请先返回并从该模型开始。否则,您的问题实际上不是关于 ReLU,而是关于整体实现 NN。

    【讨论】:

    • 你确定这是正确的吗?我有工作的反向传播代码,我更改了激活函数(在正向传播代码中)和梯度(在反向传播代码中),然后程序无法收敛以进行简单的异或测试。
    • @Yan King Yin 是的。这可能是由于自重而发生的。您也不需要更改渐变的任何内容。
    • 谢谢,在修复了一些错误之后,我认为你是对的。但是对于一些随机的初始权重,整个网络可能会死掉。
    • 除非您的渐变消失,否则不是整个网络。但它的一部分,是的。在这种情况下,如果没有 0 权重,泄漏的 relu 可能会有所帮助。
    • 在我看来这里有些混乱? OP 声明 0 / 1 输出“对于 RELU 的导数”(这是正确的),而答案假定 RELU 本身的输出。
    【解决方案2】:

    如果您有一个由单个 ReLU 组成的层,就像您的架构所建议的那样,那么是的,您可以在 0 处消除梯度。在训练期间,ReLU 将返回0 到您的输出层,如果您使用逻辑单元,它将返回00.5,并且softmax 将压缩它们。因此,在您当前的架构下,0 的值对于前向传播部分也没有多大意义。

    参见例如this。可以做的是使用“leaky ReLU”,在0处是一个很小的值,比如0.01

    我会重新考虑这种架构,但是,将单个 ReLU 馈送到一堆其他单元然后应用 softmax 对我来说没有多大意义。

    【讨论】:

      【解决方案3】:

      这是一个很好的例子,使用 ReLU 来实现 XOR: 参考,http://pytorch.org/tutorials/beginner/pytorch_with_examples.html

      # -*- coding: utf-8 -*-
      import numpy as np
      import matplotlib.pyplot as plt
      
      # N is batch size(sample size); D_in is input dimension;
      # H is hidden dimension; D_out is output dimension.
      N, D_in, H, D_out = 4, 2, 30, 1
      
      # Create random input and output data
      x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
      y = np.array([[0], [1], [1], [0]])
      
      # Randomly initialize weights
      w1 = np.random.randn(D_in, H)
      w2 = np.random.randn(H, D_out)
      
      learning_rate = 0.002
      loss_col = []
      for t in range(200):
          # Forward pass: compute predicted y
          h = x.dot(w1)
          h_relu = np.maximum(h, 0)  # using ReLU as activate function
          y_pred = h_relu.dot(w2)
      
          # Compute and print loss
          loss = np.square(y_pred - y).sum() # loss function
          loss_col.append(loss)
          print(t, loss, y_pred)
      
          # Backprop to compute gradients of w1 and w2 with respect to loss
          grad_y_pred = 2.0 * (y_pred - y) # the last layer's error
          grad_w2 = h_relu.T.dot(grad_y_pred)
          grad_h_relu = grad_y_pred.dot(w2.T) # the second laye's error 
          grad_h = grad_h_relu.copy()
          grad_h[h < 0] = 0  # the derivate of ReLU
          grad_w1 = x.T.dot(grad_h)
      
          # Update weights
          w1 -= learning_rate * grad_w1
          w2 -= learning_rate * grad_w2
      
      plt.plot(loss_col)
      plt.show()
      

      更多关于ReLU的导数,可以看这里:http://kawahara.ca/what-is-the-derivative-of-relu/

      【讨论】:

      • 关于这一行:grad_h[h &lt; 0] = 0 # the derivate of ReLU。我明白。但是,我们是否还应该添加另一行:grad_h[h &gt; 1] = 1,因为 x>0 的导数是 1?
      【解决方案4】:

      是的,原来的 Relu 函数有你描述的问题。 所以他们后来对公式进行了更改,并称其为leaky Relu 本质上,Leaky Relu 将函数的水平部分稍微倾斜了非常小的量。欲了解更多信息,请观看:

      An explantion of activation methods, and a improved Relu on youtube

      【讨论】:

        【解决方案5】:

        所以当你计算梯度时,这是否意味着我要杀死梯度 如果 x

        是的! ‌ 如果神经元(激活函数输入)的输入和偏置的加权和小于零,并且神经元使用 Relu 激活函数,则在反向传播期间导数的值为零,并且该神经元的输入权重不会改变(未更新)。

        有人可以“逐步”解释我的神经网络架构的反向传播吗?

        一个简单的例子可以展示反向传播的一个步骤。这个例子涵盖了一个步骤的完整过程。 但你也可以只检查与Relu相关的部分。这类似于所讨论的架构,为简单起见,在每一层中使用一个神经元。架构如下:

        f和g分别代表Relu和sigmoid,b代表偏差。 第1步: 首先计算输出:

        这仅表示输出计算。 “z”和“a”分别表示神经元的输入和神经元激活函数的输出值之和。 所以 h 是估计值。假设实际值为 y。

        现在使用反向传播更新权重。

        新的权重是通过计算误差函数相对于权重的梯度,并从之前的权重中减去这个梯度得到的,即:

        在反向传播中,首先计算最后一层的最后一个神经元的梯度。使用链导数规则计算:

        上面使用的三个通用术语是:

        • 实际值与估计值之差

        • 神经元输出方块

        • 还有激活函数的导数,假设最后一层的激活函数是sigmoid,我们有这样的:

        而且上面的说法也不一定会变成零。

        现在我们进入第二层。在第二层,我们将拥有:

        它由 4 个主要术语组成:

        • 实际值与估计值之差。

        • 神经元输出方块

        • 下一层相连神经元的损失导数之和

        • 激活函数的导数,由于激活函数是 Relu,我们将有:

        如果z2

        否则不一定为零:

        所以如果神经元的输入小于零,损失导数始终为零,权重不会更新。

        *重复神经元输入的总和必须小于零才能杀死梯度下降。

        给出的例子是一个非常简单的例子来说明反向传播过程。

        【讨论】:

          【解决方案6】:

          另外,在这里你可以找到一个在 caffe 框架中的实现:https://github.com/BVLC/caffe/blob/master/src/caffe/layers/relu_layer.cpp

          negative_slope 指定是否通过将负值乘以斜率值而不是将其设置为 0 来“泄漏”负值部分。当然,您应该将此参数设置为零以获得经典版本。

          【讨论】: