using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class Perceptron : MonoBehaviour { public List Layers; public List _synapses; [System.Serializable] public class Layer { public int Size; public bool AddOffsetNeuron; public int TotalSize { get { return AddOffsetNeuron ? (Size + 1) : Size; } } public void Init() { Values = new float[TotalSize]; Errors = new float[TotalSize]; if (AddOffsetNeuron) { Values[Values.Length - 1] = 1.0f; } } public float[] Values; public float[] Errors; } [System.Serializable] public class Synapses { public float[,] Values; public int InSize; public int OutSize; public Synapses(int inSize, int outSize) { InSize = inSize; OutSize = outSize; Values = new float[inSize, outSize]; SetRandom(); // SetOne(); Debug.Log($"Make synapses: {inSize}x{outSize}"); } public void SetOne() { for (int i = 0; i < InSize; ++i) { for (int j = 0; j < OutSize; ++j) { Values[i, j] = 1; } } } public void SetRandom() { for(int i = 0; i < InSize; ++i) { for (int j = 0; j < OutSize; ++j) { Values[i, j] = UnityEngine.Random.value * 2.0f - 1.0f; } } } } const float FxCapDerivative = 0.01f; //float Fx(float x) { // if(x > 1.0f) { // return 1.0f + FxCapDerivative * (x - 1.0f); // }else if(x < 0.0f) { // return FxCapDerivative * x; // } // return x; //} // //float FxDerivative(float x) { // if (x > 1.0f) { // return FxCapDerivative; // } else if (x < 0.0f) { // return FxCapDerivative; // } // return 1.0f; //} float Fx(float x) { return 1.0f / (1.0f + Mathf.Exp(-x)); } float FxDerivative(float x) { return x * (1.0f - x); } public void PropagateErrors() { for (int inLayerId = Layers.Count - 2; inLayerId >= 0; --inLayerId) { int outLayerId = inLayerId + 1; var inLayer = Layers[inLayerId]; var outLayer = Layers[outLayerId]; var weights = _synapses[inLayerId]; for(int i = 0; i < inLayer.Size; ++i) { float err = 0.0f; for (int o = 0; o < outLayer.Size; ++o) { err += outLayer.Errors[o] * weights.Values[i, o]; } inLayer.Errors[i] = err; } } } public void RecalculateWeights(float learnRate) { for (int inLayerId = 0; inLayerId < Layers.Count - 1; ++inLayerId) { int outLayerId = inLayerId + 1; var inLayer = Layers[inLayerId]; var outLayer = Layers[outLayerId]; var weights = _synapses[inLayerId]; for (int i = 0; i < inLayer.TotalSize; ++i) { for (int o = 0; o < outLayer.Size; ++o) { weights.Values[i, o] += (learnRate * outLayer.Errors[o]) * FxDerivative(outLayer.Values[o]) * inLayer.Values[i]; } } } } public void CalculateOutputs() { for (int inLayerId = 0; inLayerId < Layers.Count - 1; ++inLayerId) { int outLayerId = inLayerId + 1; var inLayer = Layers[inLayerId]; var outLayer = Layers[outLayerId]; var weights = _synapses[inLayerId]; for (int o = 0; o < outLayer.Size; ++o) { float sum = 0; for (int i = 0; i < inLayer.TotalSize; ++i) { sum += weights.Values[i, o] * inLayer.Values[i]; } outLayer.Values[o] = Fx(sum); } } } public void Learn(float learnRate) { PropagateErrors(); RecalculateWeights(learnRate); } void Start() { foreach(var layer in Layers) { layer.Init(); } _synapses = new List(); for (int i = 0; i < Layers.Count - 1; ++i) { _synapses.Add(new Synapses(Layers[i].TotalSize, Layers[i + 1].Size)); } } void Update() { } }