PyBrain训练神经网络无法收敛

32

我有以下代码,来自PyBrain教程:

from pybrain.datasets import SupervisedDataSet
from pybrain.supervised.trainers import BackpropTrainer
from pybrain.tools.shortcuts import buildNetwork
from pybrain.structure.modules import TanhLayer

ds = SupervisedDataSet(2, 1)
ds.addSample((0,0), (0,))
ds.addSample((0,1), (1,))
ds.addSample((1,0), (1,))
ds.addSample((1,1), (0,))

net     = buildNetwork(2, 3, 1, bias=True, hiddenclass=TanhLayer)
trainer = BackpropTrainer(net, ds)

for inp, tar in ds:
     print [net.activate(inp), tar]

errors  = trainer.trainUntilConvergence()

for inp, tar in ds:
     print [net.activate(inp), tar]
然而,结果是一个未经充分训练的神经网络。在查看错误输出时,网络被正确地训练,但它使用“continueEpochs”参数进行更多的训练后,网络表现变得更差了。因此,网络正在收敛,但没有办法得到最佳训练的网络。PyBrain的文档暗示返回的是训练最好的网络,但实际上它返回一组错误。将continueEpochs设置为0时出现错误(ValueError: max() arg is an empty sequence),因此continueEpochs必须大于0。PyBrain是否真的得到维护?因为文档和代码之间存在很大的差异。

哎呀,Github源代码展示了更多的例子,这些例子与文档中完全不同的解决方式。 - avanwieringen
4个回答

35

经过进一步研究,我发现PyBrain教程中的示例完全不适用。

当我们查看源代码中的方法签名时,可以发现:

def trainUntilConvergence(self, dataset=None, maxEpochs=None, verbose=None, continueEpochs=10, validationProportion=0.25):
这意味着训练集的25%用于验证。虽然在训练网络时这是一种非常有效的方法,但当您拥有完整的可能性范围时,即4行XOR 2进1输出解决方案集时,您不会这样做。当您想要训练一个XOR集并将其中一行用于验证时,这会导致训练集非常稀疏,其中一个可能的组合被省略,从而自动导致那些权重没有得到训练。
通常,当您省略25%的数据进行验证时,您会假定这些25%覆盖了网络已经遇到的“大多数”解空间。但在这种情况下,这是不正确的,并且它完全涵盖了网络完全未知的25%解空间,因为您将其用于验证而删除了它。
所以,培训师进行了正确的网络培训,但通过省略XOR问题的25%,这导致网络受到了糟糕的培训。
在PyBrain网站上提供另一个示例作为快速入门将非常方便,因为对于这种特定的XOR情况,该示例就是错误的。您可能会想知道他们是否亲自尝试过这个示例,因为它只会输出随机且训练不良的网络。

谢谢!那个例子让我很困惑。文档说XOR是一个经典的神经网络例子,但是示例代码给出了糟糕的答案。 - TG_Matt
4
这是一个经典的神经网络例子,因为它展示了使用线性函数(Sigmoid)的组合,可以训练网络学习二进制逻辑。然而,他们的教程很糟糕,因为他们似乎没有理解这个概念。 - avanwieringen
3
我建议您fork这个Github项目,将示例修改为您认为应该的样子,然后发起一个pull request。 - Firestrand

17

我上了Coursera的优秀课程《机器学习》,由Andrew Ng教授讲解,其中一部分内容涵盖了训练小型神经网络来识别异或运算。因此,我对基于快速入门的pybrain示例无法收敛感到有些困扰。

我认为其中有很多原因,包括上面提到的将最小数据集拆分为训练和验证集的原因。在课程中,Andrew曾说过:“胜出并不是拥有最好算法的人,而是拥有最多数据的人。”他接着解释说,2000年代数据可用性的爆炸是人工智能复兴的原因之一,现在称为机器学习。

因此,考虑到所有这些因素,我发现:

  1. 验证集可以有4个样本,因为这是在训练阶段后进行的。
  2. 网络只需要2个节点在隐藏层,就像我在课程中学到的一样。
  3. 在这种情况下,学习率需要相当小,如0.005,否则训练有时会跳过答案(这是课程中的重要一点,我通过调整数字证实了这一点)。
  4. 学习率越小,maxEpochs可以越小。较小的学习率意味着沿梯度向最小化方向采取更小的步长。如果学习率更大,则需要更大的maxEpochs,以便在决定已达到最小值之前等待更长时间。
  5. 在网络中需要bias = True(它将常量1节点添加到输入和隐藏层中)。阅读这个问题的答案有关于偏差的解释。
  6. 最后,也是最重要的是需要一个大的训练集。1000次迭代正确率约为75%。我怀疑这与最小化算法有关。更小的数字通常会失败。

所以这里有一些有效的代码:

from pybrain.datasets import SupervisedDataSet

dataModel = [
    [(0,0), (0,)],
    [(0,1), (1,)],
    [(1,0), (1,)],
    [(1,1), (0,)],
]

ds = SupervisedDataSet(2, 1)
for input, target in dataModel:
    ds.addSample(input, target)

# create a large random data set
import random
random.seed()
trainingSet = SupervisedDataSet(2, 1);
for ri in range(0,1000):
    input,target = dataModel[random.getrandbits(2)];
    trainingSet.addSample(input, target)

from pybrain.tools.shortcuts import buildNetwork
net = buildNetwork(2, 2, 1, bias=True)

from pybrain.supervised.trainers import BackpropTrainer
trainer = BackpropTrainer(net, ds, learningrate = 0.001, momentum = 0.99)
trainer.trainUntilConvergence(verbose=True,
                              trainingData=trainingSet,
                              validationData=ds,
                              maxEpochs=10)

print '0,0->', net.activate([0,0])
print '0,1->', net.activate([0,1])
print '1,0->', net.activate([1,0])
print '1,1->', net.activate([1,1])

2
trainer = BackpropTrainer(net, ds, learningrate = 0.9, momentum=0.0, weightdecay=0.0, verbose=True) 
trainer.trainEpochs(epochs=1000)

这种方法可以收敛。如果学习率太小(例如0.01),它会陷入局部最小值。经过我的测试,学习率在0.3-30之间,它可以收敛。


这仍然没有解决一个事实,即该示例无效,因为它使用子集进行验证,并在训练过程中省略了该子集,错误地假设剩余集合覆盖了大部分解空间。 - avanwieringen

0

以下似乎始终给出正确的结果:

from pybrain.tools.shortcuts import buildNetwork
from pybrain.structure import TanhLayer
from pybrain.datasets import SupervisedDataSet
from pybrain.supervised.trainers import BackpropTrainer

#net = buildNetwork(2, 3, 1, bias=True, hiddenclass=TanhLayer)
net = buildNetwork(2, 3, 1, bias=True)

ds = SupervisedDataSet(2, 1)
ds.addSample((0, 0), (0,))
ds.addSample((0, 1), (1,))
ds.addSample((1, 0), (1,))
ds.addSample((1, 1), (0,))
ds.addSample((0, 0), (0,))
ds.addSample((0, 1), (1,))
ds.addSample((1, 0), (1,))
ds.addSample((1, 1), (0,))
ds.addSample((0, 0), (0,))
ds.addSample((0, 1), (1,))
ds.addSample((1, 0), (1,))
ds.addSample((1, 1), (0,))
ds.addSample((0, 0), (0,))
ds.addSample((0, 1), (1,))
ds.addSample((1, 0), (1,))
ds.addSample((1, 1), (0,))
ds.addSample((0, 0), (0,))
ds.addSample((0, 1), (1,))
ds.addSample((1, 0), (1,))
ds.addSample((1, 1), (0,))
ds.addSample((0, 0), (0,))
ds.addSample((0, 1), (1,))
ds.addSample((1, 0), (1,))
ds.addSample((1, 1), (0,))

trainer = BackpropTrainer(net, ds, learningrate=0.001, momentum=0.99)

trainer.trainUntilConvergence(verbose=True)

print net.activate([0,0])
print net.activate([0,1])
print net.activate([1,0])
print net.activate([1,1])

是的,但您提供了多次相同的样本,这意味着当训练算法分离训练和验证集时,训练集完整的可能性很高。然而,也有可能不完整...尽管您已经解决了这个问题,但网站上的示例明显是错误的,这一事实并没有改变。 - avanwieringen

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