Skip to content

Instantly share code, notes, and snippets.

@HaddadBenjamin
Last active May 12, 2016 16:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HaddadBenjamin/53f0f1e52d75c30d3335b105fca9c234 to your computer and use it in GitHub Desktop.
Save HaddadBenjamin/53f0f1e52d75c30d3335b105fca9c234 to your computer and use it in GitHub Desktop.
Réseau neuronal avec sauvegarde et chargement et choix du type d'IA : simple perceptron ou réseau neuronal avec back propagation.
using UnityEngine;
[System.Serializable]
public class MyPerceptron
{
#region Internal Fields
/// <summary>
/// Facteur d'importance de chacunes des entrées.
/// </summary>
[SerializeField, HideInInspector]
private float[] weights;
/// <summary>
/// Vitesse d'apprentissage de notre neurone.
/// </summary>
[SerializeField]
private float learningSpeed = 0.001f;
/// <summary>
/// Valeur d'initialization aléatoire du facteur d'importance de chacunes des entrées.
/// </summary>
[SerializeField]
private float weightInitializationRange;
private float guessAnswerOutput;
#endregion
#region Properties
public float GuessAnswerOutput
{
get { return guessAnswerOutput; }
private set { guessAnswerOutput = value; }
}
public float[] Weights
{
get { return weights; }
private set { weights = value; }
}
public float LearningSpeed
{
get { return learningSpeed; }
private set { learningSpeed = value; }
}
public float WeightInitializationRange
{
get { return weightInitializationRange; }
private set { weightInitializationRange = value; }
}
#endregion
#region Constructor
public MyPerceptron(int numberOfInputs)
{
this.weights = new float[numberOfInputs];
this.InitializeWeights();
}
public MyPerceptron() { }
#endregion
#region Internal Behaviour
/// <summary>
/// Renvoit la somme de chaque entrée multiplié par son facteur d'importance (son poids).
/// </summary>
/// <param name="inputs"></param>
/// <returns></returns>
private float FeedForward(float[] inputs)
{
float sumOfInputsWeighted = 0;
for (int weightIndex = 0; weightIndex < this.weights.Length; weightIndex++)
sumOfInputsWeighted += inputs[weightIndex] * this.weights[weightIndex];
return sumOfInputsWeighted;
//return Activation(sumOfInputsWeighted);
}
/// <summary>
/// Initialize la valeur d'importance de chacune des entrées avec une valeur comprise entre 2 nombres.
/// </summary>
private void InitializeWeights()
{
for (int weightIndex = 0; weightIndex < this.weights.Length; weightIndex++)
this.weights[weightIndex] = MathHelper.GenerateRandomBeetweenTwoFloats(-this.weightInitializationRange, this.weightInitializationRange);
}
#endregion
#region Behaviour Method
/// <summary>
/// Entraine la valeur d'importance de chacunes des entrées en fonction de leur marge d'erreur et un facteur de rapidité d'apprentissage.
/// </summary>
/// <param name="inputs"></param>
/// <param name="desiredOutput"></param>
public void Train(float[] inputs, float desiredOutput)
{
this.guessAnswerOutput = this.FeedForward(inputs);
float errorDifference = desiredOutput - this.guessAnswerOutput;
for (int weightIndex = 0; weightIndex < this.weights.Length; weightIndex++)
this.weights[weightIndex] += this.learningSpeed * errorDifference;
}
public void ProcessAnswerWithoutTraining(float[] inputs)
{
this.guessAnswerOutput = this.FeedForward(inputs);
}
#endregion
}
using UnityEngine;
public class NeuralConnection
{
#region Internal Fields
private Neuron connectedNeuron;
/// <summary>
/// Valeur d'importance du neuronne.
/// </summary>
private float weight;
#endregion
#region Properties
public Neuron ConnectedNeuron
{
get { return connectedNeuron; }
private set { connectedNeuron = value; }
}
public float Weight
{
get { return weight; }
set { weight = value; }
}
#endregion
#region Constructor
public NeuralConnection() { }
public NeuralConnection(Neuron connectedNeuron)
{
float weightInitialziationRange = ServiceContainer.Instance.NeuralNetwork.NeuronsWeightInitializationRange;
this.connectedNeuron = connectedNeuron;
this.weight = Random.Range(-weightInitialziationRange, weightInitialziationRange);
}
#endregion
}
using System;
using UnityEngine;
[System.Serializable]
public class NeuralNetwork : AServiceComponent
{
#region Internal Fields
#region Modifiable Values
/// <summary>
/// Vitesse d'apprentissage de notre neurone.
/// </summary>
[SerializeField]
private float learningSpeed = 1.0f;
/// <summary>
/// Détermine si l'algorithme est entrain d'apprendre ou non.
/// </summary>
[SerializeField]
private bool isInTrainingMode;
[SerializeField]
private bool ifTrueUseNeuralNetworkElseUseASimplePerceptron = true;
/// <summary>
/// Valeur d'initialization aléatoire du facteur d'importance de chaque neuronnes.
/// </summary>
[SerializeField]
private float neuronsWeightInitializationRange;
[SerializeField]
private int numberOfIterationPerTrain;
/// <summary>
/// Nombre d'entrées de la couche d'entrée.
/// </summary>
[SerializeField]
private int numberOfInputsNeurons = 1;
/// <summary>
/// Nombre d'entrées de la couche cachée.
/// </summary>
[SerializeField]
private int numberOfHiddensNeurons = 10;
/// <summary>
/// Nombre d'entrées de la couche de sortie.
/// </summary>
[SerializeField]
private int numberOfOutputsNeurons = 1;
#endregion
#region Non Modifiable Values
/// <summary>
/// Les valeurs de la couche d'entrée, ici il ne s'agit pas de neuronnes car on ne s'intéresse seulement à la valeur d'entrée.
/// </summary>
private Neuron[] inputsNeuronsLayer;
/// <summary>
/// Les neuronnes de la couche cachée.
/// </summary>
private Neuron[] hiddensNeuronsLayer;
/// <summary>
/// Les neuronnes de la couche de sortie.
/// </summary>
private Neuron[] outputsNeuronsLayer;
/// <summary>
/// Les réponses attendues par notre système de réseau neuronal.
/// </summary>
private float[] expectedOutputs;
#region Useful Only For Deserialization & Deserialization
// Ces variables sont juste présente pour gérer leur sauvegarde et leur chargement.
[SerializeField, HideInInspector]
private float[] inputsNeuronsWeights;
[SerializeField, HideInInspector]
private float[] hiddensNeuronsWeights;
[SerializeField, HideInInspector]
private float[] outputsNeuronsWeights;
#endregion
#endregion
#endregion
#region Properties
public bool IsInTrainingMode
{
get { return isInTrainingMode; }
private set { isInTrainingMode = value; }
}
public float LearningSpeed
{
get { return learningSpeed; }
private set { learningSpeed = value; }
}
public float NeuronsWeightInitializationRange
{
get { return neuronsWeightInitializationRange; }
private set { neuronsWeightInitializationRange = value; }
}
public int NumberOfIterationPerTrain
{
get { return numberOfIterationPerTrain; }
private set { numberOfIterationPerTrain = value; }
}
public bool IfTrueUseNeuralNetworkElseUseASimplePerceptron
{
get { return ifTrueUseNeuralNetworkElseUseASimplePerceptron; }
private set { ifTrueUseNeuralNetworkElseUseASimplePerceptron = value; }
}
public Neuron[] InputsNeuronsLayer
{
get { return inputsNeuronsLayer; }
private set { inputsNeuronsLayer = value; }
}
public Neuron[] HiddensNeuronsLayer
{
get { return hiddensNeuronsLayer; }
private set { hiddensNeuronsLayer = value; }
}
public Neuron[] OutputsNeuronsLayer
{
get { return outputsNeuronsLayer; }
private set { outputsNeuronsLayer = value; }
}
public float[] ExpectedOutputs
{
get { return expectedOutputs; }
private set { expectedOutputs = value; }
}
#region Useful Only For Serialization & Deserialization
public float[] HiddensNeuronsWeights
{
get { return hiddensNeuronsWeights; }
private set { hiddensNeuronsWeights = value; }
}
public float[] InputsNeuronsWeights
{
get { return inputsNeuronsWeights; }
private set { inputsNeuronsWeights = value; }
}
public float[] OutputsNeuronsWeights
{
get { return outputsNeuronsWeights; }
private set { outputsNeuronsWeights = value; }
}
#endregion
#endregion
#region Constructor
public NeuralNetwork() { }
#endregion
#region Override Methods
/// <summary>
/// Créer les couches de neuronnes et les connectent entre-elles.
/// </summary>
public override void InitializeByserviceContainer()
{
this.InitializeNeuralNetwork();
ServiceContainer.Instance.EventManagerParamsBool.SubcribeToEvent(EEventParamsBool.DoesTrainingModeIsEnableHaveBeenModified, this.SetTrainingMode);
ServiceContainer.Instance.EventManagerParamsBool.SubcribeToEvent(EEventParamsBool.NeuralNetworkEnableHaveBeenModified, this.SetNeuralNetworkOrSimplePerceptron);
}
#endregion
#region Behaviour Methods
/// <summary>
/// Entraine notre système de réseau neuronal si il est en mode d'entraînement puis génère les sorties;
/// </summary>
/// <param name="inputs"></param>
/// <param name="outputs"></param>
public void ProcessNeuralNetwork(float[] inputs = null, float[] outputs = null)
{
for (int iterationIndex = 0; iterationIndex < this.numberOfIterationPerTrain; iterationIndex++)
{
this.GenerateOutputs(inputs);
if (this.isInTrainingMode)
this.BackpropagationTraining(outputs == null ? this.expectedOutputs : outputs);
}
}
/// <summary>
/// Entraine notre système de réseau neuronal si il est en mode d'entraînement puis génère les sorties et les renvoies.
/// </summary>
/// <param name="inputs"></param>
/// <param name="outputs"></param>
public float[] ProcessNeuralNetworkAndGetOuputs(float[] inputs = null, float[] outputs = null)
{
this.ProcessNeuralNetwork(inputs, outputs);
return this.GetOutputs();
}
/// <summary>
/// Permet de définir les entrées de la couche d'entrées et de générer les sorties de chaque neurones de la couche cachée et de sortie.
/// </summary>
/// <param name="inputs"></param>
public void GenerateOutputs(float[] inputs = null)
{
if (null != inputs) // inputs ne vaut rien dans le cas où l'on souhaite définir chaque input individuellement avec la méthode SetInput(index)
this.SetInputs(inputs); // Défini les réponses de chaque neurones de la couche d'entrée.
// Génére les sorties de la couche cachée.
for (int hiddenNeuronLayerIndex = 0; hiddenNeuronLayerIndex < this.hiddensNeuronsLayer.Length; hiddenNeuronLayerIndex++)
this.hiddensNeuronsLayer[hiddenNeuronLayerIndex].FeedForward();
// Génére les sorties de la couche de sortie.
for (int outputNeuronLayerIndex = 0; outputNeuronLayerIndex < this.outputsNeuronsLayer.Length; outputNeuronLayerIndex++)
this.outputsNeuronsLayer[outputNeuronLayerIndex].FeedForward();
}
/// <summary>
/// Récupère la réponse du neurone de la couche de sortie correspond à l'index enumerationIndex.
/// </summary>
/// <typeparam name="EnumerationType"></typeparam>
/// <param name="enumerationIndex"></param>
/// <returns></returns>
public float GetOutput<EnumerationType>(EnumerationType enumerationIndex) where EnumerationType : struct, IConvertible
{
return this.outputsNeuronsLayer[EnumHelper.GetIndex<EnumerationType>(enumerationIndex)].GuessAnswerOutput;
}
/// <summary>
/// Récupère les réponses du neurone de la couche de sortie.
/// </summary>
/// <returns></returns>
public float[] GetOutputs()
{
return Array.ConvertAll(this.outputsNeuronsLayer, outputNeuron => outputNeuron.GuessAnswerOutput);
}
/// <summary>
/// Défini la réponse du neuronne de la couche d'entrée correspondant à l'index enumerationIndex.
/// </summary>
/// <typeparam name="EnumerationType"></typeparam>
/// <param name="enumerationIndex"></param>
/// <param name="inputValue"></param>
public void SetInput<EnumerationType>(EnumerationType enumerationIndex, float inputValue) where EnumerationType : struct, IConvertible
{
this.inputsNeuronsLayer[EnumHelper.GetIndex<EnumerationType>(enumerationIndex)].GuessAnswerOutput = inputValue;
}
/// <summary>
/// Défini les réponses de chaque neurones de la couche d'entrée.
/// </summary>
/// <param name="inputs"></param>
public void SetInputs(float[] inputs)
{
for (int inputNeuronIndex = 0; inputNeuronIndex < this.inputsNeuronsLayer.Length; inputNeuronIndex++)
this.inputsNeuronsLayer[inputNeuronIndex].GuessAnswerOutput = inputs[inputNeuronIndex];
}
/// <summary>
/// Permet de définir une des valeurs attendu par notre système de réseau neuronal à l'index enumerationIndex, la valeur attendu doit être comprise entre 0 et 1.
/// </summary>
/// <typeparam name="EnumerationType"></typeparam>
/// <param name="enumerationIndex"></param>
/// <param name="excpectedValue"></param>
public void SetNormalizedExpectedOutput<EnumerationType>(EnumerationType enumerationIndex, float excpectedValue) where EnumerationType : struct, IConvertible
{
this.expectedOutputs[EnumHelper.GetIndex<EnumerationType>(enumerationIndex)] = excpectedValue;
}
/// <summary>
/// Permet de définir toute les valeurs attendu par notre système de réseau neuronal.
/// </summary>
/// <param name="expectedOutputs"></param>
public void SetOutputs(float[] expectedOutputs)
{
this.expectedOutputs = expectedOutputs;
}
#endregion
#region Internal Behaviour
public void InitializeNeuralNetwork()
{
this.inputsNeuronsLayer = new Neuron[this.numberOfInputsNeurons];
this.hiddensNeuronsLayer = new Neuron[this.numberOfHiddensNeurons];
this.outputsNeuronsLayer = new Neuron[this.numberOfOutputsNeurons];
this.expectedOutputs = new float[this.numberOfOutputsNeurons];
// Créer la couche d'entrée.
for (int inputNeuronIndex = 0; inputNeuronIndex < this.inputsNeuronsLayer.Length; inputNeuronIndex++)
this.inputsNeuronsLayer[inputNeuronIndex] = new Neuron();
// Créer la couche cachées et lie chacun des neuronnes quelle comporte à chacune des entrées.
for (int hiddenNeuronLayerIndex = 0; hiddenNeuronLayerIndex < this.hiddensNeuronsLayer.Length; hiddenNeuronLayerIndex++)
this.hiddensNeuronsLayer[hiddenNeuronLayerIndex] = new Neuron(this.inputsNeuronsLayer);
// Créer la couche de sortie et lie chacun des neuronnes quelle comporte à chaque neuronne de la couche cachée.
for (int outputNeuronLayerIndex = 0; outputNeuronLayerIndex < this.outputsNeuronsLayer.Length; outputNeuronLayerIndex++)
this.outputsNeuronsLayer[outputNeuronLayerIndex] = new Neuron(this.hiddensNeuronsLayer);
}
/// <summary>
/// Permet d'entrainer notre système de réseau neuronal par un algorithme de back propagation.
/// </summary>
/// <param name="outputs"></param>
private void BackpropagationTraining(float[] outputs)
{
// Entrainement des neurones de la couche de sortie.
for (int outputNeuronLayerIndex = 0; outputNeuronLayerIndex < this.outputsNeuronsLayer.Length; outputNeuronLayerIndex++)
this.outputsNeuronsLayer[outputNeuronLayerIndex].TrainOutputNeuron(outputs[outputNeuronLayerIndex]);
// Entrainement de la couche cachée.
for (int hiddenNeuronLayerIndex = 0; hiddenNeuronLayerIndex < this.hiddensNeuronsLayer.Length; hiddenNeuronLayerIndex++)
{
Neuron hiddenNeuron = this.hiddensNeuronsLayer[hiddenNeuronLayerIndex];
float hiddenNeuronGuessAnswerOutput = hiddenNeuron.GuessAnswerOutput;
float sum = 0;
for (int outputNeuronLayerIndex = 0; outputNeuronLayerIndex < this.outputsNeuronsLayer.Length; outputNeuronLayerIndex++)
{
Neuron outputNeuron = this.outputsNeuronsLayer[outputNeuronLayerIndex];
sum += outputNeuron.RetrieveNeuralConnectionWeightIfPossible(hiddenNeuron) * outputNeuron.ErrorDifferenceBetweenExpectedAnswerAndCurrentAnswer;
}
float error = hiddenNeuronGuessAnswerOutput * (1 - hiddenNeuronGuessAnswerOutput) * sum;
hiddenNeuron.Train(error);
}
}
#endregion
#region Event Manager Callbacks
private void SetNeuralNetworkOrSimplePerceptron(bool isNeuralNetwork)
{
this.ifTrueUseNeuralNetworkElseUseASimplePerceptron = isNeuralNetwork;
}
private void SetTrainingMode(bool trainingMode)
{
this.isInTrainingMode = trainingMode;
}
#endregion
#region Useful Methods Only For Serialization & Deserialization
public void PrepareTheSerializationOfNeuralNetwork()
{
this.hiddensNeuronsWeights = Array.ConvertAll(this.HiddensNeuronsLayer[0].NeuralConnections, neuralConnection => neuralConnection.Weight);
this.outputsNeuronsWeights = Array.ConvertAll(this.OutputsNeuronsLayer[0].NeuralConnections, neuralConnection => neuralConnection.Weight);
}
public void DeserializeNeuralNetwork()
{
this.SetHiddensNeuralConnectionsWeights();
this.SetOutputsNeuralConnectionsWeights();
}
public void SetHiddensNeuralConnectionsWeights()
{
for (int hiddenNeuronIndex = 0; hiddenNeuronIndex < this.hiddensNeuronsLayer.Length; hiddenNeuronIndex++)
{
for (int hiddenNeuralConnexionIndex = 0; hiddenNeuralConnexionIndex < this.hiddensNeuronsLayer[hiddenNeuronIndex].NeuralConnections.Length; hiddenNeuralConnexionIndex++)
this.hiddensNeuronsLayer[hiddenNeuronIndex].NeuralConnections[hiddenNeuralConnexionIndex].Weight = this.hiddensNeuronsWeights[hiddenNeuronIndex];
}
}
public void SetOutputsNeuralConnectionsWeights()
{
for (int outputNeuronIndex = 0; outputNeuronIndex < this.outputsNeuronsLayer.Length; outputNeuronIndex++)
{
for (int outputNeuralConnexionIndex = 0; outputNeuralConnexionIndex < this.outputsNeuronsLayer[outputNeuronIndex].NeuralConnections.Length; outputNeuralConnexionIndex++)
this.outputsNeuronsLayer[outputNeuronIndex].NeuralConnections[outputNeuralConnexionIndex].Weight = this.outputsNeuronsWeights[outputNeuronIndex];
}
}
#endregion
}
using UnityEngine;
using System;
public class Neuron
{
#region Internal Fields
/// <summary>
/// Les connections à chaque neuronnes.
/// </summary>
private NeuralConnection[] neuralConnections;
/// <summary>
/// Représente la réponse de notre neuronne.
/// </summary>
private float guessAnswerOutput = 0f;
/// <summary>
/// La différence entre la réponse attendu et la réponse de notre neuronne.
/// </summary>
private float errorDifferenceBetweenExpectedAnswerAndCurrentAnswer;
#endregion
#region Properties
public float GuessAnswerOutput
{
get { return guessAnswerOutput; }
set { guessAnswerOutput = value; }
}
public float ErrorDifferenceBetweenExpectedAnswerAndCurrentAnswer
{
get { return errorDifferenceBetweenExpectedAnswerAndCurrentAnswer; }
private set { errorDifferenceBetweenExpectedAnswerAndCurrentAnswer = value; }
}
public NeuralConnection[] NeuralConnections
{
get { return neuralConnections; }
private set { neuralConnections = value; }
}
#endregion
#region Constructor
/// <summary>
/// Permet de lier un neuronne à une couche de neuronnes.
/// </summary>
/// <param name="neuronLayerToLink"></param>
public Neuron(Neuron[] neuralLayer)
{
this.neuralConnections = new NeuralConnection[neuralLayer.Length];
for (int neuralConnectionIndex = 0; neuralConnectionIndex < this.neuralConnections.Length; neuralConnectionIndex++)
this.neuralConnections[neuralConnectionIndex] = new NeuralConnection(neuralLayer[neuralConnectionIndex]);
}
public Neuron() { }
#endregion
#region Behaviour Methods
/// <summary>
/// Renvoie la somme de chaque sortie de neurone multiplié par leur propre poids.
/// </summary>
/// <returns></returns>
public void FeedForward()
{
float sumOfNeuralConnectionsWeighted = 0.0f;
foreach (NeuralConnection neuralConnection in this.neuralConnections)
sumOfNeuralConnectionsWeighted += neuralConnection.ConnectedNeuron.guessAnswerOutput * neuralConnection.Weight;
this.guessAnswerOutput = this.Sigmoid(sumOfNeuralConnectionsWeighted);
//return Activation(sumOfInputsWeighted);
}
/// <summary>
/// Entraine la valeur d'importance de chacunes des entrées en fonction de leur marge d'erreur et un facteur de rapidité d'apprentissage.
/// </summary>
/// <param name="errorGradient"></param>
public void Train(float errorGradient)
{
float learningSpeed = ServiceContainer.Instance.NeuralNetwork.LearningSpeed;
for (int neuralConnectionIndex = 0; neuralConnectionIndex < this.neuralConnections.Length; neuralConnectionIndex++)
this.neuralConnections[neuralConnectionIndex].Weight += learningSpeed * errorGradient * this.neuralConnections[neuralConnectionIndex].ConnectedNeuron.guessAnswerOutput;
this.errorDifferenceBetweenExpectedAnswerAndCurrentAnswer = errorGradient;
}
// retrieve weight from an input Perceptron
public float RetrieveNeuralConnectionWeightIfPossible(Neuron neuralConnectionWanted)
{
NeuralConnection neuralConnectionFound = Array.Find(this.neuralConnections, neuralConnection => neuralConnection.ConnectedNeuron == neuralConnectionWanted);
return null == neuralConnectionWanted ? 0 : neuralConnectionFound.Weight;
}
public void TrainOutputNeuron(float expectedOutput)
{
float errorGradiant = this.guessAnswerOutput * (1 - this.guessAnswerOutput) * (expectedOutput - this.guessAnswerOutput);
this.Train(errorGradiant);
}
#endregion
#region Internal Behaviour
/// <summary>
/// Sigmoid threshold function that modifies the input to an output, RIEN COMPRIS ICI.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private float Sigmoid(float sumOfInputsWeighted)
{
return 1.0f / (1.0f + Mathf.Exp(-1.0f * sumOfInputsWeighted));
}
private float SigmoiBipolar(float sumOfInputsWeighted)
{
return -1.0f + 2.0f / Mathf.Exp(-1.0f * sumOfInputsWeighted);
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment