def __init__(self, config_space, objective_func, R, num_iter=10, n_workers=1, eta=3, es_gap=9, rho=0.7, random_state=1, method_id="Default"): BaseFacade.__init__(self, objective_func, n_workers=n_workers, need_lc=True, method_name=method_id) self.seed = random_state self.config_space = config_space self.config_space.seed(self.seed) self.R = R self.num_iter = num_iter self.eta = eta self.logeta = lambda x: log(x) / log(self.eta) self.s_max = int(self.logeta(R)) self.inner_iteration_n = (self.s_max + 1) * (self.s_max + 1) types, bounds = get_types(config_space) self.num_config = len(bounds) self.surrogate = RandomForestWithInstances(types=types, bounds=bounds) self.acquisition_func = EI(model=self.surrogate) # TODO: add SMAC's optimization algorithm. self.acq_optimizer = RandomSampling(self.acquisition_func, config_space, n_samples=max(500, 50 * self.num_config)) self.incumbent_configs = [] self.incumbent_obj = [] self.lcnet_model = LC_ES() self.early_stop_gap = es_gap self.es_rho = rho self.lc_training_x = None self.lc_training_y = None
def __init__(self, config_space: ConfigurationSpace, objective_func, R, num_iter=10000, eta=3, p=0.3, n_workers=1, random_state=1, method_id='Default'): BaseFacade.__init__(self, objective_func, n_workers=n_workers, method_name=method_id) self.config_space = config_space self.seed = random_state self.config_space.seed(self.seed) self.p = p self.R = R self.eta = eta self.logeta = lambda x: log(x) / log(self.eta) self.s_max = int(self.logeta(self.R)) self.B = (self.s_max + 1) * self.R self.num_iter = num_iter types, bounds = get_types(config_space) self.num_config = len(bounds) self.surrogate = RandomForestWithInstances(types=types, bounds=bounds) self.acquisition_func = EI(model=self.surrogate) self.acq_optimizer = RandomSampling(self.acquisition_func, config_space, n_samples=max( 500, 50 * self.num_config)) self.incumbent_configs = [] self.incumbent_obj = []
def update_weight(self): max_r = self.iterate_r[-1] incumbent_configs = self.target_x[max_r] test_x = convert_configurations_to_array(incumbent_configs) test_y = np.array(self.target_y[max_r], dtype=np.float64) r_list = self.weighted_surrogate.surrogate_r K = len(r_list) if len(test_y) >= 3: # Get previous weights if self.weight_method in [ 'rank_loss_softmax', 'rank_loss_single', 'rank_loss_p_norm' ]: preserving_order_p = list() preserving_order_nums = list() for i, r in enumerate(r_list): fold_num = 5 if i != K - 1: mean, var = self.weighted_surrogate.surrogate_container[ r].predict(test_x) tmp_y = np.reshape(mean, -1) preorder_num, pair_num = MFSE.calculate_preserving_order_num( tmp_y, test_y) preserving_order_p.append(preorder_num / pair_num) preserving_order_nums.append(preorder_num) else: if len(test_y) < 2 * fold_num: preserving_order_p.append(0) else: # 5-fold cross validation. kfold = KFold(n_splits=fold_num) cv_pred = np.array([0] * len(test_y)) for train_idx, valid_idx in kfold.split(test_x): train_configs, train_y = test_x[ train_idx], test_y[train_idx] valid_configs, valid_y = test_x[ valid_idx], test_y[valid_idx] types, bounds = get_types(self.config_space) _surrogate = RandomForestWithInstances( types=types, bounds=bounds) _surrogate.train(train_configs, train_y) pred, _ = _surrogate.predict(valid_configs) cv_pred[valid_idx] = pred.reshape(-1) preorder_num, pair_num = MFSE.calculate_preserving_order_num( cv_pred, test_y) preserving_order_p.append(preorder_num / pair_num) preserving_order_nums.append(preorder_num) if self.weight_method == 'rank_loss_softmax': order_weight = np.array(np.sqrt(preserving_order_nums)) trans_order_weight = order_weight - np.max(order_weight) # Softmax mapping. new_weights = np.exp(trans_order_weight) / sum( np.exp(trans_order_weight)) elif self.weight_method == 'rank_loss_p_norm': trans_order_weight = np.array(preserving_order_p) power_sum = np.sum( np.power(trans_order_weight, self.power_num)) new_weights = np.power(trans_order_weight, self.power_num) / power_sum else: _idx = np.argmax(np.array(preserving_order_nums)) new_weights = [0.] * K new_weights[_idx] = 1. elif self.weight_method == 'rank_loss_prob': # For basic surrogate i=1:K-1. mean_list, var_list = list(), list() for i, r in enumerate(r_list[:-1]): mean, var = self.weighted_surrogate.surrogate_container[ r].predict(test_x) mean_list.append(np.reshape(mean, -1)) var_list.append(np.reshape(var, -1)) sample_num = 100 min_probability_array = [0] * K for _ in range(sample_num): order_preseving_nums = list() # For basic surrogate i=1:K-1. for idx in range(K - 1): sampled_y = np.random.normal(mean_list[idx], var_list[idx]) _num, _ = MFSE.calculate_preserving_order_num( sampled_y, test_y) order_preseving_nums.append(_num) fold_num = 5 # For basic surrogate i=K. cv if len(test_y) < 2 * fold_num: order_preseving_nums.append(0) else: # 5-fold cross validation. kfold = KFold(n_splits=fold_num) cv_pred = np.array([0] * len(test_y)) for train_idx, valid_idx in kfold.split(test_x): train_configs, train_y = test_x[train_idx], test_y[ train_idx] valid_configs, valid_y = test_x[valid_idx], test_y[ valid_idx] types, bounds = get_types(self.config_space) _surrogate = RandomForestWithInstances( types=types, bounds=bounds) _surrogate.train(train_configs, train_y) _pred, _var = _surrogate.predict(valid_configs) sampled_pred = np.random.normal( _pred.reshape(-1), _var.reshape(-1)) cv_pred[valid_idx] = sampled_pred _num, _ = MFSE.calculate_preserving_order_num( cv_pred, test_y) order_preseving_nums.append(_num) max_id = np.argmax(order_preseving_nums) min_probability_array[max_id] += 1 new_weights = np.array(min_probability_array) / sample_num elif self.weight_method == 'opt_based': mean_list, var_list = list(), list() for i, r in enumerate(r_list): if i != K - 1: mean, var = self.weighted_surrogate.surrogate_container[ r].predict(test_x) tmp_y = np.reshape(mean, -1) tmp_var = np.reshape(var, -1) mean_list.append(tmp_y) var_list.append(tmp_var) else: if len(test_y) < 8: mean_list.append(np.array([0] * len(test_y))) var_list.append(np.array([0] * len(test_y))) else: # 5-fold cross validation. kfold = KFold(n_splits=5) cv_pred = np.array([0] * len(test_y)) cv_var = np.array([0] * len(test_y)) for train_idx, valid_idx in kfold.split(test_x): train_configs, train_y = test_x[ train_idx], test_y[train_idx] valid_configs, valid_y = test_x[ valid_idx], test_y[valid_idx] types, bounds = get_types(self.config_space) _surrogate = RandomForestWithInstances( types=types, bounds=bounds) _surrogate.train(train_configs, train_y) pred, var = _surrogate.predict(valid_configs) cv_pred[valid_idx] = pred.reshape(-1) cv_var[valid_idx] = var.reshape(-1) mean_list.append(cv_pred) var_list.append(cv_var) means = np.array(mean_list) vars = np.array(var_list) + 1e-8 def min_func(x): x = np.reshape(np.array(x), (1, len(x))) ensemble_vars = 1 / (x @ (1 / vars)) ensemble_means = x @ (means / vars) * ensemble_vars ensemble_means = np.reshape(ensemble_means, -1) self.logger.info("Loss:" + str(x)) return MFSE.calculate_ranking_loss(ensemble_means, test_y) constraints = [{ 'type': 'eq', 'fun': lambda x: np.sum(x) - 1 }, { 'type': 'ineq', 'fun': lambda x: x - 0 }, { 'type': 'ineq', 'fun': lambda x: 1 - x }] res = minimize(min_func, np.array([1e-8] * K), constraints=constraints) new_weights = res.x else: raise ValueError('Invalid weight method: %s!' % self.weight_method) else: old_weights = list() for i, r in enumerate(r_list): _weight = self.weighted_surrogate.surrogate_weight[r] old_weights.append(_weight) new_weights = old_weights.copy() self.logger.info( '[%s] %d-th Updating weights: %s' % (self.weight_method, self.weight_changed_cnt, str(new_weights))) # Assign the weight to each basic surrogate. for i, r in enumerate(r_list): self.weighted_surrogate.surrogate_weight[r] = new_weights[i] self.weight_changed_cnt += 1 # Save the weight data. self.hist_weights.append(new_weights) np.save( 'data/%s_weights_%s.npy' % (self.method_name, self.method_name), np.asarray(self.hist_weights))
class SMAC_ES(BaseFacade): def __init__(self, config_space, objective_func, R, num_iter=10, n_workers=1, eta=3, es_gap=9, rho=0.7, random_state=1, method_id="Default"): BaseFacade.__init__(self, objective_func, n_workers=n_workers, need_lc=True, method_name=method_id) self.seed = random_state self.config_space = config_space self.config_space.seed(self.seed) self.R = R self.num_iter = num_iter self.eta = eta self.logeta = lambda x: log(x) / log(self.eta) self.s_max = int(self.logeta(R)) self.inner_iteration_n = (self.s_max + 1) * (self.s_max + 1) types, bounds = get_types(config_space) self.num_config = len(bounds) self.surrogate = RandomForestWithInstances(types=types, bounds=bounds) self.acquisition_func = EI(model=self.surrogate) # TODO: add SMAC's optimization algorithm. self.acq_optimizer = RandomSampling(self.acquisition_func, config_space, n_samples=max(500, 50 * self.num_config)) self.incumbent_configs = [] self.incumbent_obj = [] self.lcnet_model = LC_ES() self.early_stop_gap = es_gap self.es_rho = rho self.lc_training_x = None self.lc_training_y = None def iterate(self): for _ in range(self.inner_iteration_n): T = self.choose_next(self.num_workers) extra_info = None lc_info = dict() lc_conf_mapping = dict() # assume no same configuration in the same batch: T for item in T: conf_id = get_configuration_id(item.get_dictionary()) sha = hashlib.sha1(conf_id.encode('utf8')) conf_id = sha.hexdigest() lc_conf_mapping[conf_id] = item total_iter_num = self.R // self.early_stop_gap for iter_num in range(1, 1 + total_iter_num): self.logger.info('start iteration gap %d' % iter_num) ret_val, early_stops = self.run_in_parallel(T, self.early_stop_gap, extra_info) val_losses = [item['loss'] for item in ret_val] ref_list = [item['ref_id'] for item in ret_val] for item in ret_val: conf_id = item['ref_id'] if not self.restart_needed: if conf_id not in lc_info: lc_info[conf_id] = [] lc_info[conf_id].extend(item['lc_info']) else: lc_info[conf_id] = item['lc_info'] if iter_num == total_iter_num: self.incumbent_configs.extend(T) self.incumbent_obj.extend(val_losses) T = [config for i, config in enumerate(T) if not early_stops[i]] extra_info = [ref for i, ref in enumerate(ref_list) if not early_stops[i]] if len(T) == 0: break if len(self.incumbent_obj) >= 2 * self.num_config and iter_num != total_iter_num: # learning curve based early stop strategy. ref_list = extra_info early_stops = self.stop_early(T) T = [config for i, config in enumerate(T) if not early_stops[i]] extra_info = [ref for i, ref in enumerate(ref_list) if not early_stops[i]] if len(T) == 0: break # keep learning curve data for item, config in lc_conf_mapping.items(): lc_data = lc_info[item] if len(lc_data) > 0: n_epochs = len(lc_data) # self.logger.info('insert one learning curve data into dataset.') t_idx = np.arange(1, n_epochs + 1) / n_epochs conf_data = convert_configurations_to_array([config]) x = np.repeat(conf_data, t_idx.shape[0], axis=0) x = np.concatenate((x, t_idx[:, None]), axis=1) y = np.array(lc_data) if self.lc_training_x is None: self.lc_training_x, self.lc_training_y = x, y else: self.lc_training_x = np.concatenate((self.lc_training_x, x), 0) self.lc_training_y = np.concatenate((self.lc_training_y, y), 0) self.logger.info('training data shape: %s' % str(self.lc_training_x.shape)) if len(self.incumbent_obj) >= 2 * self.num_config and len(self.incumbent_obj) % self.num_config == 0: self.lcnet_model.train(self.lc_training_x, self.lc_training_y) self.add_stage_history(self.stage_id, self.global_incumbent) self.stage_id += 1 self.remove_immediate_model() @BaseFacade.process_manage def run(self): try: for iter in range(self.num_iter): self.logger.info('-' * 50) self.logger.info("SMAC with ES algorithm: %d/%d iteration starts" % (iter, self.num_iter)) start_time = time.time() self.iterate() time_elapsed = (time.time() - start_time) / 60 self.logger.info("iteration took %.2f min." % time_elapsed) self.save_intemediate_statistics() except Exception as e: print(e) self.logger.error(str(e)) # clear the immediate result. self.remove_immediate_model() def stop_early(self, T): configs_data = convert_configurations_to_array(T) x_test = None for i in range(configs_data.shape[0]): x = np.concatenate((configs_data[i, None, :], np.array([[1.0]])), axis=1) if x_test is None: x_test = x else: x_test = np.concatenate((x_test, x), 0) m, v = self.lcnet_model.predict(x_test) best_accuracy = 1 - self.global_incumbent s = np.sqrt(v) less_p = norm.cdf((best_accuracy - m) / s) self.logger.info('early stop prob: %s' % str(less_p)) early_stop_flag = (less_p >= self.es_rho) self.logger.info('early stop vector: %s' % str(early_stop_flag)) for i, flag in enumerate(early_stop_flag): if flag: self.incumbent_configs.append(T[i]) self.incumbent_obj.append(m[i]) return early_stop_flag def choose_next(self, num_config): if len(self.incumbent_obj) < 2 * self.num_config: return sample_configurations(self.config_space, num_config) # print('choose next starts!') self.logger.info('train feature is: %s' % str(self.incumbent_configs[-5:])) self.logger.info('train target is: %s' % str(self.incumbent_obj)) self.surrogate.train(convert_configurations_to_array(self.incumbent_configs), np.array(self.incumbent_obj, dtype=np.float64)) conf_cnt = 0 total_cnt = 0 next_configs = [] while conf_cnt < num_config and total_cnt < 5 * num_config: incumbent = dict() best_index = np.argmin(self.incumbent_obj) incumbent['obj'] = self.incumbent_obj[best_index] incumbent['config'] = self.incumbent_configs[best_index] self.acquisition_func.update(model=self.surrogate, eta=incumbent) rand_config = self.acq_optimizer.maximize(batch_size=1)[0] if rand_config not in next_configs: next_configs.append(rand_config) conf_cnt += 1 total_cnt += 1 if conf_cnt < num_config: next_configs = expand_configurations(next_configs, self.config_space, num_config) return next_configs def get_incumbent(self, num_inc=1): assert (len(self.incumbent_obj) == len(self.incumbent_configs)) indices = np.argsort(self.incumbent_obj) configs = [self.incumbent_configs[i] for i in indices[0:num_inc]] targets = [self.incumbent_obj[i] for i in indices[0: num_inc]] return configs, targets
class BOHB(BaseFacade): """ The implementation of BOHB. The paper can be found in https://arxiv.org/abs/1807.01774 . """ def __init__(self, config_space: ConfigurationSpace, objective_func, R, num_iter=10000, eta=3, p=0.3, n_workers=1, random_state=1, method_id='Default'): BaseFacade.__init__(self, objective_func, n_workers=n_workers, method_name=method_id) self.config_space = config_space self.seed = random_state self.config_space.seed(self.seed) self.p = p self.R = R self.eta = eta self.logeta = lambda x: log(x) / log(self.eta) self.s_max = int(self.logeta(self.R)) self.B = (self.s_max + 1) * self.R self.num_iter = num_iter types, bounds = get_types(config_space) self.num_config = len(bounds) self.surrogate = RandomForestWithInstances(types=types, bounds=bounds) self.acquisition_func = EI(model=self.surrogate) self.acq_optimizer = RandomSampling(self.acquisition_func, config_space, n_samples=max( 500, 50 * self.num_config)) self.incumbent_configs = [] self.incumbent_obj = [] def iterate(self, skip_last=0): for s in reversed(range(self.s_max + 1)): # Set initial number of configurations n = int(ceil(self.B / self.R / (s + 1) * self.eta**s)) # Set initial number of iterations per config r = self.R * self.eta**(-s) # Sample n configurations according to BOHB strategy. T = self.choose_next(n) extra_info = None last_run_num = None for i in range((s + 1) - int(skip_last)): # changed from s + 1 # Run each of the n configs for <iterations> # and keep best (n_configs / eta) configurations. n_configs = n * self.eta**(-i) n_iterations = r * self.eta**(i) n_iter = n_iterations if last_run_num is not None and not self.restart_needed: n_iter -= last_run_num last_run_num = n_iterations self.logger.info( "BOHB: %d configurations x %d iterations each" % (int(n_configs), int(n_iterations))) ret_val, early_stops = self.run_in_parallel( T, n_iter, extra_info) val_losses = [item['loss'] for item in ret_val] ref_list = [item['ref_id'] for item in ret_val] if int(n_iterations) == self.R: self.incumbent_configs.extend(T) self.incumbent_obj.extend(val_losses) # Select a number of best configurations for the next loop. # Filter out early stops, if any. indices = np.argsort(val_losses) if len(T) == sum(early_stops): break if len(T) >= self.eta: T = [T[i] for i in indices if not early_stops[i]] extra_info = [ ref_list[i] for i in indices if not early_stops[i] ] reduced_num = int(n_configs / self.eta) T = T[0:reduced_num] extra_info = extra_info[0:reduced_num] else: T = [T[indices[0]]] extra_info = [ref_list[indices[0]]] incumbent_loss = val_losses[indices[0]] self.add_stage_history( self.stage_id, min(self.global_incumbent, incumbent_loss)) self.stage_id += 1 self.remove_immediate_model() @BaseFacade.process_manage def run(self): try: for iter in range(self.num_iter): self.logger.info('-' * 50) self.logger.info("BOHB algorithm: %d/%d iteration starts" % (iter, self.num_iter)) start_time = time.time() self.iterate() time_elapsed = (time.time() - start_time) / 60 self.logger.info("Iteration took %.2f min." % time_elapsed) self.save_intemediate_statistics() except Exception as e: print(e) self.logger.error(str(e)) # Clean the immediate result. self.remove_immediate_model() def choose_next(self, num_config): if len(self.incumbent_obj) < 3: return sample_configurations(self.config_space, num_config) self.logger.info('Train feature is: %s' % str(self.incumbent_configs[:5])) self.logger.info('Train target is: %s' % str(self.incumbent_obj)) self.surrogate.train( convert_configurations_to_array(self.incumbent_configs), np.array(self.incumbent_obj, dtype=np.float64)) config_cnt = 0 total_sample_cnt = 0 config_candidates = [] while config_cnt < num_config and total_sample_cnt < 3 * num_config: if random.random() < self.p: rand_config = self.config_space.sample_configuration(1) else: # print('use surrogate to produce candidate.') incumbent = dict() best_index = np.argmin(self.incumbent_obj) incumbent['obj'] = self.incumbent_obj[best_index] incumbent['config'] = self.incumbent_configs[best_index] self.acquisition_func.update(model=self.surrogate, eta=incumbent) rand_config = self.acq_optimizer.maximize(batch_size=1)[0] if rand_config not in config_candidates: config_candidates.append(rand_config) config_cnt += 1 total_sample_cnt += 1 if config_cnt < num_config: config_candidates = expand_configurations(config_candidates, self.config_space, num_config) return config_candidates def get_incumbent(self, num_inc=1): assert (len(self.incumbent_obj) == len(self.incumbent_configs)) indices = np.argsort(self.incumbent_obj) configs = [self.incumbent_configs[i] for i in indices[0:num_inc]] targets = [self.incumbent_obj[i] for i in indices[0:num_inc]] return configs, targets