129 lines
4.8 KiB
C#
129 lines
4.8 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using Unity.Mathematics;
|
|
|
|
namespace NanoBrain {
|
|
|
|
/// <summary>
|
|
/// A Synapse connects the ouput of a Neuron to another Neuron
|
|
/// </summary>
|
|
[Serializable]
|
|
public class Synapse {
|
|
/// <summary>
|
|
/// The neuron from which input is received
|
|
/// </summary>
|
|
[SerializeReference]
|
|
[HideInInspector]
|
|
public Neuron neuron;
|
|
|
|
/// <summary>
|
|
/// The weight value to apply to the Neuron input
|
|
/// </summary>
|
|
public float weight;
|
|
|
|
/// <summary>
|
|
/// Indicator whether the weight can be trained
|
|
/// </summary>
|
|
public bool trainable = false;
|
|
|
|
/// <summary>
|
|
/// Create a new Synapse
|
|
/// </summary>
|
|
/// <param name="nucleus">The neuron from which input is received</param>
|
|
/// <param name="weight">The weight value to apply to the Neuron input</param>
|
|
public Synapse(Neuron nucleus, float weight = 1.0f) {
|
|
this.neuron = nucleus;
|
|
this.weight = weight;
|
|
}
|
|
|
|
public virtual void BackPropagation(Neuron receiver, float derivative, float learningRate) {
|
|
switch (receiver.activator) {
|
|
case Neuron.ActivationType.Linear:
|
|
derivative *= 1;
|
|
break;
|
|
case Neuron.ActivationType.Power:
|
|
// untested
|
|
derivative *= 2 * math.length(this.neuron.combination);
|
|
break;
|
|
case Neuron.ActivationType.Reciprocal:
|
|
// untested
|
|
derivative *= -1 / Mathf.Pow(math.length(this.neuron.combination), 2);
|
|
break;
|
|
default:
|
|
Debug.Log("other activator");
|
|
break;
|
|
}
|
|
|
|
this.neuron.BackPropagation1D(derivative * this.weight, learningRate);
|
|
|
|
derivative *= math.length(this.neuron.activation);
|
|
|
|
if (this.trainable) {
|
|
float deltaWeight = learningRate * derivative;
|
|
this.weight += deltaWeight;
|
|
}
|
|
}
|
|
|
|
public virtual void BackPropagation3D(Neuron receiver, Vector3 derivative, float learningRate) {
|
|
// As the weight cannot change the direction of the derivative
|
|
// we can use the simpler, 1D backpropagation here
|
|
// But we still need to determine the sign of the derivative
|
|
|
|
if (Synapse.AreOpposed(derivative, this.neuron.activation))
|
|
BackPropagation(receiver, -derivative.magnitude, learningRate);
|
|
else
|
|
BackPropagation(receiver, derivative.magnitude, learningRate);
|
|
|
|
// switch (receiver.activator) {
|
|
// case Neuron.ActivationType.Linear:
|
|
// derivative *= 1;
|
|
// break;
|
|
// case Neuron.ActivationType.Power:
|
|
// // untested
|
|
// derivative *= 2 * math.length(this.neuron.combination);
|
|
// break;
|
|
// case Neuron.ActivationType.Reciprocal:
|
|
// // untested
|
|
// derivative *= -1 / Mathf.Pow(math.length(this.neuron.combination), 2);
|
|
// break;
|
|
// default:
|
|
// Debug.Log("other activator");
|
|
// break;
|
|
// }
|
|
|
|
// this.neuron.BackPropagation3D(derivative * this.weight, learningRate);
|
|
|
|
// derivative *= math.length(this.neuron.activation);
|
|
|
|
// if (this.trainable) {
|
|
// float deltaWeight = learningRate * derivative.magnitude;
|
|
// // Compared to the 1D solution, this does not decrease the weight because magnitude is always positive
|
|
// // derivative.direction and derivative.sign are different....
|
|
// if (AreOpposed(derivative, this.neuron.activation))
|
|
// this.weight -= deltaWeight;
|
|
// else
|
|
// this.weight += deltaWeight;
|
|
// }
|
|
}
|
|
|
|
public static bool AreOpposed(Vector3 a, Vector3 b) {
|
|
// Check if the angle between the vectors is > 90 degrees
|
|
return Vector3.Dot(a, b) < 0f;
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class SynapseData {
|
|
public string clusterName;
|
|
public string neuronName;
|
|
public float weight;
|
|
public bool trainable;
|
|
|
|
public SynapseData(Synapse synapse) {
|
|
this.clusterName = synapse.neuron.parent.name;
|
|
this.neuronName = synapse.neuron.name;
|
|
this.weight = synapse.weight;
|
|
this.trainable = synapse.trainable;
|
|
}
|
|
}
|
|
} |