neat

package module
v0.0.0-...-9ec678d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 18, 2018 License: GPL-3.0 Imports: 9 Imported by: 0

README

alt text GoDoc Go Report Card cover.run go

CURRENTLY NOT WORKING! There will be a further notice when it's updated.

NEAT (NeuroEvolution of Augmenting Topologies) is a neuroevolution algorithm by Dr. Kenneth O. Stanley which evolves not only neural networks' weights but also their topologies. This method starts the evolution process with genomes with minimal structure, then complexifies the structure of each genome as it progresses. You can read the original paper from here.

Installation

To install neat run the following:

$ go get -u github.com/jinyeom/neat

Usage

This NEAT package is as simple as plug and play. All you have to do is to create a new instance of NEAT, given the configuration from a JSON file, for which the template is provided below, and an evaluation method of a neural network, and run.

{
	"experimentName": "XOR Test",
	"verbose": true,
	"numInputs": 3,
	"numOutputs": 1,
	"fullyConnected": false,
	"numGenerations": 50,
	"populationSize": 100,
	"initFitness": 9999.0,
	"minimizeFitness": true,
	"survivalRate": 0.5,
	"stagnationLimit": 5,
	"ratePerturb": 0.2,
	"rateAddNode": 0.2,
	"rateAddConn": 0.2,
	"rateMutateChild": 0.5,
	"distanceThreshold": 20.0,
	"coeffUnmatching": 1.0,
	"coeffMatching": 1.0,
	"cppnActivations": [],
}

Now that you have the configuration JSON file is ready as config.json, we can start experiment with NEAT. Below is an example XOR experiment.

package main

import (
	"log"
	"math"

	// Import NEAT package after installing the package through
	// the instruction provided above.
	"github.com/jinyeom/neat"
)

func main() {

	// First, create a new instance of Config from the JSON file created above.
	// If there's a file import error, the program will crash.
	config, err := neat.NewConfigJSON("config.json")
	if err != nil{
		log.Fatal(err)
	}

	// Then, we can define the evaluation function, which is a type of function
	// which takes a neural network, evaluates its performance, and returns some
	// score that indicates its performance. This score is essentially a genome's
	// fitness score. With the configuration and the evaluation function we
	// defined, we can create a new instance of NEAT and start the evolution 
	// process.
	neat.New(config, neat.XORTest()).Run()
}

License

This package is under GNU General Public License.

Documentation

Overview

Package neat provides an implementation of NeuroEvolution of Augmenting Topologies (NEAT) as a plug-and-play framework, which can be used by simply adding and appropriate configuration and an evaluation function.

NEAT

NEAT (NeuroEvolution of Augmenting Topologies) is a neuroevolution algorithm by Dr. Kenneth O. Stanley which evolves not only neural networks' weights but also their topologies. This method starts the evolution process with genomes with minimal structure, then complexifies the structure of each genome as it progresses. You can read the original paper from here: http://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf

Example

This NEAT package is as simple as plug and play. All you have to do is to create a new instance of NEAT, given the configuration from a JSON file, for which the template is provided below, and an evaluation method of a neural network, and run.

{
	"experimentName": "XOR Test",
	"verbose": true,
	"numInputs": 3,
	"numOutputs": 1,
	"numGenerations": 50,
	"populationSize": 100,
	"initFitness": 9999.0,
	"minimizeFitness": true,
	"survivalRate": 0.5,
	"stagnationLimit": 5,
	"ratePerturb": 0.2,
	"rateAddNode": 0.2,
	"rateAddConn": 0.2,
	"rateMutateChild": 0.5,
	"distanceThreshold": 20.0,
	"coeffUnmatching": 1.0,
	"coeffMatching": 1.0
}

Now that you have the configuration JSON file is ready as `config.json`, we can start experiment with NEAT. Below is an example XOR experiment.

package main

import (
	"log"
	"math"

	// Import NEAT package after installing the package through
	// the instruction provided above.
	"github.com/jinyeom/neat"
)

func main() {

	// First, create a new instance of Config from the JSON file created above.
	// If there's a file import error, the program will crash.
	config, err := neat.NewConfigJSON("config.json")
	if err != nil{
		log.Fatal(err)
	}

  // Then, we can define the evaluation function, which is a type of function
  // which takes a neural network, evaluates its performance, and returns some
  // score that indicates its performance. This score is essentially a
  // genome's fitness score. With the configuration and the evaluation
  // function we defined, we can create a new instance of NEAT and start the
  // evolution process.
	neat.New(config, neat.XORTest()).Run()
}

Index

Constants

This section is empty.

Variables

View Source
var (
	// ActivationSet is a set of functions that can be used as activation
	// functions by neurons.
	ActivationSet = map[string]*ActivationFunc{
		"linear":   Linear(),
		"sigmoid":  Sigmoid(),
		"tanh":     Tanh(),
		"sin":      Sin(),
		"cos":      Cos(),
		"relu":     ReLU(),
		"log":      Log(),
		"exp":      Exp(),
		"abs":      Abs(),
		"square":   Square(),
		"cube":     Cube(),
		"gaussian": Gaussian(0.0, 1.0),
	}
)

Functions

func Compatibility

func Compatibility(g0, g1 *Genome, c0, c1 float64) float64

Compatibility computes the compatibility distance between two argument genomes.

Compatibility distance of two genomes is utilized for differentiating their species during speciation. The distance is computed as follows:

d = c0 * U + c1 * W

c0, c1, are hyperparameter coefficients, and U, W are respectively number of unmatching genes, and the average weight differences of matching genes. This approach is a slightly modified version of Dr. Kenneth Stanley's original approach in which unmatching genes are separated into excess and disjoint genes.

func NewNetwork

func NewNetwork(g *Genome)

Types

type ActivationFunc

type ActivationFunc int
const (
	Linear ActivationFunc = iota
	Sigmoid
	Tanh
	ReLU
	Sine
	Gaussian
)

func Abs

func Abs() *ActivationFunc

Abs returns the absolute value function as an activation function.

func Cos

func Cos() *ActivationFunc

Cos returns the cosine function as an activation function.

func Cube

func Cube() *ActivationFunc

Cube returns the cube function as an activation function.

func Exp

func Exp() *ActivationFunc

Exp returns the exponential function as an activation function.

func Gaussian

func Gaussian(mean, stdev float64) *ActivationFunc

Gaussian returns the Gaussian function as an activation function, given a mean and a standard deviation.

func Identity

func Identity() *ActivationFunc

Identity returns the identity function as an activation function. This function is only used for sensor nodes.

func Linear

func Linear() *ActivationFunc

func Log

func Log() *ActivationFunc

Log returns the log function as an activation function.

func RandActivationFunc

func RandActivationFunc() *ActivationFunc

func ReLU

func ReLU() *ActivationFunc

ReLU returns a rectifier linear unit as an activation function.

func Sigmoid

func Sigmoid() *ActivationFunc

Sigmoid returns the sigmoid function as an activation function.

func Sin

func Sin() *ActivationFunc

Sin returns the sin function as an activation function.

func Sine

func Sine() *ActivationFunc

func Square

func Square() *ActivationFunc

Square returns the square function as an activation function.

func Tanh

func Tanh() *ActivationFunc

Tanh returns the hyperbolic tangent function as an activation function.

func (*ActivationFunc) Activate

func (a *ActivationFunc) Activate(x float64) float64

func (*ActivationFunc) Name

func (a *ActivationFunc) Name() float64

type ComparisonFunc

type ComparisonFunc func(g0, g1 *Genome) bool

ComparisonFunc is a type of function that returns a boolean value that indicates whether the first argument genome is better than the second one in terms of its fitness.

func NewComparisonFunc

func NewComparisonFunc(minimize bool) ComparisonFunc

NewComparisonFunc returns a new comparison function, given an indicator of whether the fitness is better when minimized.

type Config

type Config struct {
	// general settings
	ExperimentName string `json:"experimentName"` // name of the experiment
	Verbose        bool   `json:"verbose"`        // verbose mode (terminal)

	// neural network settings
	NumInputs      int  `json:"numInputs"`      // number of inputs
	NumOutputs     int  `json:"numOutputs"`     // number of outputs
	FullyConnected bool `json:"fullyConnected"` // initially fully connected

	// evolution settings
	NumGenerations  int     `json:"numGenerations"`  // number of generations
	PopulationSize  int     `json:"populationSize"`  // size of population
	InitFitness     float64 `json:"initFitness"`     // initial fitness score
	MinimizeFitness bool    `json:"minimizeFitness"` // true if minimizing fitness
	SurvivalRate    float64 `json:"survivalRate"`    // survival rate
	StagnationLimit int     `json:"stagnationLimit"` // limit of stagnation

	// mutation rates settings
	RatePerturb     float64 `json:"ratePerturb"`     // by perturbing weights
	RateAddNode     float64 `json:"rateAddNode"`     // by adding a node
	RateAddConn     float64 `json:"rateAddConn"`     // by adding a connection
	RateMutateChild float64 `json:"rateMutateChild"` // mutation of a child

	// compatibility distance coefficient settings
	DistanceThreshold float64 `json:"distanceThreshold"` // distance threshold
	CoeffUnmatching   float64 `json:"coeffUnmatching"`   // unmatching genes
	CoeffMatching     float64 `json:"coeffMatching"`     // matching genes

	// CPPN settings
	CPPNActivations []string `json:"cppnActivations"` // additional activations
}

Config consists of all hyperparameter settings for NEAT. It can be imported from a JSON file.

func NewConfigJSON

func NewConfigJSON(filename string) (*Config, error)

NewConfigJSON creates a new instance of Config, given the name of a JSON file that consists of the hyperparameter settings.

func (*Config) Summarize

func (c *Config) Summarize()

Summarize prints the summarized configuration on terminal.

type ConnGene

type ConnGene struct {
	From     int     `json:"from"`     // input node
	To       int     `json:"to"`       // output node
	Weight   float64 `json:"weight"`   // connection weight
	Disabled bool    `json:"disabled"` // true if disabled
}

ConnGene is an implementation of a connection between two nodes in the graph representation of a genome. Each connection includes its input node, output node, connection weight, and an indication of whether this connection is disabled

func NewConnGene

func NewConnGene(from, to int, weight float64) *ConnGene

NewConnGene returns a new instance of ConnGene, given the input and output node genes. By default, the connection is enabled.

func (*ConnGene) Copy

func (c *ConnGene) Copy() *ConnGene

Copy returns a deep copy of this connection gene.

func (*ConnGene) String

func (c *ConnGene) String() string

String returns the string representation of this connection.

type ConnectionGene

type ConnectionGene struct {
	// contains filtered or unexported fields
}

func NewConnectionGene

func NewConnectionGene(innovID int, src, dst *NodeGene) *ConnectionGene

func (*ConnectionGene) Disable

func (c *ConnectionGene) Disable()

func (*ConnectionGene) Dst

func (c *ConnectionGene) Dst() *NodeGene

func (*ConnectionGene) Enable

func (c *ConnectionGene) Enable()

func (*ConnectionGene) Expressed

func (c *ConnectionGene) Expressed() bool

func (*ConnectionGene) Id

func (c *ConnectionGene) Id() int

func (*ConnectionGene) Src

func (c *ConnectionGene) Src() *NodeGene

func (*ConnectionGene) String

func (c *ConnectionGene) String() string

func (*ConnectionGene) Weight

func (c *ConnectionGene) Weight() float64

type EvaluationFunc

type EvaluationFunc func(*NeuralNetwork) float64

EvaluationFunc is a type of function that evaluates an argument neural network and returns a its fitness (performance) score.

func PoleBalancingTest

func PoleBalancingTest(randomStart bool, maxTime int) EvaluationFunc

PoleBalancingTest returns the pole balancing task as an evaluation function. The fitness is measured with how long the network can balanced the pole, given a max time. Suggested max time is 120000 ticks.

func XORTest

func XORTest() EvaluationFunc

XORTest returns an XOR test as an evaluation function. The fitness is measured with the total error, which should be minimized.

type Genome

type Genome struct {
	// contains filtered or unexported fields
}

func Crossover

func Crossover(id int, g0, g1 *Genome, initFitness float64) *Genome

Crossover returns a new child genome by performing crossover between the two argument genomes.

innovations is a temporary dictionary for the child genome's connection genes; it essentially stores all connection genes that will be contained in the child genome.

Initially, all of one parent genome's connections are recorded to innovations. Then, as the other parent genome's connections are added, it checks if each connection already exists; if it does, swap with the other parent's connection by 50% chance. Otherwise, append the new connection.

func NewFCGenome

func NewFCGenome(id, numInputs, numOutputs int, initFitness float64) *Genome

NewFCGenome returns an instance of initial Genome with fully connected input and output layers.

func NewGenome

func NewGenome(id, numInputs, numOutputs int, initFitness float64) *Genome

NewGenome returns an instance of initial Genome with no initial connections.

func (*Genome) Connect

func (g *Genome) Connect(srcId, dstId int) error

Connect

func (*Genome) ConnenctionGenes

func (g *Genome) ConnenctionGenes() []*ConnectionGene

func (*Genome) Copy

func (g *Genome) Copy() *Genome

Copy returns a deep copy of this genome.

func (*Genome) Evaluate

func (g *Genome) Evaluate(evaluate EvaluationFunc)

Evaluate takes an evaluation function and evaluates its fitness. Only perform the evaluation if it hasn't yet. If the lamarckian indicator is true, encode the phenotype neural network back into the genome.

func (*Genome) ExportJSON

func (g *Genome) ExportJSON(format bool) error

ExportJSON exports a JSON file that contains this genome's information. If the argument format indicator is true, the exported JSON file will be formatted with indentations.

func (*Genome) MutateAddConn

func (g *Genome) MutateAddConn(rate float64)

MutateAddConn mutates the genome by adding a connection.

func (*Genome) MutateAddNode

func (g *Genome) MutateAddNode(rate float64, activation *ActivationFunc)

MutateAddNode mutates the genome by adding a node with the argument activation function.

func (*Genome) MutatePerturb

func (g *Genome) MutatePerturb(rate float64)

MutatePerturb mutates the genome by perturbation of its weights by the argument rate.

func (*Genome) NodeGenes

func (g *Genome) NodeGenes() []*NodeGene

func (*Genome) Push

func (g *Genome) Push(nodeType NodeType, activation ActivationFunc) *NodeGene

PushNode creates and appends a new node gene to this genome.

func (*Genome) String

func (g *Genome) String() string

String returns the string representation of the genome.

type NEAT

type NEAT struct {
	Config      *Config           // configuration
	Population  []*Genome         // population of genome
	Species     []*Species        // species of subpopulation of genomes
	Activations []*ActivationFunc // set of activation functions
	Evaluation  EvaluationFunc    // evaluation function
	Comparison  ComparisonFunc    // comparison function
	Best        *Genome           // best genome
	Statistics  *Statistics       // statistics
	// contains filtered or unexported fields
}

NEAT is the implementation of NeuroEvolution of Augmenting Topology (NEAT).

func New

func New(config *Config, evaluation EvaluationFunc) *NEAT

New creates a new instance of NEAT with provided argument configuration and an evaluation function.

func (*NEAT) Evaluate

func (n *NEAT) Evaluate()

Evaluate evaluates fitness of every genome in the population. After the evaluation, their fitness scores are recored in each genome.

func (*NEAT) Reproduce

func (n *NEAT) Reproduce()

Reproduce performs reproduction of genomes in each species. Reproduction is performed under the assumption of speciation being already executed. The number of eliminated genomes in each species is determined by rate of elimination specified in n.Config; after some number of genomes are eliminated, the empty space is filled with resulting genomes of crossover among surviving genomes. If the number of eliminated genomes is 0 or less then 2 genomes survive, every member survives and mutates.

func (*NEAT) Run

func (n *NEAT) Run() *Genome

Run executes evolution and return the best genome.

func (*NEAT) Speciate

func (n *NEAT) Speciate()

Speciate performs speciation of each genome. The speciation mechanism is as follows (from http://nn.cs.utexas.edu/downloads/papers/stanley.phd04.pdf):

The Genome Loop:
	Take next genome g from P
	The Species Loop:
		If all species in S have been checked:
			create new species snew and place g in it
		Else:
			get next species s from S
			If g is compatible with s:
				add g to s
		If g has not been placed:
			Species Loop
	If not all genomes in G have been placed:
		Genome Loop
	Else STOP

func (*NEAT) Summarize

func (n *NEAT) Summarize(gen int)

Summarize summarizes current state of evolution process.

type Network

type Network struct {
	// contains filtered or unexported fields
}

type NeuralNetwork

type NeuralNetwork struct {
	Neurons []*Neuron // all neurons in the network
	// contains filtered or unexported fields
}

NeuralNetwork is an implementation of the phenotype neural network that is decoded from a genome.

func NewNeuralNetwork

func NewNeuralNetwork(g *Genome) *NeuralNetwork

NewNeuralNetwork returns a new instance of NeuralNetwork given a genome to decode from.

func (*NeuralNetwork) FeedForward

func (n *NeuralNetwork) FeedForward(inputs []float64) ([]float64, error)

FeedForward propagates inputs signals from input neurons to output neurons, and return output signals.

func (*NeuralNetwork) String

func (n *NeuralNetwork) String() string

String returns the string representation of NeuralNetwork.

type Neuron

type Neuron struct {
	ID         int                 // neuron ID
	Type       string              // neuron type
	Signal     float64             // signal held by this neuron
	Synapses   map[*Neuron]float64 // synapse from input neurons
	Activation *ActivationFunc     // activation function
	// contains filtered or unexported fields
}

Neuron is an implementation of a single neuron of a neural network.

func NewNeuron

func NewNeuron(nodeGene *NodeGene) *Neuron

NewNeuron returns a new instance of neuron, given a node gene.

func (*Neuron) Activate

func (n *Neuron) Activate() float64

Activate retrieves signal from neurons that are connected to this neuron and return its signal.

func (*Neuron) String

func (n *Neuron) String() string

String returns the string representation of Neuron.

type NodeGene

type NodeGene struct {
	// contains filtered or unexported fields
}

func NewNodeGene

func NewNodeGene(id int, ntype string, activation *ActivationFunc) *NodeGene

NewNodeGene returns a new instance of NodeGene, given its ID, its type, and the activation function of this node.

func (*NodeGene) Activation

func (n *NodeGene) Activation() ActivationFunc

func (*NodeGene) Copy

func (n *NodeGene) Copy() *NodeGene

Copy returns a deep copy of this node gene.

func (*NodeGene) Id

func (n *NodeGene) Id() int

func (*NodeGene) String

func (n *NodeGene) String() string

String returns a string representation of the node.

func (*NodeGene) Type

func (n *NodeGene) Type() NodeType

type NodeType

type NodeType int
const (
	Sensor NodeType = iota
	Output
	Hidden
)

type Species

type Species struct {
	ID             int       // species ID
	Stagnation     int       // number of generations of stagnation
	Representative *Genome   // genome that represents this species (permanent)
	BestFitness    float64   // best fitness score in this species
	Members        []*Genome // member genomes
}

Species is an implementation of species, or niche for speciation of genomes that are differentiated by their toplogical differences, measured with compatibility distance. Each species is created with a new genome that is not compatible with other genomes in the population, i.e., when a genome is not compatible with any other species.

func NewSpecies

func NewSpecies(id int, g *Genome) *Species

NewSpecies creates and returns a new instance of Species, given an initial genome that will also become the new species' representative.

func (*Species) ExplicitFitnessSharing

func (s *Species) ExplicitFitnessSharing()

ExplicitFitnessSharing adjust this species' members fitness via explicit fitness sharing.

func (*Species) Flush

func (s *Species) Flush()

Flush empties the species membership, except for its representative.

func (*Species) Register

func (s *Species) Register(g *Genome, minimizeFitness bool)

Register adds an argument genome as a new member of this species; in addition, if the new member genome outperforms this species' best genome, it replaces the best genome in this species.

type Statistics

type Statistics struct {
	NumSpecies []int     // number of species in each generation
	MinFitness []float64 // minimum fitness in each generation
	MaxFitness []float64 // maximum fitness in each generation
	AvgFitness []float64 // average fitness in each generation
}

Statistics is a data structure that records statistical information of each generation during the evolutionary process.

func NewStatistics

func NewStatistics(numGenerations int) *Statistics

NewStatistics returns a new instance of Statistics.

func (*Statistics) Update

func (s *Statistics) Update(currGen int, n *NEAT)

Update the statistics of current generation

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL