Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
    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):
Ejemplo n.º 3
0
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()
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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')