neat

package module
v0.0.0-...-24a9f77 Latest Latest
Warning

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

Go to latest
Published: Feb 14, 2018 License: GPL-3.0 Imports: 10 Imported by: 0

README

alt text GoDoc Go Report Card cover.run go

WORKING AGAIN!

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/azaryc2s/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": "",
	"cppnActivations": ["sigmoid"],
	"outputActivation": "sigmoid",
	"verbose": true,
	"numInputs": 0,
	"numOutputs": 0,
	"fullyConnected": true,
	
	"numGenerations": 0,
	"populationSize": 0,
	"tournamentSize": 3,
	"initFitness": 0.0,
	"initConnWeight": 1,
	"survivalRate": 0.0,
	
	"rateCrossover": 0.0,
	"ratePerturb": 0.0,
	"rangeMutWeight": 0.0,
	"capWeight": 0.0,
	"rateAddNode": 0.0,
	"rateAddConn": 0.0,
	"rateEnableConn": 0.0,
	"rateDisableConn": 0.0,
	"rateMutateActFunc": 0.0,
	
	"targetSpecies": 0,
	"stagnationLimit": 0,
	"distanceThreshold": 0,
	"distanceMod": 0.0,
	"minDistanceTreshold": 0,
	"coeffUnmatching": 0,
	"coeffMatching": 0
}

Now that you have the configuration JSON file ready as config.json, we can start working 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/azaryc2s/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. The neural network will always maximize the fitness, so if you wish
	// to minimize some fitness value, you have to return 1/fitness in this function.
	// Note: watch out not to return 1/0 which is defined as 'Inf' in Go.
	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 in "config_template.json" and an evaluation method of a neural network, and run.

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. After successful run, the function returns the best found genome.
	best := neat.New(config, neat.XORTest()).Run()

	// You can either save this genome for later use (export as Json for example)
	// or use it directly and create a NeuralNetwork with it, that can process data
	nn := neat.NewNeuralNetwork(best)

	// You can process data by using the FeedForward function. Here an example for 2 input nodes (+1 bias)
	// The output is an []float64 slice with the length equal to the number of output nodes
	output := nn.FeedForward([]float64{1.0, 1.0, 1.0})
}

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{
		"identity": Identity(),
		"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),
	}
)
View Source
var (
	Innovation int
)

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 MinFloatSlice

func MinFloatSlice(fs ...float64) (m float64)

func MinIntSlice

func MinIntSlice(fs ...int) (m int)

func SortFloat

func SortFloat(f []float64) []int

Types

type ActivationFunc

type ActivationFunc struct {
	Name string                  `json:"name"` // name of the function
	Fn   func(x float64) float64 `json:"-"`    // activation function
}

ActivationFunc is a wrapper type for activation functions.

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 Log

func Log() *ActivationFunc

Log returns the log function as an activation function.

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 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.

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() ComparisonFunc

NewComparisonFunc returns a new comparison function We stick to maximizing the fitness if you wish to minimize it, you can still return 1/ff in your evaluation function

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
	InitConnWeight float64 `json:"initConnWeight"` // initial weight of new connections
	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
	TournamentSize int     `json:"tournamentSize"` // size of the tournament for parent pairs at reproducing
	InitFitness    float64 `json:"initFitness"`    // initial fitness score
	SurvivalRate   float64 `json:"survivalRate"`   // survival rate

	// mutation / reproduction rates settings
	RatePerturb       float64 `json:"ratePerturb"`       // by perturbing weights
	RangeMutWeight    float64 `json:"rangeMutWeight"`    // range in which the weight mutation is applied
	CapWeight         float64 `json:"capWeight"`         // max weight cap
	RateAddNode       float64 `json:"rateAddNode"`       // by adding a node
	RateAddConn       float64 `json:"rateAddConn"`       // by adding a connection
	RateEnableConn    float64 `json:"rateEnableConn"`    // rate to enable a connection
	RateDisableConn   float64 `json:"rateDisableConn"`   // rate to disable a connection
	RateMutateActFunc float64 `json:"rateMutateActFunc"` // rate to mutate the activation function
	RateCrossover     float64 `json:"rateCrossover"`     // crossover chance when reproducing

	//Speciation parameters
	StagnationLimit     int     `json:"stagnationLimit"`     // limit of stagnation
	TargetSpecies       int     `json:"targetSpecies"`       // target species number
	DistanceMod         float64 `json:"distanceMod"`         // modification to distance treshold if not enough/too many species
	MinDistanceTreshold float64 `json:"minDistanceTreshold"` // minimum distance treshold
	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
	OutputActivation string   `json:"outputActivation"` // activation on the output nodes
}

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

var (
	NeatConfig *Config
)

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

	Generation int `json:"generation"` // generation in which this connection gene was created
	Innovation int `json:"innovation"` // innovation number of this gene
}

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 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. Trains the network to return values between 0.0 and 1.0, where <= 0.5 means 0 and > 0.5 means 1

type Genome

type Genome struct {
	ID          int         `json:"id"`          // genome ID
	SpeciesID   int         `json:"speciesID"`   // genome's species ID
	NodeGenes   []*NodeGene `json:"nodeGenes"`   // all nodes
	InputNodes  []*NodeGene `json:"inputNodes"`  // input nodes
	HiddenNodes []*NodeGene `json:"hiddenNodes"` // hidden nodes
	OutputNodes []*NodeGene `json:"outputNodes"` // output nodes
	ConnGenes   []*ConnGene `json:"connGenes"`   // connections in the genome
	Fitness     float64     `json:"fitness"`     // fitness score
	// contains filtered or unexported fields
}

Genome encodes the weights and topology of the output network as a collection of nodes and connection genes.

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 ImportJSON

func ImportJSON(jsonReader io.Reader) (*Genome, error)

func NewFCGenome

func NewFCGenome(id, numInputs, numOutputs int, initFitness float64, outputActivation string) *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, outputActivation string) *Genome

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

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) MutateActFunc

func (g *Genome) MutateActFunc(id int, acts []*ActivationFunc)

func (*Genome) MutateAddConn

func (g *Genome) MutateAddConn()

MutateAddConn mutates the genome by adding a connection.

func (*Genome) MutateAddNode

func (g *Genome) MutateAddNode(id int, activation *ActivationFunc)

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

func (*Genome) MutateDisEnConn

func (g *Genome) MutateDisEnConn(enRate, disRate float64)

Disable/ReEnable Connections

func (*Genome) MutatePerturb

func (g *Genome) MutatePerturb(rate, rang, capt float64)

MutatePerturb mutates the genome by perturbation of its weights by the argument rate by the given mutation range (called rang, because range is a keyword).

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.

func (*NEAT) Summarize

func (n *NEAT) Summarize(gen int)

Summarize summarizes current state of evolution process.

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) FeedRecurrent

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

func (*NeuralNetwork) ResetNeurons

func (n *NeuralNetwork) ResetNeurons()

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
	ConnGenes  []*ConnGene     // connections to this neuron
	Synapses   map[int]*Neuron // 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 {
	ID         int             `json:"id"`         // node ID
	Type       string          `json:"type"`       // node type
	Activation *ActivationFunc `json:"activation"` // activation function
}

NodeGene is an implementation of each node in the graph representation of a genome. Each node consists of a node ID, its type, and the activation type.

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) Copy

func (n *NodeGene) Copy() *NodeGene

Copy returns a deep copy of this node gene.

func (*NodeGene) String

func (n *NodeGene) String() string

String returns a string representation of the node.

type SortSlice

type SortSlice struct {
	sort.Interface
	// contains filtered or unexported fields
}

Wrapper to get the indexes of any sortable struct after sort

func (SortSlice) Swap

func (s SortSlice) Swap(i, j int)

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 at the moment
	BestEverFitness float64   // best fitness this species has ever scored
	SharedFitness   float64   // Shared species fitness
	Offspring       int       // Value representing how many children the species "deserves" to get when reproducing
	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) Flush

func (s *Species) Flush()

Flush empties the species membership, except for its representative which is being reassigned to be the most fit member at the moment

func (*Species) Register

func (s *Species) Register(g *Genome)

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