BackPropagation implementations

This commit is contained in:
Pascal Serrarens 2026-06-30 16:44:13 +02:00
parent eb2adaeec3
commit 51002e78df

View File

@ -6,8 +6,7 @@ using Unity.Mathematics;
using static Unity.Mathematics.math; using static Unity.Mathematics.math;
#endif #endif
namespace NanoBrain namespace NanoBrain {
{
/// <summary> /// <summary>
/// A neuron is a basic Nucleus /// A neuron is a basic Nucleus
@ -22,20 +21,17 @@ namespace NanoBrain
/// Each connection has a weight which is used to multiply the output of that other neuron /// Each connection has a weight which is used to multiply the output of that other neuron
/// before it is used by the combinator. /// before it is used by the combinator.
[Serializable] [Serializable]
public class Neuron : Nucleus public class Neuron : Nucleus {
{
/// <summary> /// <summary>
/// Create a new Neuron in a Cluster instance /// Create a new Neuron in a Cluster instance
/// </summary> /// </summary>
/// <param name="parent">The parent cluster in which the new Neuron should be created</param> /// <param name="parent">The parent cluster in which the new Neuron should be created</param>
/// <param name="name">The name of the new Neuron</param> /// <param name="name">The name of the new Neuron</param>
public Neuron(Cluster parent, string name) public Neuron(Cluster parent, string name) {
{
this.parent = parent; this.parent = parent;
this.name = name; this.name = name;
if (this.parent != null) if (this.parent != null) {
{
this.parent.nuclei ??= new(); this.parent.nuclei ??= new();
this.parent.nuclei.Add(this); this.parent.nuclei.Add(this);
} }
@ -67,8 +63,7 @@ namespace NanoBrain
/// <param name="weight">The weight applied to the input. Default value = 1</param> /// <param name="weight">The weight applied to the input. Default value = 1</param>
/// <returns>The created Synapse</returns> /// <returns>The created Synapse</returns>
/// This will add a new input to this nucleus with the given weight. /// This will add a new input to this nucleus with the given weight.
public Synapse AddSynapse(Neuron sendingNucleus, float weight = 1) public Synapse AddSynapse(Neuron sendingNucleus, float weight = 1) {
{
Synapse synapse = new(sendingNucleus, weight); Synapse synapse = new(sendingNucleus, weight);
this.synapses.Add(synapse); this.synapses.Add(synapse);
return synapse; return synapse;
@ -79,8 +74,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="sender">The sender of the input to the Synapse</param> /// <param name="sender">The sender of the input to the Synapse</param>
/// <returns>The found Synapse or null when the sender has no synapse to this nucleus.</returns> /// <returns>The found Synapse or null when the sender has no synapse to this nucleus.</returns>
public Synapse GetSynapse(Nucleus sender) public Synapse GetSynapse(Nucleus sender) {
{
foreach (Synapse synapse in this.synapses) foreach (Synapse synapse in this.synapses)
if (synapse.neuron == sender) if (synapse.neuron == sender)
return synapse; return synapse;
@ -91,8 +85,7 @@ namespace NanoBrain
/// Remove a synapse from a Nucleus /// Remove a synapse from a Nucleus
/// </summary> /// </summary>
/// <param name="sendingNucleus">Remote the synapse connecting to this Nucleus</param> /// <param name="sendingNucleus">Remote the synapse connecting to this Nucleus</param>
public void RemoveSynapse(Nucleus sendingNucleus) public void RemoveSynapse(Nucleus sendingNucleus) {
{
this.synapses.RemoveAll(synapse => synapse.neuron == sendingNucleus); this.synapses.RemoveAll(synapse => synapse.neuron == sendingNucleus);
} }
@ -102,8 +95,7 @@ namespace NanoBrain
/// Set the bias, recalculate the output and update all Nuclei receiving from this Nucleus /// Set the bias, recalculate the output and update all Nuclei receiving from this Nucleus
/// </summary> /// </summary>
/// <param name="inputValue"></param> /// <param name="inputValue"></param>
public virtual void SetBias(Vector3 inputValue) public virtual void SetBias(Vector3 inputValue) {
{
this.bias = inputValue; this.bias = inputValue;
this.lastUpdate = Time.time; this.lastUpdate = Time.time;
this.parent?.UpdateFromNucleus(this); this.parent?.UpdateFromNucleus(this);
@ -113,8 +105,7 @@ namespace NanoBrain
/// The type of combinators /// The type of combinators
/// </summary> /// </summary>
/// A combinator combines the weighted values of the synapses to a single value /// A combinator combines the weighted values of the synapses to a single value
public enum CombinatorType public enum CombinatorType {
{
/// Add the weighted values together /// Add the weighted values together
Sum, Sum,
/// Multiply the weighted values /// Multiply the weighted values
@ -129,8 +120,7 @@ namespace NanoBrain
/// <summary> /// <summary>
/// The type of /// The type of
/// </summary> /// </summary>
public enum ActivationType public enum ActivationType {
{
Linear, Linear,
Power, Power,
Sqrt, Sqrt,
@ -149,11 +139,9 @@ namespace NanoBrain
/// <summary> /// <summary>
/// The activation funtion /// The activation funtion
/// </summary> /// </summary>
public ActivationType activator public ActivationType activator {
{
get { return _activator; } get { return _activator; }
set set {
{
_activator = value; _activator = value;
//this.curve = GenerateCurve(); //this.curve = GenerateCurve();
} }
@ -171,16 +159,15 @@ namespace NanoBrain
/// <summary> /// <summary>
/// The output value of the neuron /// The output value of the neuron
/// </summary> /// </summary>
public virtual float3 outputValue public virtual float3 outputValue {
{
get { return _outputValue; } get { return _outputValue; }
set set {
{
_outputValue = value; _outputValue = value;
if (this.isFiring) if (this.isFiring)
WhenFiring?.Invoke(); WhenFiring?.Invoke();
} }
} }
public float3 activation => outputValue;
/// <summary> /// <summary>
/// The magnitude of the neuron output /// The magnitude of the neuron output
/// </summary> /// </summary>
@ -238,10 +225,8 @@ namespace NanoBrain
/// Check if the neuron is sleeping. /// Check if the neuron is sleeping.
/// </summary> /// </summary>
/// This will reset the output value if it is sleeping /// This will reset the output value if it is sleeping
public void SleepCheck() public void SleepCheck() {
{ if (this.isSleeping && this.outputSqrMagnitude > 0) {
if (this.isSleeping && this.outputSqrMagnitude > 0)
{
#if UNITY_MATHEMATICS #if UNITY_MATHEMATICS
this._outputValue = new float3(0, 0, 0); this._outputValue = new float3(0, 0, 0);
#else #else
@ -267,10 +252,8 @@ namespace NanoBrain
public bool breakOnUpdate = false; public bool breakOnUpdate = false;
/// \copydoc NanoBrain::Nucleus::ShallowCloneTo /// \copydoc NanoBrain::Nucleus::ShallowCloneTo
public override Nucleus ShallowCloneTo(Cluster parent) public override Nucleus ShallowCloneTo(Cluster parent) {
{ Neuron clone = new(parent, this.name) {
Neuron clone = new(parent, this.name)
{
// prefabNucleus = this // prefabNucleus = this
}; };
CloneFields(clone); CloneFields(clone);
@ -281,8 +264,7 @@ namespace NanoBrain
/// Copy relevant fields of this neuron to the given neuron /// Copy relevant fields of this neuron to the given neuron
/// </summary> /// </summary>
/// <param name="clone"></param> /// <param name="clone"></param>
protected virtual void CloneFields(Neuron clone) protected virtual void CloneFields(Neuron clone) {
{
clone.bias = this.bias; clone.bias = this.bias;
clone.persistOutput = this.persistOutput; clone.persistOutput = this.persistOutput;
clone.combinator = this.combinator; clone.combinator = this.combinator;
@ -294,45 +276,34 @@ namespace NanoBrain
/// Delete the give neuron /// Delete the give neuron
/// </summary> /// </summary>
/// <param name="nucleus">The neuron to delete</param> /// <param name="nucleus">The neuron to delete</param>
public static void Delete(Nucleus nucleus) public static void Delete(Nucleus nucleus) {
{
if (nucleus == null) if (nucleus == null)
return; return;
if (nucleus is Neuron neuron) if (nucleus is Neuron neuron) {
{ foreach (Synapse synapse in neuron.synapses) {
foreach (Synapse synapse in neuron.synapses) if (synapse.neuron is Neuron synapse_nucleus) {
{ if (synapse_nucleus.receivers.Count > 1) {
if (synapse.neuron is Neuron synapse_nucleus)
{
if (synapse_nucleus.receivers.Count > 1)
{
// there is another nucleus feeding into this input nucleus // there is another nucleus feeding into this input nucleus
synapse_nucleus.receivers.RemoveAll(r => r == nucleus); synapse_nucleus.receivers.RemoveAll(r => r == nucleus);
} }
else else {
{
// No other links, delete it. // No other links, delete it.
Neuron.Delete(synapse_nucleus); Neuron.Delete(synapse_nucleus);
} }
} }
} }
foreach (Nucleus receiver in neuron.receivers) foreach (Nucleus receiver in neuron.receivers) {
{
if (receiver is not Neuron receiverNeuron) if (receiver is not Neuron receiverNeuron)
continue; continue;
if (receiver != null && receiverNeuron.synapses != null) if (receiver != null && receiverNeuron.synapses != null)
receiverNeuron.synapses.RemoveAll(s => s.neuron == nucleus); receiverNeuron.synapses.RemoveAll(s => s.neuron == nucleus);
} }
} }
else if (nucleus is Cluster cluster) else if (nucleus is Cluster cluster) {
{
// remove all receivers for this cluster // remove all receivers for this cluster
foreach (Nucleus clusterNucleus in cluster.nuclei) foreach (Nucleus clusterNucleus in cluster.nuclei) {
{ if (clusterNucleus is Neuron output) {
if (clusterNucleus is Neuron output) foreach (Nucleus receiver in output.receivers) {
{
foreach (Nucleus receiver in output.receivers)
{
if (receiver is not Neuron receiverNeuron) if (receiver is not Neuron receiverNeuron)
continue; continue;
receiverNeuron.synapses.RemoveAll(s => s.neuron == output); receiverNeuron.synapses.RemoveAll(s => s.neuron == output);
@ -341,22 +312,19 @@ namespace NanoBrain
} }
} }
if (nucleus.parent.prefab != null) if (nucleus.parent.prefab != null) {
{
nucleus.parent.nuclei.RemoveAll(n => n == nucleus); nucleus.parent.nuclei.RemoveAll(n => n == nucleus);
nucleus.parent.RefreshOutputs(); nucleus.parent.RefreshOutputs();
} }
} }
/// \copydoc NanoBrain::Nucleus::UpdateStateIsolated /// \copydoc NanoBrain::Nucleus::UpdateStateIsolated
public override void UpdateStateIsolated() public override void UpdateStateIsolated() {
{ if (breakOnUpdate) {
if (breakOnUpdate)
{
Debug.Break(); Debug.Break();
} }
this.combinationValue = Combinator(this.bias, this.synapses); this.combination = Combinator(this.bias, this.synapses);
this.outputValue = Activator(this.combinationValue); this.outputValue = Activator(this.combination);
this.lastUpdate = Time.time; this.lastUpdate = Time.time;
} }
@ -364,7 +332,7 @@ namespace NanoBrain
#if UNITY_MATHEMATICS #if UNITY_MATHEMATICS
[NonSerialized] [NonSerialized]
public float3 combinationValue; public float3 combination;
/// <summary> /// <summary>
/// The combinator which combines the bias with the values from all synapses /// The combinator which combines the bias with the values from all synapses
@ -372,10 +340,8 @@ namespace NanoBrain
/// <param name="bias">The bias of the neuron</param> /// <param name="bias">The bias of the neuron</param>
/// <param name="synapses">The synapses of the neuron</param> /// <param name="synapses">The synapses of the neuron</param>
/// <returns></returns> /// <returns></returns>
protected float3 Combinator(float3 bias, List<Synapse> synapses) protected float3 Combinator(float3 bias, List<Synapse> synapses) {
{ switch (combinator) {
switch (combinator)
{
case CombinatorType.Sum: case CombinatorType.Sum:
return CombinatorSum(bias, synapses); return CombinatorSum(bias, synapses);
case CombinatorType.Product: case CombinatorType.Product:
@ -391,11 +357,9 @@ namespace NanoBrain
/// <param name="bias">The bias of the neuron</param> /// <param name="bias">The bias of the neuron</param>
/// <param name="synapses">The synapses of the neuron</param> /// <param name="synapses">The synapses of the neuron</param>
/// <returns></returns> /// <returns></returns>
public static float3 CombinatorSum(float3 bias, List<Synapse> synapses) public static float3 CombinatorSum(float3 bias, List<Synapse> synapses) {
{
float3 sum = bias; float3 sum = bias;
foreach (Synapse synapse in synapses) foreach (Synapse synapse in synapses) {
{
synapse.neuron.SleepCheck(); synapse.neuron.SleepCheck();
sum += synapse.weight * synapse.neuron.outputValue; sum += synapse.weight * synapse.neuron.outputValue;
} }
@ -408,11 +372,9 @@ namespace NanoBrain
/// <param name="bias">The bias of the neuron</param> /// <param name="bias">The bias of the neuron</param>
/// <param name="synapses">The synapses of the neuron</param> /// <param name="synapses">The synapses of the neuron</param>
/// <returns>The result of the multiplication</returns> /// <returns>The result of the multiplication</returns>
public static float3 CombinatorProduct(float3 bias, List<Synapse> synapses) public static float3 CombinatorProduct(float3 bias, List<Synapse> synapses) {
{
float3 product = bias; float3 product = bias;
foreach (Synapse synapse in synapses) foreach (Synapse synapse in synapses) {
{
synapse.neuron.SleepCheck(); synapse.neuron.SleepCheck();
product *= synapse.weight * synapse.neuron.outputValue; product *= synapse.weight * synapse.neuron.outputValue;
} }
@ -479,10 +441,8 @@ namespace NanoBrain
/// <param name="inputValue"></param> /// <param name="inputValue"></param>
/// <returns>The result of applying the activation function</returns> /// <returns>The result of applying the activation function</returns>
// This does not allocate memory and seems faster than a switch expression // This does not allocate memory and seems faster than a switch expression
protected float3 Activator(float3 inputValue) protected float3 Activator(float3 inputValue) {
{ switch (activator) {
switch (activator)
{
case ActivationType.Linear: case ActivationType.Linear:
return ActivatorLinear(inputValue); return ActivatorLinear(inputValue);
case ActivationType.Sqrt: case ActivationType.Sqrt:
@ -507,8 +467,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="input">Input value</param> /// <param name="input">Input value</param>
/// <returns>The unchanged value</returns> /// <returns>The unchanged value</returns>
protected float3 ActivatorLinear(float3 input) protected float3 ActivatorLinear(float3 input) {
{
return input; return input;
} }
@ -517,8 +476,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="input">Input value</param> /// <param name="input">Input value</param>
/// <returns>The square root of the input</returns> /// <returns>The square root of the input</returns>
protected float3 ActivatorSqrt(float3 input) protected float3 ActivatorSqrt(float3 input) {
{
float3 result = normalize(input) * MathF.Sqrt(length(input)); float3 result = normalize(input) * MathF.Sqrt(length(input));
return result; return result;
} }
@ -528,8 +486,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="input">Input value</param> /// <param name="input">Input value</param>
/// <returns>The input to the power of 2</returns> /// <returns>The input to the power of 2</returns>
protected float3 ActivatorPower(float3 input) protected float3 ActivatorPower(float3 input) {
{
float3 result = normalize(input) * MathF.Pow(length(input), 2); float3 result = normalize(input) * MathF.Pow(length(input), 2);
return result; return result;
} }
@ -539,8 +496,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="input">Input value</param> /// <param name="input">Input value</param>
/// <returns>1/input value</returns> /// <returns>1/input value</returns>
protected float3 ActivatorReciprocal(float3 input) protected float3 ActivatorReciprocal(float3 input) {
{
float magnitude = length(input); float magnitude = length(input);
if (magnitude == 0) if (magnitude == 0)
return new float3(0, 0, 0); return new float3(0, 0, 0);
@ -554,8 +510,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="input">Input value</param> /// <param name="input">Input value</param>
/// <returns>Tanh(input value)</returns> /// <returns>Tanh(input value)</returns>
protected float3 ActivatorTanh(float3 input) protected float3 ActivatorTanh(float3 input) {
{
float magnitude = length(input); float magnitude = length(input);
float3 result = normalize(input) * MathF.Tanh(magnitude); float3 result = normalize(input) * MathF.Tanh(magnitude);
return result; return result;
@ -565,8 +520,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="input">Input value</param> /// <param name="input">Input value</param>
/// <returns>An uniform vector with magnitude between 0 and 1</returns> /// <returns>An uniform vector with magnitude between 0 and 1</returns>
protected float3 ActivatorBinary(float3 input) protected float3 ActivatorBinary(float3 input) {
{
float magnitude = length(input); float magnitude = length(input);
float value = Mathf.Clamp01(magnitude); float value = Mathf.Clamp01(magnitude);
return float3(value, value, value); return float3(value, value, value);
@ -577,8 +531,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="input">Input value</param> /// <param name="input">Input value</param>
/// <returns>The normalized vector</returns> /// <returns>The normalized vector</returns>
protected float3 ActivatorNormalized(float3 input) protected float3 ActivatorNormalized(float3 input) {
{
if (lengthsq(input) == 0) if (lengthsq(input) == 0)
return input; return input;
float3 result = normalize(input); float3 result = normalize(input);
@ -664,8 +617,7 @@ namespace NanoBrain
/// <summary> /// <summary>
/// The nuclei which have a synapse to this neuron /// The nuclei which have a synapse to this neuron
/// </summary> /// </summary>
public virtual List<Nucleus> receivers public virtual List<Nucleus> receivers {
{
get { return _receivers; } get { return _receivers; }
set { _receivers = value; } set { _receivers = value; }
} }
@ -675,8 +627,7 @@ namespace NanoBrain
/// </summary> /// </summary>
/// <param name="receiverToAdd">The receiver to add</param> /// <param name="receiverToAdd">The receiver to add</param>
/// <param name="weight">The weight to use for the synapse to his neuron</param> /// <param name="weight">The weight to use for the synapse to his neuron</param>
public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) public virtual void AddReceiver(Nucleus receiverToAdd, float weight = 1) {
{
if (receiverToAdd is not Neuron receiverNeuron) if (receiverToAdd is not Neuron receiverNeuron)
return; return;
this._receivers.Add(receiverNeuron); this._receivers.Add(receiverNeuron);
@ -689,8 +640,7 @@ namespace NanoBrain
/// Remove a receiver to this neuron /// Remove a receiver to this neuron
/// </summary> /// </summary>
/// <param name="receiverToRemove">The receiver to remove</param> /// <param name="receiverToRemove">The receiver to remove</param>
public virtual void RemoveReceiver(Nucleus receiverToRemove) public virtual void RemoveReceiver(Nucleus receiverToRemove) {
{
if (receiverToRemove is not Neuron receiverNeuron) if (receiverToRemove is not Neuron receiverNeuron)
return; return;
this._receivers.RemoveAll(receiver => receiver == receiverNeuron); this._receivers.RemoveAll(receiver => receiver == receiverNeuron);
@ -708,8 +658,7 @@ namespace NanoBrain
#region Back propagation #region Back propagation
public void BackPropagation(Synapse synapse, Vector3 error, float learningRate) public void BackPropagation(Synapse synapse, Vector3 error, float learningRate) {
{
// Loss function: // Loss function:
// Mean Squared Error (MSE) 1/n * sum(errors^2) // Mean Squared Error (MSE) 1/n * sum(errors^2)
// We use simplified here 1/2 * (error^2) // We use simplified here 1/2 * (error^2)
@ -720,17 +669,16 @@ namespace NanoBrain
// Backpropagation = loss * d(combinator) // Backpropagation = loss * d(combinator)
Vector3 delta2; Vector3 delta2;
switch (activator) switch (activator) {
{
case ActivationType.Linear: case ActivationType.Linear:
// Derivative of this (f'()) would be 1. // Derivative of this (f'()) would be 1.
delta2 = loss * 1; delta2 = loss * 1;
break; break;
case ActivationType.Power: case ActivationType.Power:
delta2 = loss * (2 * this.combinationValue); delta2 = loss * (2 * this.combination);
break; break;
case ActivationType.Reciprocal: case ActivationType.Reciprocal:
delta2 = loss * (-1 / (this.combinationValue * this.combinationValue)); delta2 = loss * (-1 / (this.combination * this.combination));
break; break;
default: default:
delta2 = loss; delta2 = loss;
@ -743,43 +691,108 @@ namespace NanoBrain
Debug.Log($"Updated weight: {error.magnitude} {error} {scaledOutput} {synapse.weight}"); Debug.Log($"Updated weight: {error.magnitude} {error} {scaledOutput} {synapse.weight}");
} }
public void BackPropagationWithLoss(Synapse synapse, Vector3 loss, float learningRate) public void BackPropagationWithLoss(Synapse synapse, Vector3 loss, float learningRate) {
{ Vector3 delta2 = activator switch {
// loss is a derivative of error ActivationType.Linear => loss * 1,
// Backpropagation = loss * d(combinator) ActivationType.Power => (Vector3)(loss * (2 * this.combination)),
ActivationType.Reciprocal => (Vector3)(loss * (-1 / (this.combination * this.combination))),
Vector3 delta2; _ => loss,
switch (activator) };
{
case ActivationType.Linear:
// Derivative of this (f'()) would be 1.
delta2 = loss * 1;
break;
case ActivationType.Power:
delta2 = loss * (2 * this.combinationValue);
break;
case ActivationType.Reciprocal:
delta2 = loss * (-1 / (this.combinationValue * this.combinationValue));
break;
default:
delta2 = loss;
break;
}
Vector3 scaledOutput = Vector3.Scale(delta2, synapse.neuron.outputValue); Vector3 scaledOutput = Vector3.Scale(delta2, synapse.neuron.outputValue);
float deltaWeight = Mathf.Abs(scaledOutput.x) + Mathf.Abs(scaledOutput.y) + Mathf.Abs(scaledOutput.z); float deltaWeight = Mathf.Abs(scaledOutput.x) + Mathf.Abs(scaledOutput.y) + Mathf.Abs(scaledOutput.z);
synapse.weight += learningRate * deltaWeight; synapse.weight += learningRate * deltaWeight;
Debug.Log($"Updated weight: {loss.magnitude} {loss} {scaledOutput} {synapse.weight}"); Debug.Log($"Updated weight: {loss.magnitude} {loss} {scaledOutput} {synapse.weight}");
} }
public void BackPropagation1(Vector3 cost, Vector3 error, float learningRate) {
cost = Vector3.Scale(error, error); // error^2
float3 derivative = 2 * error; // derivative of (error^2)
// inverted because it uses the non-convential
// error=(actual-taget) instead of (target-actual)
// dSSR / dPredicted
// Bias
float3 deltaBias = derivative;
// deltaBias *= 1; // because bias is always fully applied
Vector3 stepSize = deltaBias * learningRate;
this.bias -= stepSize;
foreach (Synapse synapse in this.synapses) {
// derivative for the weight?
float3 deltaSynapse = derivative; // dSSR/dPredicted
// derivative for the previous activation
deltaSynapse *= synapse.neuron.activation; // dPredicted/dWeight
// // derivative for the activator
// switch (activator) {
// case ActivationType.Linear:
// //delta2 *= 1;
// break;
// default:
// break;
// }
float deltaWeight = length(deltaSynapse);
synapse.weight += learningRate * deltaWeight;
synapse.neuron.BackPropagation2(derivative * synapse.weight, learningRate);
}
}
public void BackPropagation0(Vector3 error, float learningRate) {
float3 derivative = 2 * error; // derivative of (error^2)
// inverted because it uses the non-convential
// error=(actual-taget) instead of (target-actual)
// dSSR / dPredicted
BackPropagation2(derivative, learningRate);
}
public void BackPropagation2(Vector3 derivative, float learningRate) {
// Bias
float3 deltaBias = derivative; // dSSR/dActivator
switch (activator) { // dActivator/dBias
case ActivationType.Linear:
//deltaBias *= 1;
break;
default:
break;
}
// deltaBias *= 1; // because bias is always fully applied
Vector3 stepSize = deltaBias * learningRate;
this.bias -= stepSize;
foreach (Synapse synapse in this.synapses) {
// derivative for the weight?
float3 deltaSynapse = derivative; // dSSR/dActivator
// derivative for the activator
// dActivator/dCombinator
switch (activator) {
case ActivationType.Linear:
//deltaSynapse *= 1;
break;
default:
break;
}
// derivative for the previous activation
// dCombinator/dWeight
deltaSynapse *= synapse.neuron.activation;
float deltaWeight = length(deltaSynapse);
synapse.weight += learningRate * deltaWeight;
BackPropagation2(derivative * synapse.weight, learningRate);
}
}
#endregion Back propagation #endregion Back propagation
/// <summary> /// <summary>
/// Process an external stimulus /// Process an external stimulus
/// </summary> /// </summary>
/// <param name="inputValue">The value of the stimulus</param> /// <param name="inputValue">The value of the stimulus</param>
public virtual void ProcessStimulus(Vector3 inputValue) public virtual void ProcessStimulus(Vector3 inputValue) {
{
this.lastUpdate = Time.time; this.lastUpdate = Time.time;
this.bias = inputValue; this.bias = inputValue;
this.parent?.UpdateFromNucleus(this); this.parent?.UpdateFromNucleus(this);