我怎样才能对一个带有 sigmoid 激活的简单神经网络进行反向传播?
how can I do a back propagation for a simple neural net with sigmoid activation?
我是深度学习的初学者。我目前正在为反向传播算法而苦苦挣扎。我在网上找到了一段带有 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]
的向量作为成本函数的局部梯度吗?我在这里假设它使用分析梯度。
我认为这段代码取自这里:http://iamtrask.github.io/2015/07/12/basic-python-network/
每一步都有很好的解释。
此处的成本函数是实际值与预期值之间的差异:
k2_error = y - k2
因此我们将其乘以激活函数的导数。
您可以照原样使用 k2_error
。我测试了您的代码(在进行格式更改后)并确认它最小化了绝对误差,这与 k2_error
(算法中梯度下降的表面但不是实际目标)不同。 k2_delta = k2_error*nonlin(k2, deriv=True)
因为算法正在最小化绝对误差而不是 k2_error
。工作原理如下:
k2_error
与输入到k2
的关系
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
有两种实际可能性,每种可能性都意味着一种不同的策略来最小化绝对误差(我们的真正目标)。 (我们可以忽略不太可能的第三种可能性。)
情况一:y
<k2
,即k2_error
<0
- 为了让
y
和k2
靠得更近(最小化绝对误差),我们需要让误差larger/more-positive。我们从第一部分知道,我们可以通过将 k2
的输入向下推(当 k2
的输入减少时 k2_error
增加)来做到这一点。
情况2:y
>k2
,即k2_error
>0
- 为了使
y
和k2
靠得更近(最小化绝对误差),我们需要使误差smaller/more-negative。我们从第一部分知道,我们可以通过将 k2
的输入推高来做到这一点(当 k2
的输入增加时,k2_error
减少)。
总而言之,如果 k2_error
为负(情况 1),我们通过将 k2
的输入下推来最小化绝对误差。如果 k2_error
为正(情况 2),我们通过将 k2
的输入上推来最小化绝对误差。
k2_delta
的解释
我们现在知道 k2_error
的梯度下降最小化总是希望将 k2
的输入向上推,但这只会在 y
> [= 时最小化绝对误差15=](上面的案例 2)。在情况1中,将k2
的输入上推会增加绝对误差——所以我们修改k2
输入处的梯度,称为k2_delta
,每当我们面对案例 1 时,通过翻转它的符号。案例 1 意味着 k2_error
<0,这意味着我们可以通过将 k2_delta
乘以 [=10 来翻转梯度的符号=]!使用这种翻转意味着当我们看到情况 1 时,梯度下降想要将 k2
的输入向下推而不是向上(我们强制梯度下降放弃其默认行为)。
总而言之,仅当我们面对情况 1 时,使用 k2_delta = k2_error*nonlin(k2, deriv=True)
才翻转通常梯度的符号,这确保我们始终最小化绝对误差(与最小化 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
变小,我们想移动更小的权重。
我是深度学习的初学者。我目前正在为反向传播算法而苦苦挣扎。我在网上找到了一段带有 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]
的向量作为成本函数的局部梯度吗?我在这里假设它使用分析梯度。
我认为这段代码取自这里:http://iamtrask.github.io/2015/07/12/basic-python-network/
每一步都有很好的解释。
此处的成本函数是实际值与预期值之间的差异:
k2_error = y - k2
因此我们将其乘以激活函数的导数。
您可以照原样使用 k2_error
。我测试了您的代码(在进行格式更改后)并确认它最小化了绝对误差,这与 k2_error
(算法中梯度下降的表面但不是实际目标)不同。 k2_delta = k2_error*nonlin(k2, deriv=True)
因为算法正在最小化绝对误差而不是 k2_error
。工作原理如下:
k2_error
与输入到k2
的关系
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
有两种实际可能性,每种可能性都意味着一种不同的策略来最小化绝对误差(我们的真正目标)。 (我们可以忽略不太可能的第三种可能性。)
情况一:
y
<k2
,即k2_error
<0- 为了让
y
和k2
靠得更近(最小化绝对误差),我们需要让误差larger/more-positive。我们从第一部分知道,我们可以通过将k2
的输入向下推(当k2
的输入减少时k2_error
增加)来做到这一点。
- 为了让
情况2:
y
>k2
,即k2_error
>0- 为了使
y
和k2
靠得更近(最小化绝对误差),我们需要使误差smaller/more-negative。我们从第一部分知道,我们可以通过将k2
的输入推高来做到这一点(当k2
的输入增加时,k2_error
减少)。
- 为了使
总而言之,如果 k2_error
为负(情况 1),我们通过将 k2
的输入下推来最小化绝对误差。如果 k2_error
为正(情况 2),我们通过将 k2
的输入上推来最小化绝对误差。
k2_delta
我们现在知道 k2_error
的梯度下降最小化总是希望将 k2
的输入向上推,但这只会在 y
> [= 时最小化绝对误差15=](上面的案例 2)。在情况1中,将k2
的输入上推会增加绝对误差——所以我们修改k2
输入处的梯度,称为k2_delta
,每当我们面对案例 1 时,通过翻转它的符号。案例 1 意味着 k2_error
<0,这意味着我们可以通过将 k2_delta
乘以 [=10 来翻转梯度的符号=]!使用这种翻转意味着当我们看到情况 1 时,梯度下降想要将 k2
的输入向下推而不是向上(我们强制梯度下降放弃其默认行为)。
总而言之,仅当我们面对情况 1 时,使用 k2_delta = k2_error*nonlin(k2, deriv=True)
才翻转通常梯度的符号,这确保我们始终最小化绝对误差(与最小化 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
变小,我们想移动更小的权重。