def test_raises_error_on_key_conflict(self): """Ensure that an informative exception is raised on key conflict.""" with self.assertRaisesRegex(TypeError, r"Cannot unflatten dict"): unflatten_dict({"a": 1, "a/b": 2, "a/c": 3}) with self.assertRaisesRegex(TypeError, r"Cannot unflatten dict"): unflatten_dict({"a/b": 2, "a/b/c": 3})
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._nevergrad_opt: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if self.max_concurrent: if len(self._live_trial_mapping) >= self.max_concurrent: return None if self._points_to_evaluate is not None: if len(self._points_to_evaluate) > 0: point_to_evaluate = self._points_to_evaluate.pop(0) self._nevergrad_opt.suggest(point_to_evaluate) suggested_config = self._nevergrad_opt.ask() self._live_trial_mapping[trial_id] = suggested_config # in v0.2.0+, output of ask() is a Candidate, # with fields args and kwargs if not suggested_config.kwargs: if self._parameters: return unflatten_dict( dict(zip(self._parameters, suggested_config.args[0]))) return unflatten_dict(suggested_config.value) else: return unflatten_dict(suggested_config.kwargs)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._skopt_opt: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if self._initial_points: suggested_config = self._initial_points.pop(0) skopt_config = [suggested_config[par] for par in self._parameters] else: skopt_config = self._skopt_opt.ask() suggested_config = dict(zip(self._parameters, skopt_config)) self._live_trial_mapping[trial_id] = skopt_config if self._convert_to_python: for k, v in list(suggested_config.items()): if isinstance(v, np.number): suggested_config[k] = v.item() return unflatten_dict(suggested_config)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._space: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format( cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format( cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if trial_id not in self._ot_trials: self._ot_trials[trial_id] = self._ot_study.ask() ot_trial = self._ot_trials[trial_id] # getattr will fetch the trial.suggest_ function on Optuna trials params = { args[0] if len(args) > 0 else kwargs["name"]: getattr( ot_trial, fn)(*args, **kwargs) for (fn, args, kwargs) in self._space } return unflatten_dict(params)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._space: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if trial_id not in self._ot_trials: ot_trial_id = self._storage.create_new_trial( self._ot_study._study_id) self._ot_trials[trial_id] = ot.trial.Trial(self._ot_study, ot_trial_id) ot_trial = self._ot_trials[trial_id] if self._points_to_evaluate: params = self._points_to_evaluate.pop(0) else: # getattr will fetch the trial.suggest_ function on Optuna trials params = { args[0] if len(args) > 0 else kwargs["name"]: getattr(ot_trial, fn)(*args, **kwargs) for (fn, args, kwargs) in self._space } return unflatten_dict(params)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._space: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format( cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format( cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if callable(self._space): # Define-by-run case if trial_id not in self._ot_trials: self._ot_trials[trial_id] = self._ot_study.ask() ot_trial = self._ot_trials[trial_id] params = self._suggest_from_define_by_run_func( self._space, ot_trial) else: # Use Optuna ask interface (since version 2.6.0) if trial_id not in self._ot_trials: self._ot_trials[trial_id] = self._ot_study.ask( fixed_distributions=self._space) ot_trial = self._ot_trials[trial_id] params = ot_trial.params return unflatten_dict(params)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._ax: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if self.max_concurrent: if len(self._live_trial_mapping) >= self.max_concurrent: return None if self._points_to_evaluate: config = self._points_to_evaluate.pop(0) parameters, trial_index = self._ax.attach_trial(config) else: try: parameters, trial_index = self._ax.get_next_trial() except (MaxParallelismReachedException, DataRequiredError): return None self._live_trial_mapping[trial_id] = trial_index return unflatten_dict(parameters)
def suggest(self, trial_id: str) -> Optional[Dict]: """Return new point to be explored by black box function. Args: trial_id: Id of the trial. This is a short alphanumerical string. Returns: Either a dictionary describing the new point to explore or None, when no new point is to be explored for the time being. """ if not self.optimizer: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if self._points_to_evaluate: config = self._points_to_evaluate.pop(0) else: # We compute the new point to explore config = self.optimizer.suggest(self.utility) config_hash = _dict_hash(config, self.repeat_float_precision) # Check if already computed already_seen = config_hash in self._config_counter self._config_counter[config_hash] += 1 top_repeats = max(self._config_counter.values()) # If patience is set and we've repeated a trial numerous times, # we terminate the experiment. if self._patience is not None and top_repeats > self._patience: return Searcher.FINISHED # If we have seen a value before, we'll skip it. if already_seen and self._skip_duplicate: logger.info("Skipping duplicated config: {}.".format(config)) return None # If we are still in the random search part and we are waiting for # trials to complete if len(self._buffered_trial_results) < self.random_search_trials: # We check if we have already maxed out the number of requested # random search trials if self._total_random_search_trials == self.random_search_trials: # If so we stop the suggestion and return None return None # Otherwise we increase the total number of rndom search trials if config: self._total_random_search_trials += 1 # Save the new trial to the trial mapping self._live_trial_mapping[trial_id] = config # Return a deep copy of the mapping return unflatten_dict(config)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._dim_dict or not self.optimizer: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="dim_dict")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) _solution = self.optimizer.suggest() if _solution == "FINISHED": if ray.__version__ >= "0.8.7": return Searcher.FINISHED else: return None if _solution: self.solution_dict[str(trial_id)] = _solution _x = _solution.get_x() new_trial = dict(zip(self._dim_keys, _x)) self._live_trial_mapping[trial_id] = new_trial return unflatten_dict(new_trial)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._opt: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if not self._live_trial_mapping: self._batch_filled = False if self._initial_points: params = self._initial_points.pop(0) suggestion = pd.DataFrame(params, index=[0]) else: if (self._batch_filled or len(self._live_trial_mapping) >= self._max_concurrent): return None if not self._suggestions_cache: suggestion = self._opt.suggest( n_suggestions=self._max_concurrent) self._suggestions_cache = suggestion.to_dict("records") params = self._suggestions_cache.pop(0) suggestion = pd.DataFrame(params, index=[0]) self._live_trial_mapping[trial_id] = suggestion if len(self._live_trial_mapping) >= self._max_concurrent: self._batch_filled = True return unflatten_dict(params)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._space: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if isinstance(self._space, list): # Keep for backwards compatibility # Deprecate: 1.5 if trial_id not in self._ot_trials: self._ot_trials[trial_id] = self._ot_study.ask() ot_trial = self._ot_trials[trial_id] # getattr will fetch the trial.suggest_ function on Optuna trials params = { args[0] if len(args) > 0 else kwargs["name"]: getattr(ot_trial, fn)(*args, **kwargs) for (fn, args, kwargs) in self._space } else: # Use Optuna ask interface (since version 2.6.0) if trial_id not in self._ot_trials: self._ot_trials[trial_id] = self._ot_study.ask( fixed_distributions=self._space) ot_trial = self._ot_trials[trial_id] params = ot_trial.params return unflatten_dict(params)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._opt: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if self._initial_points: suggested_config = self._initial_points[0] del self._initial_points[0] else: try: suggested_config = self._opt.ask() except Exception as exc: logger.warning( "Dragonfly errored when querying. This may be due to a " "higher level of parallelism than supported. Try reducing " "parallelism in the experiment: %s", str(exc), ) return None self._live_trial_mapping[trial_id] = suggested_config config = dict(zip(self._point_parameter_names, suggested_config)) # Keep backwards compatibility config.update(point=suggested_config) return unflatten_dict(config)
def suggest(self, trial_id: str) -> Optional[Dict]: """Return new point to be explored by black box function. Args: trial_id (str): Id of the trial. This is a short alphanumerical string. Returns: Either a dictionary describing the new point to explore or None, when no new point is to be explored for the time being. """ if not self.optimizer: raise RuntimeError( "Trying to sample a configuration from {}, but no search " "space has been defined. Either pass the `{}` argument when " "instantiating the search algorithm, or pass a `config` to " "`tune.run()`.".format(self.__class__.__name__, "space")) # If we have more active trials than the allowed maximum total_live_trials = len(self._live_trial_mapping) if self.max_concurrent and self.max_concurrent <= total_live_trials: # we stop the suggestion and return None. return None # We compute the new point to explore config = self.optimizer.suggest(self.utility) config_hash = _dict_hash(config, self.repeat_float_precision) # Check if already computed already_seen = config_hash in self._config_counter self._config_counter[config_hash] += 1 top_repeats = max(self._config_counter.values()) # If patience is set and we've repeated a trial numerous times, # we terminate the experiment. if self._patience is not None and top_repeats > self._patience: return Searcher.FINISHED # If we have seen a value before, we'll skip it. if already_seen and self._skip_duplicate: logger.info("Skipping duplicated config: {}.".format(config)) return None # If we are still in the random search part and we are waiting for # trials to complete if len(self._buffered_trial_results) < self.random_search_trials: # We check if we have already maxed out the number of requested # random search trials if self._total_random_search_trials == self.random_search_trials: # If so we stop the suggestion and return None return None # Otherwise we increase the total number of rndom search trials if config: self._total_random_search_trials += 1 # Save the new trial to the trial mapping self._live_trial_mapping[trial_id] = config # Return a deep copy of the mapping return unflatten_dict(config)
def create(self, init_config: Dict, obj: float, cost: float) -> Searcher: flow2 = FLOW2(init_config, self.metric, self.mode, self._cat_hp_cost, unflatten_dict(self.space), self.prune_attr, self.min_resource, self.max_resource, self.resource_multiple_factor, self._seed + 1) flow2.best_obj = obj * self.metric_op # minimize internally flow2.cost_incumbent = cost return flow2
def _increase_resource(self, trial_id): # consider increasing resource using sum eval cost of complete # configs old_resource = self._resource self._resource = self._round(self._resource * self.resource_multiple_factor) self.cost_incumbent *= self._resource / old_resource config = self.best_config.copy() config[self.resource_attr] = self._resource self._direction_tried = None self._configs[trial_id] = (config, self.step) return unflatten_dict(config)
def suggest(self, trial_id): if not self._nevergrad_opt: raise RuntimeError( "Trying to sample a configuration from {}, but no search " "space has been defined. Either pass the `{}` argument when " "instantiating the search algorithm, or pass a `config` to " "`tune.run()`.".format(self.__class__.__name__, "space")) if self.max_concurrent: if len(self._live_trial_mapping) >= self.max_concurrent: return None suggested_config = self._nevergrad_opt.ask() self._live_trial_mapping[trial_id] = suggested_config # in v0.2.0+, output of ask() is a Candidate, # with fields args and kwargs if not suggested_config.kwargs: if self._parameters: return unflatten_dict( dict(zip(self._parameters, suggested_config.args[0]))) return unflatten_dict(suggested_config.value) else: return unflatten_dict(suggested_config.kwargs)
def suggest(self, trial_id): if not self._ax: raise RuntimeError( "Trying to sample a configuration from {}, but no search " "space has been defined. Either pass the `{}` argument when " "instantiating the search algorithm, or pass a `config` to " "`tune.run()`.".format(self.__class__.__name__, "space")) if self.max_concurrent: if len(self._live_trial_mapping) >= self.max_concurrent: return None parameters, trial_index = self._ax.get_next_trial() self._live_trial_mapping[trial_id] = trial_index return unflatten_dict(parameters)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._dim_dict or not self.optimizer: raise RuntimeError( "Trying to sample a configuration from {}, but no search " "space has been defined. Either pass the `{}` argument when " "instantiating the search algorithm, or pass a `config` to " "`tune.run()`.".format(self.__class__.__name__, "space")) _solution = self.optimizer.suggest() if _solution: self.solution_dict[str(trial_id)] = _solution _x = _solution.get_x() new_trial = dict(zip(self._dim_keys, _x)) self._live_trial_mapping[trial_id] = new_trial return unflatten_dict(new_trial)
def suggest(self, trial_id): if not self._space: raise RuntimeError( "Trying to sample a configuration from {}, but no search " "space has been defined. Either pass the `{}` argument when " "instantiating the search algorithm, or pass a `config` to " "`tune.run()`.".format(self.__class__.__name__, "space")) if len(self.running) < self._max_concurrent: # This parameter is not used in hpbandster implementation. config, info = self.bohber.get_config(None) self.trial_to_params[trial_id] = copy.deepcopy(config) self.running.add(trial_id) return unflatten_dict(config) return None
def complete_config(self, partial_config: Dict, lower: Optional[Dict] = None, upper: Optional[Dict] = None) -> Dict: ''' generate a complete config from the partial config input add minimal resource to config if available ''' if self._reset_times and partial_config == self.init_config: # not the first time to complete init_config, use random gaussian normalized = self.normalize(partial_config) for key in normalized: # don't change unordered cat choice if key not in self._unordered_cat_hp: if upper and lower: u, l = upper[key], lower[key] gauss_std = u - l or self.STEPSIZE # allowed bound u += self.STEPSIZE l -= self.STEPSIZE elif key in self._bounded_keys: u, l, gauss_std = 1, 0, 1.0 else: u, l, gauss_std = np.Inf, -np.Inf, 1.0 if key in self._bounded_keys: u = min(u, 1) l = max(l, 0) delta = self.rand_vector_gaussian(1, gauss_std)[0] normalized[key] = max(l, min(u, normalized[key] + delta)) # use best config for unordered cat choice config = self.denormalize(normalized) else: # first time init_config, or other configs, take as is config = partial_config.copy() if partial_config == self.init_config: self._reset_times += 1 config = flatten_dict(config) for key, value in self.space.items(): if key not in config: config[key] = value # logger.debug(f'before random {config}') for _, generated in generate_variants({'config': config}): config = generated['config'] break # logger.debug(f'after random {config}') if self._resource: config[self.prune_attr] = self.min_resource return unflatten_dict(config)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._skopt_opt: raise RuntimeError( "Trying to sample a configuration from {}, but no search " "space has been defined. Either pass the `{}` argument when " "instantiating the search algorithm, or pass a `config` to " "`tune.run()`.".format(self.__class__.__name__, "space")) if self.max_concurrent: if len(self._live_trial_mapping) >= self.max_concurrent: return None if self._initial_points: suggested_config = self._initial_points[0] del self._initial_points[0] else: suggested_config = self._skopt_opt.ask() self._live_trial_mapping[trial_id] = suggested_config return unflatten_dict(dict(zip(self._parameters, suggested_config)))
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._ax: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if self.max_concurrent: if len(self._live_trial_mapping) >= self.max_concurrent: return None parameters, trial_index = self._ax.get_next_trial() self._live_trial_mapping[trial_id] = trial_index return unflatten_dict(parameters)
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._space: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if len(self.running) < self._max_concurrent: # This parameter is not used in hpbandster implementation. config, info = self.bohber.get_config(None) self.trial_to_params[trial_id] = copy.deepcopy(config) self.running.add(trial_id) return unflatten_dict(config) return None
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._opt: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if self._initial_points: params = self._initial_points.pop(0) suggestion = pd.DataFrame(params, index=[0]) else: suggestion = self._opt.suggest() params = suggestion.iloc[0].to_dict() self._live_trial_mapping[trial_id] = suggestion return unflatten_dict(params)
def test_multi_level_nested(self): result = unflatten_dict({"a/b/c/d": 1, "b/c/d": 2, "c/d": 3, "e": 4}) assert result == { "a": { "b": { "c": { "d": 1, }, }, }, "b": { "c": { "d": 2, }, }, "c": { "d": 3, }, "e": 4, }
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._skopt_opt: raise RuntimeError( UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) if self.max_concurrent: if len(self._live_trial_mapping) >= self.max_concurrent: return None if self._initial_points: suggested_config = self._initial_points[0] del self._initial_points[0] else: suggested_config = self._skopt_opt.ask() self._live_trial_mapping[trial_id] = suggested_config return unflatten_dict(dict(zip(self._parameters, suggested_config)))
def suggest(self, trial_id: str) -> Optional[Dict]: if not self._space: raise RuntimeError( "Trying to sample a configuration from {}, but no search " "space has been defined. Either pass the `{}` argument when " "instantiating the search algorithm, or pass a `config` to " "`tune.run()`.".format(self.__class__.__name__, "space")) if trial_id not in self._ot_trials: ot_trial_id = self._storage.create_new_trial( self._ot_study._study_id) self._ot_trials[trial_id] = ot.trial.Trial(self._ot_study, ot_trial_id) ot_trial = self._ot_trials[trial_id] # getattr will fetch the trial.suggest_ function on Optuna trials params = { args[0] if len(args) > 0 else kwargs["name"]: getattr(ot_trial, fn)(*args, **kwargs) for (fn, args, kwargs) in self._space } return unflatten_dict(params)
def suggest(self, trial_id: str) -> Optional[Dict]: ''' suggest a new config, one of the following cases: 1. same incumbent, increase resource 2. same resource, move from the incumbent to a random direction 3. same resource, move from the incumbent to the opposite direction ''' if self._num_complete4incumbent > 0 and self.cost_incumbent and \ self._resource and self._resource < self.max_resource and ( self._cost_complete4incumbent >= self.cost_incumbent * self.resource_multiple_factor): # consider increasing resource using sum eval cost of complete # configs self._resource = self._round(self._resource * self.resource_multiple_factor) config = self.best_config.copy() config[self.prune_attr] = self._resource # self.incumbent[self.prune_attr] = self._resource self._direction_tried = None self._configs[trial_id] = config return config self._num_allowed4incumbent -= 1 move = self.incumbent.copy() if self._direction_tried is not None: # return negative direction for i, key in enumerate(self._tunable_keys): move[key] -= self._direction_tried[i] self._direction_tried = None # propose a new direction self._direction_tried = self.rand_vector_unit_sphere( self.dim) * self.step for i, key in enumerate(self._tunable_keys): move[key] += self._direction_tried[i] self._project(move) config = self.denormalize(move) self._proposed_by[trial_id] = self.incumbent self._configs[trial_id] = config return unflatten_dict(config)
def test_one_level_nested(self): result = unflatten_dict({"a/b": 1, "c/d": 2, "e": 3}) assert result == {"a": {"b": 1}, "c": {"d": 2}, "e": 3}
def test_output_type(self): in_ = OrderedDict({"a/b": 1, "c/d": 2, "e": 3}) out = unflatten_dict(in_) assert type(in_) is type(out)