NEAT

Edit on Github


The built-in NEAT class allows you create evolutionary algorithms with just a few lines of code. If you want to evolve neural networks to conform a given dataset, check out this page. The following code is from the Agario-AI built with Neataptic.

/** Construct the genetic algorithm */
function initNeat(){
  neat = new Neat(
    1 + PLAYER_DETECTION * 3 + FOOD_DETECTION * 2,
    2,
    null,
    {
      mutation: methods.mutation.ALL
      popsize: PLAYER_AMOUNT,
      mutationRate: MUTATION_RATE,
      elitism: Math.round(ELITISM_PERCENT * PLAYER_AMOUNT),
      network: new architect.Random(
        1 + PLAYER_DETECTION * 3 + FOOD_DETECTION * 2,
        START_HIDDEN_SIZE,
        2
      )
    }
  );

  if(USE_TRAINED_POP) neat.population = population;
}

/** Start the evaluation of the current generation */
function startEvaluation(){
  players = [];
  highestScore = 0;

  for(var genome in neat.population){
    genome = neat.population[genome];
    new Player(genome);
  }
}

/** End the evaluation of the current generation */
function endEvaluation(){
  console.log('Generation:', neat.generation, '- average score:', neat.getAverage());

  neat.sort();
  var newPopulation = [];

  // Elitism
  for(var i = 0; i < neat.elitism; i++){
    newPopulation.push(neat.population[i]);
  }

  // Breed the next individuals
  for(var i = 0; i < neat.popsize - neat.elitism; i++){
    newPopulation.push(neat.getOffspring());
  }

  // Replace the old population with the new population
  neat.population = newPopulation;
  neat.mutate();

  neat.generation++;
  startEvaluation();
}

You might also want to check out the target-seeking project built with Neataptic.

Options

The constructor comes with various options. The constructor works as follows:

new Neat(input, output, fitnessFunction, options); // options should be an object

Every generation, each genome will be tested on the fitnessFunction. The fitness function should return a score (a number). Through evolution, the genomes will try to maximize the output of the fitness function.

Negative scores are allowed.

You can provide the following options in an object for the options argument:

popsize Sets the population size of each generation. Default is 50.

elitism Sets the elitism of every evolution loop. Default is 0.

provenance Sets the provenance of the genetic algorithm. Provenance means that during every evolution, the given amount of genomes will be inserted which all have the original network template (which is Network(input,output) when no network option is given). Default is 0.

mutation Sets the allowed mutation methods used in the evolutionary process. Must be an array (e.g. [methods.mutation.ADD_NODE, methods.mutation.SUB_NODE]). Default mutation methods are all non-recurrent mutation methods. A random mutation method will be chosen from the array when mutation occrus.

selection Sets the allowed selection method used in the evolutionary process. Must be a single method (e.g. Selection.FITNESS_PROPORTIONATE). Default is FITNESS_PROPORTIONATE.

crossover Sets the allowed crossover methods used in the evolutionary process. Must be an array. disabled as of now

fitnessPopulation If set to true, you will have to specify a fitness function that takes an array of genomes as input and sets their .score property.

mutationRate Sets the mutation rate. If set to 0.3, 30% of the new population will be mutated. Default is 0.3.

mutationAmount If mutation occurs (randomNumber < mutationRate), sets the amount of times a mutation method will be applied to the network. Default is 1.

network If you want to start the algorithm from a specific network, specify your network here.

equal If set to true, all networks will be viewed equal during crossover. This stimulates more diverse network architectures. Default is false.

clear Clears the context of the network before activating the fitness function. Should be applied to get consistent outputs from recurrent networks. Default is false.

Properties

There are only a few properties

input The amount of input neurons each genome has

output The amount of output neurons each genome has

fitness The fitness function that is used to evaluate genomes

generation Generation counter

population An array containing all the genomes of the current generation

Functions

There are a few built-in functions. For the client, only getFittest() and evolve() is important. In the future, there will be a combination of backpropagation and evolution. Stay tuned

createPool() Initialises the first set of genomes. Should not be called manually.

async evolve() Loops the generation through a evaluation, selection, crossover and mutation process.

async evaluate() Evaluates the entire population by passing on the genome to the fitness function and taking the score.

sort() Sorts the entire population by score. Should be called after evaluate()

getFittest() Returns the fittest genome (highest score) of the current generation

mutate() Mutates genomes in the population, each genome has mutationRate chance of being mutated.

getOffspring() This function selects two genomes from the population with getParent(), and returns the offspring from those parents.

getAverage() Returns the average fitness of the current population

getParent() Returns a parent selected using one of the selection methods provided. Should be called after evaluation. Should not be called manually.

export() Exports the current population of the set up algorithm to a list containing json objects of the networks. Can be used later with import(json) to reload the population

import(json) Imports population from a json. Must be an array of networks that have converted to json (with myNetwork.toJSON())