def evaluate_population(pop: Population, cfg: Config, cpu: int, experiment_id: int): """Evaluate the given population.""" pop.log(f"{pop.name} - Evaluating the population...") _, game_ids_eval = get_game_ids(experiment_id=experiment_id) multi_env = get_multi_env(pop=pop, game_config=cfg) multi_env.set_games(game_ids_eval, noise=False) pool = mp.Pool(mp.cpu_count() - cpu) manager = mp.Manager() return_dict = manager.dict() pbar = tqdm(total=len(pop.population), desc="Evaluating") def update(*_): pbar.update() for genome_id, genome in pop.population.items(): pool.apply_async(func=multi_env.eval_genome, args=((genome_id, genome), return_dict), callback=update) pool.close() # Close the pool pool.join() # Postpone continuation until everything is finished pbar.close() # Calculate the fitness from the given return_dict pop.log(f"{pop.name} - Calculating fitness scores...") fitness = calc_pop_fitness( fitness_cfg=pop.config.evaluation, game_cfg=cfg.game, game_obs=return_dict, gen=pop.generation, ) for i, genome in pop.population.items(): genome.fitness = fitness[i] # Get the fittest genome best = None for g in pop.population.values(): if best is None or g.fitness > best.fitness: best = g pop.best_genome = best # Save the results pop.save() # Visualize most fit genome visualize_genome( debug=True, genome=best, population=pop, ) # Trace the most fit genome trace_most_fit( debug=False, games=game_ids_eval, genome=best, population=pop, unused_cpu=cpu, )
parser.add_argument('--debug', type=bool, default=False) parser.add_argument('--duration', type=int, default=60) parser.add_argument('--use_backup', type=bool, default=False) args = parser.parse_args() # Setup the population pop = Population( name='NEAT-GRU/example', folder_name=get_folder(args.experiment), use_backup=args.use_backup, ) game_ids_train, game_ids_eval = get_game_ids(experiment_id=args.experiment) # Potentially modify the population if not pop.best_genome: pop.best_genome = list(pop.population.values())[0] # Chosen genome used for genome-evaluation chosen_genome = None # chosen_genome = pop.population[47280] try: if args.train: train( debug=args.debug, duration=args.duration, games=game_ids_train, iterations=args.iterations, population=pop, unused_cpu=args.unused_cpu, )
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 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 main(pop_name: str, version: int, unused_cpu: int = 2, use_backup: bool = False): # Check if valid population name if pop_name not in SUPPORTED: raise Exception(f"Population '{pop_name}' not supported!") # Create the population cfg = get_config() cfg.population.specie_elitism = 1 folder = get_folder(experiment_id=7) pop = Population( name=f'{pop_name}/v{version}', config=cfg, folder_name=folder, use_backup=use_backup, ) # Replace the population's initial population with the requested topologies genomes if pop.generation == 0: for g_id in pop.population.keys(): pop.population[g_id] = get_topology(pop_name, gid=g_id, cfg=cfg) pop.species.speciate(config=pop.config, population=pop.population, generation=pop.generation, logger=pop.log) pop.log(f"\n\n\n===> RUNNING EXPERIMENT 7 <===\n") # Set games and environment used for training and evaluation games_train, games_eval = get_game_ids(experiment_id=7) train_env = get_multi_env(config=cfg) eval_env = get_multi_env(config=cfg) eval_env.set_games(games_eval, noise=False) solution_found = False while not solution_found: # Train the population for a single iteration pop.log("\n===> TRAINING <===") train_env.set_games(games_train, noise=True) # 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)) # 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=train_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=pop.config.evaluation, game_cfg=cfg.game, game_obs=return_dict, gen=pop.generation, ) for i, genome in genomes: genome.fitness = fitness[i] # Update the population's best_genome 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_fitness[pop.generation] = genomes[0][1].fitness pop.best_genome_hist[pop.generation] = genomes[0] pop.best_genome = best # Let population evolve evolve(pop, pop_name) # End generation pop.reporters.end_generation(population=pop.population, name=str(pop), species_set=pop.species, logger=pop.log) # Test if evaluation finds a solution for the new generation, impossible if fitness < 0.7 if pop.best_genome.fitness > 0.7 or pop.generation % 10 == 0: pop.log("\n===> EVALUATING <===") genomes = list(iteritems(pop.population)) pool = mp.Pool(mp.cpu_count() - unused_cpu) manager = mp.Manager() return_dict = manager.dict() for genome in genomes: pool.apply_async(func=eval_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 finished = calc_finished_ratio( fitness_cfg=cfg.evaluation, game_obs=return_dict, ) best = None for i, genome in genomes: genome.fitness = finished[i] if best is None or finished[i] > best.fitness: best = genome pop.log(f"Best genome:\n{best}\n{best.nodes[2]}") # Solution is found if best.fitness == 1: pop.best_genome = best pop.log(f"Solution found!") solution_found = True # End the outer while-loop # Save the population with their evaluation results pop.save()
def main(topology_id: int, iterations: int, eval_interval: int, experiment_id: int, hops: float = 0.1, weight_range: float = 1, mutate_combine: bool = False, mutate_bias: bool = True, mutate_reset: bool = True, mutate_update: bool = True, mutate_candidate: bool = True, init_population_size: int = 1000, unused_cpu: int = 2): """ Run the fifth experiment. :param topology_id: Chosen topology to investigate :param iterations: Number of training iterations performed :param eval_interval: After how much training iterations evaluation is performed :param experiment_id: ID of the experiment used to train and evaluate the population on :param hops: Hops between variable-configurations :param weight_range: Range of deviation for one's weights :param mutate_combine: Combine the best results of each mutation type :param mutate_bias: Mutate the GRU's bias values :param mutate_reset: Mutate the reset-gate's weights :param mutate_update: Mutate the update-gate's weights :param mutate_candidate: Mutate the candidate-state's weights :param init_population_size: Initial size of the randomized population :param unused_cpu: Number of CPU-cores not used """ # Get the population name = f"experiment{experiment_id}_topology{topology_id}_hops{hops}_range{weight_range}" pop = Population( name=name, folder_name='experiment5', use_backup=False, ) # Define the games specific to the experiment _, game_ids_eval = get_game_ids(experiment_id=experiment_id) # Set the genomes if population is new if pop.generation == 0: # Get the requested topology-type if topology_id == 1: topology = get_topology1 elif topology_id == 2: topology = get_topology2 else: raise Exception(f"Topology {topology_id} not supported.") # Initialize the population with a randomized population pop.population = dict() for gid in range(init_population_size): new_genome = topology(pop.config, random_init=True) new_genome.key = gid pop.population[gid] = new_genome # Perform an initial training train_population(pop=pop, games=game_ids_eval, unused_cpu=unused_cpu) pop.generation = 0 # Don't count initial training as a generation # Get the fittest genome best = None for g in pop.population.values(): if best is None or g.fitness > best.fitness: best = g pop.best_genome = best visualize_best_genome(pop=pop) # Test the initial population test_population(pop=pop, games=game_ids_eval) # Set the most fit genome as the starting-point set_population(pop=pop, hops=hops, weight_range=weight_range, mutate_bias=mutate_bias, mutate_reset=mutate_reset, mutate_update=mutate_update, mutate_candidate=mutate_candidate) # Evaluate the population for i in range(iterations): train_population(pop=pop, games=game_ids_eval, unused_cpu=unused_cpu) evaluate_fitness(pop=pop, hops=hops, weight_range=weight_range, mutate_combine=mutate_combine, mutate_bias=mutate_bias, mutate_reset=mutate_reset, mutate_update=mutate_update, mutate_candidate=mutate_candidate) visualize_best_genome(pop=pop) if pop.generation % eval_interval == 0: test_population(pop=pop, games=game_ids_eval) set_population(pop=pop, hops=hops, weight_range=weight_range, mutate_bias=mutate_bias, mutate_reset=mutate_reset, mutate_update=mutate_update, mutate_candidate=mutate_candidate)
def evaluate_fitness(pop: Population, hops: float, weight_range: float, mutate_combine: bool = False, mutate_bias: bool = True, mutate_reset: bool = True, mutate_update: bool = True, mutate_candidate: bool = True): """Visualize the fitness-values of the population.""" # Initialization r = int(weight_range / hops) dim = 2 * r + 1 genome_key = 0 # Enroll the previous best genome init_gru = pop.best_genome.nodes[2] best_bias_genome = None best_reset_genome = None best_update_genome = None best_candidate_genome = None # Create genome-mutations if mutate_bias: bias_result = np.zeros((3, dim)) for i in range(3): for a in range(dim): g = pop.population[genome_key] bias_result[i, a] = g.fitness if best_bias_genome is None or g.fitness > best_bias_genome.fitness: best_bias_genome = g genome_key += 1 # Formalize the data points = [[x, y] for x in range(3) for y in range(dim)] points_normalized = [[p1, (p2 - r) * hops] for p1, p2 in points] values = [bias_result[p[0], p[1]] for p in points] grid_x, grid_y = np.mgrid[0:3:1, -r * hops:(r + 1) * hops:hops] # Create the figure plt.figure(figsize=( 10, 2.5)) # Rather horizontal plot due to limited number of rows knn_data = griddata(points_normalized, values, (grid_x, grid_y), method='nearest') ax = sns.heatmap( knn_data, annot=True, fmt='.3g', # vmin=0, # vmax=1, xticklabels=[round((i - r) * hops, 2) for i in range(dim)], yticklabels=['r', 'z', 'h'], cbar_kws={ "pad": 0.02, "fraction": 0.05 }, ) ax.invert_yaxis() plt.title('Bias mutation') plt.xlabel(r'$\Delta bias_h$' + f' (init={list(init_gru.bias_h)!r})') plt.ylabel(f'bias components') plt.tight_layout() path = f"population/storage/{pop.folder_name}/{pop}/" path = get_subfolder(path, 'images') path = get_subfolder(path, f'gen{pop.generation:05d}') plt.savefig(f"{path}bias.png") plt.close() if mutate_reset: reset_result = np.zeros((dim, dim)) for a in range(dim): for b in range(dim): g = pop.population[genome_key] reset_result[a, b] = g.fitness if best_reset_genome is None or g.fitness > best_reset_genome.fitness: best_reset_genome = g genome_key += 1 # Formalize the data points = [[x, y] for x in range(dim) for y in range(dim)] points_normalized = [[(p1 - r) * hops, (p2 - r) * hops] for p1, p2 in points] values = [reset_result[p[0], p[1]] for p in points] grid_x, grid_y = np.mgrid[-r * hops:(r + 1) * hops:hops, -r * hops:(r + 1) * hops:hops] # Create the figure plt.figure( figsize=(15, 15) ) # Rather horizontal plot due to limited number of rows TODO set back to (5, 5) knn_data = griddata(points_normalized, values, (grid_x, grid_y), method='nearest') ax = sns.heatmap( knn_data, annot=True, fmt='.3g', # vmin=0, # vmax=1, xticklabels=[round((i - r) * hops, 2) for i in range(dim)], yticklabels=[round((i - r) * hops, 2) for i in range(dim)], cbar_kws={ "pad": 0.02, "fraction": 0.05 }, ) ax.invert_yaxis() plt.title('Reset-gate mutation') plt.xlabel(r'$\Delta W_{hr}$' + f' (init={round(init_gru.weight_hh[0, 0], 3)})') plt.ylabel(r'$\Delta W_{xr}$' + f' (init={round(init_gru.weight_xh[0, 0], 3)})') plt.tight_layout() path = f"population/storage/{pop.folder_name}/{pop}/" path = get_subfolder(path, 'images') path = get_subfolder(path, f'gen{pop.generation:05d}') plt.savefig(f"{path}reset_gate.png") plt.close() if mutate_update: update_result = np.zeros((dim, dim)) for a in range(dim): for b in range(dim): g = pop.population[genome_key] update_result[a, b] = g.fitness if best_update_genome is None or g.fitness > best_update_genome.fitness: best_update_genome = g genome_key += 1 # Formalize the data points = [[x, y] for x in range(dim) for y in range(dim)] points_normalized = [[(p1 - r) * hops, (p2 - r) * hops] for p1, p2 in points] values = [update_result[p[0], p[1]] for p in points] grid_x, grid_y = np.mgrid[-r * hops:(r + 1) * hops:hops, -r * hops:(r + 1) * hops:hops] # Create the figure plt.figure( figsize=(15, 15) ) # Rather horizontal plot due to limited number of rows TODO set back to (5, 5) knn_data = griddata(points_normalized, values, (grid_x, grid_y), method='nearest') ax = sns.heatmap( knn_data, annot=True, fmt='.3g', # vmin=0, # vmax=1, xticklabels=[round((i - r) * hops, 2) for i in range(dim)], yticklabels=[round((i - r) * hops, 2) for i in range(dim)], cbar_kws={ "pad": 0.02, "fraction": 0.05 }, ) ax.invert_yaxis() plt.title('Update-gate mutation') plt.xlabel(r'$\Delta W_{hz}$' + f' (init={round(init_gru.weight_hh[1, 0], 3)})') plt.ylabel(r'$\Delta W_{xz}$' + f' (init={round(init_gru.weight_xh[1, 0], 3)})') plt.tight_layout() path = f"population/storage/{pop.folder_name}/{pop}/" path = get_subfolder(path, 'images') path = get_subfolder(path, f'gen{pop.generation:05d}') plt.savefig(f"{path}update_gate.png") plt.close() if mutate_candidate: candidate_result = np.zeros((dim, dim)) for a in range(dim): for b in range(dim): g = pop.population[genome_key] candidate_result[a, b] = g.fitness if best_candidate_genome is None or g.fitness > best_candidate_genome.fitness: best_candidate_genome = g genome_key += 1 # Formalize the data points = [[x, y] for x in range(dim) for y in range(dim)] points_normalized = [[(p1 - r) * hops, (p2 - r) * hops] for p1, p2 in points] values = [candidate_result[p[0], p[1]] for p in points] grid_x, grid_y = np.mgrid[-r * hops:(r + 1) * hops:hops, -r * hops:(r + 1) * hops:hops] # Create the figure plt.figure( figsize=(15, 15) ) # Rather horizontal plot due to limited number of rows TODO set back to (5, 5) knn_data = griddata(points_normalized, values, (grid_x, grid_y), method='nearest') ax = sns.heatmap( knn_data, annot=True, fmt='.3g', # vmin=0, # vmax=1, xticklabels=[round((i - r) * hops, 2) for i in range(dim)], yticklabels=[round((i - r) * hops, 2) for i in range(dim)], cbar_kws={ "pad": 0.02, "fraction": 0.05 }, ) ax.invert_yaxis() plt.title('Candidate-state mutation') plt.xlabel(r'$\Delta W_{hh}$' + f' (init={round(init_gru.weight_hh[2, 0], 3)})') plt.ylabel(r'$\Delta W_{xh}$' + f' (init={round(init_gru.weight_xh[2, 0], 3)})') plt.tight_layout() path = f"population/storage/{pop.folder_name}/{pop}/" path = get_subfolder(path, 'images') path = get_subfolder(path, f'gen{pop.generation:05d}') plt.savefig(f"{path}candidate_state.png") plt.close() # Set the most fit genome pop.best_genome.fitness = 0 if mutate_bias and best_bias_genome.fitness > pop.best_genome.fitness: pop.best_genome = copy.deepcopy(best_bias_genome) if mutate_reset and best_reset_genome.fitness > pop.best_genome.fitness: pop.best_genome = copy.deepcopy(best_reset_genome) if mutate_update and best_update_genome.fitness > pop.best_genome.fitness: pop.best_genome = copy.deepcopy(best_update_genome) if mutate_candidate and best_candidate_genome.fitness > pop.best_genome.fitness: pop.best_genome = copy.deepcopy(best_candidate_genome) if mutate_combine: pop.best_genome.nodes[2].bias_h = best_bias_genome.nodes[2].bias_h pop.best_genome.nodes[2].weight_xh_full[ 0, 0] = best_reset_genome.nodes[2].weight_xh_full[0, 0] pop.best_genome.nodes[2].weight_xh_full[ 1, 0] = best_update_genome.nodes[2].weight_xh_full[1, 0] pop.best_genome.nodes[2].weight_xh_full[ 2, 0] = best_candidate_genome.nodes[2].weight_xh_full[2, 0] pop.best_genome.nodes[2].weight_hh[ 0, 0] = best_reset_genome.nodes[2].weight_hh[0, 0] pop.best_genome.nodes[2].weight_hh[ 1, 0] = best_update_genome.nodes[2].weight_hh[1, 0] pop.best_genome.nodes[2].weight_hh[ 2, 0] = best_candidate_genome.nodes[2].weight_hh[2, 0]
def evaluate_and_evolve( self, pop: Population, n: int = 1, parallel=True, save_interval: int = 1, ): """ Evaluate the population on the same set of games. :param pop: Population object :param n: Number of generations :param parallel: Parallel the code (disable parallelization for debugging purposes) :param save_interval: Indicates how often a population gets saved """ multi_env = get_multi_env(pop=pop, game_config=self.game_config) msg = f"Repetitive evaluating on games: {self.games} for {n} iterations" pop.log(msg, print_result=False) # Iterate and evaluate over the games saved = True for iteration in range(n): # Set and randomize the games multi_env.set_games(self.games, noise=True) # 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: # Initialize the evaluation-pool pool = mp.Pool(mp.cpu_count() - self.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 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 fitness = calc_pop_fitness( fitness_cfg=pop.config.evaluation, game_cfg=self.game_config.game, game_obs=return_dict, gen=pop.generation, ) for i, genome in genomes: genome.fitness = fitness[i] # 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_fitness[pop.generation] = genomes[0][1].fitness pop.best_genome_hist[pop.generation] = genomes[0] 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) # Save the population if (iteration + 1) % save_interval == 0: pop.save() saved = True else: saved = False # Make sure that last iterations saves if not saved: pop.save()
def main(topology_id: int, batch_size: int = 1000, train_batch: int = 3, min_finished: float = MIN_FINISHED, unused_cpu: int = 2, save_pop: bool = False, use_backup: bool = False): """Run a population infinitely long and store all its good genomes.""" # Get the CSV used to store the results in csv_path, csv_name, added = get_csv_path(topology_id, use_backup=use_backup, batch_size=batch_size) # Create the population name = csv_name if save_pop else 'dummy' cfg = get_config() folder = get_folder(experiment_id=6) pop = Population( name=name, config=cfg, folder_name=folder, use_backup=use_backup, overwrite=True, # Every iteration, create a new population from scratch ) # Replace the population's initial population with the requested topologies genomes for g_id in pop.population.keys(): pop.population[g_id] = get_genome(topology_id, g_id=g_id, cfg=cfg) pop.species.speciate(config=pop.config, population=pop.population, generation=pop.generation, logger=pop.log) # Set games and environment used for training and evaluation pop.log(f"\n\n\n===> RUNNING EXPERIMENT 6 <===\n") games_train, games_eval = get_game_ids(experiment_id=6) train_env = get_multi_env(config=cfg) eval_env = get_multi_env(config=cfg) eval_env.set_games(games_eval, noise=False) # Keep training and evolving the network until the complete CSV is filled last_saved = pop.generation try: while added < batch_size: t = time.localtime() pop.log(f"\n\n===> Selective genome creation at {added / batch_size * 100}%, " f"storing in csv '{csv_path.split('/')[-1]}' " f"({t.tm_hour:02d}h-{t.tm_min:02d}m-{t.tm_sec:02d}s) <===") # Train the population pop.log("\n===> Training <===") for _ in tqdm(range(train_batch), desc="Training"): train_env.set_games(games_train, noise=True) genomes = list(iteritems(pop.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=train_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=pop.config.evaluation, game_cfg=cfg.game, game_obs=return_dict, gen=pop.generation, ) for i, genome in genomes: genome.fitness = fitness[i] # Update the population's best_genome best = None for g in itervalues(pop.population): if best is None or g.fitness > best.fitness: best = g genomes = sorted(pop.population.items(), key=lambda x: x[1].fitness, reverse=True) pop.best_fitness[pop.generation] = genomes[0][1].fitness pop.best_genome_hist[pop.generation] = genomes[0] pop.best_genome = best pop.log(f"Best training fitness: {best.fitness}") # Let population evolve pop.evolve() # Constraint each of the population's new genomes to the given topology for g in pop.population.values(): enforce_topology(g, topology_id=topology_id) # Save the population after training if pop.generation - last_saved >= 100: pop.save() last_saved = pop.generation # Evaluate the current population as was done in experiment6 pop.log("\n===> EVALUATING <===") genomes = list(iteritems(pop.population)) pool = mp.Pool(mp.cpu_count() - unused_cpu) manager = mp.Manager() return_dict = manager.dict() for genome in genomes: pool.apply_async(func=eval_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 finished = calc_finished_ratio( fitness_cfg=cfg.evaluation, game_obs=return_dict, ) best = None for i, genome in genomes: genome.fitness = finished[i] if best is None or finished[i] > best.fitness: best = genome # Give evaluation overview of population pop.log(f"Best evaluation finish ratio: {round(best.fitness, 2)}") best_str = str(best).replace("\n", "\n\t") best_str += "\n\t" + str(best.nodes[2]).replace("\n", "\n\t") pop.log(f"Best genome: \n\t{best_str}") sids = list(iterkeys(pop.species.species)) sids.sort() msg = f"\nPopulation '{name}' has {len(pop.species.species):d} species:" \ f"\n\t specie age size finished stag " \ f"\n\t======== ===== ====== ========== ======" pop.log(msg) if pop.log else print(msg) for sid in sids: s = pop.species.species[sid] a = pop.generation - s.created n = len(s.members) sf = [g.fitness for g in s.members.values() if g.fitness] f = "--" if len(sf) == 0 else f"{max(sf):.2f}" st = pop.generation - s.last_improved msg = f"\t{sid:^8} {a:^5} {n:^6} {f:^10} {st:^6}" pop.log(msg) if pop.log else print(msg) # Write the result to CSV with open(csv_path, 'a', newline='') as f: writer = csv.writer(f) for _, g in genomes: # Only write the genomes that exceed the minimum 'finished ratio' threshold! if g.fitness >= min_finished: writer.writerow(get_genome_parameters(g, topology_id=topology_id)) added += 1 finally: # Remove the dummy population if it exists pop.save() path = f"population{'_backup' if use_backup else ''}/storage/{pop.folder_name}/dummy/" if os.path.exists(path): shutil.rmtree(path)