Не могу решить прболему вторую неделю... Юнити, нейросети, неточности. Код прилагается
Генерирую поколение особей. Выставляю мутацию на ноль и вижу следующий треш:
- Все особи при нулевой мутации хоть и имеют одинаковую расстановку весов в нейросети, выдают немного разное поведение от поколения к поколению, что сильно влияет на результаты.
- На протяжении текущего поколения никакого рассинхрона не наблюдается. Результаты и поведение одинаковы для всех особей.
- Самое интересное. Отбор лучшей особи осуществляется пузырьковой сортировкой по значению "фитнесс". "Фитнесс" = координата Х. Значение присваивается каждое обновление физики. При этом!!! В логах наблюдаю примерно следующее:
Дебаг лог: фитнесс лучшего типочка = -0.5
Дебаг лог: фитнесс лучшего типочка = 1.2
Дебаг лог: фитнесс лучшего типочка = -0.5
и далее по кругу... То есть обращаю ваше внимание, нейросеть в каждом поколении ОДНА И ТА ЖЕ, при этом результаты РАЗНЫЕ и они еще и повторяются циклично.
Короче вот код, поставлю свечку в храме за того кто сможет помочь...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Assets.Brakodel;
public class ArenaManager : MonoBehaviour
{
public GameObject referenceObj;
public GameObject Arena;
public GameObject spawn;
public int populationSize;
public int inputs;
public int[] layers;
public int outputs;
public float powerOfMutation;
public float genTime;
private Brakodel dubolom;
private List<GameObject> duboloms;
private List<GameObject> obolduys;
// Start is called before the first frame update
void Start()
{
// walls
for (int i = 0; i < populationSize; i++)
{
//Instantiate(Arena, new Vector3(i * 22, 0), Quaternion.identity);
}
duboloms = new List<GameObject>();
for (int i = 0; i < populationSize; i++)
{
duboloms.Add(Instantiate(referenceObj, new Vector3(0, 0), Quaternion.identity));
duboloms[i].name = "Drone " + i;
duboloms[i].GetComponent<Spider>().bungler = new Brakodel(inputs, outputs, layers);
duboloms[i].GetComponent<Spider>().bungler.RandomizeWeights();
}
Invoke("Generate", genTime);
}
void Generate()
{
//Brakodel AlphaBB = BubbleSort(obolduys);
Brakodel BetaBB = BubbleSort(duboloms);
// Debug.Log(BetaBB.axons[0][0][0]);
// KILL THEM ALL
for (int dr = 0; dr < populationSize; dr++)
{
Destroy(duboloms[dr]);
//Destroy(obolduys[dr]);
}
duboloms[0] = Instantiate(referenceObj, new Vector3(0, 0), Quaternion.identity);
duboloms[0].name = "Beta " + 0;
duboloms[0].GetComponent<Spider>().bungler = new Brakodel(inputs, outputs, layers);
duboloms[0].GetComponent<Spider>().bungler.CopyNetwork(BetaBB);
Debug.Log(duboloms[0].GetComponent<Spider>().bungler.axons[0][0][0]);
// Создание мужиков с мутациями
for (int i = 1; i < populationSize; i++)
{
duboloms[i] = Instantiate(referenceObj, new Vector3(0, 0), Quaternion.identity);
duboloms[i].name = "Beta " + i;
duboloms[i].GetComponent<Spider>().bungler = new Brakodel(inputs, outputs, layers);
duboloms[i].GetComponent<Spider>().bungler.CopyNetwork(BetaBB);
duboloms[i].GetComponent<Spider>().bungler.Mutate(powerOfMutation);
}
Invoke("Generate", genTime);
}
Brakodel BubbleSort(List<GameObject> Drones)
{
GameObject booferObj;
Brakodel bestBrakodel;
// Fitness calc
for (int i = 0; i < Drones.Count; i++)
{
//Debug.Log(Drones[i].GetComponent<Spider>().fitness);
}
float booferFit1;
float booferFit2;
for (int i = 0; i < Drones.Count; i++)
{
for (int j = 0; j < Drones.Count - 1; j++)
{
booferFit1 = Drones[j].GetComponent<Spider>().fitness;
booferFit2 = Drones[j + 1].GetComponent<Spider>().fitness;
if (booferFit1 < booferFit2)
{
booferObj = Drones[j + 1];
Drones[j + 1] = Drones[j];
Drones[j] = booferObj;
}
}
}
bestBrakodel = new Brakodel(inputs, outputs, layers);
bestBrakodel.CopyNetwork(Drones[0].GetComponent<Spider>().bungler);
Debug.Log("Best fit = " + Drones[0].GetComponent<Spider>().fitness);
//Debug.Log("Bestax = " + Drones[0].GetComponent<Spider>().bungler.axons[0][0][0]);
return bestBrakodel;
}
}
public class Spider : MonoBehaviour
{
BoxCollider2D[] colliders;
HingeJoint2D[] joints;
public Transform head;
Raycasting rc;
float[] sens;
float[] mot;
private ContactFilter2D filter;
public Brakodel bungler;
public Rigidbody2D eye;
public float fitness;
int zaebalo;
// Start is called before the first frame update
void Start()
{
colliders = GetComponentsInChildren<BoxCollider2D>();
joints = GetComponentsInChildren<HingeJoint2D>();
filter = new ContactFilter2D();
filter.SetLayerMask(LayerMask.GetMask("Default")) ;
rc = eye.gameObject.AddComponent<Raycasting>();
rc.Initializer(5, 120);
sens = new float[29];
mot = new float[5];
Debug.Log("Heuta" + bungler.axons[0][0][0]);
}
// Update is called once per frame
void FixedUpdate()
{
float[] dists = rc.CastRays(5f);
for (int i = 0; i < dists.Length; i++)
{
sens[i] = dists[i];
}
for (int i = 0; i < colliders.Length; i++)
{
sens[i + dists.Length] = IsOnGround(i);
}
sens[dists.Length + colliders.Length] = 1f;//eye.angularVelocity;
//sens[dists.Length + colliders.Length + 1]
mot = bungler.Podumat(sens);
//.Log(mot[0]);
for (int i = 0; i < joints.Length; i++)
{
JointMotor2D jm = new JointMotor2D();
jm.motorSpeed = mot[i] * 100;
jm.maxMotorTorque = 100f;
joints[i].motor = jm;
}
fitness = head.position.x;
//Debug.Log(zaebalo + " " + fitness);
zaebalo += 1;
}
float IsOnGround(int index)
{
if (colliders[index].IsTouchingLayers(0))
{
return (1);
}
else
{
return 0;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Assets.Brakodel
{
public class Brakodel
{
public List<List<float[]>> axons;
public List<float[]> neurons;
public float[] inputs;
public float[] outputs;
public Brakodel(int inps, int outs, int[] neurs)
{
// Neuro Initialize
neurons = new List<float[]>();
for (int i = 0; i < neurs.Length; i++)
{
neurons.Add(new float[neurs[i]]);
}
/*
* debug cycle for neurons
for (int layer = 0; layer < neurons.Count; layer++)
{
Debug.Log("neurons layer is " + layer);
for (int neuron = 0; neuron < neurons[layer].Length; neuron++)
{
Debug.Log("neurons count in this layer is " + neurons[layer][neuron]);
}
}
*/
// inputs
inputs = new float[inps];
//outputs
outputs = new float[outs];
axons = new List<List<float[]>>();
for (int layer = 0; layer < 1 + neurs.Length; layer++)
{
axons.Add(new List<float[]>());
}
// First axons layer
for (int neuron = 0; neuron < neurs[0]; neuron++)
{
axons[0].Add(new float[inps]);
}
// For hidden axon layers
for (int neuronLayer = 1; neuronLayer < neurs.Length; neuronLayer++)
{
for (int neuron = 0; neuron < neurs[neuronLayer]; neuron++)
{
axons[neuronLayer].Add(new float[neurs[neuronLayer - 1]]);
}
}
// For outputs axon;
for (int neuron = 0; neuron < outs; neuron++)
{
axons[axons.Count - 1].Add(new float[neurs[neurs.Length - 1]]);
}
/*
// Debug cycle
Debug.Log("Axon layers:" + axons.Count);
//Debug.Log()
for (int layer = 0; layer < axons.Count; layer++)
{
Debug.Log("kolvo puchkov na sloe " + layer + " ravno " + axons[layer].Count);
for (int puchok = 0; puchok < axons[layer].Count; puchok++)
{
Debug.Log("kolvo axonov v pucke = " + axons[layer][puchok].Length);
}
}
*/
}
public void RandomizeWeights()
{
//Debug.Log(axons.Count);
for (int layer = 0; layer < axons.Count; layer++)
{
//Debug.Log("Layer n " + layer);
for (int puchok = 0; puchok < axons[layer].Count; puchok++)
{
//Debug.Log("puchok n " + puchok);
for (int axon = 0; axon < axons[layer][puchok].Length; axon++)
{
axons[layer][puchok][axon] = Random.Range(-1f, 1f);
//Debug.Log(axons[layer][puchok][axon]);
}
}
}
}
// Используй ЭТО, чтобы подумать. (Много думать вредно)
public float[] Podumat(float[] sensorsInfo)
{
// Активация входных данных
for(int neuron = 0; neuron < sensorsInfo.Length; neuron++)
{
inputs[neuron] = Tg(sensorsInfo[neuron]);
}
// First HL
for(int neuron = 0; neuron < neurons[0].Length; neuron++)
{
neurons[0][neuron] = 0;
for (int axon = 0; axon < axons[0][neuron].Length; axon++)
{
neurons[0][neuron] += axons[0][neuron][axon] * inputs[axon];
}
neurons[0][neuron] = Tg(neurons[0][neuron]);
}
// Other HL
for (int layer = 1; layer < neurons.Count; layer++)
{
for (int neuron = 0; neuron < neurons[layer].Length; neuron++)
{
neurons[layer][neuron] = 0;
for (int axon = 0; axon < axons[layer][neuron].Length; axon++)
{
neurons[layer][neuron] += axons[layer][neuron][axon] * neurons[layer-1][axon];
}
neurons[layer][neuron] = Tg(neurons[layer][neuron]);
}
}
// Outputs
for (int neuron = 0; neuron < outputs.Length; neuron++)
{
outputs[neuron] = 0;
for (int axon = 0; axon < axons[axons.Count - 1][neuron].Length; axon++)
{
outputs[neuron] += axons[axons.Count - 1][neuron][axon] * neurons[neurons.Count - 1][axon];
}
outputs[neuron] = Tg(outputs[neuron]);
}
return outputs;
}
public static float Tg(float value)
{
//return (Mathf.Exp(value) - Mathf.Exp(-value)) / (Mathf.Exp(value) + Mathf.Exp(-value));
//return (1 / (1 + Mathf.Exp(-value)));
//return value / (1 + Mathf.Abs(value));
return 2 / (1 + Mathf.Exp(-2 * value)) - 1; //TG
/*
if (value < -1f)
{
return (-1f);
}
else if (value > 1f)
{
return (1f);
}
else
{
return (value);
}
*/
}
public void Mutate(float power)
{
for (int layer = 0; layer < axons.Count; layer++)
{
for (int puchok = 0; puchok < axons[layer].Count; puchok++)
{
for(int axon = 0; axon < axons[layer][puchok].Length; axon++)
{
axons[layer][puchok][axon] = axons[layer][puchok][axon] + Random.Range(-1f, 1f) * power;
}
}
}
}
public void ChunkMutation(float power, float blockChance, float puchokChance)
{
for (int layer = 0; layer < axons.Count; layer++)
{
if (blockChance > Random.Range(0, 100))
{
for (int puchok = 0; puchok < axons[layer].Count; puchok++)
{
if (puchokChance > Random.Range(0, 100))
{
for (int axon = 0; axon < axons[layer][puchok].Length; axon++)
{
axons[layer][puchok][axon] = axons[layer][puchok][axon] + Random.Range(-1f, 1f) * power;
}
}
}
}
}
}
public void CopyNetwork(Brakodel obolduy)
{
for (int layer = 0; layer < obolduy.axons.Count; layer++)
{
for (int puchok = 0; puchok < obolduy.axons[layer].Count; puchok++)
{
for (int axon = 0; axon < obolduy.axons[layer][puchok].Length; axon++)
{
//Debug.Log("before"+axons[layer][puchok][axon]);
this.axons[layer][puchok][axon] = obolduy.axons[layer][puchok][axon];
//Debug.Log("after" + axons[layer][puchok][axon]+"index:"+layer+"."+puchok+"."+axon);
}
}
}
}
public float Lst()
{
return axons[0][0][0];
}
}
}