/// <summary> Calculates the changes this <see cref="NeuronLayer"/> wishes to make to its <see cref="neurons"/>. </summary> /// <param name="neuronLayerChange"> The changes to make to the <see cref="NeuronLayer"/>. </param> /// <param name="weightLayerChange"> The changes to make to the <see cref="WeightLayer"/>. </param> /// <param name="deltaOrError"> The delta or error of the next <see cref="NeuronLayer"/>, or calculated from the output of the <see cref="NeuralNetwork"/>. </param> public void CalculateChanges(NeuronLayerChange neuronLayerChange, WeightLayerChange weightLayerChange, IReadOnlyList <float> deltaOrError) { #if DEBUG if (neuronLayerChange.NeuronLayer != this) { throw new Exception("Neuron layer mismatch."); } if (weightLayerChange.WeightLayer != PreviousWeightLayer) { throw new Exception("Weight layer mismatch."); } #endif // If this is the output layer, calculate based on the error. if (NextWeightLayer is null) { for (uint i = 0; i < Count; i++) { neuronLayerChange.SetDelta(this[i], neurons[i].CalculateChanges(neuronLayerChange, weightLayerChange, deltaOrError[(int)i])); } } // Otherwise; if this is a hidden layer, calculate based on the delta of the next layer. else { for (uint i = 0; i < Count; i++) { neuronLayerChange.SetDelta(this[i], neurons[i].CalculateErrorThenChanges(neuronLayerChange, weightLayerChange, deltaOrError)); } } }
/// <summary> Calculates and adds the changes to make to this <see cref="Neuron"/> to the given <paramref name="neuronLayerChange"/> and <paramref name="weightLayerChange"/> using the given <paramref name="delta"/>. </summary> /// <param name="neuronLayerChange"> The changes to make to the containing <see cref="NeuronLayer"/>. </param> /// <param name="weightLayerChange"> The changes to make to the previous <see cref="WeightLayer"/>. </param> /// <param name="delta"> The calculated delta, used to calculate the desired change. </param> private void addChanges(NeuronLayerChange neuronLayerChange, WeightLayerChange weightLayerChange, float delta) { // Calculate the change in bias based on the delta. neuronLayerChange.AddBias(this, delta); // Calculate the changes to the input weights of this neuron. // The change to make is based on the output of the neuron to which the weight is connected. "Neurons that fire together, wire together". foreach (uint previousLayerNeuronID in NeuronLayer.PreviousWeightLayer.GetPreviousLayerConnections(this)) { weightLayerChange.AddWeightChangeBetweenNeurons(previousNeuronLayer[previousLayerNeuronID], this, previousNeuronLayer[previousLayerNeuronID].Output * delta); } }
/// <summary> Calculates and adds the changes to be made to this <see cref="Neuron"/> from the given <paramref name="previousDelta"/>. </summary> /// <param name="neuronLayerChange"> The changes to make to the containing <see cref="NeuronLayer"/>. </param> /// <param name="weightLayerChange"> The changes to make to the previous <see cref="WeightLayer"/>. </param> /// <param name="previousDelta"> The calculated deltas from the previous <see cref="NeuronLayer"/> of this <see cref="Neuron"/>. </param> /// <returns> The calculated delta of this <see cref="Neuron"/>. </returns> public float CalculateErrorThenChanges(NeuronLayerChange neuronLayerChange, WeightLayerChange weightLayerChange, IReadOnlyList <float> previousDelta) { // Calculate the summed error using the connections from this neuron to the next layer. float summedError = 0; foreach (uint nextLayerNeuronID in NeuronLayer.NextWeightLayer.GetNextLayerConnections(this)) { summedError += NeuronLayer.NextWeightLayer.GetWeightBetweenNeurons(this, nextNeuronLayer[nextLayerNeuronID]) * previousDelta[(int)nextLayerNeuronID]; } // Finish the delta calculations with the summed error. return(CalculateChanges(neuronLayerChange, weightLayerChange, summedError)); }
/// <summary> Creates a new <see cref="NetworkChange"/> for the given <paramref name="neuralNetwork"/>. </summary> /// <param name="neuralNetwork"> The <see cref="IReadOnlyNeuralNetwork"/> whose changes are being stored. </param> public NetworkChange(IReadOnlyNeuralNetwork neuralNetwork) { // Set the neural network. NeuralNetwork = neuralNetwork; // Initialise the neuron and weight changes. neuronLayerChanges = new NeuronLayerChange[neuralNetwork.NeuronLayerCount - 1]; weightLayerChanges = new WeightLayerChange[neuralNetwork.WeightLayerCount]; for (int i = 0; i < neuronLayerChanges.Length; i++) { neuronLayerChanges[i] = new NeuronLayerChange(neuralNetwork.GetNeuronLayer(i + 1)); } for (int i = 0; i < weightLayerChanges.Length; i++) { weightLayerChanges[i] = new WeightLayerChange(neuralNetwork.GetWeightLayer(i)); } }
/// <summary> Calculates and adds the changes to be made to this <see cref="Neuron"/> from the given <paramref name="error"/>. </summary> /// <param name="neuronLayerChange"> The changes to make to the containing <see cref="NeuronLayer"/>. </param> /// <param name="weightLayerChange"> The changes to make to the previous <see cref="WeightLayer"/>. </param> /// <param name="error"> The previously calculated delta. </param> /// <returns> The calculated delta of this <see cref="Neuron"/>. </returns> public float CalculateChanges(NeuronLayerChange neuronLayerChange, WeightLayerChange weightLayerChange, float error) { // Calculate the delta. float delta = activationFunction.ActivationDerivative(Output) * error; float p = activationFunction.ActivationDerivative(Output); if (p < 0) { throw new Exception(); } // Add the changes to be made. addChanges(neuronLayerChange, weightLayerChange, delta); // Return the calculated delta. return(delta); }