Python神经网络反向传播

5
我正在学习神经网络,具体来说是在研究带有反向传播实现的MLP。我试图在Python中实现自己的网络,所以在开始之前,我想先看看其他一些库。在搜索了一番之后,我找到了Neil Schemenauer的Python实现bpnn.py。(http://arctrix.com/nas/python/bpnn.py
通过阅读Christopher M. Bishop的书籍《模式识别的神经网络》,并且仔细研究了代码之后,我发现了backPropagate函数中的一个问题:
# calculate error terms for output
output_deltas = [0.0] * self.no
for k in range(self.no):
    error = targets[k]-self.ao[k]
    output_deltas[k] = dsigmoid(self.ao[k]) * error

在Bishop的书中,计算误差的代码行与其他不同。在第145页,第4.41式中,他将输出单元的误差定义为:

d_k = y_k - t_k

其中y_k是输出值,t_k是目标值。(我使用下标符号_表示下标)所以我的问题是这行代码是否正确:

error = targets[k]-self.ao[k]

确切地说:

error = self.ao[k] - targets[k]

我很可能完全错了,但是有人能帮助澄清我的困惑吗?谢谢


由于误差是当前结果与期望结果之间的差距,因此这应该是正确的代码行: error = targets[k]-self.ao[k] 但两种情况都有可能正确,这取决于 dsigmoid(self.ao[k]) 的信号。 - Arthur Julião
无论你对(y-t)^2或(t-y)^2关于y求导,结果都是一样的,总是y-t,而不是t-y。 - alfa
4个回答

2

这完全取决于您使用的错误度量。以下是一些错误度量的示例(为简洁起见,我将使用 ys 表示长度为 n 的输出向量,ts 表示长度为 n 的目标向量):

mean squared error (MSE):
    sum((y - t) ** 2 for (y, t) in zip(ys, ts)) / n

mean absolute error (MAE):
    sum(abs(y - t) for (y, t) in zip(ys, ts)) / n

mean logistic error (MLE):
    sum(-log(y) * t - log(1 - y) * (1 - t) for (y, t) in zip(ys, ts)) / n 

你使用哪个取决于上下文。当目标输出可以取任何值时,MSE和MAE可用,而当目标输出为01y在开放范围(0, 1)内时,MLE会给出非常好的结果。

话虽如此,我之前没有见过使用错误y-tt-y(我自己在机器学习方面经验不太丰富)。据我所知,你提供的源代码既不平方差异也不使用绝对值,你确定书中也没有这样做吗?我认为y-tt-y不能是很好的误差度量方法,原因如下:

n = 2                 # We only have two output neurons
ts = [ 0, 1 ]         # Our target outputs
ys = [ 0.999, 0.001 ] # Our sigmoid outputs

# Notice that your outputs are the exact opposite of what you want them to be.
# Yet, if you use (y - t) or (t - y) to measure your error for each neuron and
# then sum up to get the total error of the network, you get 0.
t_minus_y = (0 - 0.999) + (1 - 0.001)
y_minus_t = (0.999 - 0) + (0.001 - 1)

编辑:根据alfa的评论,在这本书中,y - t实际上是均方误差(MSE)的导数。在这种情况下,t - y是不正确的。然而,请注意,均方误差的实际导数是2 * (y - t) / n,而不仅仅是y - t

如果你没有除以n(所以你实际上有一个总和平方误差(SSE),而不是均方误差),那么它的导数将是2 * (y - t)。此外,如果你使用SSE / 2作为你的误差度量,那么导数中的1 / 22将抵消,最终留下y - t


1
问题是:你需要y-t用于反向传播(参见例如https://github.com/OpenANN/OpenANN/blob/master/src/Net.cpp,第323行)。它不是错误度量,而是均方误差(mean sum of squared errors)的导数。而且它应该区分是y-t还是t-y。 - alfa
哦,我明白了,我搞混了。 - Paul Manta
@alfa,你是在说bpnn.py中当前的实现有误吗? - FidesFacitFidem
那似乎是这样的...我会仔细看一下。 - alfa
1
@FidesFacitFidem 好的,实际上是错误的,但这并不重要,因为它执行的是梯度上升而不是梯度下降: self.wi[i][j] = self.wi[i][j] + Nchange + Mself.ci[i][j] 你可以将+替换为-,并使用error = self.ao[k] - targets[k],结果将相同。 - alfa
1
此外,动量的实现实际上是错误的,因为它只包含了先前的梯度。通常应该使用所有先前的梯度,并带有指数衰减的权重。 :) - alfa

0
你必须反向传播关于y的导数,其为0.5*(y-t)^2或0.5*(t-y)^2,并且始终为y-t=(y-t)(+1)=(t-y)(-1)。

0
在实际的代码中,我们经常计算损失函数关于权重w的负梯度,并使用w += eta*grad来更新权重。实际上这是一种梯度上升法。
在一些教材中,会计算正梯度并使用w -= eta*grad来更新权重。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接