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 ¶
- Variables
- func Compatibility(g0, g1 *Genome, c0, c1 float64) float64
- func MinFloatSlice(fs ...float64) (m float64)
- func MinIntSlice(fs ...int) (m int)
- func SortFloat(f []float64) []int
- type ActivationFunc
- func Abs() *ActivationFunc
- func Cos() *ActivationFunc
- func Cube() *ActivationFunc
- func Exp() *ActivationFunc
- func Gaussian(mean, stdev float64) *ActivationFunc
- func Identity() *ActivationFunc
- func Log() *ActivationFunc
- func ReLU() *ActivationFunc
- func Sigmoid() *ActivationFunc
- func Sin() *ActivationFunc
- func Square() *ActivationFunc
- func Tanh() *ActivationFunc
- type ComparisonFunc
- type Config
- type ConnGene
- type EvaluationFunc
- type Genome
- func Crossover(id int, g0, g1 *Genome, initFitness float64) *Genome
- func ImportJSON(jsonReader io.Reader) (*Genome, error)
- func NewFCGenome(id, numInputs, numOutputs int, initFitness float64, outputActivation string) *Genome
- func NewGenome(id, numInputs, numOutputs int, initFitness float64, outputActivation string) *Genome
- func (g *Genome) Copy() *Genome
- func (g *Genome) Evaluate(evaluate EvaluationFunc)
- func (g *Genome) ExportJSON(format bool) error
- func (g *Genome) MutateActFunc(id int, acts []*ActivationFunc)
- func (g *Genome) MutateAddConn()
- func (g *Genome) MutateAddNode(id int, activation *ActivationFunc)
- func (g *Genome) MutateDisEnConn(enRate, disRate float64)
- func (g *Genome) MutatePerturb(rate, rang, capt float64)
- func (g *Genome) String() string
- type NEAT
- type NeuralNetwork
- type Neuron
- type NodeGene
- type SortSlice
- type Species
- type Statistics
Constants ¶
This section is empty.
Variables ¶
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), } )
var (
Innovation int
)
Functions ¶
func Compatibility ¶
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 MinIntSlice ¶
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 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 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 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 ¶
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 ¶
NewConfigJSON creates a new instance of Config, given the name of a JSON file that consists of the hyperparameter settings.
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 ¶
NewConnGene returns a new instance of ConnGene, given the input and output node genes. By default, the connection is enabled.
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 ¶
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, outputActivation string) *Genome
NewFCGenome returns an instance of initial Genome with fully connected input and output layers.
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 ¶
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 ¶
Disable/ReEnable Connections
func (*Genome) MutatePerturb ¶
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).
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.
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.
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.
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 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 ¶
NewSpecies creates and returns a new instance of Species, given an initial genome that will also become the new species' representative.
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