弹性反向传播算法中存在Bug?

3
我正在努力正确实现Resilient Propagation。我已经实现了反向传播算法来训练神经网络,并且对于XOR-Net,它的表现符合预期,即需要大约600个Epochs才能将误差降至1%以下。现在我尝试为相同的问题实现Resilient Propagation(http://en.wikipedia.org/wiki/Rprop),并且在前几个Epochs中,误差迅速下降到23%,但之后上升到50%并一直保持在那里。
我按照http://www.heatonresearch.com/book/introduction-neural-network-math.html中的描述完全实现了它,但这是一个令人困惑的描述:它与维基百科Rprop页面和encog中的实现有所不同,而encog是由该书的作者编写的,据我所知。 我已经尝试了来自不同来源的不同实现,但没有任何效果。

各种来源之间的一些差异:
  • 使用signum(currentPartialDerivative)代替signum(currentPartialDerivative * previousPartialDerivative)来计算权重变化
  • 使用上一次的权重变化值,而不是新的更新值来计算新的权重变化
  • 首先计算权重变化,其次计算新的更新值

如何正确实现这个?以下是根据书中实现的方式:

public ResilientPropagation() {
    initialUpdateValue = 0.01;
    deltaMaximum = 50;
    deltaMinimum = 0.000001;
    negativeEta = 0.5;
    positiveEta = 1.2;
    double zeroTolerance = 0.0000000000000001;
    signum = new Signum(zeroTolerance);

    init();
}

@Override
public double calculateWeightChange(Synapse synapse, double partialDerivative) {
    if (!synapseValues.containsKey(synapse)){
        double initialPartialDerivative = 0;
        synapseValues.put(synapse, new SynapseValues(initialUpdateValue, initialPartialDerivative));
    }

    SynapseValues values = synapseValues.get(synapse);
    double signChange = signum.value(values.lastPartialDerivative * partialDerivative);
    values.lastPartialDerivative = partialDerivative;
    double weightChange = 0;
    if (signChange > 0){
        newUpdateValue = Math.min(positiveEta * values.updateValue, deltaMaximum);
        weightChange = -1*newUpdateValue;
    } else if (signChange < 0){
        newUpdateValue = Math.max(negativeEta * values.updateValue, deltaMinimum);
        weightChange = newUpdateValue;
    } else {
        newUpdateValue = values.updateValue;
        double weightChange = 0;
    }
    values.updateValue = newUpdateValue;
    return weightChange;
}

同样的方法对于正常的反向传播也可以正常工作:
@Override
public double calculateWeightChange(Synapse synapse, double partialDerivative) {
    double previousChange = previousWeightChange.get(synapse) != null ? previousWeightChange.get(synapse) : 0;
    double weightChange = learningRate * partialDerivative + momentum * previousChange;
    previousWeightChange.put(synapse, weightChange);
    return weightChange;
}
1个回答

1
有几个不同的RPROP算法变体。自出版以来,Encog已经进行了修改,以支持更多变体。该书重点介绍了由Reidmiller在一篇论文中定义的经典RPROP算法。随后的论文提出了其他算法。这解释了Encog优化的RPROP算法与书中描述之间存在一些差异。
查看您上面的代码,我有一些建议可能会有所帮助。我主要不确定您最终的else子句。您有“double weightChange = 0”,这将没有任何作用。我想您需要删除double。您还需要为“零”建立一些容差。梯度的变化很少会精确地命中零,因此我会建立一些围绕零的范围,例如-0.00001到+0.00001,以触发else子句。然后确保您实际上将weightChange设置为零。
我自己的rprop实现中记得的另一个问题是,用于反向传播的梯度的符号与用于反向传播的梯度的相反符号。您可以尝试翻转RPROP的梯度符号,这在我的Encog实现中是必要的。
这个RPROP的实现可能对你有用,它是经典的Reidmiller实现。它可以正常运行,并且误差会收敛。

https://github.com/encog/encog-java-core/blob/master/src/main/java/org/encog/neural/freeform/training/FreeformResilientPropagation.java

我不确定这会不会有帮助。没有运行代码,我只能看到这些。


1
感谢您的帮助。问题确实是我没有改变梯度符号。关于您的答案,还有一些其他注意事项:我确实有一个zeroTolerance,在上面的构造函数中可以看到(它的大小是从您的书中10^-16),如果signumValue = 0,则weightChange必须为零(根据您的书和维基百科,Encog中不同)。最后但并非最不重要的一点:赞扬您的书籍,它们真的很有帮助,特别是对于这个领域的新手,准确描述了您需要做什么,并展示了如何工作的具体示例。 - user3144065

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