我有困难理解误差反向传播算法。我阅读了很多并搜索了很多,但是我无法理解为什么我的神经网络不起作用。我想确认我每个部分都做得对。
这是我的神经网络在初始化时以及在输入 [1, 1] 和输出 [0] 设置第一行时的情况(您可以看到,我试图做异或神经网络):
我有3个层:输入层、隐藏层和输出层。第一层(输入层)和隐藏层各包含2个神经元,每个神经元中有2个突触。最后一层(输出层)也包含一个神经元,其中有2个突触。
突触包含权重和其先前的 delta 值(在开始时,它为 0)。与突触相连的输出可以在与突触相关联的 sourceNeuron 中找到,如果没有源神经元(例如在输入层中),则可以在输入数组中找到。
Layer.java 类包含神经元列表。在我的 NeuralNetwork.java 中,我初始化神经网络,然后在我的训练集中进行循环。在每次迭代中,我替换输入和输出值,并在我的误差反向传播算法上调用 train 方法,该算法为当前数据集运行特定次数(暂定为 1000 次 epoch)。
我使用的激活函数是sigmoid函数。
训练集和验证集为(input1,input2,output):
1,1,0
0,1,1
1,0,1
0,0,0
这是我的 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);
}
[...]
}
Synapse.java包含:
public Synapse(Neuron sourceNeuron) {
this.sourceNeuron = sourceNeuron;
Random r = new Random();
this.weight = (-0.5) + (0.5 - (-0.5)) * r.nextDouble();
this.delta = 0;
}
[... getter and setter ...]
我的类BackpropagationStrategy.java中的train方法运行一个while循环,并在训练集的一行上进行1000次(epoch)后停止。代码如下:
this.forwardPropagation(neuralNetwork, inputs);
this.backwardPropagation(neuralNetwork, expectedOutput);
this.updateWeights(neuralNetwork);
以下是上述方法的全部实现(学习率为0.45,动量为0.9):
public void forwardPropagation(NeuralNetwork neuralNetwork, double[] inputs) {
for (Layer layer : neuralNetwork.getLayers()) {
for (Neuron neuron : layer.getNeurons()) {
if (layer.isInput()) {
neuron.updateOutput(inputs);
} else {
neuron.updateOutput(null);
}
}
}
}
public void backwardPropagation(NeuralNetwork neuralNetwork, double realOutput) {
Layer lastLayer = null;
// Loop à travers les hidden layers et le output layer uniquement
ArrayList<Layer> layers = neuralNetwork.getLayers();
for (int i = layers.size() - 1; i > 0; i--) {
Layer layer = layers.get(i);
for (Neuron neuron : layer.getNeurons()) {
double errorToPropagate = neuron.getDerivative();
// Output layer
if (layer.isOutput()) {
errorToPropagate *= (realOutput - neuron.getOutput());
}
// Hidden layers
else {
double sumFromLastLayer = 0;
for (Neuron lastLayerNeuron : lastLayer.getNeurons()) {
for (Synapse synapse : lastLayerNeuron.getSynapses()) {
if (synapse.getSourceNeuron() == neuron) {
sumFromLastLayer += (synapse.getWeight() * lastLayerNeuron.getErrorToPropagate());
break;
}
}
}
errorToPropagate *= sumFromLastLayer;
}
neuron.setErrorToPropagate(errorToPropagate);
}
lastLayer = layer;
}
}
public void updateWeights(NeuralNetwork neuralNetwork) {
for (int i = neuralNetwork.getLayers().size() - 1; i > 0; i--) {
Layer layer = neuralNetwork.getLayers().get(i);
for (Neuron neuron : layer.getNeurons()) {
for (Synapse synapse : neuron.getSynapses()) {
double delta = this.learningRate * neuron.getError() * synapse.getSourceNeuron().getOutput();
synapse.setWeight(synapse.getWeight() + delta + this.momentum * synapse.getDelta());
synapse.setDelta(delta);
}
}
}
}
对于验证集,我只运行了这个:
this.forwardPropagation(neuralNetwork, inputs);
然后检查输出层神经元的输出。
我做错了什么吗?需要一些解释...
这是我在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
我可能存在偏见问题。我将在此答案的帮助下进行研究:神经网络中偏差的作用。它不会在下一个数据集中移回...
propagatedError
:) 在你的情况下(但请注意我可能误解了你的代码),似乎更多的是将误差传播到前一层,因此也许不是“传播误差”,而是“要传播的误差”。在这种情况下,我会称之为...(惊喜!)errorToPropagate
。 - BartoszKP