【发布时间】:2015-12-09 08:18:30
【问题描述】:
我正在尝试使用 RELU 实现神经网络。
输入层 -> 1 个隐藏层 -> relu -> 输出层 -> softmax 层
以上是我的神经网络的架构。 我对这个 relu 的反向传播感到困惑。 对于 RELU 的导数,如果 x 0,则输出为 1。 所以当你计算梯度时,这是否意味着如果 x
有人可以“逐步”解释我的神经网络架构的反向传播吗?
【问题讨论】:
标签: neural-network backpropagation
我正在尝试使用 RELU 实现神经网络。
输入层 -> 1 个隐藏层 -> relu -> 输出层 -> softmax 层
以上是我的神经网络的架构。 我对这个 relu 的反向传播感到困惑。 对于 RELU 的导数,如果 x 0,则输出为 1。 所以当你计算梯度时,这是否意味着如果 x
有人可以“逐步”解释我的神经网络架构的反向传播吗?
【问题讨论】:
标签: neural-network backpropagation
如果 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。
【讨论】:
如果您有一个由单个 ReLU 组成的层,就像您的架构所建议的那样,那么是的,您可以在 0 处消除梯度。在训练期间,ReLU 将返回0 到您的输出层,如果您使用逻辑单元,它将返回0 或0.5,并且softmax 将压缩它们。因此,在您当前的架构下,0 的值对于前向传播部分也没有多大意义。
参见例如this。可以做的是使用“leaky ReLU”,在0处是一个很小的值,比如0.01。
我会重新考虑这种架构,但是,将单个 ReLU 馈送到一堆其他单元然后应用 softmax 对我来说没有多大意义。
【讨论】:
这是一个很好的例子,使用 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 < 0] = 0 # the derivate of ReLU。我明白。但是,我们是否还应该添加另一行:grad_h[h > 1] = 1,因为 x>0 的导数是 1?
是的,原来的 Relu 函数有你描述的问题。 所以他们后来对公式进行了更改,并称其为leaky Relu 本质上,Leaky Relu 将函数的水平部分稍微倾斜了非常小的量。欲了解更多信息,请观看:
An explantion of activation methods, and a improved Relu on youtube
【讨论】:
所以当你计算梯度时,这是否意味着我要杀死梯度 如果 x
是的! 如果神经元(激活函数输入)的输入和偏置的加权和小于零,并且神经元使用 Relu 激活函数,则在反向传播期间导数的值为零,并且该神经元的输入权重不会改变(未更新)。
有人可以“逐步”解释我的神经网络架构的反向传播吗?
一个简单的例子可以展示反向传播的一个步骤。这个例子涵盖了一个步骤的完整过程。 但你也可以只检查与Relu相关的部分。这类似于所讨论的架构,为简单起见,在每一层中使用一个神经元。架构如下:
f和g分别代表Relu和sigmoid,b代表偏差。 第1步: 首先计算输出:
这仅表示输出计算。 “z”和“a”分别表示神经元的输入和神经元激活函数的输出值之和。 所以 h 是估计值。假设实际值为 y。
现在使用反向传播更新权重。
新的权重是通过计算误差函数相对于权重的梯度,并从之前的权重中减去这个梯度得到的,即:
在反向传播中,首先计算最后一层的最后一个神经元的梯度。使用链导数规则计算:
上面使用的三个通用术语是:
实际值与估计值之差
神经元输出方块
还有激活函数的导数,假设最后一层的激活函数是sigmoid,我们有这样的:
而且上面的说法也不一定会变成零。
现在我们进入第二层。在第二层,我们将拥有:
它由 4 个主要术语组成:
实际值与估计值之差。
神经元输出方块
下一层相连神经元的损失导数之和
激活函数的导数,由于激活函数是 Relu,我们将有:
如果z2
否则不一定为零:
所以如果神经元的输入小于零,损失导数始终为零,权重不会更新。
*重复神经元输入的总和必须小于零才能杀死梯度下降。
给出的例子是一个非常简单的例子来说明反向传播过程。
【讨论】:
另外,在这里你可以找到一个在 caffe 框架中的实现:https://github.com/BVLC/caffe/blob/master/src/caffe/layers/relu_layer.cpp
negative_slope 指定是否通过将负值乘以斜率值而不是将其设置为 0 来“泄漏”负值部分。当然,您应该将此参数设置为零以获得经典版本。
【讨论】: