Last active
December 28, 2016 17:14
-
-
Save llSourcell/af8a316b9591ac8efbc0ac17c92e579e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //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