func TestNeuronAddOutlinkNonRecurrent(t *testing.T) { ng.SeedRandom() madeNonRecurrentLink := false madeRecurrentLink := false for i := 0; i < 100; i++ { xnorCortex := BasicCortex() neuron := xnorCortex.NeuronUUIDMap()["hidden-neuron1"] ok, mutateResult := NeuronAddOutlinkNonRecurrent(neuron) if !ok { continue } outboundConnection := mutateResult.(*ng.OutboundConnection) if neuron.IsConnectionRecurrent(outboundConnection) { madeRecurrentLink = true } else { madeNonRecurrentLink = true } } assert.True(t, madeNonRecurrentLink) assert.False(t, madeRecurrentLink) }
func init() { logg.LogKeys["MAIN"] = true logg.LogKeys["DEBUG"] = false logg.LogKeys["NEURVOLVE"] = true logg.LogKeys["NODE_STATE"] = false ng.SeedRandom() }
func TestNeuronAddOutlinkRecurrent(t *testing.T) { ng.SeedRandom() madeNonRecurrentLink := false madeRecurrentLink := false for i := 0; i < 100; i++ { xnorCortex := BasicCortex() neuron := xnorCortex.NeuronUUIDMap()["hidden-neuron1"] numOutlinksBefore := len(neuron.Outbound) ok, mutateResult := NeuronAddOutlinkRecurrent(neuron) if !ok { continue } outboundConnection := mutateResult.(*ng.OutboundConnection) numOutlinksAfter := len(neuron.Outbound) assert.Equals(t, numOutlinksBefore+1, numOutlinksAfter) if neuron.IsConnectionRecurrent(outboundConnection) { // the first time we make a nonRecurrentInlink, // test the network out if madeRecurrentLink == false { // make sure the network actually works examples := ng.XnorTrainingSamples() fitness := xnorCortex.Fitness(examples) assert.True(t, fitness >= 0) } madeRecurrentLink = true } else { // the first time we make a nonRecurrentInlink, // test the network out if madeNonRecurrentLink == false { // make sure the network doesn't totally break examples := ng.XnorTrainingSamples() fitness := xnorCortex.Fitness(examples) assert.True(t, fitness >= 0) } madeNonRecurrentLink = true } } assert.True(t, madeNonRecurrentLink) assert.True(t, madeRecurrentLink) }
func TestNeuronMutateActivation(t *testing.T) { ng.SeedRandom() neuron := &ng.Neuron{ ActivationFunction: ng.EncodableSigmoid(), NodeId: ng.NewNeuronId("neuron", 0.25), Bias: 10, } NeuronMutateActivation(neuron) assert.True(t, neuron.ActivationFunction != nil) assert.True(t, neuron.ActivationFunction.Name != ng.EncodableSigmoid().Name) }
func TestOutspliceNonRecurrent(t *testing.T) { ng.SeedRandom() numOutspliced := 0 numIterations := 100 for i := 0; i < numIterations; i++ { cortex := BasicCortex() numNeuronsBefore := len(cortex.Neurons) neuronLayerMapBefore := cortex.NeuronLayerMap() ok, mutateResult := OutspliceNonRecurrent(cortex) neuron := mutateResult.(*ng.Neuron) if !ok { continue } else { numOutspliced += 1 } assert.True(t, neuron.ActivationFunction != nil) numNeuronsAfter := len(cortex.Neurons) assert.Equals(t, numNeuronsAfter, numNeuronsBefore+1) // should have 1 outbound and inbound assert.Equals(t, len(neuron.Inbound), 1) assert.Equals(t, len(neuron.Outbound), 1) // should be no recurrent connections assert.Equals(t, len(neuron.RecurrentInboundConnections()), 0) assert.Equals(t, len(neuron.RecurrentOutboundConnections()), 0) // should have one more layer (this makes an assumption // about the BasicCortex architecture) numLayersBefore := len(neuronLayerMapBefore) numLayersAfter := len(cortex.NeuronLayerMap()) assert.Equals(t, numLayersAfter, numLayersBefore+1) // run network make sure it runs examples := ng.XnorTrainingSamples() fitness := cortex.Fitness(examples) assert.True(t, fitness >= 0) } assert.True(t, numOutspliced > 0) }
func RunTopologyMutatingTrainer() bool { ng.SeedRandom() // training set examples := ng.XnorTrainingSamples() // create netwwork with topology capable of solving XNOR cortex := ng.BasicCortex() // verify it can not yet solve the training set (since training would be useless in that case) verified := cortex.Verify(examples) if verified { panic("neural net already trained, nothing to do") } shc := &nv.StochasticHillClimber{ FitnessThreshold: ng.FITNESS_THRESHOLD, MaxIterationsBeforeRestart: 20000, MaxAttempts: 10, WeightSaturationRange: []float64{-10000, 10000}, } tmt := &nv.TopologyMutatingTrainer{ MaxAttempts: 100, MaxIterationsBeforeRestart: 5, StochasticHillClimber: shc, } cortexTrained, succeeded := tmt.TrainExamples(cortex, examples) if succeeded { logg.LogTo("MAIN", "Successfully trained net: %v", ng.JsonString(cortexTrained)) // verify it can now solve the training set verified = cortexTrained.Verify(examples) if !verified { logg.LogTo("MAIN", "Failed to verify neural net") succeeded = false } } if !succeeded { logg.LogTo("MAIN", "Failed to train neural net") } return succeeded }
func TestAddNeuronNonRecurrent(t *testing.T) { ng.SeedRandom() numUnableToAdd := 0 numIterations := 100 for i := 0; i < numIterations; i++ { cortex := BasicCortex() numNeuronsBefore := len(cortex.Neurons) ok, mutateResult := AddNeuronNonRecurrent(cortex) if !ok { numUnableToAdd += 1 continue } neuron := mutateResult.(*ng.Neuron) assert.True(t, neuron.ActivationFunction != nil) numNeuronsAfter := len(cortex.Neurons) addedNeuron := numNeuronsAfter == numNeuronsBefore+1 assert.True(t, addedNeuron) if !addedNeuron { break } // should have 1 outbound and inbound assert.Equals(t, len(neuron.Inbound), 1) assert.Equals(t, len(neuron.Outbound), 1) // should be no recurrent connections assert.Equals(t, len(neuron.RecurrentInboundConnections()), 0) assert.Equals(t, len(neuron.RecurrentOutboundConnections()), 0) // run network make sure it runs examples := ng.XnorTrainingSamples() fitness := cortex.Fitness(examples) assert.True(t, fitness >= 0) } assert.True(t, numUnableToAdd <= (numIterations/3)) }
func DisabledTestUnmarshalCortexFitness(t *testing.T) { ng.SeedRandom() logg.LogKeys["DEBUG"] = true logg.LogKeys["NEURGO"] = true // this test is disabled by default since it can take a long time // this was a real net that was evolved by the topological mutator // before it went through the memetic step. // jsonString := `{"NodeId":{"UUID":"cortex","NodeType":"CORTEX","LayerIndex":0},"Sensors":[{"NodeId":{"UUID":"sensor","NodeType":"SENSOR","LayerIndex":0},"VectorLength":2,"Outbound":[{"NodeId":{"UUID":"neuron","NodeType":"NEURON","LayerIndex":0.25}},{"NodeId":{"UUID":"todo=394057419","NodeType":"NEURON","LayerIndex":0.25}}]}],"Neurons":[{"NodeId":{"UUID":"neuron","NodeType":"NEURON","LayerIndex":0.25},"Bias":0,"Inbound":[{"NodeId":{"UUID":"sensor","NodeType":"SENSOR","LayerIndex":0},"Weights":[20,20]}],"Outbound":[{"NodeId":{"UUID":"todo=3342881449","NodeType":"NEURON","LayerIndex":0.375}}],"ActivationFunction":{"Name":"sigmoid"}},{"NodeId":{"UUID":"todo=3342881449","NodeType":"NEURON","LayerIndex":0.375},"Bias":-0.36421027459743627,"Inbound":[{"NodeId":{"UUID":"neuron","NodeType":"NEURON","LayerIndex":0.25},"Weights":[1.8610633947514623]},{"NodeId":{"UUID":"todo=394057419","NodeType":"NEURON","LayerIndex":0.25},"Weights":[0.45355591271633067]}],"Outbound":[{"NodeId":{"UUID":"actuator","NodeType":"ACTUATOR","LayerIndex":0.5}}],"ActivationFunction":{"Name":"sigmoid"}},{"NodeId":{"UUID":"todo=394057419","NodeType":"NEURON","LayerIndex":0.25},"Bias":0.4665982575854781,"Inbound":[{"NodeId":{"UUID":"sensor","NodeType":"SENSOR","LayerIndex":0},"Weights":[0.6618610678776169,3.065256532332561]}],"Outbound":[{"NodeId":{"UUID":"todo=3342881449","NodeType":"NEURON","LayerIndex":0.375}}],"ActivationFunction":{"Name":"tanh"}}],"Actuators":[{"NodeId":{"UUID":"actuator","NodeType":"ACTUATOR","LayerIndex":0.5},"VectorLength":1,"Inbound":[{"NodeId":{"UUID":"todo=3342881449","NodeType":"NEURON","LayerIndex":0.375},"Weights":null}]}]}` jsonString2 := `{"NodeId":{"UUID":"cortex","NodeType":"CORTEX","LayerIndex":0},"Sensors":[{"NodeId":{"UUID":"sensor","NodeType":"SENSOR","LayerIndex":0},"VectorLength":2,"Outbound":[{"NodeId":{"UUID":"neuron","NodeType":"NEURON","LayerIndex":0.25}},{"NodeId":{"UUID":"todo=8550407276","NodeType":"NEURON","LayerIndex":0.25}}]}],"Neurons":[{"NodeId":{"UUID":"neuron","NodeType":"NEURON","LayerIndex":0.25},"Bias":0,"Inbound":[{"NodeId":{"UUID":"sensor","NodeType":"SENSOR","LayerIndex":0},"Weights":[20,20]}],"Outbound":[{"NodeId":{"UUID":"todo=6014372025","NodeType":"NEURON","LayerIndex":0.375}}],"ActivationFunction":{"Name":"sigmoid"}},{"NodeId":{"UUID":"todo=6014372025","NodeType":"NEURON","LayerIndex":0.375},"Bias":1.216286900747618,"Inbound":[{"NodeId":{"UUID":"neuron","NodeType":"NEURON","LayerIndex":0.25},"Weights":[1.767095335462158]},{"NodeId":{"UUID":"todo=8550407276","NodeType":"NEURON","LayerIndex":0.25},"Weights":[1.0905530851150242]}],"Outbound":[{"NodeId":{"UUID":"actuator","NodeType":"ACTUATOR","LayerIndex":0.5}}],"ActivationFunction":{"Name":"sigmoid"}},{"NodeId":{"UUID":"todo=8550407276","NodeType":"NEURON","LayerIndex":0.25},"Bias":-0.23064534306604623,"Inbound":[{"NodeId":{"UUID":"sensor","NodeType":"SENSOR","LayerIndex":0},"Weights":[-2.069786342256813,-2.0791073119872916]}],"Outbound":[{"NodeId":{"UUID":"todo=6014372025","NodeType":"NEURON","LayerIndex":0.375}}],"ActivationFunction":{"Name":"sigmoid"}}],"Actuators":[{"NodeId":{"UUID":"actuator","NodeType":"ACTUATOR","LayerIndex":0.5},"VectorLength":1,"Inbound":[{"NodeId":{"UUID":"todo=6014372025","NodeType":"NEURON","LayerIndex":0.375},"Weights":null}]}]}` jsonBytes := []byte(jsonString2) cortex := &ng.Cortex{} err := json.Unmarshal(jsonBytes, cortex) if err != nil { log.Fatal(err) } assert.True(t, err == nil) shc := &StochasticHillClimber{ FitnessThreshold: ng.FITNESS_THRESHOLD, MaxIterationsBeforeRestart: 20000, MaxAttempts: 10, } examples := ng.XnorTrainingSamples() cortexTrained, succeeded := shc.TrainExamples(cortex, examples) assert.True(t, succeeded) // verify it can now solve the training set verified := cortexTrained.Verify(examples) assert.True(t, verified) fitness := cortexTrained.Fitness(examples) log.Printf("Final fitness: %v", fitness) }
func TestAddNeuronRecurrent(t *testing.T) { ng.SeedRandom() numAdded := 0 numIterations := 100 for i := 0; i < numIterations; i++ { cortex := BasicCortex() numNeuronsBefore := len(cortex.Neurons) ok, mutateResult := AddNeuronRecurrent(cortex) neuron := mutateResult.(*ng.Neuron) if !ok { continue } else { numAdded += 1 } assert.True(t, neuron != nil) assert.True(t, neuron.ActivationFunction != nil) numNeuronsAfter := len(cortex.Neurons) addedNeuron := numNeuronsAfter == numNeuronsBefore+1 if !addedNeuron { logg.LogPanic("AddNeuronRecurrent %v did not add exactly one neuron. before: %v after: %v", i, numNeuronsBefore, numNeuronsAfter) } // run network make sure it runs examples := ng.XnorTrainingSamples() fitness := cortex.Fitness(examples) assert.True(t, fitness >= 0) } assert.True(t, numAdded > 0) }
func RunStochasticHillClimber() { ng.SeedRandom() // training set -- todo: examples := ng.XnorTrainingSamples() examples := ng.XnorTrainingSamples() // create netwwork with topology capable of solving XNOR cortex := ng.XnorCortexUntrained() // verify it can not yet solve the training set (since training would be useless in that case) verified := cortex.Verify(examples) if verified { panic("neural net already trained, nothing to do") } shc := &nv.StochasticHillClimber{ FitnessThreshold: ng.FITNESS_THRESHOLD, MaxIterationsBeforeRestart: 2000, MaxAttempts: 2000, WeightSaturationRange: []float64{-100 * math.Pi, 100 * math.Pi}, } cortexTrained, _, succeeded := shc.TrainExamples(cortex, examples) if !succeeded { panic("could not train neural net") } // verify it can now solve the training set verified = cortexTrained.Verify(examples) if !verified { panic("could not verify neural net") } logg.LogTo("DEBUG", "trained cortex: %v", cortexTrained) logg.Log("done") }
func (shc *StochasticHillClimber) Train(cortex *ng.Cortex, scape Scape) (resultNeuralNet *ng.Cortex, fitness float64, succeeded bool) { shc.validate() numAttempts := 0 fittestNeuralNet := cortex.Copy() resultNeuralNet = cortex // Apply NN to problem and save fitness fitness = scape.Fitness(fittestNeuralNet) logg.LogTo("MAIN", "Initial fitness: %v", fitness) if fitness > shc.FitnessThreshold { succeeded = true return } for i := 0; ; i++ { // Save the genotype candidateNeuralNet := fittestNeuralNet.Copy() // Perturb synaptic weights and biases PerturbParameters(candidateNeuralNet, shc.WeightSaturationRange) // Re-Apply NN to problem candidateFitness := scape.Fitness(candidateNeuralNet) logg.LogTo("DEBUG", "candidate fitness: %v", fitness) // If fitness of perturbed NN is higher, discard original NN and keep new // If fitness of original is higher, discard perturbed and keep old. if candidateFitness > fitness { logg.LogTo("MAIN", "i: %v candidateFitness: %v > fitness: %v", i, candidateFitness, fitness) i = 0 fittestNeuralNet = candidateNeuralNet resultNeuralNet = candidateNeuralNet.Copy() fitness = candidateFitness } if candidateFitness > shc.FitnessThreshold { logg.LogTo("MAIN", "candidateFitness: %v > Threshold. Success at i=%v", candidateFitness, i) succeeded = true break } if ng.IntModuloProper(i, shc.MaxIterationsBeforeRestart) { logg.LogTo("MAIN", "** restart hill climber. fitness: %f i/max: %d/%d", fitness, numAttempts, shc.MaxAttempts) numAttempts += 1 i = 0 shc.resetParametersToRandom(fittestNeuralNet) ng.SeedRandom() } if numAttempts >= shc.MaxAttempts { succeeded = false break } } return }
func TestNeuronAddInlinkNonRecurrent(t *testing.T) { ng.SeedRandom() madeNonRecurrentInlink := false madeRecurrentInlink := false firstTime := true // since it's stochastic, repeat the operation many times and make // sure that it always produces expected behavior for i := 0; i < 100; i++ { xnorCortex := ng.XnorCortex() sensor := xnorCortex.Sensors[0] neuron := xnorCortex.NeuronUUIDMap()["output-neuron"] hiddenNeuron1 := xnorCortex.NeuronUUIDMap()["hidden-neuron1"] targetLayerIndex := hiddenNeuron1.NodeId.LayerIndex // add a new neuron at the same layer index as the hidden neurons hiddenNeuron3 := &ng.Neuron{ ActivationFunction: ng.EncodableSigmoid(), NodeId: ng.NewNeuronId("hidden-neuron3", targetLayerIndex), Bias: -30, } hiddenNeuron3.Init() xnorCortex.Neurons = append(xnorCortex.Neurons, hiddenNeuron3) weights := randomWeights(sensor.VectorLength) sensor.ConnectOutbound(hiddenNeuron3) hiddenNeuron3.ConnectInboundWeighted(sensor, weights) ok, mutateResult := NeuronAddInlinkNonRecurrent(neuron) if !ok { continue } inboundConnection := mutateResult.(*ng.InboundConnection) if neuron.IsInboundConnectionRecurrent(inboundConnection) { madeRecurrentInlink = true } else { madeNonRecurrentInlink = true } if firstTime == true { // only two possibilities - the hiddenNeuron3 or the // sensor. if it was the sensor, then the hiddenNeuron3 // is "dangliing" and so lets connect it if inboundConnection.NodeId.UUID == "sensor" { weights2 := randomWeights(1) hiddenNeuron3.ConnectOutbound(neuron) neuron.ConnectInboundWeighted(hiddenNeuron3, weights2) } // run network make sure it runs examples := ng.XnorTrainingSamples() fitness := xnorCortex.Fitness(examples) assert.True(t, fitness >= 0) firstTime = false } } assert.True(t, madeNonRecurrentInlink) assert.False(t, madeRecurrentInlink) }
func TestOutspliceRecurrent(t *testing.T) { logg.LogKeys["TEST"] = true logg.LogKeys["NEURVOLVE"] = true ng.SeedRandom() numOutspliced := 0 numOutsplicedWithNewLayer := 0 numOutsplicedWithExistingLayer := 0 numIterations := 100 for i := 0; i < numIterations; i++ { cortex := BasicCortexRecurrent() // run network make sure it runs examplesBefore := ng.XnorTrainingSamples() fitnessBefore := cortex.Fitness(examplesBefore) assert.True(t, fitnessBefore >= 0) // recreate network cortex = BasicCortexRecurrent() numNeuronsBefore := len(cortex.Neurons) neuronLayerMapBefore := cortex.NeuronLayerMap() ok, mutateResult := OutspliceRecurrent(cortex) neuron := mutateResult.(*ng.Neuron) if !ok { continue } else { numOutspliced += 1 } assert.True(t, neuron.ActivationFunction != nil) numNeuronsAfter := len(cortex.Neurons) assert.Equals(t, numNeuronsAfter, numNeuronsBefore+1) // should have 1 outbound and inbound assert.Equals(t, len(neuron.Inbound), 1) assert.Equals(t, len(neuron.Outbound), 1) // increment counter if layer added numLayersBefore := len(neuronLayerMapBefore) numLayersAfter := len(cortex.NeuronLayerMap()) if numLayersAfter == numLayersBefore+1 { numOutsplicedWithNewLayer += 1 } else { numOutsplicedWithExistingLayer += 1 } // run network make sure it runs examples := ng.XnorTrainingSamples() fitness := cortex.Fitness(examples) assert.True(t, fitness >= 0) } assert.True(t, numOutspliced > 0) assert.True(t, numOutsplicedWithNewLayer > 0) assert.True(t, numOutsplicedWithExistingLayer > 0) }
func (tmt *TopologyMutatingTrainer) Train(cortex *ng.Cortex, scape Scape) (fittestCortex *ng.Cortex, succeeded bool) { ng.SeedRandom() shc := tmt.StochasticHillClimber includeNonTopological := false mutators := CortexMutatorsNonRecurrent(includeNonTopological) originalCortex := cortex.Copy() currentCortex := cortex // Apply NN to problem and save fitness logg.LogTo("MAIN", "Get initial fitness") fitness := scape.Fitness(currentCortex) logg.LogTo("MAIN", "Initial fitness: %v", fitness) if fitness > shc.FitnessThreshold { succeeded = true return } for i := 0; ; i++ { logg.LogTo("MAIN", "Before mutate. i/max: %d/%d", i, tmt.MaxAttempts) // before we mutate the cortex, we need to init it, // otherwise things like Outsplice will fail because // there are no DataChan's. currentCortex.Init() // mutate the network randInt := RandomIntInRange(0, len(mutators)) mutator := mutators[randInt] ok, _ := mutator(currentCortex) if !ok { logg.LogTo("MAIN", "Mutate didn't work, retrying...") continue } isValid := currentCortex.Validate() if !isValid { logg.LogPanic("Cortex did not validate") } filenameJson := fmt.Sprintf("cortex-%v.json", i) currentCortex.MarshalJSONToFile(filenameJson) filenameSvg := fmt.Sprintf("cortex-%v.svg", i) currentCortex.RenderSVGFile(filenameSvg) logg.LogTo("MAIN", "Post mutate cortex svg: %v json: %v", filenameSvg, filenameJson) logg.LogTo("MAIN", "Run stochastic hill climber..") // memetic step: call stochastic hill climber and see if it can solve it fittestCortex, _, succeeded = shc.Train(currentCortex, scape) logg.LogTo("MAIN", "stochastic hill climber finished. succeeded: %v", succeeded) if succeeded { succeeded = true break } if i >= tmt.MaxAttempts { succeeded = false break } if ng.IntModuloProper(i, tmt.MaxIterationsBeforeRestart) { logg.LogTo("MAIN", "** Restart . i/max: %d/%d", i, tmt.MaxAttempts) currentCortex = originalCortex.Copy() isValid := currentCortex.Validate() if !isValid { currentCortex.Repair() // TODO: remove workaround isValid = currentCortex.Validate() if !isValid { logg.LogPanic("Cortex could not be repaired") } } } } return }