Java 神经网络中的反向传播算法理解困难

Java 神经网络中的反向传播算法理解困难,java,algorithm,artificial-intelligence,neural-network,backpropagation,Java,Algorithm,Artificial Intelligence,Neural Network,Backpropagation,我很难理解反向传播算法。我读了很多书,搜索了很多,但我不明白为什么我的神经网络不工作。我想确认我每一部分都做对了 这是我的神经网络,当它初始化时,当第一行输入[1,1]和输出[0]被设置时(正如你所看到的,我正在尝试使用XOR神经网络): 我有3层:输入、隐藏和输出。第一层(输入)和隐藏层包含2个神经元,每个神经元中有2个突触。最后一层(输出)包含一个神经元和两个突触 一个突触包含一个权重和它之前的增量(在开始时,它是0)。连接到突触的输出可以通过与突触相关联的sourceNeuron找到,如

我很难理解反向传播算法。我读了很多书,搜索了很多,但我不明白为什么我的神经网络不工作。我想确认我每一部分都做对了

这是我的神经网络,当它初始化时,当第一行输入[1,1]和输出[0]被设置时(正如你所看到的,我正在尝试使用XOR神经网络):

我有3层:输入、隐藏和输出。第一层(输入)和隐藏层包含2个神经元,每个神经元中有2个突触。最后一层(输出)包含一个神经元和两个突触

一个突触包含一个权重和它之前的增量(在开始时,它是0)。连接到突触的输出可以通过与突触相关联的sourceNeuron找到,如果没有sourceNeuron,则可以在输入数组中找到(如在输入层中)

Layer.java包含神经元列表。在我的NeuralNetwork.java中,我初始化了神经网络,然后在训练集中循环。在每次迭代中,我替换输入和输出值,并调用反向传播算法上的train,该算法对当前集运行一定数量的时间(目前为1000次)

我使用的激活作用是乙状结肠

培训集和验证集为(输入1、输入2、输出):

下面是我的Neuron.java实现:

public class Neuron {

    private IActivation activation;
    private ArrayList<Synapse> synapses; // Inputs
    private double output; // Output
    private double errorToPropagate;

    public Neuron(IActivation activation) {
        this.activation = activation;
        this.synapses = new ArrayList<Synapse>();
        this.output = 0;
        this.errorToPropagate = 0;
    }

    public void updateOutput(double[] inputs) {
        double sumWeights = this.calculateSumWeights(inputs);

        this.output = this.activation.activate(sumWeights);
    }

    public double calculateSumWeights(double[] inputs) {
        double sumWeights = 0;

        int index = 0;
        for (Synapse synapse : this.getSynapses()) {
            if (inputs != null) {
                sumWeights += synapse.getWeight() * inputs[index];
            } else {
                sumWeights += synapse.getWeight() * synapse.getSourceNeuron().getOutput();
            }

            index++;
        }

        return sumWeights;
    }

    public double getDerivative() {
        return this.activation.derivative(this.output);
    }

    [...]
}
我的类中的train方法BackpropagationStrategy.java运行while循环,并在1000次(历元)后使用训练集的一行停止。看起来是这样的:

this.forwardPropagation(neuralNetwork, inputs);

this.backwardPropagation(neuralNetwork, expectedOutput);

this.updateWeights(neuralNetwork);
以下是上述方法的所有实现(learningRate=0.45,momentum=0.9):

然后检查输出层神经元的输出

我做错什么了吗?需要一些解释

以下是我在1000年后的结果:

Real: 0.0
Current: 0.025012156926937503
Real: 1.0
Current: 0.022566830709341495
Real: 1.0
Current: 0.02768416343491415
Real: 0.0
Current: 0.024903432706154027
为什么输入层中的突触没有更新?无论在哪里,它都只更新隐藏层和输出层

正如你所看到的,这是完全错误的!它不会仅对第一列车组输出(0.0)进行1.0

更新1

下面是一个使用此集合的网络迭代:[1.0,1.0,0.0]。以下是前向传播方法的结果:

=== Input Layer

== Neuron #1

= Synapse #1
Weight: -0.19283583155573614
Input: 1.0

= Synapse #2
Weight: 0.04023817185601586
Input: 1.0

Sum: -0.15259765969972028
Output: 0.461924442180935

== Neuron #2

= Synapse #1
Weight: -0.3281099260608612
Input: 1.0

= Synapse #2
Weight: -0.4388250065958519
Input: 1.0

Sum: -0.7669349326567131
Output: 0.31714251453174147

=== Hidden Layer

== Neuron #1

= Synapse #1
Weight: 0.16703288052854093
Input: 0.461924442180935

= Synapse #2
Weight: 0.31683996162148054
Input: 0.31714251453174147

Sum: 0.17763999229679783
Output: 0.5442935820534444

== Neuron #2

= Synapse #1
Weight: -0.45330313978424686
Input: 0.461924442180935

= Synapse #2
Weight: 0.3287014377113835
Input: 0.31714251453174147

Sum: -0.10514659949771789
Output: 0.47373754172497556

=== Output Layer

== Neuron #1

= Synapse #1
Weight: 0.08643751629154495
Input: 0.5442935820534444

= Synapse #2
Weight: -0.29715579267218695
Input: 0.47373754172497556

Sum: -0.09372646936373039
Output: 0.47658552081912403
更新2


我可能有偏见的问题。我将借助以下答案进行调查:。它不会在下一个数据集返回,因此…

我最终发现了问题。对于XOR,我不需要任何偏差,它正在收敛到预期值。当你对最终输出进行四舍五入时,我得到了准确的输出。所需要的是训练,然后验证,然后再次训练,直到神经网络令人满意。我一次又一次地训练每一组直到满意为止,但不是整组

// Initialize the Neural Network
algorithm.initialize(this.numberOfInputs);

int index = 0;
double errorRate = 0;

// Loop until satisfaction or after some iterations
do {
    // Train the Neural Network
    algorithm.train(this.trainingDataSets, this.numberOfInputs);

    // Validate the Neural Network and return the error rate
    errorRate = algorithm.run(this.validationDataSets, this.numberOfInputs);

    index++;
} while (errorRate > minErrorRate && index < numberOfTrainValidateIteration);
BackPropagationStrategy.java中,我更改了updateWeights方法中每个偏差的权重和增量,我将其重命名为updateWeightsAndBias

public class BackPropagationStrategy implements IStrategy, Serializable {

    [...]

    public void updateWeightsAndBias(NeuralNetwork neuralNetwork, double[] inputs) {

        for (int i = neuralNetwork.getLayers().size() - 1; i >= 0; i--) {

            Layer layer = neuralNetwork.getLayers().get(i);

            for (Neuron neuron : layer.getNeurons()) {

                [...]

                Synapse bias = neuron.getBias();
                double delta = learning * 1.0;
                bias.setWeight(bias.getWeight() + delta + this.momentum * bias.getDelta());

                bias.setDelta(delta);
            }
        }
    }

    [...]

有了真实数据,网络正在收敛。现在,要找到学习率、动量、错误率、神经元数量、隐藏层数量等的完美变量组合(如果可能的话)是一项修剪工作。

您对函数和变量使用了令人困惑的名称。至少,这会让你的代码很难理解,最多也表明你在理解算法方面还有一些不足。例如,您使用
this.error
存储输出的导数乘以错误(因此传播的是错误的值,而不是此神经元中的错误)
calculateSumWeights
似乎也有错误:此函数不能确定计算权重之和。尝试整理您的代码,并使用一个具有非常简单的数据集(一个或两个示例,具有一个或两个属性)的调试器。我应该将神经元的错误传播称为阈值吗?你叫什么名字?它可以帮助我找到一些答案。我会研究求和法,但是你看到它有什么错误吗?我不记得我曾经需要存储这个值,IIRC它只需要一次,用于传播和计算增量值。然而,在您的版本中可能需要它。我将把传播的错误称为<代码>传播错误:)在您的情况下(请注意,我可能误解了您的代码),似乎更多的是传播到前一层的错误,所以可能不是“传播错误”,而是“传播错误”。在这种情况下,我会称之为。。。(惊讶!)
errorToPropagate
。我修改了名称和我的神经元类。导数仅应用于输出层,而不应用于隐藏层。此外,我发现一个错误,我没有正确链接我的隐藏层和输出层。我现在有更好的结果,但它总是去第一组的第一个输出。。。我会进一步调查的!偏差对于解决异或问题至关重要。没有偏移,所有的分离平面(线)都通过原点。例如,不可能像这样将(0,0)和(0,1)分开。
Real: 0.0
Current: 0.025012156926937503
Real: 1.0
Current: 0.022566830709341495
Real: 1.0
Current: 0.02768416343491415
Real: 0.0
Current: 0.024903432706154027
=== Input Layer

== Neuron #1

= Synapse #1
Weight: -0.19283583155573614
Input: 1.0

= Synapse #2
Weight: 0.04023817185601586
Input: 1.0

Sum: -0.15259765969972028
Output: 0.461924442180935

== Neuron #2

= Synapse #1
Weight: -0.3281099260608612
Input: 1.0

= Synapse #2
Weight: -0.4388250065958519
Input: 1.0

Sum: -0.7669349326567131
Output: 0.31714251453174147

=== Hidden Layer

== Neuron #1

= Synapse #1
Weight: 0.16703288052854093
Input: 0.461924442180935

= Synapse #2
Weight: 0.31683996162148054
Input: 0.31714251453174147

Sum: 0.17763999229679783
Output: 0.5442935820534444

== Neuron #2

= Synapse #1
Weight: -0.45330313978424686
Input: 0.461924442180935

= Synapse #2
Weight: 0.3287014377113835
Input: 0.31714251453174147

Sum: -0.10514659949771789
Output: 0.47373754172497556

=== Output Layer

== Neuron #1

= Synapse #1
Weight: 0.08643751629154495
Input: 0.5442935820534444

= Synapse #2
Weight: -0.29715579267218695
Input: 0.47373754172497556

Sum: -0.09372646936373039
Output: 0.47658552081912403
// Initialize the Neural Network
algorithm.initialize(this.numberOfInputs);

int index = 0;
double errorRate = 0;

// Loop until satisfaction or after some iterations
do {
    // Train the Neural Network
    algorithm.train(this.trainingDataSets, this.numberOfInputs);

    // Validate the Neural Network and return the error rate
    errorRate = algorithm.run(this.validationDataSets, this.numberOfInputs);

    index++;
} while (errorRate > minErrorRate && index < numberOfTrainValidateIteration);
public class Neuron implements Serializable {

    [...]

    private Synapse bias;

    public Neuron(IActivation activation) {
        [...]
        this.bias = new Synapse(this);
        this.bias.setWeight(0.5); // Set initial weight OR keep the random number already set
    }

    public void updateOutput(double[] inputs) {
        double sumWeights = this.calculateSumWeights(inputs);

        this.output = this.activation.activate(sumWeights + this.bias.getWeight() * 1.0);
    }

    [...]
public class BackPropagationStrategy implements IStrategy, Serializable {

    [...]

    public void updateWeightsAndBias(NeuralNetwork neuralNetwork, double[] inputs) {

        for (int i = neuralNetwork.getLayers().size() - 1; i >= 0; i--) {

            Layer layer = neuralNetwork.getLayers().get(i);

            for (Neuron neuron : layer.getNeurons()) {

                [...]

                Synapse bias = neuron.getBias();
                double delta = learning * 1.0;
                bias.setWeight(bias.getWeight() + delta + this.momentum * bias.getDelta());

                bias.setDelta(delta);
            }
        }
    }

    [...]