我在理解反向传播算法上遇到了困难。我阅读了很多资料,也进行了大量的搜索,但还是无法理解为什么我的神经网络无法正常工作。我想确认我是否在每个部分都按照正确的方式进行操作。
这是我的神经网络在初始化时以及当第一行输入[1, 1]和输出[0]设置时的状态(如你所见,我正在尝试构建XOR神经网络):
我有三层:输入层、隐藏层和输出层。第一层(输入层)和隐藏层各包含两个神经元,每个神经元有两个突触。最后一层(输出层)包含一个神经元,也有两个突触。
一个突触包含一个权重和它之前的增量(在开始时,它是0)。与突触连接的输出可以通过与突触关联的源神经元找到,或者如果没有源神经元(如在输入层中),则在输入数组中找到。
Layer.java类包含一个神经元列表。在我的NeuralNetwork.java中,我初始化神经网络,然后在我的训练集中循环。在每次迭代中,我替换输入和输出值,并在我的反向传播算法上调用训练方法,算法会为当前数据集运行一定次数(目前是1000次的epoch)。
我使用的激活函数是sigmoid函数。
训练集和验证集的格式为(输入1,输入2,输出):
1,1,00,1,11,0,10,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; // 仅遍历隐藏层和输出层 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(); // 输出层 if (layer.isOutput()) { errorToPropagate *= (realOutput - neuron.getOutput()); } // 隐藏层 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个epoch后的结果:
Real: 0.0Current: 0.025012156926937503Real: 1.0Current: 0.022566830709341495Real: 1.0Current: 0.02768416343491415Real: 0.0Current: 0.024903432706154027
为什么输入层的突触没有被更新?到处都写着只更新隐藏层和输出层。
如你所见,完全错了!它只会达到第一个训练集输出(0.0),而不是1.0。
更新1
这是网络在使用这组数据[1.0,1.0,0.0]进行一次迭代的结果。这是前向传播方法的结果:
=== 输入层== 神经元#1= 突触#1权重: -0.19283583155573614输入: 1.0= 突触#2权重: 0.04023817185601586输入: 1.0总和: -0.15259765969972028输出: 0.461924442180935== 神经元#2= 突触#1权重: -0.3281099260608612输入: 1.0= 突触#2权重: -0.4388250065958519输入: 1.0总和: -0.7669349326567131输出: 0.31714251453174147=== 隐藏层== 神经元#1= 突触#1权重: 0.16703288052854093输入: 0.461924442180935= 突触#2权重: 0.31683996162148054输入: 0.31714251453174147总和: 0.17763999229679783输出: 0.5442935820534444== 神经元#2= 突触#1权重: -0.45330313978424686输入: 0.461924442180935= 突触#2权重: 0.3287014377113835输入: 0.31714251453174147总和: -0.10514659949771789输出: 0.47373754172497556=== 输出层== 神经元#1= 突触#1权重: 0.08643751629154495输入: 0.5442935820534444= 突触#2权重: -0.29715579267218695输入: 0.47373754172497556总和: -0.09372646936373039输出: 0.47658552081912403
更新2
我可能存在偏置问题。我将参考这个答案来解决这个问题:神经网络中偏置的作用。它在下一个数据集上没有回退,所以…
回答: