def test(self): X_lower = np.array([0, 0]) X_upper = np.array([1, 1]) is_env = np.array([0, 1]) X = init_random_uniform(X_lower, X_upper, 10) Y = np.sin(X)[:, None, 0] kernel = george.kernels.Matern52Kernel(np.ones([2]), ndim=2) prior = TophatPrior(-2, 2) model = GaussianProcess(kernel, prior=prior) model.train(X, Y) rec = BestProjectedObservation(model, X_lower, X_upper, is_env) inc, inc_val = rec.estimate_incumbent() # Check shapes assert len(inc.shape) == 2 assert inc.shape[0] == 1 assert inc.shape[1] == X_lower.shape[0] assert len(inc_val.shape) == 2 assert inc_val.shape[0] == 1 assert inc_val.shape[1] == 1 # Check if incumbent is in the bounds assert not np.any([np.any(inc[:, i] < X_lower[i]) for i in range(X_lower.shape[0])]) assert not np.any([np.any(inc[:, i] > X_upper[i]) for i in range(X_upper.shape[0])]) # Check if incumbent is the correct point b = np.argmin(Y) x_best = X[b, None, :] assert np.all(inc[:, is_env==0] == x_best[:, is_env==0])
def test(self): X_lower = np.array([0, 0]) X_upper = np.array([1, 1]) is_env = np.array([0, 1]) X = init_random_uniform(X_lower, X_upper, 10) Y = np.sin(X)[:, None, 0] kernel = george.kernels.Matern52Kernel(np.ones([2]), ndim=2) prior = TophatPrior(-2, 2) model = GaussianProcess(kernel, prior=prior) model.train(X, Y) rec = BestProjectedObservation(model, X_lower, X_upper, is_env) inc, inc_val = rec.estimate_incumbent() # Check shapes assert len(inc.shape) == 2 assert inc.shape[0] == 1 assert inc.shape[1] == X_lower.shape[0] assert len(inc_val.shape) == 2 assert inc_val.shape[0] == 1 assert inc_val.shape[1] == 1 # Check if incumbent is in the bounds assert not np.any( [np.any(inc[:, i] < X_lower[i]) for i in range(X_lower.shape[0])]) assert not np.any( [np.any(inc[:, i] > X_upper[i]) for i in range(X_upper.shape[0])]) # Check if incumbent is the correct point b = np.argmin(Y) x_best = X[b, None, :] assert np.all(inc[:, is_env == 0] == x_best[:, is_env == 0])
def fabolas_fmin(objective_func, X_lower, X_upper, num_iterations=100, n_init=40, burnin=100, chain_length=200, Nb=50, initX=None, initY=None): """ Interface to Fabolas [1] which models loss and training time as a function of dataset size and automatically trades off high information gain about the global optimum against computational cost. [1] Fast Bayesian Optimization of Machine Learning Hyperparameters on Large Datasets A. Klein and S. Falkner and S. Bartels and P. Hennig and F. Hutter http://arxiv.org/abs/1605.07079 Parameters ---------- objective_func : func Function handle for the objective function that get a configuration x and the training data subset size s and returns the validation error of x. See the example_fmin_fabolas.py script how the interface to this function should look like. X_lower : np.ndarray(D) Lower bound of the input space X_upper : np.ndarray(D) Upper bound of the input space num_iterations: int Number of iterations for the Bayesian optimization loop n_init: int Number of points for the initial design that is run before BO starts burnin: int Determines the length of the burnin phase of the MCMC sampling for the GP hyperparameters chain_length: int Specifies the chain length of the MCMC sampling for the GP hyperparameters Nb: int The number of representer points for approximating pmin Returns ------- x : (1, D) numpy array The estimated global optimium also called incumbent """ assert X_upper.shape[0] == X_lower.shape[0] def f(x): x_ = x[:, :-1] s = x[:, -1] return objective_func(x_, s) class Task(BaseTask): def __init__(self, X_lower, X_upper, f): super(Task, self).__init__(X_lower, X_upper) self.objective_function = f is_env = np.zeros([self.n_dims]) # Assume the last dimension to be the system size is_env[-1] = 1 self.is_env = is_env task = Task(X_lower, X_upper, f) def basis_function(x): return (1 - x)**2 # Define model for the objective function # Covariance amplitude cov_amp = 1 kernel = cov_amp # ARD Kernel for the configuration space for d in range(task.n_dims - 1): kernel *= george.kernels.Matern52Kernel(np.ones([1]) * 0.01, ndim=task.n_dims, dim=d) # Kernel for the environmental variable # We use (1-s)**2 as basis function for the Bayesian linear kernel degree = 1 env_kernel = george.kernels.BayesianLinearRegressionKernel( task.n_dims, dim=task.n_dims - 1, degree=degree) env_kernel[:] = np.ones([degree + 1]) * 0.1 kernel *= env_kernel n_hypers = 3 * len(kernel) if n_hypers % 2 == 1: n_hypers += 1 # Define the prior of the kernel's hyperparameters prior = EnvPrior(len(kernel) + 1, n_ls=task.n_dims - 1, n_lr=(degree + 1)) model = GaussianProcessMCMC(kernel, prior=prior, burnin=burnin, chain_length=chain_length, n_hypers=n_hypers, basis_func=basis_function, dim=task.n_dims - 1) # Define model for the cost function cost_cov_amp = 3000 cost_kernel = cost_cov_amp for d in range(task.n_dims - 1): cost_kernel *= george.kernels.Matern52Kernel(np.ones([1]) * 0.1, ndim=task.n_dims, dim=d) cost_degree = 1 cost_env_kernel = george.kernels.BayesianLinearRegressionKernel( task.n_dims, dim=task.n_dims - 1, degree=cost_degree) cost_env_kernel[:] = np.ones([cost_degree + 1]) * 0.1 cost_kernel *= cost_env_kernel cost_prior = EnvPrior(len(cost_kernel) + 1, n_ls=task.n_dims - 1, n_lr=(cost_degree + 1)) cost_model = GaussianProcessMCMC(cost_kernel, prior=cost_prior, burnin=burnin, chain_length=chain_length, n_hypers=n_hypers) # Define acquisition function and maximizer es = InformationGainPerUnitCost(model, cost_model, task.X_lower, task.X_upper, task.is_env, Nb=Nb) acquisition_func = IntegratedAcquisition(model, es, task.X_lower, task.X_upper, cost_model) maximizer = cmaes.CMAES(acquisition_func, task.X_lower, task.X_upper) rec = BestProjectedObservation(model, task.X_lower, task.X_upper, task.is_env) bo = Fabolas(acquisition_func=acquisition_func, model=model, cost_model=cost_model, maximize_func=maximizer, task=task, initial_points=n_init, incumbent_estimation=rec) best_x, f_min = bo.run(num_iterations, X=initX, Y=initY) return task.retransform(best_x), f_min, model, acquisition_func, maximizer
def __init__(self, acquisition_func, model, cost_model, maximize_func, task, n_tasks, save_dir=None, num_save=1, train_intervall=1, n_restarts=1, initial_points=15, incumbent_estimation=None): """ Solver class that implements MuliTask BO introduced by Swersky et al. Parameters ---------- acquisition_func : EnvironmentEntropy Object The acquisition function to determine the next point to evaluate. This object has to be an instance of EnvironmentEntropy class. model : Model object Models the objective function. The model has to be a Gaussian process. If MCMC sampling of the model's hyperparameter is performed, make sure that the acquistion_func is of an instance of IntegratedAcquisition to marginalise over the GP's hyperparameter. cost_model : model Models the cost function. The model has to be a Gaussian Process. maximize_func : Maximizer object Optimizer to maximize the acquisition function. task: Task object The task that should be optimized. Make sure that it returns the function value as well as the cost if the evaluate() function is called. save_dir : str, optional Path where the results file will be saved num_save : int, optional Specifies after how many iterations the results will be written to the output file train_intervall : int, optional Specified after how many iterations the model will be retrained n_restarts : int, optional How many local searches are performed to estimate the incumbent. initial_points : int , optional How many points are sampled for the initial design incumbent_estimation: IncumbentEstimationObject, Object to estimate the incumbent based on the current model. The incumbent is the current best guess of the global optimum and is estimated in each iteration. """ self.train_intervall = train_intervall self.acquisition_func = acquisition_func self.model = model self.maximize_func = maximize_func self.task = task self.n_tasks = n_tasks self.cost_model = cost_model self.save_dir = save_dir self.num_save = num_save if save_dir is not None: self.create_save_dir() self.X = None self.Y = None self.C = None self.model_untrained = True self.incumbent = None self.incumbents = [] self.incumbent_values = [] self.runtime = [] self.initial_design = init_random_uniform # How often we restart the local search to find the current incumbent self.n_restarts = n_restarts super(MultiTaskBO, self).__init__(acquisition_func, model, maximize_func, task, save_dir) # Posterior optimization only over the posterior of the last task, # which is assumed to be the most difficult one if incumbent_estimation == None: self.estimator = BestProjectedObservation(self.model, self.task.X_lower, self.task.X_upper, self.task.is_env) else: self.estimator = incumbent_estimation self.init_points = initial_points
class MultiTaskBO(BayesianOptimization): def __init__(self, acquisition_func, model, cost_model, maximize_func, task, n_tasks, save_dir=None, num_save=1, train_intervall=1, n_restarts=1, initial_points=15, incumbent_estimation=None): """ Solver class that implements MuliTask BO introduced by Swersky et al. Parameters ---------- acquisition_func : EnvironmentEntropy Object The acquisition function to determine the next point to evaluate. This object has to be an instance of EnvironmentEntropy class. model : Model object Models the objective function. The model has to be a Gaussian process. If MCMC sampling of the model's hyperparameter is performed, make sure that the acquistion_func is of an instance of IntegratedAcquisition to marginalise over the GP's hyperparameter. cost_model : model Models the cost function. The model has to be a Gaussian Process. maximize_func : Maximizer object Optimizer to maximize the acquisition function. task: Task object The task that should be optimized. Make sure that it returns the function value as well as the cost if the evaluate() function is called. save_dir : str, optional Path where the results file will be saved num_save : int, optional Specifies after how many iterations the results will be written to the output file train_intervall : int, optional Specified after how many iterations the model will be retrained n_restarts : int, optional How many local searches are performed to estimate the incumbent. initial_points : int , optional How many points are sampled for the initial design incumbent_estimation: IncumbentEstimationObject, Object to estimate the incumbent based on the current model. The incumbent is the current best guess of the global optimum and is estimated in each iteration. """ self.train_intervall = train_intervall self.acquisition_func = acquisition_func self.model = model self.maximize_func = maximize_func self.task = task self.n_tasks = n_tasks self.cost_model = cost_model self.save_dir = save_dir self.num_save = num_save if save_dir is not None: self.create_save_dir() self.X = None self.Y = None self.C = None self.model_untrained = True self.incumbent = None self.incumbents = [] self.incumbent_values = [] self.runtime = [] self.initial_design = init_random_uniform # How often we restart the local search to find the current incumbent self.n_restarts = n_restarts super(MultiTaskBO, self).__init__(acquisition_func, model, maximize_func, task, save_dir) # Posterior optimization only over the posterior of the last task, # which is assumed to be the most difficult one if incumbent_estimation == None: self.estimator = BestProjectedObservation(self.model, self.task.X_lower, self.task.X_upper, self.task.is_env) else: self.estimator = incumbent_estimation self.init_points = initial_points def run(self, num_iterations=10, X=None, Y=None, C=None): """ Runs the main Bayesian optimization loop Parameters ---------- num_iterations : int, optional Specifies the number of iterations. X : (N, D) numpy array, optional Initial points where BO starts from. Y : (N, D) numpy array, optional The function values of the initial points. Make sure the number of points is the same. C : (N, D) numpy array, optional The costs of the initial points. Make sure the number of points is the same. Returns ------- incumbent : (1, D) numpy array The estimated optimum that was found after the specified number of iterations. """ self.time_start = time.time() if X is None and Y is None and C is None: self.time_func_eval = np.zeros([self.init_points]) self.time_overhead = np.zeros([self.init_points]) self.X = np.zeros([1, self.task.n_dims]) self.Y = np.zeros([1, 1]) self.C = np.zeros([1, 1]) init = self.initial_design(self.task.X_lower, self.task.X_upper, self.init_points) # Evaluate only on cheaper task init[:, -1] = 0 for i, x in enumerate(init): x = x[np.newaxis, :] logger.info("Evaluate: %s" % x) start_time = time.time() y, c = self.task.evaluate(x) # Transform cost to log scale c = np.log(c) if i == 0: self.X[i] = x[0, :] self.Y[i] = y[0, :] self.C[i] = c[0, :] self.time_func_eval[i] = time.time() - start_time self.time_overhead[i] = 0.0 else: self.X = np.append(self.X, x, axis=0) self.Y = np.append(self.Y, y, axis=0) self.C = np.append(self.C, c, axis=0) time_feval = np.array([time.time() - start_time]) self.time_func_eval = np.append(self.time_func_eval, time_feval, axis=0) self.time_overhead = np.append(self.time_overhead, np.array([0]), axis=0) logger.info("Configuration achieved a" "performance of %f and %f costs in %f seconds" % (self.Y[i], self.C[i], self.time_func_eval[i])) # Use best point seen so far as incumbent best_idx = np.argmin(self.Y) best_idx = np.argmin(self.Y) # Copy because we are going to change the system size to smax self.incumbent = np.copy(self.X[best_idx]) self.incumbent_value = self.Y[best_idx] self.runtime.append(time.time() - self.start_time) self.incumbent[-1] = 1 self.incumbent = self.incumbent[np.newaxis, :] self.incumbent_value = self.incumbent_value[np.newaxis, :] self.incumbents.append(self.incumbent) self.incumbent_values.append(self.incumbent_value) if self.save_dir is not None and (i) % self.num_save == 0: self.save_iteration(i, costs=self.C[-1], hyperparameters=None, acquisition_value=0) else: self.X = X self.Y = Y self.C = C self.time_func_eval = np.zeros([self.X.shape[0]]) self.time_overhead = np.zeros([self.X.shape[0]]) for it in range(self.init_points, num_iterations): logger.info("Start iteration %d ... ", it) # Choose a new configuration start_time = time.time() if it % self.train_intervall == 0: do_optimize = True else: do_optimize = False new_x = self.choose_next(self.X, self.Y, self.C, do_optimize) # Estimate current incumbent from the posterior # over the configuration space start_time_inc = time.time() startpoints = init_random_uniform(self.task.X_lower, self.task.X_upper, self.n_restarts) self.incumbent, self.incumbent_value = \ self.estimator.estimate_incumbent(startpoints) self.incumbents.append(self.incumbent) self.incumbent_values.append(self.incumbent_value) logger.info("New incumbent %s found in %f seconds", str(self.incumbent), time.time() - start_time_inc) # Compute the time we needed to pick a new point time_overhead = time.time() - start_time self.time_overhead = np.append(self.time_overhead, np.array([time_overhead])) logger.info("Optimization overhead was " "%f seconds" % (self.time_overhead[-1])) # Evaluate the configuration logger.info("Evaluate candidate %s" % (str(new_x))) start_time = time.time() new_y, new_cost = self.task.evaluate(new_x) time_func_eval = time.time() - start_time # We model the log costs new_cost = np.log(new_cost) self.time_func_eval = np.append(self.time_func_eval, np.array([time_func_eval])) logger.info("Configuration achieved a performance " "of %f in %s seconds" % (new_y[0, 0], new_cost[0])) # Add the new observations to the data self.X = np.append(self.X, new_x, axis=0) self.Y = np.append(self.Y, new_y, axis=0) self.C = np.append(self.C, new_cost, axis=0) self.runtime.append(time.time() - self.start_time) if self.save_dir is not None and (it) % self.num_save == 0: hypers = self.model.hypers self.save_iteration( it, costs=self.C[-1], hyperparameters=hypers, acquisition_value=self.acquisition_func(new_x)) logger.info("Return %s as incumbent" % (str(self.incumbent))) return self.incumbent def choose_next(self, X=None, Y=None, C=None, do_optimize=True): """ Performs one single iteration of Bayesian optimization and estimated the next point to evaluate. Parameters ---------- X : (N, D) numpy array, optional The points that have been observed so far. The model is trained on this points. Y : (N, D) numpy array, optional The function values of the observed points. Make sure the number of points is the same. C : (N, D) numpy array, optional The costs of the observed points. Make sure the number of points is the same. do_optimze : bool, optional Specifies if the hyperparamter of the Gaussian process should be optimized. Returns ------- x : (1, D) numpy array The suggested point to evaluate. """ if X is None and Y is None and C is None: x = self.initial_design(self.task.X_lower, self.task.X_upper, self.task.is_env, N=1) elif X.shape[0] == 1: # We need at least 2 data points to train a GP x = self.initial_design(self.task.X_lower, self.task.X_upper, self.task.is_env, N=1) else: # Train the model for the objective function and the cost function try: t = time.time() self.model.train(X, Y, do_optimize) self.cost_model.train(X, C, do_optimize) logger.info("Time to train the models: %f", (time.time() - t)) except Exception: logger.error("Model could not be trained with data:", X, Y, C) raise self.model_untrained = False # Update the acquisition function with the new models self.acquisition_func.update(self.model, self.cost_model) # Maximize the acquisition function and return the suggested point t = time.time() x = self.maximize_func.maximize() #x[:, -1] = np.round(x[:, -1]) #x[:, np.where(self.task.is_env == 1)] = np.floor(x[:, -1] * self.n_tasks) logger.info("Time to maximize the acquisition function: %f", (time.time() - t)) return x
def __init__(self, acquisition_func, model, cost_model, maximize_func, task, save_dir=None, initialization=None, num_save=1, train_intervall=1, n_restarts=1, incumbent_estimation=None, initial_points=15): """ Fast Bayesian Optimization of Machine Learning Hyperparameters on Large Datasets Parameters ---------- acquisition_func : EnvironmentEntropy Object The acquisition function to determine the next point to evaluate. This object has to be an instance of EnvironmentEntropy class. model : Model object Models the objective function. The model has to be a Gaussian process. If MCMC sampling of the model's hyperparameter is performed, make sure that the acquistion_func is of an instance of IntegratedAcquisition to marginalise over the GP's hyperparameter. cost_model : model Models the cost function. The model has to be a Gaussian Process. maximize_func : Maximizer object Optimizer to maximize the acquisition function. task: Task object The task that should be optimized. Make sure that it returns the function value as well as the cost if the evaluate() function is called. save_dir : str, optional Path where the results file will be saved initialization : func, optional Initial design function to find some initial points num_save : int, optional Specifies after how many iterations the results will be written to the output file train_intervall : int, optional Specified after how many iterations the model will be retrained n_restarts : int, optional How many local searches are performed to estimate the incumbent. incumbent_estimation: IncumbentEstimationObject, Object to estimate the incumbent based on the current model. The incumbent is the current best guess of the global optimum and is estimated in each iteration. initial_points : int , optional How many points are sampled for the initial design """ self.start_time = time.time() self.train_intervall = train_intervall self.acquisition_func = acquisition_func self.model = model self.maximize_func = maximize_func self.task = task self.initialization = initialization self.cost_model = cost_model self.save_dir = save_dir self.num_save = num_save if save_dir is not None: self.create_save_dir() self.X = None self.Y = None self.C = None self.model_untrained = True self.incumbent = None self.incumbents = [] self.incumbent_values = [] self.runtime = [] # How often we restart the local search to find the current incumbent self.n_restarts = n_restarts super(Fabolas, self).__init__(acquisition_func, model, maximize_func, task, save_dir) if incumbent_estimation == None: self.estimator = BestProjectedObservation(self.model, self.task.X_lower, self.task.X_upper, self.task.is_env) else: self.estimator = incumbent_estimation self.init_points = initial_points
class Fabolas(BayesianOptimization): def __init__(self, acquisition_func, model, cost_model, maximize_func, task, save_dir=None, initialization=None, num_save=1, train_intervall=1, n_restarts=1, incumbent_estimation=None, initial_points=15): """ Fast Bayesian Optimization of Machine Learning Hyperparameters on Large Datasets Parameters ---------- acquisition_func : EnvironmentEntropy Object The acquisition function to determine the next point to evaluate. This object has to be an instance of EnvironmentEntropy class. model : Model object Models the objective function. The model has to be a Gaussian process. If MCMC sampling of the model's hyperparameter is performed, make sure that the acquistion_func is of an instance of IntegratedAcquisition to marginalise over the GP's hyperparameter. cost_model : model Models the cost function. The model has to be a Gaussian Process. maximize_func : Maximizer object Optimizer to maximize the acquisition function. task: Task object The task that should be optimized. Make sure that it returns the function value as well as the cost if the evaluate() function is called. save_dir : str, optional Path where the results file will be saved initialization : func, optional Initial design function to find some initial points num_save : int, optional Specifies after how many iterations the results will be written to the output file train_intervall : int, optional Specified after how many iterations the model will be retrained n_restarts : int, optional How many local searches are performed to estimate the incumbent. incumbent_estimation: IncumbentEstimationObject, Object to estimate the incumbent based on the current model. The incumbent is the current best guess of the global optimum and is estimated in each iteration. initial_points : int , optional How many points are sampled for the initial design """ self.start_time = time.time() self.train_intervall = train_intervall self.acquisition_func = acquisition_func self.model = model self.maximize_func = maximize_func self.task = task self.initialization = initialization self.cost_model = cost_model self.save_dir = save_dir self.num_save = num_save if save_dir is not None: self.create_save_dir() self.X = None self.Y = None self.C = None self.model_untrained = True self.incumbent = None self.incumbents = [] self.incumbent_values = [] self.runtime = [] # How often we restart the local search to find the current incumbent self.n_restarts = n_restarts super(Fabolas, self).__init__(acquisition_func, model, maximize_func, task, save_dir) if incumbent_estimation == None: self.estimator = BestProjectedObservation(self.model, self.task.X_lower, self.task.X_upper, self.task.is_env) else: self.estimator = incumbent_estimation self.init_points = initial_points def run(self, num_iterations=10, X=None, Y=None, C=None): """ Runs the main Bayesian optimization loop Parameters ---------- num_iterations : int, optional Specifies the number of iterations. X : (N, D) numpy array, optional Initial points where BO starts from. Y : (N, D) numpy array, optional The function values of the initial points. Make sure the number of points is the same. C : (N, D) numpy array, optional The costs of the initial points. Make sure the number of points is the same. Returns ------- incumbent : (1, D) numpy array The estimated optimum that was found after the specified number of iterations. """ self.time_start = time.time() if X is None and Y is None and C is None: self.time_func_eval = np.zeros([1]) self.time_overhead = np.zeros([1]) self.X = np.zeros([1, self.task.n_dims]) self.Y = np.zeros([1, 1]) self.C = np.zeros([1, 1]) init = extrapolative_initial_design(self.task, N=self.init_points) for i, x in enumerate(init): x = x[np.newaxis, :] start_time = time.time() logger.info("Evaluate: %s" % x) start_time = time.time() y, c = self.task.evaluate(x) # Transform cost to log scale c = np.log(c) if i == 0: self.X[i] = x[0, :] self.Y[i] = y[0, :] self.C[i] = c[0, :] self.time_func_eval[i] = time.time() - start_time self.time_overhead[i] = 0.0 else: self.X = np.append(self.X, x, axis=0) self.Y = np.append(self.Y, y, axis=0) self.C = np.append(self.C, c, axis=0) time_feval = np.array([time.time() - start_time]) self.time_func_eval = np.append(self.time_func_eval, time_feval, axis=0) self.time_overhead = np.append(self.time_overhead, np.array([0]), axis=0) logger.info("Configuration achieved a" "performance of %f and %f costs in %f seconds" % (self.Y[i], self.C[i], self.time_func_eval[i])) # Use best point seen so far as incumbent best_idx = np.argmin(self.Y) best_idx = np.argmin(self.Y) # Copy because we are going to change the system size to smax self.incumbent = np.copy(self.X[best_idx]) self.incumbent_value = self.Y[best_idx] bounds_subspace = self.task.X_upper[self.task.is_env == 1] self.incumbent[self.task.is_env == 1] = bounds_subspace self.incumbent = self.incumbent[np.newaxis, :] self.incumbent_value = self.incumbent_value[np.newaxis, :] self.incumbents.append(self.incumbent) self.incumbent_values.append(self.incumbent_value) self.runtime.append(time.time() - self.start_time) if self.save_dir is not None and (i) % self.num_save == 0: self.save_iteration(i, costs=self.C[-1], hyperparameters=None, acquisition_value=0) else: self.X = X self.Y = Y self.C = C self.time_func_eval = np.zeros([self.X.shape[0]]) self.time_overhead = np.zeros([self.X.shape[0]]) for it in range(0, num_iterations): logger.info("Start iteration %d ... ", it) # Choose a new configuration start_time = time.time() if it % self.train_intervall == 0: do_optimize = True else: do_optimize = False new_x = self.choose_next(self.X, self.Y, self.C, do_optimize) # Estimate current incumbent from the posterior # over the configuration space start_time_inc = time.time() startpoints = init_random_uniform(self.task.X_lower, self.task.X_upper, self.n_restarts) self.incumbent, self.incumbent_value = \ self.estimator.estimate_incumbent(startpoints) self.incumbents.append(self.incumbent) self.incumbent_values.append(self.incumbent_value) logger.info("New incumbent %s found in %f seconds"\ " with predicted performance %f", str(self.incumbent), time.time() - start_time_inc, self.incumbent_value) # Compute the time we needed to pick a new point time_overhead = time.time() - start_time self.time_overhead = np.append(self.time_overhead, np.array([time_overhead])) logger.info("Optimization overhead was " "%f seconds" % (self.time_overhead[-1])) # Evaluate the configuration logger.info("Evaluate candidate %s" % (str(new_x))) start_time = time.time() new_y, new_cost = self.task.evaluate(new_x) time_func_eval = time.time() - start_time # We model the log costs new_cost = np.log(new_cost) self.time_func_eval = np.append(self.time_func_eval, np.array([time_func_eval])) logger.info("Configuration achieved a performance " "of %f in %s seconds" % (new_y[0, 0], new_cost[0])) # Add the new observations to the data self.X = np.append(self.X, new_x, axis=0) self.Y = np.append(self.Y, new_y, axis=0) self.C = np.append(self.C, new_cost, axis=0) self.runtime.append(time.time() - self.start_time) if self.save_dir is not None and (it + self.init_points) % self.num_save == 0: hypers = self.model.hypers self.save_iteration(it + self.init_points, costs=self.C[-1], hyperparameters=hypers, acquisition_value=self.acquisition_func(new_x)) logger.info("Return %s as incumbent" % (str(self.incumbent))) return self.incumbent def choose_next(self, X=None, Y=None, C=None, do_optimize=True): """ Performs one single iteration of Bayesian optimization and estimated the next point to evaluate. Parameters ---------- X : (N, D) numpy array, optional The points that have been observed so far. The model is trained on this points. Y : (N, D) numpy array, optional The function values of the observed points. Make sure the number of points is the same. C : (N, D) numpy array, optional The costs of the observed points. Make sure the number of points is the same. do_optimze : bool, optional Specifies if the hyperparamter of the Gaussian process should be optimized. Returns ------- x : (1, D) numpy array The suggested point to evaluate. """ if X is None and Y is None and C is None: x = extrapolative_initial_design(self.task.X_lower, self.task.X_upper, self.task.is_env, N=1) elif X.shape[0] == 1: # We need at least 2 data points to train a GP x = extrapolative_initial_design(self.task.X_lower, self.task.X_upper, self.task.is_env, N=1) else: # Train the model for the objective function and the cost function try: t = time.time() self.model.train(X, Y, do_optimize) self.cost_model.train(X, C, do_optimize) logger.info("Time to train the models: %f", (time.time() - t)) except Exception: logger.error("Model could not be trained with data:", X, Y, C) raise self.model_untrained = False # Update the acquisition function with the new models self.acquisition_func.update(self.model, self.cost_model, overhead=self.time_overhead[-1]) # Maximize the acquisition function and return the suggested point t = time.time() x = self.maximize_func.maximize() logger.info("Time to maximize the acquisition function: %f", (time.time() - t)) return x