Skip to content

Instantly share code, notes, and snippets.

@llSourcell
Last active December 28, 2016 17:14
Show Gist options
  • Select an option

  • Save llSourcell/af8a316b9591ac8efbc0ac17c92e579e to your computer and use it in GitHub Desktop.

Select an option

Save llSourcell/af8a316b9591ac8efbc0ac17c92e579e to your computer and use it in GitHub Desktop.
//Intro
//Neuroevolution
//way of learning.
//While most neural learning methods focus on modifying
//only the strengths of neural connections (i.e. their connection weights) like
//backprop
//neuroevolution can additionally optimize other parameters,
//such as the structure of the network (e.g. adding neurons or connections),
//the type of computation performed by individual neurons, and even learning
//rules
//that modify the network during evaluation.
//type of reinforcement learning
//doesn't need labeled data
//the optimal actions at each point in time are not
//always known; it is only possible to observe how well a sequence of
//actions worked, e.g. resulting in a win or loss in the game.
//Neuroevolution makes it possible to find a neural network that optimizes
//behavior given only such sparse feedback, without direct information about
//what exactly it should be doing.
//create neural network
//
//Steps
//create neuroevoltuion
//neuron
//layer
//network
//genome
//generations
var Neuroevolution = function(options){
var self = this;
self.options = {
//sigmoid
activation:function(a){
ap = (-a)/1;
return (1/(1 + Math.exp(ap)))
},
//random value, we'll use to generate our weights later
randomClamped:function(){
return Math.random() * 2 - 1;
},
population:50, // Population by generation
elitism:0.2, // Best networks kepts unchanged for the next generation (rate)
randomBehaviour:0.2, // New random networks for the next generation (rate)
mutationRate:0.1, // Mutation rate on the weights of synapses
mutationRange:0.5, // Interval of the mutation changes on the synapse weight
network:[1, [1], 1], // Perceptron structure
historic:0, // Latest generations saved
lowHistoric:false, // Only save score (not the network)
scoreSort:-1, // Sort order (-1 = desc, 1 = asc)
nbChild:1 // number of child by breeding
}
//initialize set variables to have options available
self.set = function(options){
for(var i in options){
if(this.options[i] != undefined){
self.options[i] = options[i];
}
}
}
self.set(options);
//NEURON
//has an internal value and a set of connections to every
//other neuron in the next layer
var Neuron = function(){
this.value = 0;
this.weights = [];
}
//randomly initialize weight values. we want them to be distinct.
//faster learning.
Neuron.prototype.populate = function(nb){
this.weights = [];
for(var i = 0; i < nb; i++){
this.weights.push(self.options.randomClamped());
}
}
//LAYER
//has an ID and a number of neurons
var Layer = function(index){
this.id = index || 0;
this.neurons = [];
}
//population it with neurons and add the inputs to the neurons
Layer.prototype.populate = function(nbNeurons, nbInputs){
this.neurons = [];
for(var i = 0; i < nbNeurons; i++){
var n = new Neuron();
n.populate(nbInputs);
this.neurons.push(n);
}
}
//NETWORK
//network contains layers
var Network = function(){
this.layers = [];
}
//give it layer perameters
Network.prototype.perceptronGeneration = function(input, hiddens, output){
var index = 0;
var previousNeurons = 0;
var layer = new Layer(index);
layer.populate(input, previousNeurons);
previousNeurons = input;
this.layers.push(layer);
index++;
for(var i in hiddens){
var layer = new Layer(index);
layer.populate(hiddens[i], previousNeurons);
previousNeurons = hiddens[i];
this.layers.push(layer);
index++;
}
var layer = new Layer(index);
layer.populate(output, previousNeurons);
this.layers.push(layer);
}
//get data - saved weights
Network.prototype.getSave = function(){
var datas = {
neurons:[],
weights:[]
};
for(var i in this.layers){
datas.neurons.push(this.layers[i].neurons.length);
for(var j in this.layers[i].neurons){
for(var k in this.layers[i].neurons[j].weights){
datas.weights.push(this.layers[i].neurons[j].weights[k]);
}
}
}
return datas;
}
//save weights, where save is a layer
Network.prototype.setSave = function(save){
var previousNeurons = 0;
var index = 0;
var indexWeights = 0;
this.layers = [];
for(var i in save.neurons){
var layer = new Layer(index);
layer.populate(save.neurons[i], previousNeurons);
for(var j in layer.neurons){
for(var k in layer.neurons[j].weights){
layer.neurons[j].weights[k] = save.weights[indexWeights];
indexWeights++;
}
}
previousNeurons = save.neurons[i];
index++;
this.layers.push(layer);
}
}
//if layer and neurons in layer are not empty, assign inpute value
//then flow the data through the layers
//apply activation function
//return output
Network.prototype.compute = function(inputs){
for(var i in inputs){
if(this.layers[0] && this.layers[0].neurons[i]){
this.layers[0].neurons[i].value = inputs[i];
}
}
var prevLayer = this.layers[0];
for(var i = 1; i < this.layers.length; i++){
for(var j in this.layers[i].neurons){
var sum = 0;
for(var k in prevLayer.neurons){
sum += prevLayer.neurons[k].value *
this.layers[i].neurons[j].weights[k];
}
this.layers[i].neurons[j].value =
self.options.activation(sum);
}
prevLayer = this.layers[i];
}
var out = [];
var lastLayer = this.layers[this.layers.length - 1];
for(var i in lastLayer.neurons){
out.push(lastLayer.neurons[i].value);
}
return out;
}
//GENOM
//an AI. has a neural network and a score
var Genome = function(score, network){
this.score = score || 0;
this.network = network || null;
}
//GENERATION
//contains several genomes
var Generation = function(){
this.genomes = [];
}
//add a genome to our generation
//depending on which way it's sort
//don't add the genome if it's sortable
Generation.prototype.addGenome = function(genome){
for(var i = 0; i < this.genomes.length; i++){
if(self.options.scoreSort < 0){
if(genome.score > this.genomes[i].score){
break;
}
}else{
if(genome.score < this.genomes[i].score){
break;
}
}
}
//add the genome
this.genomes.splice(i, 0, genome);
}
//take two genomes
//decide number of children
//give some weights to g1
Generation.prototype.breed = function(g1, g2, nbChilds){
var datas = [];
for(var nb = 0; nb < nbChilds; nb++){
var data = JSON.parse(JSON.stringify(g1));
for(var i in g2.network.weights){
if(Math.random() <= 0.5){
data.network.weights[i] = g2.network.weights[i];
}
}
//add some mutation to each each weight
for(var i in data.network.weights){
if(Math.random() <= self.options.mutationRate){
data.network.weights[i] += Math.random() *
self.options.mutationRange * 2 - self.options.mutationRange;
}
}
datas.push(data);
}
//return list of breeded genomes
return datas;
}
//
Generation.prototype.generateNextGeneration = function(){
//store new genomes
var nexts = [];
//make only 20% of the genomes as we currenly have
for(var i = 0; i < Math.round(self.options.elitism * self.options.population); i++){
if(nexts.length < self.options.population){
nexts.push(JSON.parse(JSON.stringify(this.genomes[i].network)));
}
}
//generate random weights and assign them for each genome
for(var i = 0; i < Math.round(self.options.randomBehaviour * self.options.population); i++){
var n = JSON.parse(JSON.stringify(this.genomes[0].network));
for(var k in n.weights){
n.weights[k] = self.options.randomClamped();
}
if(nexts.length < self.options.population){
nexts.push(n);
}
}
}
//GENERATIONS
//store multiple generations
var Generations = function(){
this.generations = [];
var currentGeneration = new Generation();
}
//ADD ONe first generation
Generations.prototype.firstGeneration = function(input, hiddens, output){
var out = [];
for(var i = 0; i < self.options.population; i++){
var nn = new Network();
nn.perceptronGeneration(self.options.network[0],
self.options.network[1], self.options.network[2]);
out.push(nn.getSave());
}
this.generations.push(new Generation());
return out;
}
//generate the next one
Generations.prototype.nextGeneration = function(){
if(this.generations.length == 0){
return false;
}
var gen = this.generations[this.generations.length - 1].generateNextGeneration();
this.generations.push(new Generation());
return gen;
}
//add a genome to the latest genereation
Generations.prototype.addGenome = function(genome){
if(this.generations.length == 0){
return false;
}
return this.generations[this.generations.length - 1].addGenome(genome);
}
self.networkScore = function(network, score){
self.generations.addGenome(new Genome(score, network.getSave()));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment