def main( fitness, prob_gru: float, prob_sru: float, prob_lstm: float, version=0, unused_cpu=1, ): """ Run a population's configuration. :param fitness: Fitness function used to evaluate the population :param prob_gru: Probability of mutating towards a GRU-node :param prob_sru: Probability of mutating towards a SRU-node :param prob_lstm: Probability of mutating towards a LSTM-node :param version: Version of the model :param unused_cpu: Number of CPUs not used during training """ # Re-configure the config-file cfg = Config() cfg.bot.angular_dir = [] cfg.bot.delta_dist_enabled = False cfg.bot.dist_enabled = True cfg.game.duration = 60 # 60 seconds should be enough to reach the target from each starting orientation cfg.population.pop_size = 512 # Let inputs apply to configuration cfg.genome.rnn_prob_gru = prob_gru cfg.genome.rnn_prob_simple_rnn = prob_sru cfg.genome.rnn_prob_lstm = prob_lstm cfg.evaluation.fitness = fitness cfg.update() # Copy population over from experiment1 name = get_name(cfg=cfg, version=version) path_exp1 = f'population/storage/experiment1/{name}/' if not os.path.exists(path_exp1): raise Exception( f"Experiment 1 must be executed first for population {name}, terminating experiment 2..." ) # Population exists in experiment1, copy over to experiment2 (change experiment1 population's folder and save) pop = Population(name=name, config=cfg, folder_name=get_folder(experiment_id=1), use_backup=False) assert pop.generation > 0 # Population is not new (redundant check) folder = get_folder(experiment_id=2) pop.folder_name = folder pop.save() # Overrides pre-existing populations! # Copy over all generations as well, since these are used during population evaluation path = f"population{'_backup' if pop.use_backup else ''}/storage/{pop.folder_name}/{pop}/" copy_tree(f"{path_exp1}generations", f"{path}generations") # Give overview of population gru = cfg.genome.rnn_prob_gru sru = cfg.genome.rnn_prob_simple_rnn lstm = cfg.genome.rnn_prob_lstm msg = f"\n\n\n\n\n===> RUNNING EXPERIMENT 2 FOR THE FOLLOWING CONFIGURATION: <===" \ f"\n\t> fitness: {cfg.evaluation.fitness}" \ f"\n\t> GRU enabled: {gru > 0} (probability={round(gru, 2)})" \ f"\n\t> SRU enabled: {sru > 0} (probability={round(sru, 2)})" \ f"\n\t> LSTM enabled: {lstm > 0} (probability={round(lstm, 2)})" \ f"\n\t> Saving under folder: {folder}\n" pop.log(msg) # Set games used for evaluation _, games_eval = get_game_ids(experiment_id=2) # Execute the requested segments try: # Evaluate the trained population evaluate( games=games_eval, population=pop, unused_cpu=unused_cpu, ) except Exception as e: pop.log(traceback.format_exc(), print_result=False) raise e finally: process_killer('run_population.py') # Close all the terminated files
parser.add_argument('--plot_distribution', type=int, default=0) # Goes over all the populations types parser.add_argument('--compute_topology', type=int, default=0) # Goes over all the populations types parser.add_argument('--test_correctness', type=int, default=0) parser.add_argument('--experiment', type=int, default=3) parser.add_argument('--folder', type=str, default=None) parser.add_argument('--folder_pop', type=str, default='NEAT-SRU') parser.add_argument('--hops', type=int, default=HOPS) parser.add_argument('--max_gen', type=int, default=100) parser.add_argument('--max_v', type=int, default=30) parser.add_argument('--unused_cpu', type=int, default=2) args = parser.parse_args() # Set parameters f = args.folder if args.folder else get_folder(args.experiment) # Execute the program if bool(args.test_correctness): correctness_check( folder=f, neat=True, neat_gru=True, neat_lstm=False, neat_sru=True, neat_sru_s=False, max_gen=args.max_gen, max_v=args.max_v, ) # Use the default parameters if bool(args.evaluate_gen):
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, 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)
def main( fitness, prob_gru: float, prob_sru: float, prob_lstm: float, train_iterations=0, version=0, unused_cpu=1, ): """ Run a population's configuration. :param fitness: Fitness function used to evaluate the population :param prob_gru: Probability of mutating towards a GRU-node :param prob_sru: Probability of mutating towards a SRU-node :param prob_lstm: Probability of mutating towards a LSTM-node :param train_iterations: Number of training generations :param version: Version of the model :param unused_cpu: Number of CPUs not used during training """ # Re-configure the config-file cfg = Config() cfg.bot.angular_dir = [] cfg.bot.delta_dist_enabled = False cfg.bot.dist_enabled = True cfg.game.duration = 60 # 60 seconds should be enough to reach the target from each starting orientation cfg.population.pop_size = 512 # Let inputs apply to configuration cfg.genome.rnn_prob_gru = prob_gru cfg.genome.rnn_prob_simple_rnn = prob_sru cfg.genome.rnn_prob_lstm = prob_lstm cfg.evaluation.fitness = fitness cfg.update() # Create the population folder = get_folder(experiment_id=1) name = get_name(cfg=cfg, version=version) pop = Population( name=name, config=cfg, folder_name=folder, use_backup=False, ) # Give overview of population gru = cfg.genome.rnn_prob_gru sru = cfg.genome.rnn_prob_simple_rnn lstm = cfg.genome.rnn_prob_lstm msg = f"\n\n\n\n\n===> RUNNING EXPERIMENT 1 FOR THE FOLLOWING CONFIGURATION: <===" \ f"\n\t> fitness: {cfg.evaluation.fitness}" \ f"\n\t> GRU enabled: {gru > 0} (probability={round(gru, 2)})" \ f"\n\t> SRU enabled: {sru > 0} (probability={round(sru, 2)})" \ f"\n\t> LSTM enabled: {lstm > 0} (probability={round(lstm, 2)})" \ f"\n\t> Saving under folder: {folder}" \ f"\n\t> Training iterations: {train_iterations}\n" pop.log(msg) # Set games used for evaluation games_train, games_eval = get_game_ids(experiment_id=1) # Execute the requested segments try: train( debug=False, games=games_train, iterations=train_iterations, population=pop, unused_cpu=unused_cpu, ) # Evaluate the trained population evaluate( games=games_eval, population=pop, unused_cpu=unused_cpu, ) training_overview(population=pop, ) visualize_genome( genome=pop.best_genome, population=pop, ) except Exception as e: pop.log(traceback.format_exc(), print_result=False) raise e finally: process_killer('run_population.py') # Close all the terminated files
def visualize_generations(name, experiment_id, folder=None, hops: int = 10): """Visualize the result of the 'evaluate_generations' script. Should only be used for debugging!""" # Fetch population and evaluation games folder = folder if folder else get_folder(experiment_id) pop = Population( name=name, folder_name=folder, log_print=False, use_backup=True, ) # Parse the results fitness_dict = dict() finished_dict = dict() score_dict = dict() distance_dict = dict() time_dict = dict() max_gen = pop.generation for gen in tqdm(range(0, max_gen + 1, hops)): results: dict = load_dict( f"population{'_backup' if pop.use_backup else ''}/" f"storage/" f"{pop.folder_name}/" f"{pop}/" f"evaluation/" f"{gen:05d}/" f"results") # Fitness fitness = [results[k][D_FITNESS] for k in results.keys()] fitness_dict[gen] = fitness # Finished finished = [results[k][D_FINISHED] / 100 for k in results.keys()] # Normalize to percentage (0..1) finished_dict[gen] = finished # Score score = [results[k][D_SCORE_AVG] for k in results.keys()] score_dict[gen] = score # Distance distance = [results[k][D_DISTANCE_AVG] for k in results.keys()] distance_dict[gen] = distance # Time time = [results[k][D_TIME_AVG] for k in results.keys()] time_dict[gen] = time # Create visualizations for each of the results sf = get_subfolder( f"population{'_backup' if pop.use_backup else ''}/" f"storage/" f"{pop.folder_name}/" f"{pop}/" f"images/", 'evaluation') plot_population(fitness_dict, ylabel="Fitness score", title="Fitness (higher is better)", save_path=f'{sf}fitness_group.png') plot_elite(fitness_dict, f=max, ylabel="Fitness score", title="Fitness of population's elite (higher is better)", save_path=f'{sf}fitness_elite.png') plot_population(finished_dict, ylabel="Percentage finished (%)", title="Percentage finished (higher is better)", save_path=f'{sf}finished_group.png') plot_elite( finished_dict, f=max, ylabel="Percentage finished (%)", title="Percentage finished by population's elite (higher is better)", save_path=f'{sf}finished_elite.png') plot_population(score_dict, ylabel="Score", title="Final scores (higher is better)", save_path=f'{sf}score_group.png') plot_elite(score_dict, f=max, ylabel="Score", title="Final score of population's elite (higher is better)", save_path=f'{sf}score_elite.png') plot_population(distance_dict, ylabel="Distance (m)", title="Distance from target (lower is better)", save_path=f'{sf}distance_group.png') plot_elite( distance_dict, f=min, ylabel="Distance (m)", title="Distance from target for population's elite (lower is better)", save_path=f'{sf}distance_elite.png') plot_population(time_dict, ylabel="Time (s)", title="Time needed to reach target (lower is better)", save_path=f'{sf}time_group.png') plot_elite( time_dict, f=min, ylabel="Time (s)", title= "Time needed to reach target for population's elite (lower is better)", save_path=f'{sf}time_elite.png')