예제 #1
0
def select_storage_dirs(from_file, storage_name, root_dir):
    if from_file is not None:
        assert storage_name is None, "If launching --from_file, no storage_name should be provided"
        assert Path(from_file).suffix == '.txt', f"The provided --from_file should be a text file listing " \
                                                 f"storage_name's of directories to act on. " \
                                                 f"You provided '--from_file={from_file}'"

    if storage_name is not None:
        assert from_file is None, "Cannot launch --from_file if --storage_name"

    if from_file is not None:
        with open(from_file, "r") as f:
            storage_names = f.readlines()
        storage_names = [sto_name.strip('\n') for sto_name in storage_names]

        # drop the commented lignes in the .txt
        storage_names = [
            sto_name for sto_name in storage_names
            if not is_commented(sto_name, COMMENTING_CHAR_LIST)
        ]

        storage_dirs = [
            get_root(root_dir) / sto_name for sto_name in storage_names
        ]

    elif storage_name is not None:

        storage_dirs = [get_root(root_dir) / storage_name]

    else:
        raise NotImplementedError(
            "storage_dirs to operate over must be specified either by --from_file or --storage_name"
        )

    return storage_dirs
예제 #2
0
def compare_searches(storage_names, x_metric, y_metric, y_error_bars,
                     performance_metric, performance_aggregation, visuals_file,
                     additional_curves_files, re_run_if_exists, logger,
                     root_dir, n_eval_runs):
    """
    compare_searches compare several storage_dirs
    """

    assert type(storage_names) is list

    logger.debug(f'\n{"benchmark_vertical_densities".upper()}:')

    storage_dirs = []
    for storage_name in storage_names:
        storage_dirs.append(get_root(root_dir) / storage_name)

    for storage_dir in storage_dirs:
        if not (storage_dir / "summary" /
                f"summary_seed_scores.pkl").exists() or re_run_if_exists:
            summarize_search(storage_name=storage_dir.name,
                             n_eval_runs=n_eval_runs,
                             x_metric=x_metric,
                             y_metric=y_metric,
                             y_error_bars=y_error_bars,
                             performance_metric=performance_metric,
                             performance_aggregation=performance_aggregation,
                             re_run_if_exists=re_run_if_exists,
                             make_performance_chart=True,
                             make_learning_plots=True,
                             logger=logger,
                             root_dir=root_dir)

    _make_vertical_densities_figure(
        storage_dirs=storage_dirs,
        visuals_file=visuals_file,
        additional_curves_file=additional_curves_file,
        make_box_plot=True,
        queried_performance_metric=performance_metric,
        queried_performance_aggregation=performance_aggregation,
        load_dir="summary",
        save_dir="benchmark",
        logger=logger)

    return
예제 #3
0
def summarize_search(storage_name,
                     n_eval_runs,
                     re_run_if_exists,
                     logger,
                     root_dir,
                     x_metric,
                     y_metric,
                     y_error_bars,
                     performance_metric,
                     performance_aggregation,
                     make_performance_chart=True,
                     make_learning_plots=True):
    """
    Summaries act inside a single storage_dir
    """

    assert type(storage_name) is str

    storage_dir = get_root(root_dir) / storage_name

    if re_run_if_exists and (storage_dir / "summary").exists():
        shutil.rmtree(storage_dir / "summary")

    if make_learning_plots:
        logger.debug(f'\n{"benchmark_learning".upper()}:')

        x_data, y_data = _gather_experiments_training_curves(
            storage_dir=storage_dir,
            graph_key="task_name",
            curve_key="experiment_num",
            logger=logger,
            x_metric=x_metric,
            y_metric=y_metric)

        _make_benchmark_learning_figure(x_data=x_data,
                                        y_data=y_data,
                                        x_metric=x_metric,
                                        y_metric=y_metric,
                                        y_error_bars=y_error_bars,
                                        storage_dirs=[storage_dir],
                                        n_labels=10,
                                        save_dir="summary",
                                        logger=logger,
                                        visuals_file=None)

    if make_performance_chart:
        logger.debug(f'\n{"benchmark_performance".upper()}:')

        _compute_seed_scores(
            storage_dir=storage_dir,
            n_eval_runs=n_eval_runs,
            performance_metric=performance_metric,
            performance_aggregation=performance_aggregation,
            group_key="experiment_num",
            bar_key="storage_name" if logger.level == 10 else "alg_name",
            re_run_if_exists=re_run_if_exists,
            save_dir="summary",
            logger=logger,
            root_dir=root_dir)

        sorted_inner_keys = _make_benchmark_performance_figure(
            storage_dirs=[storage_dir],
            logger=logger,
            normalize_with_first_model=False,
            sort_bars=True,
            y_error_bars=y_error_bars,
            save_dir="summary")

        best_experiment_num = sorted_inner_keys[0]
        seed_dirs_for_best_exp = [
            path for path in (storage_dir /
                              f"experiment{best_experiment_num}").iterdir()
        ]
        copyfile(src=seed_dirs_for_best_exp[0] / "config.json",
                 dst=storage_dir / "summary" /
                 f"bestConfig_exp{best_experiment_num}.json")

    return
예제 #4
0
def compare_models(storage_names,
                   n_eval_runs,
                   re_run_if_exists,
                   logger,
                   root_dir,
                   x_metric,
                   y_metric,
                   y_error_bars,
                   visuals_file,
                   additional_curves_file,
                   performance_metric,
                   performance_aggregation,
                   make_performance_chart=True,
                   make_learning_plots=True):
    """
    compare_models compare several storage_dirs
    """

    assert type(storage_names) is list

    if make_learning_plots:
        logger.debug(f'\n{"benchmark_learning".upper()}:')

        x_data = OrderedDict()
        y_data = OrderedDict()
        storage_dirs = []

        for storage_name in storage_names:
            x_data, y_data = _gather_experiments_training_curves(
                storage_dir=get_root(root_dir) / storage_name,
                graph_key="task_name",
                curve_key="storage_name" if logger.level == 10 else "alg_name",
                logger=logger,
                x_metric=x_metric,
                y_metric=y_metric,
                x_data=x_data,
                y_data=y_data)

            storage_dirs.append(get_root(root_dir) / storage_name)

        _make_benchmark_learning_figure(
            x_data=x_data,
            y_data=y_data,
            x_metric=x_metric,
            y_metric=y_metric,
            y_error_bars=y_error_bars,
            storage_dirs=storage_dirs,
            n_labels=np.inf,
            save_dir="benchmark",
            logger=logger,
            visuals_file=visuals_file,
            additional_curves_file=additional_curves_file)

    if make_performance_chart:
        logger.debug(f'\n{"benchmark_performance".upper()}:')

        storage_dirs = []

        for storage_name in storage_names:
            _compute_seed_scores(
                storage_dir=get_root(root_dir) / storage_name,
                performance_metric=performance_metric,
                performance_aggregation=performance_aggregation,
                n_eval_runs=n_eval_runs,
                group_key="task_name",
                bar_key="storage_name" if logger.level == 10 else "alg_name",
                re_run_if_exists=re_run_if_exists,
                save_dir="benchmark",
                logger=logger,
                root_dir=root_dir)

            storage_dirs.append(get_root(root_dir) / storage_name)

        _make_benchmark_performance_figure(storage_dirs=storage_dirs,
                                           logger=logger,
                                           normalize_with_first_model=True,
                                           sort_bars=False,
                                           y_error_bars=y_error_bars,
                                           save_dir="benchmark")

    return
예제 #5
0
        compare_models(
            storage_names=benchmark_args.storage_names,
            x_metric=benchmark_args.x_metric,
            y_metric=benchmark_args.y_metric,
            y_error_bars=benchmark_args.y_error_bars,
            visuals_file=visuals_file,
            additional_curves_file=additional_curves_file,
            n_eval_runs=benchmark_args.n_eval_runs,
            performance_metric=benchmark_args.performance_metric,
            performance_aggregation=benchmark_args.performance_aggregation,
            make_performance_chart=
            False,  # TODO: add support for that chart in a compare_models context
            make_learning_plots=True,
            re_run_if_exists=benchmark_args.re_run_if_exists,
            logger=logger,
            root_dir=get_root(benchmark_args.root_dir))

    elif benchmark_args.benchmark_type == "compare_searches":
        compare_searches(
            storage_names=benchmark_args.storage_names,
            x_metric=benchmark_args.x_metric,
            y_metric=benchmark_args.y_metric,
            y_error_bars=benchmark_args.y_error_bars,
            performance_metric=benchmark_args.performance_metric,
            performance_aggregation=benchmark_args.performance_aggregation,
            n_eval_runs=benchmark_args.n_eval_runs,
            visuals_file=visuals_file,
            additional_curves_files=additional_curves_file,
            re_run_if_exists=benchmark_args.re_run_if_exists,
            logger=logger,
            root_dir=get_root(benchmark_args.root_dir))
예제 #6
0
def prepare_schedule(desc, schedule_file, root_dir, add_to_folder, resample, logger, ask_for_validation):
    # Infers the search_type (grid or random) from provided schedule_file

    schedule_file_path = Path(schedule_file)

    assert schedule_file_path.suffix == '.py', f"The provided --schedule_file should be a python file " \
                                               f"(see: alfred/schedule_examples). You provided " \
                                               f"'--schedule_file={schedule_file}'"

    if "grid_schedule" in schedule_file_path.name:
        search_type = 'grid'
    elif "random_schedule" in schedule_file_path.name:
        search_type = 'random'
    else:
        raise ValueError(f"Provided --schedule_file has the name '{schedule_file_path.name}'. "
                         "Only grid_schedule's and random_schedule's are supported. "
                         "The name of the provided '--schedule_file' must fit one of the following forms: "
                         "'grid_schedule_NAME.py' or 'random_schedule_NAME.py'.")

    if not schedule_file_path.exists():
        raise ValueError(f"Cannot find the provided '--schedule_file': {schedule_file_path}")

    # Gets experiments parameters

    schedule_module = re.sub('\.py$', '', ".".join(schedule_file.split('/')))

    if search_type == 'grid':

        VARIATIONS, ALG_NAMES, TASK_NAMES, SEEDS, experiments, varied_params, get_run_args, schedule = extract_schedule_grid(schedule_module)

    elif search_type == 'random':

        param_samples, ALG_NAMES, TASK_NAMES, SEEDS, experiments, varied_params, get_run_args, schedule = extract_schedule_random(schedule_module)

    else:
        raise NotImplementedError

    # Creates a list of alg_agent and task_name unique combinations

    if desc is not None:
        assert add_to_folder is None, "If --desc is defined, a new storage_dir folder will be created." \
                                      "No --add_to_folder should be provided."

        desc = f"{search_type}_{desc}"
        agent_task_combinations = list(itertools.product(ALG_NAMES, TASK_NAMES))
        mode = "NEW_STORAGE"

    elif add_to_folder is not None:
        assert (get_root(root_dir) / add_to_folder).exists(), f"{add_to_folder} does not exist."
        assert desc is None, "If --add_to_folder is defined, new experiments will be added to the existing folder." \
                             "No --desc should be provided."

        storage_name_id, git_hashes, alg_name, task_name, desc = \
            DirectoryTree.extract_info_from_storage_name(add_to_folder)

        agent_task_combinations = list(itertools.product([alg_name], [task_name]))
        mode = "EXISTING_STORAGE"

    else:
        raise NotImplementedError

    # Duplicates or resamples hyperparameters to match the number of agent_task_combinations

    n_combinations = len(agent_task_combinations)

    experiments = [experiments]
    if search_type == 'random':
        param_samples = [param_samples]

    if search_type == 'random' and resample:
        assert not add_to_folder
        for i in range(n_combinations - 1):
            param_sa, _, _, _, expe, varied_pa, get_run_args, _ = extract_schedule_random(schedule_module)
            experiments.append(expe)
            param_samples.append(param_sa)

    else:
        experiments = experiments * n_combinations
        if search_type == 'random':
            param_samples = param_samples * n_combinations

    # Printing summary of schedule_xyz.py

    info_str = f"\n\nPreparing a {search_type.upper()} search over {len(experiments)} experiments, {len(SEEDS)} seeds"
    info_str += f"\nALG_NAMES: {ALG_NAMES}"
    info_str += f"\nTASK_NAMES: {TASK_NAMES}"
    info_str += f"\nSEEDS: {SEEDS}"

    if search_type == "grid":
        info_str += f"\n\nVARIATIONS:"
        for key in VARIATIONS.keys():
            info_str += f"\n\t{key}: {VARIATIONS[key]}"
    else:
        info_str += f"\n\nParams to be varied over: {varied_params}"

    info_str += f"\n\nDefault {config_to_str(get_run_args(overwritten_cmd_line=''))}\n"

    logger.debug(info_str)

    # Asking for user validation

    if ask_for_validation:

        if mode == "NEW_STORAGE":
            git_hashes = DirectoryTree.get_git_hashes()

            string = "\n"
            for alg_name, task_name in agent_task_combinations:
                string += f"\n\tID_{git_hashes}_{alg_name}_{task_name}_{desc}"
            logger.debug(f"\n\nAbout to create {len(agent_task_combinations)} storage directories, "
                         f"each with {len(experiments)} experiments:"
                         f"{string}")

        else:
            n_existing_experiments = len([path for path in get_root(root_dir) / add_to_folder.iterdir()
                                          if path.name.startswith('experiment')])

            logger.debug(f"\n\nAbout to add {len(experiments)} experiment folders in the following directory"
                         f" (there are currently {n_existing_experiments} in this folder):"
                         f"\n\t{add_to_folder}")

        answer = input("\nShould we proceed? [y or n]")
        if answer.lower() not in ['y', 'yes']:
            logger.debug("Aborting...")
            sys.exit()

    logger.debug("Starting...")

    # For each storage_dir to be created

    all_storage_dirs = []

    for alg_task_i, (alg_name, task_name) in enumerate(agent_task_combinations):

        # Determines storing ID (if new storage_dir)

        if mode == "NEW_STORAGE":
            tmp_dir_tree = DirectoryTree(alg_name=alg_name, task_name=task_name, desc=desc, seed=1, root=root_dir)
            storage_name_id = tmp_dir_tree.storage_dir.name.split('_')[0]

        # For each experiments...

        for param_dict in experiments[alg_task_i]:

            # Creates dictionary pointer-access to a training config object initialized by default

            config = get_run_args(overwritten_cmd_line="")
            config_dict = vars(config)

            # Modifies the config for this particular experiment

            config.alg_name = alg_name
            config.task_name = task_name
            config.desc = desc

            config_unique_dict = {k: v for k, v in param_dict.items() if k in varied_params}
            config_unique_dict['alg_name'] = config.alg_name
            config_unique_dict['task_name'] = config.task_name
            config_unique_dict['seed'] = config.seed

            for param_name in param_dict.keys():
                if param_name not in config_dict.keys():
                    raise ValueError(f"'{param_name}' taken from the schedule is not a valid hyperparameter "
                                     f"i.e. it cannot be found in the Namespace returned by get_run_args().")
                else:
                    config_dict[param_name] = param_dict[param_name]

            # Create the experiment directory

            dir_tree = create_experiment_dir(storage_name_id, config, config_unique_dict, SEEDS, root_dir, git_hashes)

        all_storage_dirs.append(dir_tree.storage_dir)

        # Saves VARIATIONS in the storage directory

        first_experiment_created = int(dir_tree.current_experiment.strip('experiment')) - len(experiments[0]) + 1
        last_experiment_created = first_experiment_created + len(experiments[0]) - 1

        if search_type == 'grid':

            VARIATIONS['alg_name'] = ALG_NAMES
            VARIATIONS['task_name'] = TASK_NAMES
            VARIATIONS['seed'] = SEEDS

            key = f'{first_experiment_created}-{last_experiment_created}'

            if (dir_tree.storage_dir / 'variations.json').exists():
                variations_dict = load_dict_from_json(filename=str(dir_tree.storage_dir / 'variations.json'))
                assert key not in variations_dict.keys()
                variations_dict[key] = VARIATIONS
            else:
                variations_dict = {key: VARIATIONS}

            save_dict_to_json(variations_dict, filename=str(dir_tree.storage_dir / 'variations.json'))
            open(str(dir_tree.storage_dir / 'GRID_SEARCH'), 'w+').close()

        elif search_type == 'random':
            len_samples = len(param_samples[alg_task_i])
            fig_width = 2 * len_samples if len_samples > 0 else 2
            fig, ax = plt.subplots(len(param_samples[alg_task_i]), 1, figsize=(6, fig_width))
            if not hasattr(ax, '__iter__'):
                ax = [ax]

            plot_sampled_hyperparams(ax, param_samples[alg_task_i],
                                     log_params=['lr', 'tau', 'initial_alpha', 'grad_clip_value', 'lamda1', 'lamda2'])

            j = 1
            while True:
                if (dir_tree.storage_dir / f'variations{j}.png').exists():
                    j += 1
                else:
                    break
            fig.savefig(str(dir_tree.storage_dir / f'variations{j}.png'))
            plt.close(fig)

            open(str(dir_tree.storage_dir / 'RANDOM_SEARCH'), 'w+').close()

        # Printing summary

        logger.info(f'Created directories '
                    f'{str(dir_tree.storage_dir)}/experiment{first_experiment_created}-{last_experiment_created}')

    # Saving the list of created storage_dirs in a text file located with the provided schedule_file

    schedule_name = Path(schedule.__file__).parent.stem
    with open(Path(schedule.__file__).parent / f"list_searches_{schedule_name}.txt", "a+") as f:
        for storage_dir in all_storage_dirs:
            f.write(f"{storage_dir.name}\n")

    logger.info(f"\nEach of these experiments contain directories for the following seeds: {SEEDS}")
예제 #7
0
def create_retrain_best(from_file, storage_name, best_experiments_mapping,
                        n_retrain_seeds, train_time_factor, root_dir):
    logger = create_logger(name="CREATE_RETRAIN", loglevel=logging.INFO)
    logger.info("\nCREATING retrainBest directories")

    # Select storage_dirs to run over

    storage_dirs = select_storage_dirs(from_file, storage_name, root_dir)

    # Sanity-check that storages exist

    storage_dirs = [
        storage_dir for storage_dir in storage_dirs
        if sanity_check_exists(storage_dir, logger)
    ]

    # Imports schedule file to have same settings for DirectoryTree.git_repos_to_track

    if from_file:
        schedule_file = str([
            path for path in Path(from_file).parent.iterdir()
            if 'schedule' in path.name and path.name.endswith('.py')
        ][0])
        schedule_module = ".".join(schedule_file.split('/')).strip('.py')
        schedule = import_module(schedule_module)

    # Creates retrainBest directories

    retrainBest_storage_dirs = []
    new_retrainBest_storage_dirs = []
    for storage_dir in storage_dirs:

        try:
            # Checks if a retrainBest directory already exists for this search

            search_storage_id = storage_dir.name.split('_')[0]
            corresponding_retrain_directories = [
                path for path in get_root(root_dir).iterdir()
                if f"retrainBest{search_storage_id}" in path.name.split('_')
            ]

            if len(corresponding_retrain_directories) > 0:
                assert len(corresponding_retrain_directories) == 1
                retrainBest_dir = corresponding_retrain_directories[0]

                logger.info(f"Existing retrainBest\n\n"
                            f"\t{storage_dir.name} -> {retrainBest_dir.name}")

                retrainBest_storage_dirs.append(retrainBest_dir)
                continue

            else:

                # The retrainBest directory will contain one experiment with bestConfig from the search...

                if best_experiments_mapping is None:

                    # ... bestConfig is found in the summary/ folder from the search

                    best_config = [
                        path for path in (storage_dir / "summary").iterdir()
                        if path.name.startswith("bestConfig")
                    ][0]

                    assert len(best_config) == 1 and type(best_config) is list

                else:

                    # ... bestConfig is loaded based on specified --best_experiment_mapping

                    best_experiments_mapping_dict = load_dict_from_json(
                        best_experiments_mapping)
                    assert storage_dir.name in best_experiments_mapping_dict.keys(
                    )

                    best_experiment_num = best_experiments_mapping_dict[
                        storage_dir.name]
                    seed_dir = DirectoryTree.get_all_seeds(
                        experiment_dir=storage_dir /
                        f"experiment{best_experiment_num}")[0]
                    best_config = seed_dir / "config.json"

                config_dict = load_dict_from_json(filename=str(best_config))

                # Retrain experiments run for twice as long

                if config_dict['max_episodes'] is not None:
                    config_dict['max_episodes'] = int(
                        config_dict['max_episodes'] * train_time_factor)
                elif config_dict['max_steps'] is not None:
                    config_dict['max_steps'] = int(config_dict['max_steps'] *
                                                   train_time_factor)
                else:
                    raise ValueError(
                        "At least one of max_episodes or max_steps should be defined"
                    )

                # Updates the description

                if "random" in config_dict['desc'] or "grid" in config_dict[
                        'desc']:
                    new_desc = config_dict['desc'] \
                        .replace("random", f"retrainBest{search_storage_id}") \
                        .replace("grid", f"retrainBest{search_storage_id}")
                else:
                    new_desc = config_dict[
                        'desc'] + f"_retrainBest{search_storage_id}"

                config_dict['desc'] = new_desc

                # Creates config Namespace with loaded config_dict

                config = argparse.ArgumentParser().parse_args("")
                config_pointer = vars(config)
                config_pointer.update(config_dict)  # updates config

                config_unique_dict = {}
                config_unique_dict['alg_name'] = config.alg_name
                config_unique_dict['task_name'] = config.task_name
                config_unique_dict['seed'] = config.seed

                # Gets new storage_name_id

                tmp_dir_tree = DirectoryTree(alg_name="",
                                             task_name="",
                                             desc="",
                                             seed=1,
                                             root=root_dir)
                retrain_storage_id = tmp_dir_tree.storage_dir.name.split(
                    '_')[0]

                # Creates the new storage_dir for retrainBest

                dir_tree = create_experiment_dir(
                    storage_name_id=retrain_storage_id,
                    config=config,
                    config_unique_dict=config_unique_dict,
                    SEEDS=[i * 10 for i in range(n_retrain_seeds)],
                    root_dir=root_dir,
                    git_hashes=DirectoryTree.get_git_hashes())

                retrainBest_storage_dirs.append(dir_tree.storage_dir)
                new_retrainBest_storage_dirs.append(dir_tree.storage_dir)

                logger.info(
                    f"New retrainBest:\n\n"
                    f"\t{storage_dir.name} -> {dir_tree.storage_dir.name}")

        except Exception as e:
            logger.info(
                f"Could not create retrainBest-storage_dir {storage_dir}")
            logger.info(f"\n\n{e}\n{traceback.format_exc()}")

    # Saving the list of created storage_dirs in a text file located with the provided schedule_file

    schedule_name = Path(from_file).parent.stem
    with open(
            Path(from_file).parent / f"list_retrains_{schedule_name}.txt",
            "a+") as f:
        for storage_dir in new_retrainBest_storage_dirs:
            f.write(f"{storage_dir.name}\n")

    return retrainBest_storage_dirs