def rt_iterate(self): self.population.reporters.start_generation(self.population.generation) # Gather and report statistics. best = None worst = None for g in itervalues(self.population.population): if not hasattr(g, 'birth'): g.birth = self.population.generation if g.fitness is None: continue if best is None or g.fitness > best.fitness: best = g if (worst is None or g.fitness < worst.fitness) and \ self.population.generation - g.birth > self.population.config.stagnation_config.max_stagnation: worst = g population_with_fitness = dict([ p for p in iteritems(self.population.population) if p[1].fitness is not None ]) if population_with_fitness: self.population.reporters.post_evaluate(self.config, population_with_fitness, self.population.species, best) # Track the best genome ever seen. if self.population.best_genome is None or best.fitness > self.population.best_genome.fitness: self.population.best_genome = best if not self.population.config.no_fitness_termination: # End if the fitness threshold is reached. fv = self.population.fitness_criterion( g.fitness for g in itervalues(population_with_fitness)) if fv >= self.population.config.fitness_threshold: self.population.reporters.found_solution( self.population.config, self.population.generation, best) return # remove worst genome and replace with new one if worst is not None: new_genomes = self.population.reproduction.reproduce( self.population.config, self.population.species, 1, self.population.generation) new_key = np.max( [g.key for g in itervalues(self.population.population)]) + 1 for g in itervalues(new_genomes): del self.population.population[worst.key] g.key = new_key self.population.population[new_key] = g print(new_key) break # Divide the new population into species. if population_with_fitness: self.population.species.speciate(self.population.config, population_with_fitness, self.population.generation) # self.population.reporters.end_generation(self.population.config, self.population.population, # self.population.species) self.population.generation += 1
def reproduce(self, config, species, pop_size, generation): """ Handles creation of genomes, either from scratch or by sexual or asexual reproduction from parents. """ # TODO: I don't like this modification of the species and stagnation objects, # because it requires internal knowledge of the objects. # Filter out stagnated species, collect the set of non-stagnated # species members, and compute their average adjusted fitness. # The average adjusted fitness scheme (normalized to the interval # [0, 1]) allows the use of negative fitness values without # interfering with the shared fitness scheme. all_fitnesses = [] remaining_species = [] for stag_sid, stag_s, stagnant in self.stagnation.update( species, generation): if stagnant: self.reporters.species_stagnant(stag_sid, stag_s) else: all_fitnesses.extend(m.fitness for m in itervalues(stag_s.members)) remaining_species.append(stag_s) # The above comment was not quite what was happening - now getting fitnesses # only from members of non-stagnated species. # No species left. if not remaining_species: species.species = {} return {} # was [] self.calculate_adjusted_fitnesses(remaining_species, all_fitnesses) adjusted_fitnesses = [s.adjusted_fitness for s in remaining_species] avg_adjusted_fitness = mean(adjusted_fitnesses) # type: float self.reporters.info( "Average adjusted fitness: {:.3f}".format(avg_adjusted_fitness)) # Compute the number of new members for each species in the new generation. previous_sizes = [len(s.members) for s in remaining_species] min_species_size = self.reproduction_config.min_species_size # Isn't the effective min_species_size going to be max(min_species_size, # self.reproduction_config.elitism)? That would probably produce more accurate tracking # of population sizes and relative fitnesses... doing. TODO: document. min_species_size = max(min_species_size, self.reproduction_config.elitism) spawn_amounts = self.compute_spawn(adjusted_fitnesses, previous_sizes, pop_size, min_species_size) new_population = {} species.species = {} for spawn, s in zip(spawn_amounts, remaining_species): # If elitism is enabled, each species always at least gets to retain its elites. spawn = max(spawn, self.reproduction_config.elitism) assert spawn > 0 # The species has at least one member for the next generation, so retain it. old_members = list(iteritems(s.members)) s.members = {} species.species[s.key] = s # Sort members in order of descending fitness. old_members.sort(reverse=True, key=lambda x: x[1].fitness) # Transfer elites to new generation. if self.reproduction_config.elitism > 0: for i, m in old_members[:self.reproduction_config.elitism]: new_population[i] = m spawn -= 1 if spawn <= 0: continue repro_cutoff = self.calculated_cutoff(len(old_members)) old_members = old_members[:repro_cutoff] # Randomly choose parents and produce the number of offspring allotted to the species. while spawn > 0: spawn -= 1 gid, child = self.generate_child(old_members, config) new_population[gid] = child return new_population
def get_fitnesses(self): return [m.fitness for m in itervalues(self.members)]
def reproduce(self, config, species, pop_size, generation): """ Handles creation of genomes, either from scratch or by sexual or asexual reproduction from parents. """ # TODO: I don't like this modification of the species and stagnation objects, # because it requires internal knowledge of the objects. # Filter out stagnated species, collect the set of non-stagnated # species members, and compute their average adjusted fitness. # The average adjusted fitness scheme (normalized to the interval # [0, 1]) allows the use of negative fitness values without # interfering with the shared fitness scheme. all_fitnesses = [] remaining_species = [] for stag_sid, stag_s, stagnant in self.stagnation.update( species, generation): if stagnant: self.reporters.species_stagnant(stag_sid, stag_s) else: all_fitnesses.extend(m.fitness for m in itervalues(stag_s.members)) remaining_species.append(stag_s) # The above comment was not quite what was happening - now getting fitnesses # only from members of non-stagnated species. # No species left. if not remaining_species: species.species = {} return {} # was [] # Find minimum/maximum fitness across the entire population, for use in # species adjusted fitness computation. min_fitness = min(all_fitnesses) max_fitness = max(all_fitnesses) # Do not allow the fitness range to be zero, as we divide by it below. # TODO: The ``1.0`` below is rather arbitrary, and should be configurable. fitness_range = max(1.0, max_fitness - min_fitness) for afs in remaining_species: # Compute adjusted fitness. msf = mean([m.fitness for m in itervalues(afs.members)]) af = (msf - min_fitness) / fitness_range afs.adjusted_fitness = af adjusted_fitnesses = [s.adjusted_fitness for s in remaining_species] avg_adjusted_fitness = mean(adjusted_fitnesses) # type: float self.reporters.info( "Average adjusted fitness: {:.3f}".format(avg_adjusted_fitness)) # Compute the number of new members for each species in the new generation. previous_sizes = [len(s.members) for s in remaining_species] min_species_size = self.reproduction_config.min_species_size # Isn't the effective min_species_size going to be max(min_species_size, # self.reproduction_config.elitism)? That would probably produce more accurate tracking # of population sizes and relative fitnesses... doing. TODO: document. min_species_size = max(min_species_size, self.reproduction_config.elitism) spawn_amounts = self.compute_spawn(adjusted_fitnesses, previous_sizes, pop_size, min_species_size) new_population = {} species.species = {} for spawn, s in zip(spawn_amounts, remaining_species): # If elitism is enabled, each species always at least gets to retain its elites. spawn = max(spawn, self.reproduction_config.elitism) assert spawn > 0 # The species has at least one member for the next generation, so retain it. old_members = list(iteritems(s.members)) s.members = {} species.species[s.key] = s # Sort members in order of descending fitness. old_members.sort(reverse=True, key=lambda x: x[1].fitness) # Transfer elites to new generation. if self.reproduction_config.elitism > 0: for i, m in old_members[:self.reproduction_config.elitism]: new_population[i] = m spawn -= 1 if spawn <= 0: continue # Only use the survival threshold fraction to use as parents for the next generation. repro_cutoff = int( math.ceil(self.reproduction_config.survival_threshold * len(old_members))) # Use at least two parents no matter what the threshold fraction result is. repro_cutoff = max(repro_cutoff, 2) old_members = old_members[:repro_cutoff] # Randomly choose parents and produce the number of offspring allotted to the species. while spawn > 0: spawn -= 1 parent1_id, parent1 = random.choice(old_members) parent2_id, parent2 = random.choice(old_members) # Note that if the parents are not distinct, crossover will produce a # genetically identical clone of the parent (but with a different ID). # gid = next(self.genome_indexer) # child = config.genome_type(gid) # child.configure_crossover(parent1, parent2, config.genome_config) # child.mutate(config.genome_config) # new_population[gid] = child # self.ancestors[gid] = (parent1_id, parent2_id) if parent1.fitness > parent2.fitness: parent1.mutate(config.genome_config) new_population[parent1_id] = parent1 self.ancestors[parent1_id] = (parent1_id, parent1_id) else: parent2.mutate(config.genome_config) new_population[parent2_id] = parent2 self.ancestors[parent2_id] = (parent2_id, parent2_id) ''' Check cppnon sets of both parents If they're the same, perfrom crossover of non-cppnon-nodes and connections Else, Find parent with highest fitness Transfer those nodes to child perfrom crossover of non-cppnon-nodes and connections ''' return new_population
def run(): # load the config file local_dir = os.path.dirname(__file__) config_path = os.path.join(local_dir, my_config) config = neat.Config(AgentGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_path) # uses the extended NEAT population PopulationSyn that synchronizes with singularity pop = PopulationSyn(config) # add reporters stats = neat.StatisticsReporter() pop.add_reporter(stats) pop.add_reporter(neat.StdOutReporter(True)) # save a checkpoint every 100 generations or 900 seconds. rep = neat.Checkpointer(100, 900) pop.add_reporter(rep) # class for evaluating the population ec = GenomeEvaluator(ts_f, vs_f) # initializes genomes fitness and gen_best just for the first time for g in itervalues(pop.population): g.fitness = -10000000.0 ec.genomes_h.append(g) gen_best = g # initializations avg_score_v = -10000000.0 avg_score_v_ant = avg_score_v avg_score = avg_score_v iteration_counter = 0 best_fitness = -2000.0 pop_size = len(pop.population) # sets the nuber of continuous iterations num_iterations = round(200 / len(pop.population)) + 1 # repeat NEAT iterations until solved or keyboard interrupt while 1: try: # if it is not the first iteration calculate training and validation scores if iteration_counter > 0: avg_score = ec.training_validation_score(gen_best, config) # if it is not the first iteration if iteration_counter >= 0: # synchronizes with singularity migrating maximum 3 specimens # pop.syn_singularity(4, my_url, stats,avg_score,rep.current_generation, config, ec.genomes_h) pop.species.speciate(config, pop.population, pop.generation) print("\nSpeciation after migration done") # perform pending evaluations on the singularity network, max 2 pop.evaluate_pending(2) #increment iteration counter iteration_counter = iteration_counter + 1 # execute num_iterations consecutive iterations of the NEAT algorithm gen_best = pop.run(ec.evaluate_genomes, 2) # verify the training score is enough to stop the NEAT algorithm: TODO change to validation score when generalization is ok if avg_score < 2000000000: solved = False if solved: print("Solved.") # save the winners. for n, g in enumerate(best_genomes): name = 'winner-{0}'.format(n) with open(name + '.pickle', 'wb') as f: pickle.dump(g, f) break except KeyboardInterrupt: print("User break.") break env.close()
def run(): # Load the config file, which is assumed to live in # the same directory as this script. local_dir = os.path.dirname(__file__) config_path = os.path.join(local_dir, my_config) config = neat.Config(LanderGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_path) pop = neat.Population(config) stats = neat.StatisticsReporter() pop.add_reporter(stats) pop2 = neat.Population(config) stats2 = neat.StatisticsReporter() pop.add_reporter(stats) pop2.add_reporter(stats2) pop.add_reporter(neat.StdOutReporter(True)) # Checkpoint every 25 generations or 900 seconds. rep = neat.Checkpointer(25, 900) pop.add_reporter(rep) # Training set index avg_score_v = -10000000.0 avg_score_v_ant = avg_score_v avg_score = avg_score_v # asigna un gen_best para poder cargar los demás desde syn for g in itervalues(pop.population): gen_best = g g.fitness = -10000000.0 # Run until the winner from a generation is able to solve the environment # or the user interrupts the process. ec = PooledErrorCompute() temp = 0 best_fitness = -2000.0 pop_size = len(pop.population) # sets the nuber of continuous iterations num_iterations = round(200 / len(pop.population)) + 1 while 1: try: if temp > 0: # Calcula training y validation fitness best_genomes = stats.best_unique_genomes(3) solved = True best_scores = [] observation = env_t.reset() score = 0.0 step = 0 gen_best_nn = neat.nn.FeedForwardNetwork.create( gen_best, config) while 1: step += 1 output = gen_best_nn.activate(nn_format(observation)) action = (np.argmax(output[0:2]), output[3], output[4], output[5]) # buy,sell or observation, reward, done, info = env_t.step(action) score += reward env_t.render() if done: break ec.episode_score.append(score) ec.episode_length.append(step) best_scores.append(score) avg_score = sum(best_scores) / len(best_scores) print("Training Set Score =", score, " avg_score=", avg_score) # Calculate the real-validation set score best_genomes = stats.best_unique_genomes(3) solved = True best_scores = [] observation = env_v.reset() score = 0.0 step = 0 gen_best_nn = neat.nn.FeedForwardNetwork.create( gen_best, config) while 1: step += 1 output = gen_best_nn.activate(nn_format(observation)) action = (np.argmax(output[0:2]), output[3], output[4], output[5]) # buy,sell or observation, reward, done, info = env_v.step(action) score += reward #env_v.render() if done: break best_scores.append(score) avg_score_v = sum(best_scores) / len(best_scores) print("Validation Set Score = ", avg_score_v) print( "*********************************************************" ) # Calcula el best_fitness (PARA SYNC)como el promedio del score de training y el promedio del fitness de los reps. reps_local = [] reps = [gen_best] accum = 0.0 countr = 0 for sid, s in iteritems(pop.species.species): if pop.population[ s.representative.key].fitness is not None: accum = accum + pop.population[ s.representative.key].fitness countr = countr + 1 if countr > 0: best_fitness = (3 * avg_score + (accum / countr)) / 4 else: best_fitness = (avg_score) #FIN de calculo de real validation if temp >= 0: # TODO: FUNCION DE SINCRONIZACION CON SINGULARITY # Lee en pop2 el último checkpoint desde syn # Hace request de getLastParam(process_hash,use_current) a syn TODO: HACER PROCESS CONFIGURABLE Y POR HASH no por id res = requests.get( my_url + "/processes/1?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph" ) cont = res.json() print('\ncurrent_block_performance =', cont['result'][0]['current_block_performance']) print('\nlast_optimum_id =', cont['result'][0]['last_optimum_id']) last_optimum_id = cont['result'][0]['last_optimum_id'] # Si el perf reportado pop2_champion_fitness > pop1_champion_fitness de validation training print("\nPerformance = ", best_fitness) print( "*********************************************************" ) if cont['result'][0][ 'current_block_performance'] > best_fitness: # hace request GetParameter(id) res_p = requests.get( my_url + "/parameters/" + str(last_optimum_id) + "?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph" ) cont_param = res_p.json() # descarga el checkpoint del link de la respuesta si cont.parameter_link print('Parameter Downloaded') print('\nmigrations =') if cont_param['result'][0]['parameter_link'] is not None: genom_data = requests.get( cont_param['result'][0]['parameter_link']).content with open('remote_reps', 'wb') as handler: handler.write(genom_data) handler.close() # carga genom descargado en nueva población pop2 with open('remote_reps', 'rb') as f: remote_reps = pickle.load(f) # OP.MIGRATION: Reemplaza el peor de la especie pop1 más cercana por el nuevo chmpion de pop2 como http://neo.lcc.uma.es/Articles/WRH98.pdf # para cada elemento de remote_reps, busca el closer, si remote fitness > local, lo reemplaza for i in range(len(remote_reps)): closer = None min_dist = None # initialize for less fit search less_fit = None less_fitness = 10000 for g in itervalues(pop.population): if g not in remote_reps: dist = g.distance(remote_reps[i], config.genome_config) if dist is None: dist = 100000000 else: dist = 100000000 # do not count already migrated remote_reps if closer is None or min_dist is None: closer = deepcopy(g) min_dist = dist if dist < min_dist: closer = deepcopy(g) min_dist = dist if g.fitness is None: g.fitness = -10 if g.fitness < less_fitness: less_fitness = g.fitness less_fit = deepcopy(g) # For the best genom in position 0 if i == 0: tmp_genom = deepcopy(remote_reps[i]) # Hack: overwrites original genome key with the replacing one tmp_genom.key = closer.key pop.population[closer.key] = deepcopy( tmp_genom) print(" gen_best=", closer.key) pop.best_genome = deepcopy(tmp_genom) gen_best = deepcopy(tmp_genom) else: # si el remote fitness>local, reemplazar el remote de pop2 en pop1 if closer is None: # busca el pop con el menor fitness closer = less_fit if closer is not None: if closer not in remote_reps: if closer.fitness is None: closer.fitness = less_fitness if closer.fitness is not None and remote_reps[ i].fitness is not None: if remote_reps[ i].fitness > closer.fitness: tmp_genom = deepcopy( remote_reps[i]) # Hack: overwrites original genome key with the replacing one tmp_genom.key = closer.key pop.population[ closer.key] = deepcopy( tmp_genom) print("Replaced=", closer.key) # actualiza gen_best y best_genome al remoto pop.best_genome = deepcopy( tmp_genom) gen_best = deepcopy(tmp_genom) if closer.fitness is None: tmp_genom = deepcopy( remote_reps[i]) # Hack: overwrites original genome key with the replacing one tmp_genom.key = len( pop.population) + 1 pop.population[ tmp_genom.key] = tmp_genom print( "Created Por closer.fitness=NONE : ", tmp_genom.key) # actualiza gen_best y best_genome al remoto pop.best_genome = deepcopy( tmp_genom) gen_best = deepcopy(tmp_genom) else: #si closer está en remote_reps es porque no hay ningun otro cercano así que lo adiciona tmp_genom = deepcopy(remote_reps[i]) # Hack: overwrites original genome key with the replacing one tmp_genom.key = len(pop.population) + 1 pop.population[ tmp_genom.key] = tmp_genom print( "Created por Closer in rempte_reps=", tmp_genom.key) # actualiza gen_best y best_genome al remoto pop.best_genome = deepcopy(tmp_genom) gen_best = deepcopy(tmp_genom) #ejecuta speciate pop.species.speciate(config, pop.population, pop.generation) print("\nSpeciation after migration done") # Si el perf reportado es menor pero no igual al de pop1 if cont['result'][0][ 'current_block_performance'] < best_fitness: # Obtiene remote_reps # hace request GetParameter(id) remote_reps = None res_p = requests.get( my_url + "/parameters/" + str(last_optimum_id) + "?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph" ) cont_param = res_p.json() # descarga el checkpoint del link de la respuesta si cont.parameter_link print('\nNEW OPTIMUM - cont_param =', cont_param) #print('\nmigrations =') if cont_param['result'][0]['parameter_link'] is not None: genom_data = requests.get( cont_param['result'][0]['parameter_link']).content with open('remote_reps', 'wb') as handler: handler.write(genom_data) handler.close() # carga genom descargado en nueva población pop2 with open('remote_reps', 'rb') as f: remote_reps = pickle.load(f) #Guarda los mejores reps reps_local = [] reps = [gen_best] # Para cada especie, adiciona su representative a reps for sid, s in iteritems(pop.species.species): #print("\ns=",s) if s.representative not in reps_local: reps_local.append( pop.population[s.representative.key]) reps_local[len(reps_local) - 1] = deepcopy( pop.population[s.representative.key]) # TODO: Conservar los mejores reps, solo reemplazarlos por los mas cercanos if remote_reps is None: for l in reps_local: reps.append(l) reps[len(reps) - 1] = deepcopy(l) else: # para cada reps_local l for l in reps_local: # busca el closer a l en reps_remote for i in range(len(remote_reps)): closer = None min_dist = None for g in reps_local: if g not in remote_reps: dist = g.distance( remote_reps[i], config.genome_config) else: dist = 100000000 # do not count already migrated remote_reps if closer is None or min_dist is None: closer = deepcopy(g) min_dist = dist if dist < min_dist: closer = deepcopy(g) min_dist = dist # si closer is in reps if closer in reps: # adiciona l a reps si ya no estaba en reps if l not in reps: reps.append(l) reps[len(reps) - 1] = deepcopy(l) # sino else: # si l tiene más fitness que closer, if closer.fitness is not None and l.fitness is not None: if l.fitness > pop.population[ closer.key].fitness: # adiciona l a reps si ya no estaba en reps if l not in reps: reps.append(l) reps[len(reps) - 1] = deepcopy(l) # sino else: # adiciona closer a reps si ya no estaba en reps if l not in reps: reps.append( pop.population[closer.key]) reps[len(reps) - 1] = deepcopy( pop.population[closer.key]) # Guarda checkpoint de los representatives de cada especie y lo copia a ubicación para servir vía syn. # rep.save_checkpoint(config,pop,neat.DefaultSpeciesSet,rep.current_generation) print("\nreps=", reps) filename = '{0}{1}'.format("reps-", rep.current_generation) with open(filename, 'wb') as f: pickle.dump(reps, f) # # Hace request de CreateParam a syn form_data = { "process_hash": "ph", "app_hash": "ah", "parameter_link": my_url + "/genoms/" + filename, "parameter_text": pop.best_genome.key, "parameter_blob": "", "validation_hash": "", "hash": "h", "performance": best_fitness, "redir": "1", "username": "******", "pass_hash": "$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q" } # TODO: COLOCAR DIRECCION CONFIGURABLE res = requests.post( my_url + "/parameters?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph", data=form_data) res_json = res.json() # TODO FIN: FUNCION DE SINCRONIZACION CON SINGULARITY temp = temp + 1 # EVALUATE THE GENOMES WITH THE SUBSET TRAINING DATASET gen_best = pop.run(ec.evaluate_genomes, num_iterations) # TODO: # VERIFY IF THERE ARE PENDING EVALUATIONS # EVALUATE NUM_EVALUATIONS PENDING EVALUATIONS #print(gen_best) #visualize.plot_stats(stats, ylog=False, view=False, filename="fitness.svg") #plt.plot(ec.episode_score, 'g-', label='score') #plt.plot(ec.episode_length, 'b-', label='length') #plt.grid() #plt.legend(loc='best') #plt.savefig("scores.svg") #plt.close() #mfs = sum(stats.get_fitness_mean()[-5:]) / 5.0 #print("Average mean fitness over last 5 generations: {0}".format(mfs)) #mfs = sum(stats.get_fitness_stat(min)[-5:]) / 5.0 #print("Average min fitness over last 3 generations: {0}".format(mfs)) if avg_score < 2000000000: solved = False if solved: print("Solved.") # Save the winners. for n, g in enumerate(best_genomes): name = 'winner-{0}'.format(n) with open(name + '.pickle', 'wb') as f: pickle.dump(g, f) visualize.draw_net(config, g, view=False, filename=name + "-net.gv") visualize.draw_net(config, g, view=False, filename=name + "-net-enabled.gv", show_disabled=False) visualize.draw_net(config, g, view=False, filename=name + "-net-enabled-pruned.gv", show_disabled=False, prune_unused=True) break except KeyboardInterrupt: print("User break.") break env.close()
def create(genome, config, map_size): """ Receives a genome and returns its phenotype (a SwitchNeuronNetwork). """ genome_config = config.genome_config #required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) input_keys = copy.deepcopy(genome_config.input_keys) output_keys = copy.deepcopy(genome_config.output_keys) # Gather inputs and expressed connections. std_inputs = {} mod_inputs = {} children = {} node_keys = set(genome.nodes.keys()) # + list(genome_config.input_keys[:]) aux_keys = set() # Here we populate the children dictionary for each unique not isolated node. for n in genome.nodes.keys(): children[n] = [] if n in output_keys: continue #For this implementation everything besides the reward and the output is a map #if not genome.nodes[n].is_isolated: for _ in range(1, map_size): new_idx = max(node_keys) + 1 children[n].append(new_idx) node_keys.add(new_idx) #assume 2 input nodes: the first one will be scaled to a map and the second one will represent the reward n = input_keys[0] children[n] = [] for _ in range(1, map_size): new_idx = min(input_keys) - 1 children[n].append(new_idx) input_keys.append(new_idx) n = input_keys[1] children[n] = [] # We don't scale the output with the map size to keep passing the parameters of the network easy. # This part can be revised in the future for n in output_keys: children[n] = [] #Iterate over every connection for cg in itervalues(genome.connections): #If it's not enabled don't include it # if not cg.enabled: # continue i, o = cg.key #If neither node is required for output then skip the connection # if o not in required and i not in required: # continue #Find the map corresponding to each node of the connection in_map = [i] + children[i] out_map = [o] + children[o] #If the connection is modulatory and the output neuron a switch neuron then the new weights are stored #in the mod dictionary. We assume that only switch neurons have a modulatory part. if cg.is_mod and genome.nodes[o].is_switch: node_inputs = mod_inputs else: node_inputs = std_inputs for n in out_map: if n not in node_inputs.keys(): node_inputs[n] = [] if len(in_map) == map_size and len(out_map) == map_size: # Map to map connectivity if cg.one2one: if cg.extended: #extended one-to- #create a new intermediatery map for j in range(0, map_size): idx = max(node_keys.union(aux_keys)) + 1 children[idx] = [] aux_keys.add(idx) for _ in range(1, map_size): new_idx = max(node_keys.union(aux_keys)) + 1 children[idx].append(new_idx) aux_keys.add(new_idx) aux_map = [idx] + children[idx] for node in aux_map: node_inputs[node] = [] #add one to one connections between in_map and aux map with weight 1 for i in range(map_size): node_inputs[aux_map[i]].append((in_map[j], 1)) #add one to one connections between aux map and out map with stepped weights weights = calculate_weights(False, cg.weight, map_size) for i in range(map_size): node_inputs[out_map[j]].append( (aux_map[i], weights[i])) else: weight = cg.weight for i in range(map_size): node_inputs[out_map[i]].append((in_map[i], weight)) else: # 1-to-all if not cg.uniform: # Step start = -cg.weight end = cg.weight step = (end - start) / (map_size - 1) for o_n in out_map: s = start for i_n in in_map: node_inputs[o_n].append((i_n, s)) s += step else: # Uniform for o_n in out_map: for i_n in in_map: node_inputs[o_n].append((i_n, cg.weight)) else: # Map-to-isolated or isolated-to-isolated if not cg.uniform: # Step start = -cg.weight end = cg.weight step = (end - start) / (map_size - 1) for o_n in out_map: s = start for i_n in in_map: node_inputs[o_n].append((i_n, s)) s += step else: # Uniform for o_n in out_map: for i_n in in_map: node_inputs[o_n].append((i_n, cg.weight)) nodes = [] #Sometimes the output neurons end up not having any connections during the evolutionary process. While we do not #desire such networks, we should still allow them to make predictions to avoid fatal errors. for okey in output_keys: if okey not in node_keys: node_keys.add(okey) std_inputs[okey] = [] # While we cannot deduce the order of activations of the neurons due to the fact that we allow for arbitrary connection # schemes, we certainly want the output neurons to activate last. conns = {} for k in node_keys.union(aux_keys): conns[k] = [] parents = children.keys() for k in conns.keys(): if k in input_keys: continue if k not in conns.keys(): conns[k] = [] if k in std_inputs.keys(): conns[k].extend([i for i, _ in std_inputs[k]]) if k in mod_inputs.keys(): conns[k].extend([i for i, _ in mod_inputs[k]]) sorted_keys = order_of_activation(conns, input_keys, output_keys) #Edge case: when a genome has no connections, sorted keys ends up empty and crashes the program #If this happens, just activate the output nodes with the default activation: 0 if not sorted_keys: sorted_keys = output_keys for node_key in sorted_keys: #all the children are handled with the parent if node_key not in parents: continue #if the node we are examining is not in our keys set then skip it. It means that it is not required for output. # if node_key not in node_keys: # continue #if the node one of the originals present in the genotype, i.e. it's not one of the nodes we added for the #extended one to one scheme if node_key in genome.nodes: node = genome.nodes[node_key] node_map = [node_key] + children[node_key] if node.is_switch: # If the switch neuron does not have any incoming cnnections if node_key not in std_inputs.keys( ) and node_key not in mod_inputs.keys(): for n in node_map: std_inputs[n] = [] mod_inputs[n] = [] # if the switch neuron only has modulatory weights then we copy those weights for the standard part as well. # this is not the desired behaviour but it is done to avoid errors during forward pass. if node_key not in std_inputs.keys( ) and node_key in mod_inputs.keys(): for n in node_map: std_inputs[n] = mod_inputs[n] if node_key not in mod_inputs.keys(): for n in node_map: mod_inputs[n] = [] #For the guided maps, all modulatory weights to switch neurons now weight 1/m if mod_inputs[node_key]: for n in node_map: new_w = 1 / len(std_inputs[n]) new_mod_w = [(inp, new_w) for inp, w in mod_inputs[n]] mod_inputs[n] = new_mod_w for n in node_map: nodes.append(SwitchNeuron(n, std_inputs[n], mod_inputs[n])) continue ###################### Switch neuron part ends here for n in node_map: if n not in std_inputs: std_inputs[n] = [] #For these guided maps, every hidden neuron that is not a switch neuron is a gating neuron if node_key in output_keys: # Create the standard part dictionary for the neuron #We also pre-determine the output neuron to help NEAT even more params = { 'activation_function': identity, 'integration_function': sum, 'bias': node.bias, 'activity': 0, 'output': 0, 'weights': std_inputs[n] } #Everything else is a gating neuron else: params = { 'activation_function': identity, 'integration_function': prod, 'bias': node.bias, 'activity': 0, 'output': 0, 'weights': std_inputs[n] } nodes.append(Neuron(n, params)) #if the node is one of those we added with the extended one to one scheme else: node_map = [node_key] + children[node_key] for n in node_map: if n not in std_inputs: std_inputs[n] = [] # Create the standard part dictionary for the neuron for n in node_map: params = { 'activation_function': identity, 'integration_function': sum, 'bias': 0, 'activity': 0, 'output': 0, 'weights': std_inputs[n] } nodes.append(Neuron(n, params)) return SwitchNeuronNetwork(input_keys, output_keys, nodes)
def reproduce(self, config, species, pop_size, generation): # TODO: I don't like this modification of the species and stagnation objects, # because it requires internal knowledge of the objects. # Find minimum/maximum fitness across the entire population, for use in # species adjusted fitness computation. all_fitnesses = [] for sid, s in iteritems(species.species): all_fitnesses.extend(m.fitness for m in itervalues(s.members)) min_fitness = min(all_fitnesses) max_fitness = max(all_fitnesses) # Do not allow the fitness range to be zero, as we divide by it below. fitness_range = max(1.0, max_fitness - min_fitness) # Filter out stagnated species, collect the set of non-stagnated # species members, and compute their average adjusted fitness. # The average adjusted fitness scheme (normalized to the interval # [0, 1]) allows the use of negative fitness values without # interfering with the shared fitness scheme. num_remaining = 0 species_fitness = [] avg_adjusted_fitness = 0.0 for sid, s, stagnant in self.stagnation.update(species, generation): if stagnant: self.reporters.species_stagnant(sid, s) else: num_remaining += 1 # Compute adjusted fitness. msf = mean([m.fitness for m in itervalues(s.members)]) s.adjusted_fitness = (msf - min_fitness) / fitness_range species_fitness.append((sid, s, s.fitness)) avg_adjusted_fitness += s.adjusted_fitness # No species left. if 0 == num_remaining: species.species = {} return [] avg_adjusted_fitness /= len(species_fitness) self.reporters.info( "Average adjusted fitness: {:.3f}".format(avg_adjusted_fitness)) # Compute the number of new individuals to create for the new generation. spawn_amounts = [] for sid, s, sfitness in species_fitness: spawn = len(s.members) if sfitness > avg_adjusted_fitness: spawn = max(spawn + 2, spawn * 1.1) else: spawn = max(spawn * 0.9, 2) spawn_amounts.append(spawn) # Normalize the spawn amounts so that the next generation is roughly # the population size requested by the user. total_spawn = sum(spawn_amounts) norm = pop_size / total_spawn spawn_amounts = [int(round(n * norm)) for n in spawn_amounts] new_population = {} species.species = {} for spawn, (sid, s, sfitness) in zip(spawn_amounts, species_fitness): # If elitism is enabled, each species always at least gets to retain its elites. spawn = max(spawn, self.elitism) if spawn <= 0: continue # The species has at least one member for the next generation, so retain it. old_members = list(iteritems(s.members)) s.members = {} species.species[sid] = s # Sort members in order of descending fitness. old_members.sort(reverse=True, key=lambda x: x[1].fitness) # Transfer elites to new generation. if self.elitism > 0: for i, m in old_members[:self.elitism]: new_population[i] = m spawn -= 1 if spawn <= 0: continue # Only use the survival threshold fraction to use as parents for the next generation. repro_cutoff = int( math.ceil(self.survival_threshold * len(old_members))) # Use at least two parents no matter what the threshold fraction result is. repro_cutoff = max(repro_cutoff, 2) old_members = old_members[:repro_cutoff] # Randomly choose parents and produce the number of offspring allotted to the species. while spawn > 0: spawn -= 1 parent1_id, parent1 = random.choice(old_members) parent2_id, parent2 = random.choice(old_members) # Note that if the parents are not distinct, crossover will produce a # genetically identical clone of the parent (but with a different ID). gid = self.genome_indexer.get_next() child = config.genome_type(gid) child.configure_crossover(parent1, parent2, config.genome_config) child.mutate(config.genome_config) new_population[gid] = child self.ancestors[gid] = (parent1_id, parent2_id) return new_population
def run(): # Load the config file, which is assumed to live in # the same directory as this script. local_dir = os.path.dirname(__file__) config_path = os.path.join(local_dir, 'config') config = neat.Config(LanderGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_path) pop = neat.Population(config) stats = neat.StatisticsReporter() pop.add_reporter(stats) pop2 = neat.Population(config) stats2 = neat.StatisticsReporter() pop.add_reporter(stats) pop2.add_reporter(stats2) pop.add_reporter(neat.StdOutReporter(True)) # Checkpoint every 25 generations or 900 seconds. rep = neat.Checkpointer(25, 900) pop.add_reporter(rep) # Run until the winner from a generation is able to solve the environment # or the user interrupts the process. ec = PooledErrorCompute() temp = 0 while 1: try: if temp > 0: # TODO: FUNCION DE SINCRONIZACION CON SINGULARITY # Lee en pop2 el último checkpoint desde syn # Hace request de getLastParam(process_hash,use_current) a syn TODO: HACER PROCESS CONFIGURABLE Y POR HASH no por id res = requests.get( my_url + "/processes/1?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph" ) cont = res.json() print('\ncurrent_block_performance =', cont['result'][0]['current_block_performance']) print('\nlast_optimum_id =', cont['result'][0]['last_optimum_id']) last_optimum_id = cont['result'][0]['last_optimum_id'] # Si el perf reportado pop2_champion_fitness > pop1_champion_fitness best_fitness = gen_best.fitness print('\nbest_fitness =', best_fitness) if cont['result'][0][ 'current_block_performance'] > best_fitness: # hace request GetParameter(id) res_p = requests.get( my_url + "/parameters/" + str(last_optimum_id) + "?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph" ) cont_param = res_p.json() # descarga el checkpoint del link de la respuesta si cont.parameter_link print('\ncont_param =', cont_param) if cont_param['result'][0]['parameter_link'] is not None: genom_data = requests.get( cont_param['result'][0]['parameter_link']).content with open('remote_genom', 'wb') as handler: handler.write(genom_data) handler.close() # carga genom descargado en nueva población pop2 with open('remote_genom', 'rb') as f: remote_genom = pickle.load(f) # OP.MIGRATION: Reemplaza el peor de la especie pop1 más cercana por el nuevo chmpion de pop2 como http://neo.lcc.uma.es/Articles/WRH98.pdf closer = None min_dist = None #for g in itervalues(pop.population): # dist = g.fitness # if closer is None or min_dist is None or dist < min_dist: # closer = g # min_dist = dist # se selecciona el que tenga menos distancia al pop2.champion en los representantes de pop1 closer = None min_dist = None # descarga el checkpoint del link de la respuesta si cont.parameter_link for g in itervalues(pop.population): dist = g.distance(remote_genom, config.genome_config) if closer is None or min_dist is None: closer = deepcopy(g) min_dist = dist if dist < min_dist: closer = deepcopy(g) min_dist = dist # reemplazar el champ de pop2 en pop1 tmp_genom = deepcopy(remote_genom) # Hack: overwrites original genome key with the replacing one tmp_genom.key = closer.key pop.population[closer.key] = deepcopy(tmp_genom) # actualiza gen_best y best_genome al remoto pop.best_genome = deepcopy(tmp_genom) gen_best = deepcopy(tmp_genom) #ejecuta speciate pop.species.speciate(config, pop.population, pop.generation) print("\ndone") # Si el perf reportado es menor pero no igual al de pop1 if cont['result'][0][ 'current_block_performance'] < best_fitness: # Guarda checkpoint del mejor genoma y lo copia a ubicación para servir vía syn. # rep.save_checkpoint(config,pop,neat.DefaultSpeciesSet,rep.current_generation) filename = '{0}{1}'.format("best-genome-", rep.current_generation) with open(filename, 'wb') as f: pickle.dump(gen_best, f) # Hace request de CreateParam a syn form_data = { "process_hash": "ph", "app_hash": "ah", "parameter_link": my_url + "/genoms/" + filename, "parameter_text": pop.best_genome.key, "parameter_blob": "", "validation_hash": "", "hash": "h", "performance": best_fitness, "redir": "1", "username": "******", "pass_hash": "$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q" } # TODO: COLOCAR DIRECCION CONFIGURABLE res = requests.post( my_url + "/parameters?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph", data=form_data) res_json = res.json() # TODO FIN: FUNCION DE SINCRONIZACION CON SINGULARITY temp = temp + 1 gen_best = pop.run(ec.evaluate_genomes, 5) #print(gen_best) visualize.plot_stats(stats, ylog=False, view=False, filename="fitness.svg") plt.plot(ec.episode_score, 'g-', label='score') plt.plot(ec.episode_length, 'b-', label='length') plt.grid() plt.legend(loc='best') plt.savefig("scores.svg") plt.close() mfs = sum(stats.get_fitness_mean()[-5:]) / 5.0 print("Average mean fitness over last 5 generations: {0}".format( mfs)) mfs = sum(stats.get_fitness_stat(min)[-5:]) / 5.0 print( "Average min fitness over last 5 generations: {0}".format(mfs)) # Use the best genomes seen so far as an ensemble-ish control system. best_genomes = stats.best_unique_genomes(3) best_networks = [] for g in best_genomes: best_networks.append( neat.nn.FeedForwardNetwork.create(g, config)) solved = True best_scores = [] for k in range(100): observation = env.reset() score = 0 step = 0 while 1: step += 1 # Use the total reward estimates from all five networks to # determine the best action given the current state. votes = np.zeros((4, )) for n in best_networks: output = n.activate(nn_format(observation)) votes[np.argmax(output)] += 1 best_action = np.argmax(votes) observation, reward, done, info = env.step(best_action) score += reward env.render() if done: break ec.episode_score.append(score) ec.episode_length.append(step) best_scores.append(score) avg_score = sum(best_scores) / len(best_scores) print(k, score, avg_score) if avg_score < 2000000000: solved = False break if solved: print("Solved.") # Save the winners. for n, g in enumerate(best_genomes): name = 'winner-{0}'.format(n) with open(name + '.pickle', 'wb') as f: pickle.dump(g, f) visualize.draw_net(config, g, view=False, filename=name + "-net.gv") visualize.draw_net(config, g, view=False, filename=name + "-net-enabled.gv", show_disabled=False) visualize.draw_net(config, g, view=False, filename=name + "-net-enabled-pruned.gv", show_disabled=False, prune_unused=True) break except KeyboardInterrupt: print("User break.") break env.close()
def run(self, fitness_function, n): """ Runs NEAT's genetic algorithm for at most n generations. The user-provided fitness_function should take two arguments, a list of all genomes in the population, and its return value is ignored. This function is free to maintain external state, perform evaluations in parallel, and probably any other thing you want. Each individual genome's fitness member must be set to a floating point value after this function returns. It is assumed that fitness_function does not modify the list of genomes, the genomes themselves (apart from updating the fitness member), or the configuration object. """ for g in range(n): self.generation += 1 self.reporters.start_generation(self.generation) # Evaluate all individuals in the population using the user-provided function. # TODO: Add an option to only evaluate each genome once, to reduce number of # fitness evaluations in cases where the fitness is known to be the same if the # genome doesn't change--in these cases, evaluating unmodified elites in each # generation is a waste of time. The user can always take care of this in their # fitness function in the time being if they wish. fitness_function(list(iteritems(self.population)), self.config) # Gather and report statistics. best = None for g in itervalues(self.population): if best is None or g.fitness > best.fitness: best = g self.reporters.post_evaluate(self.config, self.population, self.species, best) # Track the best genome ever seen. if self.best_genome is None or best.fitness > self.best_genome.fitness: self.best_genome = best # End if the fitness threshold is reached. if best.fitness >= self.config.max_fitness_threshold: self.reporters.found_solution(self.config, self.generation, best) break # Create the next generation from the current generation. self.population = self.reproduction.reproduce( self.config, self.species, self.config.pop_size) # Check for complete extinction. if not self.species.species: self.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config.reset_on_extinction: self.population = self.reproduction.create_new( self.config.pop_size) else: raise CompleteExtinctionException() # Update species age. # TODO: Wouldn't it be easier to remember creation time? for s in itervalues(self.species.species): s.age += 1 # Divide the new population into species. self.species.speciate(self.config, self.population) self.reporters.end_generation(self.config, self.population, self.species) return self.best_genome
def reproduce(self, config: Config, species: DefaultSpecies, generation: int, logger=None): """Handles creation of genomes, either from scratch or by sexual or asexual reproduction from parents.""" # Check is one of the species has become stagnant (i.e. must be removed) remaining_fitness = [] remaining_species = [] for stag_sid, stag_s, stagnant in self.stagnation.update( config=config, species_set=species, gen=generation): # If specie is stagnant, then remove if stagnant: self.reporters.species_stagnant(stag_sid, stag_s, logger=logger) # Add the specie to remaining_species and save each of its members' fitness else: remaining_fitness.extend(m.fitness for m in itervalues(stag_s.members)) remaining_species.append(stag_s) # If no species is left then force hard-reset if not remaining_species: species.species = dict() return dict() # Calculate the adjusted fitness, normalized by the minimum fitness across the entire population for specie in remaining_species: # Adjust a specie's fitness in a fitness sharing manner. A specie's fitness gets normalized by the number of # members it has, this to ensure that a better performing specie does not takes over the population # A specie's fitness is determined by its most fit genome specie_fitness = max([m.fitness for m in specie.members.values()]) specie_size = len(specie.members) specie.adjusted_fitness = specie_fitness / max( specie_size, config.population.min_specie_size) # Minimum specie-size is defined by the number of elites and the minimal number of genomes in a population spawn_amounts = self.compute_spawn( adjusted_fitness=[s.adjusted_fitness for s in remaining_species], previous_sizes=[len(s.members) for s in remaining_species], pop_size=config.population.pop_size, min_species_size=max(config.population.min_specie_size, config.population.genome_elitism)) # Setup the next generation by filling in the new species with their elites and offspring new_population = dict() species.species = dict() for spawn_amount, specie in zip(spawn_amounts, remaining_species): # If elitism is enabled, each species will always at least gets to retain its elites spawn_amount = max(spawn_amount, config.population.genome_elitism) assert spawn_amount > 0 # Get all the specie's old (evaluated) members old_members = list(iteritems( specie.members)) # Temporarily save members of last generation specie.members = dict() # Reset members species.species[specie.key] = specie # Sort members in order of descending fitness (i.e. most fit members in front) old_members.sort(reverse=True, key=lambda x: x[1].fitness) # Make sure that all the specie's elites are added to the new generation if config.population.genome_elitism > 0: # Add the specie's elites to the global population for i, m in old_members[:config.population.genome_elitism]: new_population[i] = m spawn_amount -= 1 # Add the specie's past elites as well if requested for i in range( min(len(specie.elite_list), config.population.genome_elite_stagnation - 1)): gid, g = specie.elite_list[-(i + 1)] if gid not in new_population: # Only add genomes not yet present in the population new_population[gid] = g spawn_amount -= 1 # Update the specie's elite_list specie.elite_list.append(old_members[0]) # Check if the specie has the right to add more genomes to the population if spawn_amount <= 0: continue # Only use the survival threshold fraction to use as parents for the next generation, use at least all the # elite of a population as parents reproduction_cutoff = max( round(config.population.parent_selection * len(old_members)), config.population.genome_elitism) # Since asexual reproduction, at least one parent must be chosen reproduction_cutoff = max(reproduction_cutoff, 1) parents = old_members[:reproduction_cutoff] # Add the elites again to the parent-set such that these have a greater likelihood of being chosen parents += old_members[:config.population.genome_elitism] # Fill the specie with offspring based, which is a mutation of the chosen parent while spawn_amount > 0: spawn_amount -= 1 # Init genome dummy (values are overwritten later) gid = next(self.genome_indexer) child: Genome = Genome(gid, num_outputs=config.genome.num_outputs, bot_config=config.bot) # Choose the parents, note that if the parents are not distinct, crossover will produce a genetically # identical clone of the parent (but with a different ID) p1_id, p1 = choice(parents) child.connections = copy.deepcopy(p1.connections) child.nodes = copy.deepcopy(p1.nodes) # Mutate the child child.mutate(config.genome) # Ensure that the child is connected while len(child.get_used_connections()) == 0: child.mutate_add_connection(config.genome) # Add the child to the global population new_population[gid] = child return new_population
def single_evaluation(multi_env, parallel: bool, pop: Population, unused_cpu: int): """ Perform a single evaluation-iteration. :param multi_env: Environment used to execute the game-simulation in :param parallel: Boolean indicating if training happens in parallel or not :param pop: Population used to evaluate on :param unused_cpu: Number of CPU-cores not used during evaluation """ # Prepare the generation's reporters for the generation pop.reporters.start_generation(gen=pop.generation, logger=pop.log) # Fetch the dictionary of genomes genomes = list(iteritems(pop.population)) if parallel: pbar = tqdm(total=len(genomes), desc="parallel training") # Initialize the evaluation-pool pool = mp.Pool(mp.cpu_count() - unused_cpu) manager = mp.Manager() return_dict = manager.dict() def cb(*_): """Update progressbar after finishing a single genome's evaluation.""" pbar.update() for genome in genomes: pool.apply_async(func=multi_env.eval_genome, args=(genome, return_dict), callback=cb) pool.close() # Close the pool pool.join() # Postpone continuation until everything is finished pbar.close() # Close the progressbar else: return_dict = dict() for genome in tqdm(genomes, desc="sequential training"): multi_env.eval_genome(genome, return_dict) # Calculate the fitness from the given return_dict try: fitness = calc_pop_fitness( fitness_config=pop.config.evaluation, game_observations=return_dict, game_params=multi_env.get_game_params(), generation=pop.generation, ) for i, genome in genomes: genome.fitness = fitness[i] except Exception: # TODO: Fix! Sometimes KeyError in fitness (path problem) pop.log( f"Exception at fitness calculation: \n{traceback.format_exc()}", print_result=False) warnings.warn( f"Exception at fitness calculation: \n{traceback.format_exc()}") # Set fitness to zero for genomes that have no fitness set yet for i, genome in genomes: if not genome.fitness: genome.fitness = 0.0 # Gather and report statistics best = None for g in itervalues(pop.population): if best is None or g.fitness > best.fitness: best = g pop.reporters.post_evaluate(population=pop.population, species=pop.species, best_genome=best, logger=pop.log) # Update the population's best_genome genomes = sorted(pop.population.items(), key=lambda x: x[1].fitness, reverse=True) pop.best_genome_hist[ pop.generation] = genomes[:pop.config.population.genome_elitism] if pop.best_genome is None or best.fitness > pop.best_genome.fitness: pop.best_genome = best # Let population evolve pop.evolve() # End generation pop.reporters.end_generation(population=pop.population, name=str(pop), species_set=pop.species, logger=pop.log)
def reproduce(self, config, species, pop_size, generation, exp, syllabus): all_fitnesses = [] for sid, s in iteritems(species.species): all_fitnesses.extend(m.fitness for m in itervalues(s.members)) min_fitness = min(all_fitnesses) max_fitness = max(all_fitnesses) fitness_range = max(1.0, max_fitness - min_fitness) num_remaining = 0 species_fitness = [] avg_adjusted_fitness = 0.0 for sid, s, stagnant in self.stagnation.update(species, generation): if stagnant: self.reporters.species_stagnant(sid, s) else: num_remaining += 1 msf = mean([m.fitness for m in itervalues(s.members)]) s.adjusted_fitness = (msf - min_fitness) / fitness_range species_fitness.append((sid, s, s.fitness)) avg_adjusted_fitness += s.adjusted_fitness if 0 == num_remaining: species.species = {} return [] avg_adjusted_fitness /= len(species_fitness) self.reporters.info( "Average adjusted fitness: {:.3f}".format(avg_adjusted_fitness)) spawn_amounts = [] for sid, s, sfitness in species_fitness: spawn = len(s.members) if sfitness > avg_adjusted_fitness: spawn = max(spawn + 2, spawn * 1.1) else: spawn = max(spawn * 0.9, 2) spawn_amounts.append(spawn) total_spawn = sum(spawn_amounts) norm = pop_size / total_spawn spawn_amounts = [int(round(n * norm)) for n in spawn_amounts] new_population = {} species.species = {} learning_function = None if exp.learning_function == 'BP': learning_function = backpropagation elif exp.learning_function == 'BT': learning_function = batch has_learning = not learning_function is None if has_learning: (writer, lessons) = syllabus if exp.syllabus_source == 'EXP': lessons_to_learn = random.sample( lessons, min(exp.syllabus_size, len(lessons))) elif exp.syllabus_source == 'RND': lessons_to_learn = lessons for spawn, (sid, s, sfitness) in zip(spawn_amounts, species_fitness): spawn = max(spawn, self.elitism) if spawn <= 0: continue old_members = list(iteritems(s.members)) s.members = {} species.species[sid] = s old_members.sort(reverse=True, key=lambda x: x[1].fitness) if self.elitism > 0: for i, m in old_members[:self.elitism]: new_population[i] = m spawn -= 1 if spawn <= 0: continue repro_cutoff = int( math.ceil(self.survival_threshold * len(old_members))) repro_cutoff = max(repro_cutoff, 2) old_members = old_members[:repro_cutoff] if has_learning and (exp.learning_target == 'Ambos' or exp.learning_target == 'Pais'): for (_, g) in old_members: if g != writer: learning_function(g, lessons_to_learn, exp.learning_rate) while spawn > 0: spawn -= 1 parent1_id, parent1 = random.choice(old_members) parent2_id, parent2 = random.choice(old_members) gid = self.genome_indexer.next() child = config.genome_type(gid) child.configure_crossover(parent1, parent2, config.genome_config) child.mutate(config.genome_config) child.net = FeedForwardNetwork.create(child, self.config) if has_learning and (exp.learning_target == 'Ambos' or exp.learning_target == 'Filhos'): if exp.children_inclusion == 'Inicial' or ( exp.children_inclusion == 'Tardia' and generation >= exp.generations / 2): learning_function(child, lessons_to_learn, exp.learning_rate) new_population[gid] = child self.ancestors[gid] = (parent1_id, parent2_id) return new_population
def neat_manual_looping(pop, generation_limit=None): if pop.config.no_fitness_termination and (generation_limit is None): raise RuntimeError( "Cannot have no generational limit with no fitness termination") yield k = 0 while generation_limit is None or k < generation_limit: k += 1 pop.reporters.start_generation(pop.generation) genomes = list(iteritems(pop.population)) finesses = yield genomes for (genome_id, genome), fitness in zip(genomes, finesses): genome.fitness = fitness # Gather and report statistics. best = None for g in itervalues(pop.population): if best is None or g.fitness > best.fitness: best = g pop.reporters.post_evaluate(pop.config, pop.population, pop.species, best) # Track the best genome ever seen. if pop.best_genome is None or best.fitness > pop.best_genome.fitness: pop.best_genome = best if not pop.config.no_fitness_termination: # End if the fitness threshold is reached. fv = pop.fitness_criterion(g.fitness for g in itervalues(pop.population)) if fv >= pop.config.fitness_threshold: pop.reporters.found_solution(pop.config, pop.generation, best) break # Create the next generation from the current generation. pop.population = pop.reproduction.reproduce(pop.config, pop.species, pop.config.pop_size, pop.generation) # Check for complete extinction. if not pop.species.species: pop.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if pop.config.reset_on_extinction: pop.population = pop.reproduction.create_new( pop.config.genome_type, pop.config.genome_config, pop.config.pop_size) else: raise CompleteExtinctionException() # Divide the new population into species. pop.species.speciate(pop.config, pop.population, pop.generation) pop.reporters.end_generation(pop.config, pop.population, pop.species) pop.generation += 1 if pop.config.no_fitness_termination: pop.reporters.found_solution(pop.config, pop.generation, pop.best_genome)
import neat import itertools from neat.six_util import itervalues import game_ret from collections import Counter CONFIG = neat.Config(neat.DefaultGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, 'config-feedforward') NUM_CHAMPIONS = 5 match_files = ["match190", "match193", "match196", "match199"] mismatch_files = ["mismatch190", "mismatch193", "mismatch196", "mismatch199"] matches = [[ g for g in itervalues( neat.checkpoint.Checkpointer.restore_checkpoint(match_file).population) ] for match_file in match_files] mismatches = [[ g for g in itervalues( neat.checkpoint.Checkpointer.restore_checkpoint( mismatch_file).population) ] for mismatch_file in mismatch_files] def get_champions(gs): champions = [] for i in range(NUM_CHAMPIONS): champion = max(gs, key=lambda g: g.fitness) gs.remove(max(gs, key=lambda g: g.fitness)) champions.append(neat.nn.FeedForwardNetwork.create(champion, CONFIG))
def process(): global program_state, p global config global obj_topo, road_topo global CAR_ROW_START, CAR_COL_START, CAR_SHAPE_COL, sand_stall global frame, new_distance, distance_stall, old_distance, base_distance global old_time, new_time, base_time global fitness_val, genome_fitness, generation_fitness, generation_fitness_index, generation_average_fitness global genome_rank, generation_rank, generation_rank_index global generation_count, genome_count, genome_list, genome, current_net, best_genome global key_press global winner print(program_state) # Stop the data processing if the agent is currently training if program_state == STATE_BUFFERING: time.sleep(0.1) pass elif program_state == STATE_BEGIN: program_state = STATE_BUFFERING # Report the current generation p.reporters.start_generation(p.generation) genome_list = list(iteritems(p.population)) program_state = STATE_PRESSING_F1 time.sleep(0.1) pass elif program_state == STATE_PRESSING_F1: time.sleep(0.1) pass elif program_state == STATE_RESETTED: program_state = STATE_BUFFERING # Grab the genome if there is still genome to check if genome_count < NUM_GENOMES: _, genome = genome_list[genome_count] current_net = neat.nn.FeedForwardNetwork.create(genome, config) program_state = STATE_EVALUATING else: genome_count = 0 program_state = STATE_POST_PROCESSING pass # Sleep for the stability of the algorithm time.sleep(0.1) elif program_state == STATE_EVALUATING: old_distance = new_distance old_time = new_time # Get the data from the request start = time.time() data = request.body.read().decode('utf8') data = json.loads(data) # pixels = data["Pixels"] speed = data["Speed"] new_time = data["Time"] if new_time > BASE_TIME: new_time = 0 new_distance = data["Distance"] if new_distance == BASE_DISTANCE: new_distance = 0 rank = data["Rank"] nitro = data["Nitro"] # Determine a set of background color, we will try to remove this color from the game. road_topo = topo_from_original_array(pixels)[: TOPO_SHAPE[0] // 2, :] end = time.time() # Calculate decision from current net input_arr = road_topo.flatten() key_press = generate_key_presses(current_net, input_arr) + "0" # Don't press F1 to reset # Update frame counter and distance_stall and time if new_distance < old_distance: base_distance += BASE_DISTANCE if new_distance == old_distance: distance_stall += 1 else: distance_stall = 0 frame += 1 if new_time < old_time: base_time += BASE_TIME if not road_topo[CAR_ROW_START + SHAPE_ROW, CAR_COL_START: CAR_COL_START + SHAPE_COL].any(): sand_stall += 1 else: sand_stall = 0 fitness_val = (base_distance + new_distance) * DIST_C \ - (base_time + new_time) * TIME_C \ - rank * RANK_C \ + BASE_FITNESS # Print necessary information os.system('cls') print("Current generation: " + str(generation_count) + "/" + str(NUM_GENERATIONS)) print("----") print_topo(road_topo) print(end - start) print_key_presses(key_press, BUTTONS) print("Speed: " + str(speed)) print("Time: " + str(base_time + new_time)) print("Distance: " + str(base_distance + new_distance)) print("Rank: " + str(rank)) print("Nitro: " + str(nitro)) print("Current genome: " + str(genome_count + 1) + "/" + str(NUM_GENOMES)) print("Fitness value: " + str(fitness_val)) print('--------------') print('| Gen | Best Agent | Score | Rank | Avg Score |') print('|----------------------------------------------------|') for i in range(len(generation_fitness)): print("| {:3d} | {:10d} | {:10d} | {:4d} | {:11.3f} |".format( \ i, generation_fitness_index[i], generation_fitness[i], generation_rank[i], generation_average_fitness[i])) # Stop the running if stall limit is reached if distance_stall > STALL_LIMIT: genome.fitness = fitness_val print("- Evaluated Genome " + str(genome_count) + ": " + str(fitness_val)) # Add a bunch of statistical variables genome_fitness.append(fitness_val) genome_rank.append(rank) # Reset reset_variables() genome_count += 1 program_state = STATE_PRESSING_F1 # Return something cuz a post go with a get pass elif program_state == STATE_POST_PROCESSING: program_state = STATE_BUFFERING # Gather and report statistics. best = None for g in itervalues(p.population): if best is None or g.fitness > best.fitness: best = g p.reporters.post_evaluate(p.config, p.population, p.species, best) # Track the best gen ome ever seen. if p.best_genome is None or best.fitness > p.best_genome.fitness: p.best_genome = best # End if the fitness threshold is reached. fv = p.fitness_criterion(g.fitness for g in itervalues(p.population)) if fv >= p.config.fitness_threshold: p.reporters.found_solution(p.config, p.generation, best) program_state = STATE_RETURN_BEST # Create the next generation from the current generation. p.population = p.reproduction.reproduce(p.config, p.species, p.config.pop_size, p.generation) # Check for complete extinction. if not p.species.species: p.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if p.config.reset_on_extinction: p.population = p.reproduction.create_new(p.config.genome_type, p.config.genome_config, p.config.pop_size) else: raise neat.CompleteExtinctionException() # Divide the new population into species. p.species.speciate(p.config, p.population, p.generation) p.reporters.end_generation(p.config, p.population, p.species) p.generation += 1 # Add the best genome value into the statistics generation_fitness.append(max(genome_fitness)) generation_average_fitness.append(np.average(genome_fitness)) generation_fitness_index.append(np.argmax(genome_fitness)) genome_fitness = [] generation_rank.append(min(genome_rank)) generation_rank_index.append(np.argmin(genome_rank)) genome_rank = [] # Increment generation count and switch state np.savetxt("best_genome_by_generation.npy",generation_fitness_index) generation_count += 1 if generation_count == NUM_GENERATIONS: program_state = STATE_RETURN_BEST else: program_state = STATE_BEGIN pass elif program_state == STATE_RETURN_BEST: best_genome = p.best_genome program_state = STATE_TEST_BEST pass elif program_state == STATE_TEST_BEST: print("Should start testing stuff now") pass return("Finished")
def create(genome, config, quantize=8): """ Receives a genome and returns its phenotype (a FeedForwardNetwork). """ idx = 0 valueIDMap_neat2fpga = dict() valueIDMap_fpga2neat = dict() for o_id in config.genome_config.input_keys + config.genome_config.output_keys: valueIDMap_neat2fpga[o_id] = idx valueIDMap_fpga2neat[idx] = o_id idx += 1 # Gather expressed connections. connections = [ cg.key for cg in itervalues(genome.connections) if cg.enabled ] layers = feed_forward_layers(config.genome_config.input_keys, config.genome_config.output_keys, connections) layer = [] for c in connections: for i in range(2): if c[i] not in valueIDMap_neat2fpga: o_id = c[i] valueIDMap_neat2fpga[o_id] = idx valueIDMap_fpga2neat[idx] = o_id idx += 1 total_nodes = idx command_layer = len(layers) + 1 command_init_total_node = len(valueIDMap_neat2fpga) command_init_in_nodes = len(config.genome_config.input_keys) command_s = [ command_layer, command_init_total_node, command_init_in_nodes ] resp_s = [0] * total_nodes bias_s = [0] * total_nodes for idx in range(command_init_in_nodes, total_nodes, 1): o_id = valueIDMap_fpga2neat[idx] ng = genome.nodes[o_id] resp_s[idx] = int(ng.response * 2**quantize) bias_s[idx] = int(ng.bias * 2**(quantize + quantize + quantize)) serial_in_pre = command_s serial_in_post = [] for idx, layer in enumerate(layers): inputVectorThisLayer = [] i_id = [] outputVectorThisLayer = [] o_id = [] key_weight_dict = dict() for node in layer: for conn_key in connections: inode, onode = conn_key if onode == node: if inode not in inputVectorThisLayer: inputVectorThisLayer.append(inode) i_id.append(valueIDMap_neat2fpga[inode]) if onode not in outputVectorThisLayer: outputVectorThisLayer.append(onode) o_id.append(valueIDMap_neat2fpga[onode]) indx_i = inputVectorThisLayer.index(inode) indx_o = outputVectorThisLayer.index(onode) cg = genome.connections[conn_key] key_weight_dict.update({(indx_i, indx_o): cg.weight}) weight_matrix = np.zeros( (len(outputVectorThisLayer), len(inputVectorThisLayer))) for key, val in key_weight_dict.items(): indx_i, indx_o = key weight_matrix[indx_o][indx_i] = int(val * 2**quantize) # if idx ==0: # print("init_in_total: ", command_init_in_nodes, "in_first :", len(inputVectorThisLayer)) serial_in_pre.append(len(inputVectorThisLayer)) serial_in_pre.append(len(outputVectorThisLayer)) serial_in_post = serial_in_post + o_id + i_id + list( np.ravel(weight_matrix)) serial_in_pre = serial_in_pre + resp_s + bias_s return FeedForwardNetworkFPGA(serial_in_pre, serial_in_post, command_init_in_nodes, len(layer), quantize)
def reset(self): """Reset all neurons to their default state.""" for n in itervalues(self.neurons): n.reset()
def train( population: Population, game_config: Config, games: list, iterations: int, unused_cpu: int = 0, save_interval: int = 10, ): """Train the population on the requested number of iterations. Manual adaptation of main's train().""" population.log("\n===> TRAINING <===\n") multi_env = get_multi_env(pop=population, game_config=game_config) msg = f"Repetitive evaluating on games: {games} for {iterations} iterations" population.log(msg, print_result=False) # Iterate and evaluate over the games saved = True for iteration in range(iterations): # Set and randomize the games multi_env.set_games(games, noise=True) # Prepare the generation's reporters for the generation population.reporters.start_generation(gen=population.generation, logger=population.log) # Fetch the dictionary of genomes genomes = list(iteritems(population.population)) # Initialize the evaluation-pool pool = mp.Pool(mp.cpu_count() - unused_cpu) manager = mp.Manager() return_dict = manager.dict() for genome in genomes: pool.apply_async(func=multi_env.eval_genome, args=(genome, return_dict)) pool.close() # Close the pool pool.join() # Postpone continuation until everything is finished # Calculate the fitness from the given return_dict fitness = calc_pop_fitness( fitness_cfg=population.config.evaluation, game_cfg=game_config.game, game_obs=return_dict, gen=population.generation, ) for i, genome in genomes: genome.fitness = fitness[i] # Gather and report statistics best = None for g in itervalues(population.population): if best is None or g.fitness > best.fitness: best = g population.reporters.post_evaluate(population=population.population, species=population.species, best_genome=best, logger=population.log) # Update the population's best_genome genomes = sorted(population.population.items(), key=lambda x: x[1].fitness, reverse=True) population.best_fitness[population.generation] = genomes[0][1].fitness population.best_genome_hist[population.generation] = genomes[0] population.best_genome = best # Let population evolve population.evolve() # Update the genomes such all have one hidden node for g in population.population.values(): n_hidden, _ = g.size() while n_hidden < 1: g.mutate_add_connection(population.config.genome) n_hidden, _ = g.size() # End generation population.reporters.end_generation(population=population.population, name=str(population), species_set=population.species, logger=population.log) # Save the population if (iteration + 1) % save_interval == 0: population.save() saved = True else: saved = False # Make sure that last iterations saves if not saved: population.save()
def syn_singularity(self, num_replacements, my_url, stats, gen_best, avg_score, rep, config): # requests the last optimization state TODO: HACER PROCESS CONFIGURABLE Y POR HASH no por id for multi-process res = requests.get( my_url + "/processes/1?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph" ) cont = res.json() # print results of request print('\ncurrent_block_performance =', cont['result'][0]['current_block_performance']) print('\nlast_optimum_id =', cont['result'][0]['last_optimum_id']) last_optimum_id = cont['result'][0]['last_optimum_id'] # calcualte the best fitness as the weitgthed average of the best performers best_genomes = stats.best_unique_genomes(num_replacements) reps_local = [] reps = [gen_best] accum = 0.0 countr = 0 for g in best_genomes: if g.fitness is not None: accum = accum + g.fitness countr = countr + 1 if countr > 0: best_fitness = (3 * avg_score + (accum / countr)) / 4 else: best_fitness = (avg_score) print("\nPerformance = ", best_fitness) print("*********************************************************") # Si el perf reportado pop2_champion_fitness > pop1_champion_fitness de validation training if cont['result'][0]['current_block_performance'] > best_fitness: # hace request GetParameter(id) res_p = requests.get( my_url + "/parameters/" + str(last_optimum_id) + "?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph" ) cont_param = res_p.json() # descarga el checkpoint del link de la respuesta si cont.parameter_link print('Parameter Downloaded') print('\nmigrations =') if cont_param['result'][0]['parameter_link'] is not None: genom_data = requests.get( cont_param['result'][0]['parameter_link']).content with open('remote_reps', 'wb') as handler: handler.write(genom_data) handler.close() # carga genom descargado en nueva población pop2 with open('remote_reps', 'rb') as f: remote_reps = pickle.load(f) # OP.MIGRATION: Reemplaza el peor de la especie pop1 más cercana por el nuevo chmpion de pop2 como http://neo.lcc.uma.es/Articles/WRH98.pdf # para cada elemento de remote_reps, busca el closer, si remote fitness > local, lo reemplaza for i in range(len(remote_reps)): closer = None min_dist = None # initialize for less fit search less_fit = None less_fitness = 10000 for g in itervalues(self.population): if g not in remote_reps: dist = g.distance(remote_reps[i], config.genome_config) if dist is None: dist = 100000000 else: dist = 100000000 # do not count already migrated remote_reps if closer is None or min_dist is None: closer = deepcopy(g) min_dist = dist if dist < min_dist: closer = deepcopy(g) min_dist = dist if g.fitness is None: g.fitness = -10 if g.fitness < less_fitness: less_fitness = g.fitness less_fit = deepcopy(g) # For the best genom in position 0 if i == 0 and remote_reps[0].fitness > gen_best.fitness: if closer is None: # busca el pop con el menor fitness closer = less_fit tmp_genom = deepcopy(remote_reps[i]) # Hack: overwrites original genome key with the replacing one tmp_genom.key = closer.key self.population[closer.key] = deepcopy(tmp_genom) print("gen_best=", closer.key) self.best_genome = deepcopy(tmp_genom) #gen_best = deepcopy(tmp_genom) else: # si el remote fitness>local, reemplazar el remote de pop2 en pop1 if closer is None: # busca el pop con el menor fitness closer = less_fit if closer is not None: if closer not in remote_reps: if closer.fitness is None: closer.fitness = less_fitness if closer.fitness is not None and remote_reps[ i].fitness is not None: if remote_reps[i].fitness > closer.fitness: tmp_genom = deepcopy(remote_reps[i]) # Hack: overwrites original genome key with the replacing one tmp_genom.key = closer.key self.population[closer.key] = deepcopy( tmp_genom) print("Replaced=", closer.key) # actualiza gen_best y best_genome al remoto self.best_genome = deepcopy(tmp_genom) #gen_best = deepcopy(tmp_genom) if closer.fitness is None: tmp_genom = deepcopy(remote_reps[i]) # Hack: overwrites original genome key with the replacing one tmp_genom.key = len(self.population) + 1 self.population[tmp_genom.key] = tmp_genom print("Created Por closer.fitness=NONE : ", tmp_genom.key) # actualiza gen_best y best_genome al remoto self.best_genome = deepcopy(tmp_genom) #gen_best = deepcopy(tmp_genom) else: #si closer está en remote_reps es porque no hay ningun otro cercano así que lo adiciona tmp_genom = deepcopy(remote_reps[i]) # Hack: overwrites original genome key with the replacing one tmp_genom.key = len(self.population) + 1 self.population[tmp_genom.key] = tmp_genom print("Created por Closer in rempte_reps=", tmp_genom.key) # actualiza gen_best y best_genome al remoto self.best_genome = deepcopy(tmp_genom) #gen_best = deepcopy(tmp_genom) #ejecuta speciate self.species.speciate(config, self.population, self.generation) print("\nSpeciation after migration done") # Si el perf reportado es menor pero no igual al de pop1 if cont['result'][0]['current_block_performance'] < best_fitness: # Obtiene remote_reps # hace request GetParameter(id) remote_reps = None res_p = requests.get( my_url + "/parameters/" + str(last_optimum_id) + "?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph" ) cont_param = res_p.json() # descarga el checkpoint del link de la respuesta si cont.parameter_link print('\nNEW OPTIMUM - cont_param =', cont_param) #print('\nmigrations =') if cont_param['result'][0]['parameter_link'] is not None: genom_data = requests.get( cont_param['result'][0]['parameter_link']).content with open('remote_reps', 'wb') as handler: handler.write(genom_data) handler.close() # carga genom descargado en nueva población pop2 with open('remote_reps', 'rb') as f: remote_reps = pickle.load(f) #Guarda los mejores reps reps_local = [] reps = [gen_best] # Para los mejores genes best_genomes = stats.best_unique_genomes(num_replacements) for g in best_genomes: #print("\ns=",s) if g not in reps_local: reps_local.append(g) reps_local[len(reps_local) - 1] = deepcopy(g) # TODO: Conservar los mejores reps, solo reemplazarlos por los mas cercanos if remote_reps is None: for l in reps_local: reps.append(l) reps[len(reps) - 1] = deepcopy(l) else: # para cada reps_local l for l in reps_local: # busca el closer a l en reps_remote for i in range(len(remote_reps)): closer = None min_dist = None for g in reps_local: if g not in remote_reps: dist = g.distance(remote_reps[i], config.genome_config) else: dist = 100000000 # do not count already migrated remote_reps if closer is None or min_dist is None: closer = deepcopy(g) min_dist = dist if dist < min_dist: closer = deepcopy(g) min_dist = dist # si closer is in reps if closer in reps: # adiciona l a reps si ya no estaba en reps if l not in reps: reps.append(l) reps[len(reps) - 1] = deepcopy(l) # sino else: # si l tiene más fitness que closer, if closer.fitness is not None and l.fitness is not None: if l.fitness > closer.fitness: # adiciona l a reps si ya no estaba en reps if l not in reps: reps.append(l) reps[len(reps) - 1] = deepcopy(l) # sino else: # adiciona closer a reps si ya no estaba en reps if l not in reps: reps.append(closer) reps[len(reps) - 1] = deepcopy(closer) # Guarda checkpoint de los representatives de cada especie y lo copia a ubicación para servir vía syn. # rep.save_checkpoint(config,pop,neat.DefaultSpeciesSet,rep.current_generation) print("\nreps=", reps) filename = '{0}{1}'.format("reps-", rep.current_generation) with open(filename, 'wb') as f: pickle.dump(reps, f) # # Hace request de CreateParam a syn form_data = { "process_hash": "ph", "app_hash": "ah", "parameter_link": my_url + "/genoms/" + filename, "parameter_text": self.best_genome.key, "parameter_blob": "", "validation_hash": "", "hash": "h", "performance": best_fitness, "redir": "1", "username": "******", "pass_hash": "$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q" } # TODO: COLOCAR DIRECCION CONFIGURABLE res = requests.post( my_url + "/parameters?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph", data=form_data) res_json = res.json() # TODO FIN: FUNCION DE SINCRONIZACION CON SINGULARITY return 0
def continue_processing(self, current_genome_list): print("Continue processing") # self.population = {} for genome_key in current_genome_list: print("Currently opening: ", genome_key) with open( neat_path + "pklDumps/genome_" + str(genome_key) + ".pkl", 'rb') as nnPklRead: current_gen = pickle.load(nnPklRead) self.population[genome_key].fitness = current_gen.fitness best = None for g in itervalues(self.population): if best is None or g.fitness > best.fitness: best = g self.reporters.post_evaluate(self.config, self.population, self.species, best) print("Chosen current best as: ", best.key) # Track the best genome ever seen. if self.best_genome is None or best.fitness > self.best_genome.fitness: self.best_genome = best print("Chosen overall best as: ", self.best_genome.key) if not self.config.no_fitness_termination: # End if the fitness threshold is reached. fv = self.fitness_criterion(g.fitness for g in itervalues(self.population)) if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, best) return # Create the next generation from the current generation. self.population = self.reproduction.reproduce(self.config, self.species, self.config.pop_size, self.generation) # Check for complete extinction. if not self.species.species: self.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config.reset_on_extinction: self.population = self.reproduction.create_new( self.config.genome_type, self.config.genome_config, self.config.pop_size) else: raise CompleteExtinctionException() # Divide the new population into species. self.species.speciate(self.config, self.population, self.generation) self.reporters.end_generation(self.config, self.population, self.species) self.generation += 1 # continue to top of loop for next k self.dump_genomes_pop(current_genome_list)
def run(self, fitness_function, n=None): # Variables needed to save archive on each update winner_name_prefix = "archive_checkpoint_" + self.winner_name if self.config.no_fitness_termination and (n is None): raise RuntimeError("Cannot have no generational limit with no fitness termination") k = 0 while n is None or k < n: k += 1 self.reporters.start_generation(self.generation) not_evaluated = {} evaluated = [] # len(population) = 2*pop_size for gid, g in self.population.items(): if g.fitness is None: not_evaluated[gid] = g else: evaluated.append((gid, g)) # Evaluate all genomes using the user-provided function. fitness_function(list(iteritems(not_evaluated)), self.config) self.KNNdistances(self.population, self.novelty_archive, self.n_neighbors) if len(evaluated) != 0: self.species.speciate(self.config, self.population, self.generation) dim = 0 max_spec_dim = 0 max_sid = -1 for sid, s in iteritems(self.species.species): s.members = self.get_best_n_members(s.members, math.ceil(len(s.members) / 2)) d = len(s.members) if d > max_spec_dim: max_spec_dim = d max_sid = sid dim += d diff = dim - self.config.pop_size if diff > 0 and diff > max_spec_dim: s = self.species.species[max_sid] s.members = self.get_best_n_members(s.members, len(s.members) - diff) new_population = {} for sid, s in iteritems(self.species.species): for gid, g in s.members.items(): new_population[gid] = g self.population = new_population self.species.speciate(self.config, self.population, self.generation) # Check for complete extinction. if not self.species.species: self.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config.reset_on_extinction: self.population = self.reproduction.create_new(self.config.genome_type, self.config.genome_config, self.config.pop_size) else: raise CompleteExtinctionException() # Gather and report statistics. best = None for g in itervalues(self.population): if best is None or g.dist > best.dist: best = g if g.dist > self.novelty_threshold: if g not in self.novelty_archive: self.novelty_archive.append(g) print("Distanza di aggiunta: ", g.dist) self.n_add_archive += 1 self.last_archive_modified = self.generation with open(winner_name_prefix, "wb") as f: pickle.dump(self.novelty_archive, f) self.reporters.post_evaluate(self.config, self.population, self.species, best) if not self.config.no_fitness_termination: # End if the fitness threshold is reached. fv = self.fitness_criterion(g.dist for g in itervalues(self.population)) if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, best) break # Create the next generation from the current generation. self.population = self.reproduction.reproduce(self.config, self.species, self.config.pop_size, self.generation) # Check for complete extinction. if not self.species.species: self.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config.reset_on_extinction: self.population = self.reproduction.create_new(self.config.genome_type, self.config.genome_config, self.config.pop_size) else: raise CompleteExtinctionException() # Divide the new population into species. self.species.speciate(self.config, self.population, self.generation) self.reporters.end_generation(self.config, self.population, self.species) time_diff = self.generation - self.last_archive_modified if time_diff > 60: self.novelty_threshold -= self.novelty_threshold * 0.05 if self.n_add_archive > 4 and time_diff <= 30: self.novelty_threshold += self.novelty_threshold * 0.05 self.n_add_archive = 0 if time_diff > 30: self.n_add_archive = 0 self.reporters.info("Novelty's archive size: {}\n".format(len(self.novelty_archive))) if len(self.novelty_archive) > 0: self.reporters.info("Archive's best: {}".format( max(self.novelty_archive, key=lambda x: x.fitness[0]).fitness[0])) else: self.reporters.info("Archive's best: 0") self.generation += 1 if self.config.no_fitness_termination: self.reporters.found_solution(self.config, self.generation, self.best_genome) return self.novelty_archive
def run(self, fitness_function, n=None): # Variables needed to save archive on each update winner_name_prefix = "archive_checkpoint_" + self.winner_name if self.config.no_fitness_termination and (n is None): raise RuntimeError( "Cannot have no generational limit with no fitness termination" ) k = 0 while n is None or k < n: k += 1 self.reporters.start_generation(self.generation) # Evaluate all genomes using the user-provided function. fitness_function(list(iteritems(self.population)), self.config) self.KNNdistances(self.population, self.novelty_archive, self.n_neighbors) # Gather and report statistics. best = None for g in itervalues(self.population): if best is None or g.dist > best.dist: best = g if g.dist > self.novelty_threshold: if g not in self.novelty_archive: self.novelty_archive.append(g) print("Distanza di aggiunta: ", g.dist) self.n_add_archive += 1 self.last_archive_modified = self.generation with open(winner_name_prefix, "wb") as f: pickle.dump(self.novelty_archive, f) self.reporters.post_evaluate(self.config, self.population, self.species, best) # if self.last_genome_added is None: # self.last_genome_added = best # Track the best genome ever seen. # if self.last_genome_added.fitness != best.fitness and best.dist > self.novelty_threshold: # print("Distanza di aggiunta: ", best.dist) # self.last_genome_added = best # self.last_archive_modified = self.generation # self.n_add_archive += 1 # self.novelty_archive.append(best) # with open(winner_name_prefix, "wb") as f: # pickle.dump(self.novelty_archive, f) if not self.config.no_fitness_termination: # End if the fitness threshold is reached. fv = self.fitness_criterion( g.dist for g in itervalues(self.population)) if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, best) break # Create the next generation from the current generation. self.population = self.reproduction.reproduce( self.config, self.species, self.config.pop_size, self.generation) # Check for complete extinction. if not self.species.species: self.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config.reset_on_extinction: self.population = self.reproduction.create_new( self.config.genome_type, self.config.genome_config, self.config.pop_size) else: raise CompleteExtinctionException() # Divide the new population into species. self.species.speciate(self.config, self.population, self.generation) self.reporters.end_generation(self.config, self.population, self.species) time_diff = self.generation - self.last_archive_modified if time_diff > 60: self.novelty_threshold -= self.novelty_threshold * 0.05 if self.n_add_archive > 4 and time_diff <= 30: self.novelty_threshold += self.novelty_threshold * 0.05 self.n_add_archive = 0 if time_diff > 30: self.n_add_archive = 0 self.reporters.info("Novelty's archive size: {}\n".format( len(self.novelty_archive))) self.generation += 1 if self.config.no_fitness_termination: self.reporters.found_solution(self.config, self.generation, self.best_genome) return self.novelty_archive
def reproduce(self, config, species, pop_size, generation): """ Handles creation of genomes, either from scratch or by sexual or asexual reproduction from parents. """ # TODO: I don't like this modification of the species and stagnation objects, # because it requires internal knowledge of the objects. # Filter out stagnated species, collect the set of non-stagnated # species members, and compute their average adjusted fitness. # The average adjusted fitness scheme (normalized to the interval # [0, 1]) allows the use of negative fitness values without # interfering with the shared fitness scheme. all_fitnesses = [] remaining_species = [] old_population = [] for stag_sid, stag_s, stagnant in self.stagnation.update( species, generation): old_population.append(itervalues(stag_s.members)) if stagnant: self.reporters.species_stagnant(stag_sid, stag_s) else: all_fitnesses.extend(m.fitness for m in itervalues(stag_s.members)) remaining_species.append(stag_s) # The above comment was not quite what was happening - now getting fitnesses # only from members of non-stagnated species. # No species left. if not remaining_species: species.species = {} return {} # was [] # Find minimum/maximum fitness across the entire population, for use in # species adjusted fitness computation. min_fitness = min(all_fitnesses) max_fitness = max(all_fitnesses) # Do not allow the fitness range to be zero, as we divide by it below. # TODO: The ``1.0`` below is rather arbitrary, and should be configurable. fitness_range = max(1.0, max_fitness - min_fitness) for afs in remaining_species: # Compute adjusted fitness. msf = mean([m.fitness for m in itervalues(afs.members)]) af = (msf - min_fitness) / fitness_range afs.adjusted_fitness = af adjusted_fitnesses = [s.adjusted_fitness for s in remaining_species] avg_adjusted_fitness = mean(adjusted_fitnesses) # type: float self.reporters.info( "Average adjusted fitness: {:.3f}".format(avg_adjusted_fitness)) # Compute the number of new members for each species in the new generation. previous_sizes = [len(s.members) for s in remaining_species] min_species_size = self.reproduction_config.min_species_size # Isn't the effective min_species_size going to be max(min_species_size, # self.reproduction_config.elitism)? That would probably produce more accurate tracking # of population sizes and relative fitnesses... doing. TODO: document. min_species_size = max(min_species_size, self.reproduction_config.elitism) spawn_amounts = self.compute_spawn(adjusted_fitnesses, previous_sizes, pop_size, min_species_size) # Bandit Update # TODO Reconsider reward scheme to be invariant of actual fitness value # Percentage improvement to fitness? # Fitness improvement as a fraction of best improvement? - seems good, then one mutation is always 1, rest are < 1 # How to reward or perceive "failure"? Same as fraction but for decreases? # 1 if best arm in generation else 0? if generation > 0: mutation_delta = [] for mutant, old_fitness, mutations in self.records[-1]: fit_delta = mutant.fitness - old_fitness for m in mutations: mutation_delta.append((m, fit_delta)) # print(mutation_delta) self.bandit.update_all(mutation_delta) # if m not in mutations_deltas: # mutations_deltas[m] = [fit_delta] # else: # mutations_deltas[m].append(fit_delta) # for mutations, deltas in mutations_deltas.items(): # self.bandit.update(mutations, deltas) # len deltas can be removed, the number of rewards is the number of plays # print([(g.key, g.fitness-f, m) for g,f,m in self.records[-1]]) self.reporters.post_reproduction(config, self.bandit, None) self.records.append([]) new_population = {} species.species = {} for spawn, s in zip(spawn_amounts, remaining_species): # If elitism is enabled, each species always at least gets to retain its elites. spawn = max(spawn, self.reproduction_config.elitism) assert spawn > 0 # The species has at least one member for the next generation, so retain it. old_members = list(iteritems(s.members)) s.members = {} species.species[s.key] = s # Sort members in order of descending fitness. old_members.sort(reverse=True, key=lambda x: x[1].fitness) # Transfer elites to new generation. if self.reproduction_config.elitism > 0: for i, m in old_members[:self.reproduction_config.elitism]: new_population[i] = m spawn -= 1 if spawn <= 0: continue # Only use the survival threshold fraction to use as parents for the next generation. repro_cutoff = int( math.ceil(self.reproduction_config.survival_threshold * len(old_members))) # Use at least two parents no matter what the threshold fraction result is. repro_cutoff = max(repro_cutoff, 2) old_members = old_members[:repro_cutoff] # Randomly choose parents and produce the number of offspring allotted to the species. while spawn > 0: spawn -= 1 parent1_id, parent1 = choice(old_members) parent2_id, parent2 = (parent1_id, parent1) gid = next(self.genome_indexer) child = config.genome_type(gid) child.configure_crossover(parent1, parent2, config.genome_config) mutation_directives = self.bandit.play(generation) child.mutate(config.genome_config, mutation_directives) new_population[gid] = child self.ancestors[gid] = (parent1_id, parent2_id) self.records[-1].append( (child, parent1.fitness, mutation_directives)) return new_population
def run(self, fitness_function, n=None): """ Runs NEAT's genetic algorithm for at most n generations. If n is None, run until solution is found or extinction occurs. The user-provided fitness_function must take only two arguments: 1. The population as a list of (genome id, genome) tuples. 2. The current configuration object. The return value of the fitness function is ignored, but it must assign a Python float to the `fitness` member of each genome. The fitness function is free to maintain external state, perform evaluations in parallel, etc. It is assumed that fitness_function does not modify the list of genomes, the genomes themselves (apart from updating the fitness member), or the configuration object. """ if self.config.no_fitness_termination and (n is None): raise RuntimeError("Cannot have no generational limit with no fitness termination") k = 0 while n is None or k < n: if not (self.state): print("Training: break") self.reached_limit = False break k += 1 self.reporters.start_generation(self.generation) # Evaluate all genomes using the user-provided function. fitness_function(list(iteritems(self.population)), self.config) # Gather and report statistics. best = None for g in itervalues(self.population): if best is None or g.fitness > best.fitness: best = g self.reporters.post_evaluate(self.config, self.population, self.species, best) # Track the best genome ever seen. if self.best_genome is None or best.fitness > self.best_genome.fitness: self.best_genome = best if not self.config.no_fitness_termination: # End if the fitness threshold is reached. fv = self.fitness_criterion(g.fitness for g in itervalues(self.population)) if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, best) self.reached_limit = False break # Create the next generation from the current generation. self.population = self.reproduction.reproduce(self.config, self.species, self.config.pop_size, self.generation) # Check for complete extinction. if not self.species.species: self.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config.reset_on_extinction: self.population = self.reproduction.create_new(self.config.genome_type, self.config.genome_config, self.config.pop_size) else: raise CompleteExtinctionException() # Divide the new population into species. self.species.speciate(self.config, self.population, self.generation) self.reporters.end_generation(self.config, self.population, self.species) self.generation += 1 if self.config.no_fitness_termination: self.reporters.found_solution(self.config, self.generation, self.best_genome) return self.best_genome
def get_fitnesses(self): return [ m.fitness for m in itervalues(self.members) if not np.isneginf(m.fitness) ]
def run(self, fitness_function, n=None): # Variables needed to save winner winner_interval = 10 winner_name_prefix = "winner_checkpoint_" last_winner_checkpoint = 0 if self.config.no_fitness_termination and (n is None): raise RuntimeError("Cannot have no generational limit with no fitness termination") k = 0 while n is None or k < n: k += 1 self.reporters.start_generation(self.generation) start_time_gen = time.time() # Evaluate all genomes using the user-provided function. not_evaluated = {} evaluated = [] # len(population) = 2*pop_size for gid, g in self.population.items(): if g.fitness is None: not_evaluated[gid] = g else: evaluated.append((gid, g)) diff = round(time.time() - start_time_gen, 3) self.sum_times += diff fitness_function(list(iteritems(not_evaluated)), self.config) start_time_gen = time.time() if self.random_replace: i = 0 self.population = {} for gid, g in not_evaluated.items(): if len(evaluated) <= i or g.fitness > evaluated[i][1].fitness: self.population[gid] = g else: self.population[evaluated[i][0]] = evaluated[i][1] i = i + 1 self.species.speciate(self.config, self.population, self.generation) elif self.mu_lambda: self.population = [] self.population += evaluated for key, v in not_evaluated.items(): self.population.append((key, v)) self.population.sort(reverse=True, key=lambda x: x[1].fitness) self.population = from_list_to_dict(self.population[:self.config.pop_size]) self.species.speciate(self.config, self.population, self.generation) else: self.species.speciate(self.config, self.population, self.generation) dim = 0 max_spec_dim = 0 max_sid = -1 for sid, s in iteritems(self.species): s.members = self.get_best_half_members(s.members) d = len(s.members) if d > max_spec_dim: max_spec_dim = d max_sid = sid dim += d diff = dim - self.config.pop_size if diff > 0 and diff > max_spec_dim: s = self.species[max_sid] s.members = s.members[:len(s.members) - diff] # Check for complete extinction. if not self.species.species: self.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config.reset_on_extinction: self.population = self.reproduction.create_new(self.config.genome_type, self.config.genome_config, self.config.pop_size) else: raise CompleteExtinctionException() # Gather and report statistics. best = None for g in itervalues(self.population): if best is None or g.fitness > best.fitness: best = g self.reporters.post_evaluate(self.config, self.population, self.species, best) # Track the best genome ever seen. if self.best_genome is None or best.fitness > self.best_genome.fitness: self.best_genome = best # Code to save the best after winner_interval generations if self.overwrite: filename = winner_name_prefix else: filename = '{0}{1}'.format(winner_name_prefix, self.generation) last_winner_checkpoint = self.generation with open(filename, 'wb') as f: pickle.dump(self.best_genome, f) if not self.config.no_fitness_termination: # End if the fitness threshold is reached. fv = self.fitness_criterion(g.fitness for g in itervalues(self.population)) if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, best) break self.reporters.end_generation(self.config, self.population, self.species) # Create the next generation from the current generation. self.population = self.reproduction.reproduce(self.config, self.species, self.config.pop_size, self.generation) self.generation += 1 print_file("\nGen: " + str(k) + " tempo: " + str(round(time.time() - start_time_gen,3)) + " sec\n") diff = round(time.time() - start_time_gen, 3) self.sum_times += diff self.reporters.info("\nGen: " + str(k) + " tempo: " + str(diff) + " sec\n") if self.config.no_fitness_termination: self.reporters.found_solution(self.config, self.generation, self.best_genome) self.reporters.info("Computation mean time: " + str(self.sum_times / (self.generation + 1))) return self.best_genome
def create(genome, config, map_size): """ Receives a genome and returns its phenotype (a SwitchNeuronNetwork). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) input_keys = genome_config.input_keys output_keys = genome_config.output_keys # Gather inputs and expressed connections. std_inputs = {} mod_inputs = {} children = {} node_keys = set(genome.nodes.keys()) # + list(genome_config.input_keys[:]) # Here we populate the children dictionay for each unique not isolated node. for n in genome.nodes.keys(): children[n] = [] if not genome.nodes[n].is_isolated: for _ in range(1, map_size): new_idx = max(node_keys) + 1 children[n].append(new_idx) node_keys.add(new_idx) # We don't scale the output with the map size to keep passing the parameters of the network easy. # This part can be revised in the future for n in chain(input_keys, output_keys): children[n] = [] #Iterate over every connection for cg in itervalues(genome.connections): #If it's not enabled don't include it if not cg.enabled: continue i, o = cg.key #If neither node is required for output then skip the connection if o not in required and i not in required: continue #Find the map corresponding to each node of the connection in_map = [i] + children[i] out_map = [o] + children[o] #If the connection is modulatory and the output neuron a switch neuron then the new weights are stored #in the mod dictionary. We assume that only switch neurons have a modulatory part. if cg.is_mod and genome.nodes[o].is_switch: node_inputs = mod_inputs else: node_inputs = std_inputs for n in out_map: if n not in node_inputs.keys(): node_inputs[n] = [] if len(in_map) == map_size and len(out_map) == map_size: # Map to map connectivity if cg.one2one: # 1-to-1 mapping weight = cg.weight for i in range(map_size): node_inputs[out_map[i]].append((in_map[i], weight)) else: # 1-to-all if not cg.uniform: # Step start = -cg.weight end = cg.weight step = (end - start) / (map_size - 1) for o_n in out_map: s = start for i_n in in_map: node_inputs[o_n].append((i_n, s)) s += step else: # Uniform for o_n in out_map: for i_n in in_map: node_inputs[o_n].append((i_n, cg.weight)) else: # Map-to-isolated or isolated-to-isolated if not cg.uniform: # Step start = -cg.weight end = cg.weight step = (end - start) / (map_size - 1) for o_n in out_map: s = start for i_n in in_map: node_inputs[o_n].append((i_n, s)) s += step else: # Uniform for o_n in out_map: for i_n in in_map: node_inputs[o_n].append((i_n, cg.weight)) nodes = [] #Sometimes the output neurons end up not having any connections during the evolutionary process. While we do not #desire such networks, we should still allow them to make predictions to avoid fatal errors. for okey in output_keys: if okey not in node_keys: node_keys.add(okey) std_inputs[okey] = [] # While we cannot deduce the order of activations of the neurons due to the fact that we allow for arbitrary connection # schemes, we certainly want the output neurons to activate last. input_keys = genome_config.input_keys output_keys = genome_config.output_keys conns = {} for k in genome.nodes.keys(): if k not in std_inputs: std_inputs[k] = [] if k in children: for c in children[k]: std_inputs[c] = [] conns[k] = [i for i, _ in std_inputs[k]] sorted_keys = order_of_activation(conns, input_keys, output_keys) for node_key in sorted_keys: #if the node we are examining is not in our keys set then skip it. It means that it is not required for output. if node_key not in node_keys: continue node = genome.nodes[node_key] node_map = [node_key] + children[node_key] if node.is_switch: #If the node doesn't have any inputs then it is not needed if node_key not in std_inputs.keys( ) and node_key not in mod_inputs.keys(): continue # if the switch neuron only has modulatory weights then we copy those weights for the standard part as well. # this is not the desired behaviour but it is done to avoid errors during forward pass. if node_key not in std_inputs.keys( ) and node_key in mod_inputs.keys(): for n in node_map: std_inputs[n] = mod_inputs[n] if node_key not in mod_inputs: for n in node_map: mod_inputs[n] = [] for n in node_map: nodes.append(SwitchNeuron(n, std_inputs[n], mod_inputs[n])) continue for n in node_map: if n not in std_inputs: std_inputs[n] = [] # Create the standard part dictionary for the neuron for n in node_map: params = { 'activation_function': genome_config.activation_defs.get(node.activation), 'integration_function': genome_config.aggregation_function_defs.get(node.aggregation), 'bias': node.bias, 'activity': 0, 'output': 0, 'weights': std_inputs[n] } nodes.append(Neuron(n, params)) return SwitchNeuronNetwork(input_keys, output_keys, nodes)
def speciate(self, config, population, generation): """ Place genomes into species by genetic similarity. Note that this method assumes the current representatives of the species are from the old generation, and that after speciation has been performed, the old representatives should be dropped and replaced with representatives from the new generation. If you violate this assumption, you should make sure other necessary parts of the code are updated to reflect the new behavior. """ assert isinstance(population, dict) compatibility_threshold = self.species_set_config.compatibility_threshold # Find the best representatives for each existing species. unspeciated = set(iterkeys(population)) distances = GenomeDistanceCache(config.genome_config) new_representatives = {} new_members = {} for sid, s in iteritems(self.species): candidates = [] for gid in unspeciated: g = population[gid] d = distances(s.representative, g) candidates.append((d, g)) # The new representative is the genome closest to the current representative. ignored_rdist, new_rep = min(candidates, key=lambda x: x[0]) new_rid = new_rep.key new_representatives[sid] = new_rid new_members[sid] = [new_rid] unspeciated.remove(new_rid) # Partition population into species based on genetic similarity. while unspeciated: gid = unspeciated.pop() g = population[gid] # Find the species with the most similar representative. candidates = [] for sid, rid in iteritems(new_representatives): rep = population[rid] d = distances(rep, g) if d < compatibility_threshold: candidates.append((d, sid)) if candidates: ignored_sdist, sid = min(candidates, key=lambda x: x[0]) new_members[sid].append(gid) else: # No species is similar enough, create a new species, using # this genome as its representative. sid = next(self.indexer) new_representatives[sid] = gid new_members[sid] = [gid] # Update species collection based on new speciation. self.genome_to_species = {} for sid, rid in iteritems(new_representatives): s = self.species.get(sid) if s is None: s = Species(sid, generation) self.species[sid] = s members = new_members[sid] for gid in members: self.genome_to_species[gid] = sid member_dict = dict((gid, population[gid]) for gid in members) s.update(population[rid], member_dict) gdmean = mean(itervalues(distances.distances)) gdstdev = stdev(itervalues(distances.distances)) '''
def create(genome, config): """ Receives a genome and returns its phenotype (a GRUNetwork). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections, genome.nodes) connections = [ cg.key for cg in itervalues(genome.connections) if cg.enabled ] node_evals = [] node_inputs = {} for cg in itervalues(genome.connections): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if o not in node_inputs: node_inputs[o] = [(i, cg.weight)] else: node_inputs[o].append((i, cg.weight)) node_evals = [] gate_list = set() # Add the gates first for proper computation order for node_key, inputs in iteritems(node_inputs): ng = genome.nodes[node_key] if type(ng) is GRUNodeGene: for gate_key in [ng.read_gate, ng.forget_gate]: if type(gate_key) is int and gate_key not in gate_list: gate_list = gate_list.union(gate_key) gate_g = genome.nodes[gate_key] activation_function = genome_config.activation_defs.get( gate_g.activation) aggregation_function = genome_config.aggregation_function_defs.get( gate_g.aggregation) node_evals.append( (gate_key, activation_function, aggregation_function, gate_g.bias, gate_g.response, inputs, [gate_g.read_gate, gate_g.forget_gate] if type(gate_g) is GRUNodeGene else None)) for node_key, inputs in iteritems(node_inputs): if node_key not in gate_list: ng = genome.nodes[node_key] activation_function = genome_config.activation_defs.get( ng.activation) aggregation_function = genome_config.aggregation_function_defs.get( ng.aggregation) node_evals.append((node_key, activation_function, aggregation_function, ng.bias, ng.response, inputs, [ng.read_gate, ng.forget_gate] if type(ng) is GRUNodeGene else None)) nw = GRUNetwork(genome_config.input_keys, genome_config.output_keys, node_evals, gate_list) nw.genome = genome return nw