如何对Sigmoid激活的简单神经网络进行反向传播?

时间:2017-07-19 16:07:43

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

我是深度学习的初学者。我目前正在努力使用反向传播算法。我在网上找到了这个带有S形激活函数的简单神经网络反向传播的代码。

#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]的向量作为成本函数的局部梯度吗?我假设它使用分析梯度。

2 个答案:

答案 0 :(得分:0)

我认为此代码来自此处:http://iamtrask.github.io/2015/07/12/basic-python-network/

每一步都有很好的解释。

此处的成本函数是实际值和预期值之间的差异:

k2_error = y - k2

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

答案 1 :(得分:0)

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

k2_errork2的输入之间的关系

k2_error相对于k2的导数为-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:y< k2,表示k2_error< 0

    • 为了使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_error< 0,这意味着我们可以通过将k2_delta乘以k2_error来翻转渐变的符号!使用此翻转意味着当我们看到案例1时,渐变下降想要将k2的输入向下推而不是向上(我们强制梯度下降以放弃其默认行为)。

总而言之,只有当我们面对案例1时,使用k2_delta = k2_error*nonlin(k2, deriv=True)翻转通常渐变的符号,这确保我们始终最小化绝对错误(而不是最小化k2_error)。 / p>

重要提示

您的算法通过添加负梯度来修改权重。通常,梯度下降通过减去梯度来修改权重。添加负梯度是一回事,但它确实使我的答案复杂化了。例如,输入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变小时将权重移动较小的数量。