Ejemplo n.º 1
0
def params_to_skopt(param_space: ParamSpace):
    """ Converts a parameter space to a list of Dimention objects that can be used with
    a skopt Optimizer.

    A skopt Optimizer only receives 3 types of Dimensions: Categorical, Real, or Integer
    we convert parameters from our parameter space into one of those 3 types. Note that we only
    convert parameters that have either bounds or with a categorical domain with more than 1 value.
    If we have constant values in our parameter space, these don't need to be optimized anyway.

    Another function is provided to convert skopt output values back into a dictionary with
    a full configuration according to the parameter space (@see values_to_params).

    Args:
        param_space: a ParameterSpace where we can get the domain of each parameter

    Returns:
        a list of Dimension that can be passed to a skopt Optimizer

    """
    dimensions = []
    for param_name in param_space.param_names():
        domain_param = param_space.domain(param_name)
        domain = domain_param["domain"]
        dtype = DTypes.from_type(domain_param["dtype"])
        if len(domain) > 1:
            if dtype == DTypes.INT:
                low = min(domain)
                high = max(domain)
                dimensions.append(Integer(low, high, name=param_name))
            elif dtype == DTypes.FLOAT:
                low = min(domain)
                high = max(domain)
                prior = domain_param.get("prior", None)
                dimensions.append(Real(low, high, prior=prior,
                                       name=param_name))
            elif dtype == DTypes.CATEGORICAL:
                prior = domain_param.get("prior", None)
                dimensions.append(
                    Categorical(domain,
                                prior,
                                transform="onehot",
                                name=param_name))
    return dimensions
Ejemplo n.º 2
0
def run(params, module, workers, gpu, n, surrogate, acquisition, name, plot,
        out, sync, kappa, xi, kuma):
    logger = logging.getLogger(__name__)
    handler = logging.FileHandler('{name}.log'.format(name=name), delay=True)
    handler.setLevel(logging.ERROR)
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    opt_results = None
    out_file = None
    try:
        if gpu:
            # detecting available gpus with load < 0.1
            gpu_ids = [g.id for g in GPUtil.getGPUs() if g.load < 0.2]
            num_workers = min(workers, len(gpu_ids))

            if num_workers <= 0:
                sys.exit(1)
        else:
            num_workers = min(workers, mp.cpu_count())

            logger.log(logging.DEBUG,
                       "Spawning {} workers".format(num_workers))
            if num_workers <= 0:
                logger.log(logging.ERROR, "--workers cannot be 0")
                sys.exit(1)

        # prepare output file
        out_file_name = '{}_configurations.csv'.format(name)
        out = out_file_name if out is None else out
        if out is not None and os.path.isdir(out):
            out_file_path = os.path.join(out, out_file_name)
        else:
            out_file_path = out

        out_dir = os.path.abspath(os.path.join(out_file_path, os.pardir))
        out_file_path = os.path.join(out_dir, out_file_name)

        param_space = ParamSpace(params)

        dimensions = params_to_skopt(param_space)
        optimizer_dims = [d.name for d in dimensions]
        acquisition_kwargs = None
        if acquisition == "LCB":
            acquisition_kwargs = {'kappa': kappa}
        elif acquisition == "EI":
            acquisition_kwargs = {'xi': xi}

        optimizer = Optimizer(dimensions=dimensions,
                              acq_func_kwargs=acquisition_kwargs,
                              base_estimator=surrogate,
                              acq_func=acquisition)

        out_file = open(out_file_path, 'w')
        out_writer = csv.DictWriter(out_file,
                                    fieldnames=param_space.param_names() +
                                    ["id", "evaluation"])
        out_writer.writeheader()

        # setup process pool and queues
        # manager = mp.Manager()
        config_queue = Queue()
        result_queue = Queue()
        error_queue = Queue()

        terminate_flags = [Event() for _ in range(num_workers)]
        processes = [
            Process(target=worker,
                    args=(i, module, config_queue, result_queue, error_queue,
                          terminate_flags[i])) for i in range(num_workers)
        ]

        configs = []
        scores = {}
        # get initial points at random and submit one job per worker
        submit(num_workers, optimizer, optimizer_dims, configs, param_space,
               config_queue)
        # cfg_if: score

        num_completed = 0
        pending = len(configs)
        cancel = False

        for p in processes:
            p.daemon = True
            p.start()

        if plot:
            fig = plt.gcf()
            fig.show()
            fig.canvas.draw()

        progress_bar = tqdm(total=n, leave=True)

        if kuma:
            update_progress_kuma(progress_bar)

        while num_completed < n and not cancel:
            try:
                res = result_queue.get(timeout=1)
                pid, cfg_id, result = res
                if not isinstance(result, Exception):
                    cfg = configs[cfg_id]
                    # convert dictionary to x vector that optimizer takes
                    x = [cfg[param] for param in optimizer_dims]
                    # store scores for each config
                    scores[cfg_id] = result

                    out_row = dict(cfg)
                    out_row["evaluation"] = result
                    out_writer.writerow(out_row)
                    # make sure we can see the results in the file as we run the optimizer
                    out_file.flush()
                    opt_results = optimizer.tell(x, result)

                    num_completed += 1
                    pending -= 1

                    if plot:
                        plots.plot_convergence(opt_results)
                        fig.canvas.draw()

                    # sync submission of jobs means we wait for all workers to finish
                    if sync and pending == 0:
                        if num_completed != n:
                            num_submit = min(num_workers, n - num_completed)
                            submit(num_submit, optimizer, optimizer_dims,
                                   configs, param_space, config_queue)
                            pending = num_submit
                        else:
                            terminate_flags[pid].set()

                    # async submission of jobs: as soon as we receive one result we submit the next
                    if not sync:
                        if (num_completed + pending) != n:
                            submit(1, optimizer, optimizer_dims, configs,
                                   param_space, config_queue)
                            pending += 1
                        else:
                            # signal the current worker for termination
                            terminate_flags[pid].set()

                    progress_bar.update()
                    progress_bar.set_postfix(
                        {"best solution ": opt_results["fun"]})

                    if kuma:
                        update_progress_kuma(progress_bar)

                else:
                    _, cfg_id_err, err = error_queue.get()
                    logger.error("configuration {} failed".format(cfg_id_err))
                    logger.error(err)

                    cancel = True
            except Empty:
                pass

        # try to wait for process termination
        for process in processes:
            process.join(timeout=0.5)

            if process.is_alive():
                process.terminate()

        progress_bar.close()

    except TomlDecodeError as e:
        logger.error(traceback.format_exc())
        print("\n\n[Invalid parameter file] TOML decode error:\n {}".format(e),
              file=sys.stderr)
    except ParamDecodeError as e:
        logger.error(traceback.format_exc())
        print("\n\n[Invalid parameter file]\n {}".format(e), file=sys.stderr)
    except Exception as e:
        logger.error(traceback.format_exc())
        raise e
    except KeyboardInterrupt:
        pass
    finally:
        # debugging
        if opt_results is not None and plot:
            plt_file = '{}_convergence.pdf'.format(name)
            out_path = os.path.join(out_dir, plt_file)
            plt.savefig(out_path, bbox_inches='tight')

        if out_file is not None:
            out_file.close()