175 lines
4.5 KiB
C#
175 lines
4.5 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class Perceptron : MonoBehaviour
|
|
{
|
|
public List<Layer> Layers;
|
|
public List<Synapses> _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<Synapses>();
|
|
for (int i = 0; i < Layers.Count - 1; ++i) {
|
|
_synapses.Add(new Synapses(Layers[i].TotalSize, Layers[i + 1].Size));
|
|
}
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
|
|
}
|
|
}
|