def init_random_generator(cls, rdm_object=None): """ Return a RandomGenerator. Can deal with multiple input: Seed/ RandomGenerator/None """ if (rdm_object is None): return RandomGenerator() elif (ut.is_int(rdm_object)): return RandomGenerator(seed=rdm_object) elif (isinstance(rdm_object, RandomGenerator)): return rdm_object else: raise NotImplementedError()
def _init_params_BO(self, **args_optim): """ Provides different ways to initialize bo depending of the input type: <None> / <int>: returns an integer (nb of points to be eval) e.g. None >> 2 * nb_params <str>: random_init based on a string. returns a <P x N np.array> P is the population size N number of parameters e.g. '40_lhs' >> 40 points drawn by latin hypercube sampling '50_uniform' >> 50 points drawn uniformly range of each params is infered from bounds <P * N array>: passs it though """ init_obj = args_optim['init_obj'] nb_params = args_optim['nb_params'] bounds = args_optim['bounds_params'] if (init_obj is None): init_args = nb_params *2 elif ut.is_int(init_obj): init_args = init_obj elif ut.is_string(init_obj): bits = init_obj.split("_",1) nb_points_init = int(bits[0]) if(bits[1] == 'lhs'): size_pop = [nb_points_init, nb_params] limits = np.array(bounds).T init_args = self.rdm_gen.init_population_lhs(size_pop, limits) else: distrib_one_param = [bits[1]+'_'+ bounds[i][0] + '_' + bounds[i][1] for i in range(nb_params)] init_matrix = [self.rdm_gen.gen_rdmfunc_from_string(d, dim = nb_points_init) for d in distrib_one_param] init_args = np.array(init_matrix).T else: init_args = init_obj return init_args
def gen_rdmfunc_from_string(self, method_rdm, dim=1): """return a function that when called return random variables according to the distribution described by the string, with specific dim convention: dim[-1] correspond to the dim of the RV while dim[:-1] to the size of the population TODO: works only for 1D dim """ if ut.is_list(method_rdm): # return a lst of function # should it be a function returning a list?? func = [ self.gen_rdmfunc_from_string(meth, dim) for meth in method_rdm ] else: args = ut.splitString(method_rdm) if (ut.is_int(dim)): dimRV = dim else: dimRV = dim[-1] if (args[0] in ['uniform', 'normal']): if (len(args) == 1): first_args = np.repeat(0, dimRV) second_args = np.repeat(1, dimRV) elif (len(args) == 3): first_args = np.repeat(float(args[1]), dimRV) second_args = np.repeat(float(args[2]), dimRV) elif (len(args) == (1 + 2 * dimRV)): first_args = np.array( [float(args[1 + 2 * d]) for d in range(dimRV)]) second_args = np.array( [float(args[2 + 2 * d]) for d in range(dimRV)]) else: raise NotImplementedError() if (dim == 1): # such that function return a value instead of an array # may change first_args, second_args = first_args[0], second_args[0] dim = None if args[0] == 'normal': def func(): return self.normal(first_args, second_args, size=dim) else: def func(): return self.uniform(first_args, second_args, size=dim) elif (args[0] == 'determ'): if (len(args) == 1): constant = np.repeat(0, dim) elif (len(args) == 2): constant = np.repeat(float(args[1]), dim) elif (len(args) == (1 + dimRV)): constant = np.array(args[1:]) if (ut.is_list(dim)): dim_pop = np.product(dim[:-1]) constant = np.tile(constant, dim_pop).reshape(dim) else: raise NotImplementedError() def func(): return constant else: raise NotImplementedError() return func
def _run_BO2(self, options, **args_call): """ Bayesian optimization using GPYOpt def __init__(self, f, domain = None, constraints = None, cost_withGradients = None, model_type = 'GP', X = None, Y = None, initial_design_numdata = 5, initial_design_type='random', acquisition_type ='EI', normalize_Y = True, exact_feval = False, acquisition_optimizer_type = 'lbfgs', model_update_interval=1, evaluator_type = 'sequential',batch_size = 1, num_cores = 1, verbosity=False, verbosity_model = False, maximize=False, de_duplication=False, **kwargs) def run_optimization(self, maxiter = 0, max_time = np.inf, eps = 1e-8, context = None, verbosity=False, save_models_parameters= True, report_file = None, evaluations_file = None, models_file=None): parameters ---------- options.constraints: <str> or None encode the type of constraints which can be used. This string is converted to be passed to the BayesianOptimizer constructor with the following structure [{'name':'name1', 'constraint':obj_constraint1}, .., {'name':'nameN', 'constraint':obj_constraintN}] where obj_constraintX can be a function or a string which can be exec to generate a function a constraint is of the form c(x) <= 0 (i.e. a parameter is accepted if c(x) <= 0) e.g. 'step_a' difference between two consecutive parameters should be less than a e.g. 'step_a_a0_aN' same as above plus the first (last) parameter is also compared to a0 (aN) e.g. 'smooth_s' smoothness calculated should be <=s """ #Init BO model = options['model'] nb_params = options['nb_params'] name_params = [str(i) for i in range(nb_params)] options['name_params'] = name_params bounds_bo = [{'name': name_params[i], 'type': 'continuous', 'domain': options['bounds_params'][i]} for i in range(nb_params)] constraints = options.get('constraints') cst = self._build_constraints(constraints, **options) args_BO = {'acquisition_type':options['acq'], 'acquisition_optimizer_type':options['acq_opt_type'], 'num_cores':options['num_cores'], 'domain': bounds_bo, 'optim_num_anchor':options['optim_num_anchor'], 'optim_num_samples':options['optim_num_samples'], 'acquisition_jitter':options['acquisition_jitter'], 'acquisition_weight':options['acquisition_weight'], 'batch_size':options['batch_size'], 'evaluator_type':options['batch_method'], 'num_inducing':options['num_inducing'], 'model_type':options['model_type'], 'ARD':options['ARD'], 'acquisition_weight_lindec':options['acquisition_weight_lindec'], 'constraints':cst} # definition of the cost function def cost(params): return model(np.squeeze(params), **args_call) #V0.1 NON DEFAULT LIKELIHOOD if(args_BO['model_type'] == 'GP_CUSTOM_LIK'): logger.info('Use of GP_CUSTOM_LIK: enforce **normalize_Y** as False ') logger.info('Use of GP_CUSTOM_LIK: enforce **inf_method** as Laplace') args_BO['normalize_Y'] = False args_BO['inf_method'] = 'Laplace' args_BO['likelihood'] = options.get('likelihood', 'Binomial_10') #V0.1 transfer learning to_transfer = options['to_transfer'] if((to_transfer is not None) and (args_BO['model_type'] == 'GP_STACKED')): args_BO_transfer = copy.copy(args_BO) args_BO_transfer['model_type'] = 'GP' args_BO_transfer['X'] = to_transfer['X'] args_BO_transfer['Y'] = to_transfer['Y'] regressor_to_transfer = GPyOpt.methods.BayesianOptimization(cost, **args_BO) regressor_to_transfer.model._create_model(args_BO_transfer['X'], args_BO_transfer['Y']) args_BO['prev'] = regressor_to_transfer.model init = options['init_params'] if(init is None): args_BO['initial_design_numdata'] = int(3 * nb_params) args_BO['initial_design_type'] = options['initial_design_type'] elif(ut.is_int(init)): args_BO['initial_design_numdata'] = init args_BO['initial_design_type'] = options['initial_design_type'] else: args_BO['X'] = init logger.info('Init of the GP: acquisition of {} points'.format(init.shape[0])) args_BO['Y'] = np.array([cost(x) for x in init]) ker = options['kernel'] # matern52 /matern32 if(ker is not None): ard = options['ARD'] if(ker == 'matern52'): args_BO['ker'] = eval("GPy.kern.Matern52(self.input_dim = nb_params, ARD={})".format(ard)) elif(ker == 'matern32'): args_BO['ker'] = eval("GPy.kern.Matern32(self.input_dim = nb_params, ARD={})".format(ard)) else: logger.warning("{} not a valid kernel".format(ker)) bo = GPyOpt.methods.BayesianOptimization(cost, **args_BO) #Initialization phase # Exploration-Exploitation phase max_time = options['max_time'] bo.run_optimization(max_iter = options['maxiter'], max_time = max_time) time_left = max_time - bo.cum_time max_reached = (time_left < 0) # Exploitation phase # should it be forced eben if max_time is reached ? No so far exploit_steps = options['exploit_steps'] if(exploit_steps > 0): if(not(max_reached)): bo.acquisition_type = 'LCB' bo.acquisition_weight = 0.000001 bo.kwargs['acquisition_weight'] = 0.000001 bo.acquisition = bo._acquisition_chooser() #maybe to add new logic when Batch/Sparse if(bo.batch_size > 1): bo.batch_size = 1 bo.evaluator = bo._evaluator_chooser() logger.info('Exploitation (i.e. ucb with k=0) for {}'.format(exploit_steps)) logger.info('Still {} s left'.format(time_left)) bo.run_optimization(exploit_steps, max_time = time_left) time_left -= bo.cum_time max_reached = (time_left < 0) if((_still_potentially_better(bo)) and (exploit_steps <= 50) and not(max_reached)): logger.info('2nd round of exploitation for {}'.format(exploit_steps)) logger.info('Still {} s left'.format(time_left)) bo.run_optimization(exploit_steps, max_time = time_left) # generate results optim_params = bo.x_opt optim_value = bo.fx_opt optim_params_exp = _get_best_exp_from_BO(bo) optim_value_cputed = model(optim_params) nfev = len(bo.X) resultTest = {'x': optim_params, 'x_exp':optim_params_exp, 'fun': optim_value, 'fun_ver': optim_value_cputed, 'nfev':nfev, 'nit':nfev, 'sucess':True} resultTest['gp_kernel_optim_names'] = bo.model.model.parameter_names() resultTest['gp_kernel_optim_vals'] = bo.model.model.param_array resultTest['nb_processes'] = bo.num_cores resultTest['nb_cpus'] = self.mp.n_cpus resultTest['X_evol'] = bo.X resultTest['Y_evol'] = bo.Y resultTest['Y_best'] = bo.Y_best resultTest['still_potentially_better'] = _still_potentially_better(bo) resultTest['maxtime'] = max_reached # Close pool of processors used (if needed) self.mp.close_mp() return resultTest