【问题标题】:how can I do a back propagation for a simple neural net with sigmoid activation?如何对具有 sigmoid 激活的简单神经网络进行反向传播?
【发布时间】:2017-12-25 00:46:24
【问题描述】:

我是深度学习的初学者。我目前正在为反向传播算法而苦苦挣扎。我在网上找到了一段带有 sigmoid 激活函数的简单神经网络的反向传播的代码。

#Step 1 Collect Data
x = np.array([[0,0,1], [0,1,1], [1,0,1], [1,1,1]])
y = np.array([[0], [1], [1], [0]])

#Step 2 build model
num_epochs = 60000

#initialize weights
syn0 = 2np.random.random((3,4)) - 1 
syn1 = 2np.random.random((4,1)) - 1

def nonlin(x,deriv=False):
    if(deriv==True): return x*(1-x)
        return 1/(1+np.exp(-x))
    for j in xrange(num_epochs): #feed forward through layers 0,1, and 2
        k0 = x
        k1 = nonlin(np.dot(k0, syn0))
        k2 = nonlin(np.dot(k1, syn1))

        #how much did we miss the target value?
        k2_error = y - k2

        if (j% 10000) == 0:
            print "Error:" + str(np.mean(np.abs(k2_error)))

        #in what direction is the target value?
        k2_delta = k2_error*nonlin(k2, deriv=True)

        #how much did each k1 value contribute to k2 error
        k1_error = k2_delta.dot(syn1.T)
        k1_delta= k1_error * nonlin(k1,deriv=True)
        syn1 += k1.T.dot(k2_delta)
        syn0 += k0.T.dot(k1_delta)

我没有得到这行代码:k2_delta = k2_error*nonlin(k2, deriv=True)。在计算局部梯度时,为什么它使用k2_error 乘以 k2 的导数。我们应该使用不同的东西来代替k2_error,因为该算法中的成本函数是绝对值,那么我应该使用[-1,1,1,-1] 的向量作为成本函数的局部梯度吗?我假设这里它使用分析梯度。

【问题讨论】:

  • 你展示的表达方式,它们是使用任何特定语言的吗?使用任何特定的框架?然后请edit your question 包含语言和可能的框架作为标签。

标签: python numpy machine-learning neural-network backpropagation


【解决方案1】:

你可以使用k2_error,因为它是写的。我测试了您的代码(在进行格式更改后)并确认它可以最大限度地减少绝对误差,这与 k2_error (算法中梯度下降的表面但不是实际目标)不同。 k2_delta = k2_error*nonlin(k2, deriv=True) 因为算法是最小化绝对误差而不是k2_error。以下是它的工作原理:

k2_errork2的输入之间的关系

k2_errork2 的导数是-1。使用链式法则,k2_error 相对于k2 的输入的导数是(-1)*(nonlin(k2, deriv=True))

因此:

  • k2_error 相对于k2 输入的导数始终为负。这是因为(nonlin(k2, deriv=True)) 总是积极的。
  • 因此,k2_error 的常规梯度下降最小化将始终希望将 k2 的输入向上推(使其更正)以使 k2_error 更负。

最小化绝对误差

k2_error = y-k2 有两种实际可能性,每种可能性都意味着最小化绝对误差(我们的真正目标)的不同策略。 (我们可以忽略不太可能的第三种可能性。)

  • 案例1:yk2,表示k2_error

    • 为了使yk2 更接近(最小化绝对误差),我们需要使误差更大/更正。从第一部分我们知道,我们可以通过将k2 的输入向下推(k2_error 的输入随着k2 的输入减少而增加)来做到这一点。
  • 案例2:y>k2,表示k2_error>0

    • 为了使yk2 更接近(最小化绝对误差),我们需要使误差更小/更负。从第一部分我们知道,我们可以通过向上推k2 的输入来做到这一点(k2_error 会随着k2 的输入增加而减少)。

总而言之,如果k2_error 为负数(案例1),我们通过向下推k2 的输入来最小化绝对误差。如果k2_error 为正(情况2),我们通过向上推k2 的输入来最小化绝对误差。

k2_delta的解释

我们现在知道k2_error 的梯度下降最小化总是希望将k2 的输入向上推,但这只会在y > k2 时最小化绝对误差(上面的案例2)。在案例 1 中,将k2 的输入向上推会增加绝对误差——因此我们通过翻转其符号来修改k2 输入处的梯度,称为k2_delta每当我们面对案例 1 时。案例 1 意味着 k2_errork2_delta 乘以 k2_error 来翻转梯度的符号!使用这种翻转意味着当我们看到案例 1 时,梯度下降希望将 k2 的输入向下而不是向上推(我们强制梯度下降放弃其默认行为)。

总而言之,使用k2_delta = k2_error*nonlin(k2, deriv=True) 只会在我们面对案例 1 时翻转通常梯度的符号,从而确保我们始终最小化绝对误差(而不是最小化 k2_error)。

重要提示

您的算法通过添加负梯度来修改权重。通常,梯度下降通过减去梯度来修改权重。添加负梯度是一回事,但它确实使我的答案有些复杂。比如k2输入的梯度实际上是k2_delta = k2_error*(-1)*nonlin(k2, deriv=True),而不是k2_delta = k2_error*nonlin(k2, deriv=True)

您可能想知道为什么我们使用k2_error 而不是sign(k2_error),这是因为随着k2_error 变小,我们希望将权重移动更小的量。

【讨论】:

  • 非常感谢您的详细解答。你是救命稻草!
【解决方案2】:

我认为这段代码取自这里:http://iamtrask.github.io/2015/07/12/basic-python-network/

每一步都有很好的解释。

这里的成本函数是实际值和期望值之间的差异:

k2_error = y - k2

因此我们将它乘以激活函数的导数。

【讨论】:

  • 我认为这里真正的损失函数是k2_error的绝对值,它的导数应该是[-1,1,1,-1]。
猜你喜欢
  • 2016-09-19
  • 2018-05-16
  • 2017-08-13
  • 2012-09-06
  • 2016-08-17
  • 2015-03-03
  • 2012-02-21
  • 2011-01-05
  • 1970-01-01
相关资源
最近更新 更多