Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
YamLyubov committed Nov 27, 2023
1 parent 1be17e0 commit afbaecf
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 174 deletions.
13 changes: 3 additions & 10 deletions golem/core/tuning/iopt_tuner.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,12 @@ def __init__(self, objective_evaluate: ObjectiveEvaluate,
epsR=np.double(eps_r),
refineSolution=refine_solution)

def tune(self, graph: DomainGraphForTune, show_progress: bool = True) -> DomainGraphForTune:
graph = self.adapter.adapt(graph)
def _tune(self, graph: DomainGraphForTune, show_progress: bool = True) -> DomainGraphForTune:
problem_parameters, initial_parameters = self._get_parameters_for_tune(graph)

has_parameters_to_optimize = (len(problem_parameters.discrete_parameters_names) > 0 or
len(problem_parameters.float_parameters_names) > 0)
self.init_check(graph)
if self._check_tuning_possible(graph, has_parameters_to_optimize):
if self._check_if_tuning_possible(graph, has_parameters_to_optimize):
if initial_parameters:
initial_point = Point(**initial_parameters)
self.solver_parameters.startPoint = initial_point
Expand All @@ -167,15 +165,10 @@ def tune(self, graph: DomainGraphForTune, show_progress: bool = True) -> DomainG
final_graph = self.set_arg_graph(graph, best_parameters)

self.was_tuned = True

# Validate if optimisation did well
final_graph = self.final_check(final_graph)
else:
final_graph = graph
self.obtained_metric = self.init_metric

final_graph = self.adapter.restore(final_graph)
return [final_graph]
return final_graph

def _get_parameters_for_tune(self, graph: OptGraph) -> Tuple[IOptProblemParameters, dict]:
""" Method for defining the search space
Expand Down
85 changes: 39 additions & 46 deletions golem/core/tuning/optuna_tuner.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,58 +33,51 @@ def __init__(self, objective_evaluate: ObjectiveFunction,
timeout,
n_jobs,
deviation, **kwargs)
self.objectives_number = 1
self.study = None

def tune(self, graph: DomainGraphForTune, show_progress: bool = True) -> \
def _tune(self, graph: DomainGraphForTune, show_progress: bool = True) -> \
Union[DomainGraphForTune, Sequence[DomainGraphForTune]]:
graph = self.adapter.adapt(graph)
predefined_objective = partial(self.objective, graph=graph)

with Timer() as global_tuner_timer:
self.init_check(graph)
self.objectives_number = len(ensure_wrapped_in_sequence(self.init_metric))
is_multi_objective = self.objectives_number > 1

self.study = optuna.create_study(directions=['minimize'] * self.objectives_number)

init_parameters, has_parameters_to_optimize = self._get_initial_point(graph)
remaining_time = self._get_remaining_time(global_tuner_timer)
if self._check_tuning_possible(graph,
has_parameters_to_optimize,
remaining_time,
supports_multi_objective=True):
# Enqueue initial point to try
if init_parameters:
self.study.enqueue_trial(init_parameters)

verbosity_level = optuna.logging.INFO if show_progress else optuna.logging.WARNING
optuna.logging.set_verbosity(verbosity_level)

self.study.optimize(predefined_objective,
n_trials=self.iterations,
n_jobs=self.n_jobs,
timeout=remaining_time,
callbacks=[self.early_stopping_callback],
show_progress_bar=show_progress)

if not is_multi_objective:
best_parameters = self.study.best_trials[0].params
tuned_graphs = self.set_arg_graph(graph, best_parameters)
self.was_tuned = True
else:
tuned_graphs = []
for best_trial in self.study.best_trials:
best_parameters = best_trial.params
tuned_graph = self.set_arg_graph(deepcopy(graph), best_parameters)
tuned_graphs.append(tuned_graph)
self.was_tuned = True
final_graphs = self.final_check(tuned_graphs, is_multi_objective)
self.objectives_number = len(ensure_wrapped_in_sequence(self.init_metric))
is_multi_objective = self.objectives_number > 1

self.study = optuna.create_study(directions=['minimize'] * self.objectives_number)

init_parameters, has_parameters_to_optimize = self._get_initial_point(graph)
remaining_time = self._get_remaining_time()
if self._check_if_tuning_possible(graph,
has_parameters_to_optimize,
remaining_time,
supports_multi_objective=True):
# Enqueue initial point to try
if init_parameters:
self.study.enqueue_trial(init_parameters)

verbosity_level = optuna.logging.INFO if show_progress else optuna.logging.WARNING
optuna.logging.set_verbosity(verbosity_level)

self.study.optimize(predefined_objective,
n_trials=self.iterations,
n_jobs=self.n_jobs,
timeout=remaining_time,
callbacks=[self.early_stopping_callback],
show_progress_bar=show_progress)

if not is_multi_objective:
best_parameters = self.study.best_trials[0].params
tuned_graphs = self.set_arg_graph(graph, best_parameters)
self.was_tuned = True
else:
final_graphs = graph
self.obtained_metric = self.init_metric
final_graphs = self.adapter.restore(final_graphs)
return final_graphs
tuned_graphs = []
for best_trial in self.study.best_trials:
best_parameters = best_trial.params
tuned_graph = self.set_arg_graph(deepcopy(graph), best_parameters)
tuned_graphs.append(tuned_graph)
self.was_tuned = True
else:
tuned_graphs = graph
return tuned_graphs

def objective(self, trial: Trial, graph: OptGraph) -> Union[float, Sequence[float, ]]:
new_parameters = self._get_parameters_from_trial(graph, trial)
Expand Down
91 changes: 38 additions & 53 deletions golem/core/tuning/sequential.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,64 +39,49 @@ def __init__(self, objective_evaluate: ObjectiveFunction,

self.inverse_node_order = inverse_node_order

def tune(self, graph: DomainGraphForTune) -> DomainGraphForTune:
def _tune(self, graph: DomainGraphForTune, **kwargs) -> DomainGraphForTune:
""" Method for hyperparameters tuning on the entire graph
Args:
graph: graph which hyperparameters will be tuned
"""
graph = self.adapter.adapt(graph)

with Timer() as global_tuner_timer:

# Check source metrics for data
self.init_check(graph)
remaining_time = self._get_remaining_time(global_tuner_timer)
if self._check_tuning_possible(graph, parameters_to_optimize=True, remaining_time=remaining_time):
# Calculate amount of iterations we can apply per node
nodes_amount = graph.length
iterations_per_node = round(self.iterations / nodes_amount)
iterations_per_node = int(iterations_per_node)
if iterations_per_node == 0:
iterations_per_node = 1

# Calculate amount of seconds we can apply per node
if remaining_time is not None:
seconds_per_node = round(remaining_time / nodes_amount)
seconds_per_node = int(seconds_per_node)
else:
seconds_per_node = None

# Tuning performed sequentially for every node - so get ids of nodes
nodes_ids = self.get_nodes_order(nodes_number=nodes_amount)
for node_id in nodes_ids:
node = graph.nodes[node_id]
operation_name = node.name

# Get node's parameters to optimize
node_params = get_node_parameters_for_hyperopt(self.search_space, node_id, operation_name)

if not node_params:
self.log.info(f'"{operation_name}" operation has no parameters to optimize')
else:
# Apply tuning for current node
self._optimize_node(node_id=node_id,
graph=graph,
node_params=node_params,
iterations_per_node=iterations_per_node,
seconds_per_node=seconds_per_node)

# Validate if optimisation did well
final_graph = self.final_check(graph)

self.was_tuned = True
remaining_time = self._get_remaining_time()
if self._check_if_tuning_possible(graph, parameters_to_optimize=True, remaining_time=remaining_time):
# Calculate amount of iterations we can apply per node
nodes_amount = graph.length
iterations_per_node = round(self.iterations / nodes_amount)
iterations_per_node = int(iterations_per_node)
if iterations_per_node == 0:
iterations_per_node = 1

# Calculate amount of seconds we can apply per node
if remaining_time is not None:
seconds_per_node = round(remaining_time / nodes_amount)
seconds_per_node = int(seconds_per_node)
else:
final_graph = graph
self.obtained_metric = self.init_metric
seconds_per_node = None

final_graph = self.adapter.restore(final_graph)
# Tuning performed sequentially for every node - so get ids of nodes
nodes_ids = self.get_nodes_order(nodes_number=nodes_amount)
for node_id in nodes_ids:
node = graph.nodes[node_id]
operation_name = node.name

return final_graph
# Get node's parameters to optimize
node_params = get_node_parameters_for_hyperopt(self.search_space, node_id, operation_name)

if not node_params:
self.log.info(f'"{operation_name}" operation has no parameters to optimize')
else:
# Apply tuning for current node
self._optimize_node(node_id=node_id,
graph=graph,
node_params=node_params,
iterations_per_node=iterations_per_node,
seconds_per_node=seconds_per_node)

self.was_tuned = True
return graph

def get_nodes_order(self, nodes_number: int) -> range:
""" Method returns list with indices of nodes in the graph
Expand Down Expand Up @@ -126,7 +111,7 @@ def tune_node(self, graph: DomainGraphForTune, node_index: int) -> DomainGraphFo
"""
graph = self.adapter.adapt(graph)

with Timer() as global_tuner_timer:
with self.timer:
self.init_check(graph)

node = graph.nodes[node_index]
Expand All @@ -137,8 +122,8 @@ def tune_node(self, graph: DomainGraphForTune, node_index: int) -> DomainGraphFo
node_id=node_index,
operation_name=operation_name)

remaining_time = self._get_remaining_time(global_tuner_timer)
if self._check_tuning_possible(graph, len(node_params) > 1, remaining_time):
remaining_time = self._get_remaining_time()
if self._check_if_tuning_possible(graph, len(node_params) > 1, remaining_time):
# Apply tuning for current node
self._optimize_node(graph=graph,
node_id=node_index,
Expand Down
97 changes: 42 additions & 55 deletions golem/core/tuning/simultaneous.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SimultaneousTuner(HyperoptTuner):
Class for hyperparameters optimization for all nodes simultaneously
"""

def tune(self, graph: DomainGraphForTune, show_progress: bool = True) -> DomainGraphForTune:
def _tune(self, graph: DomainGraphForTune, show_progress: bool = True) -> DomainGraphForTune:
""" Function for hyperparameters tuning on the entire graph
Args:
Expand All @@ -26,62 +26,49 @@ def tune(self, graph: DomainGraphForTune, show_progress: bool = True) -> DomainG
Returns:
Graph with tuned hyperparameters
"""

graph = self.adapter.adapt(graph)
parameters_dict, init_parameters = self._get_parameters_for_tune(graph)

with Timer() as global_tuner_timer:
self.init_check(graph)
remaining_time = self._get_remaining_time(global_tuner_timer)

if self._check_tuning_possible(graph, parameters_dict, remaining_time):
trials = Trials()

try:
# try searching using initial parameters
# (uses original search space with fixed initial parameters)
trials, init_trials_num = self._search_near_initial_parameters(graph,
parameters_dict,
init_parameters,
trials,
remaining_time,
show_progress)
remaining_time = self._get_remaining_time(global_tuner_timer)
if remaining_time > MIN_TIME_FOR_TUNING_IN_SEC:
fmin(partial(self._objective, graph=graph),
parameters_dict,
trials=trials,
algo=self.algo,
max_evals=self.iterations,
show_progressbar=show_progress,
early_stop_fn=self.early_stop_fn,
timeout=remaining_time)
else:
self.log.message('Tunner stopped after initial search due to the lack of time')

best = space_eval(space=parameters_dict, hp_assignment=trials.argmin)
# check if best point was obtained using search space with fixed initial parameters
is_best_trial_with_init_params = trials.best_trial.get('tid') in range(init_trials_num)
if is_best_trial_with_init_params:
best = {**best, **init_parameters}

final_graph = self.set_arg_graph(graph=graph, parameters=best)

self.was_tuned = True

# Validate if optimisation did well
final_graph = self.final_check(final_graph)

except Exception as ex:
self.log.warning(f'Exception {ex} occurred during tuning')
final_graph = graph
self.obtained_metric = self.init_metric

else:
remaining_time = self._get_remaining_time()

if self._check_if_tuning_possible(graph, parameters_dict, remaining_time):
trials = Trials()

try:
# try searching using initial parameters
# (uses original search space with fixed initial parameters)
trials, init_trials_num = self._search_near_initial_parameters(graph,
parameters_dict,
init_parameters,
trials,
remaining_time,
show_progress)
remaining_time = self._get_remaining_time()
if remaining_time > MIN_TIME_FOR_TUNING_IN_SEC:
fmin(partial(self._objective, graph=graph),
parameters_dict,
trials=trials,
algo=self.algo,
max_evals=self.iterations,
show_progressbar=show_progress,
early_stop_fn=self.early_stop_fn,
timeout=remaining_time)
else:
self.log.message('Tunner stopped after initial search due to the lack of time')

best = space_eval(space=parameters_dict, hp_assignment=trials.argmin)
# check if best point was obtained using search space with fixed initial parameters
is_best_trial_with_init_params = trials.best_trial.get('tid') in range(init_trials_num)
if is_best_trial_with_init_params:
best = {**best, **init_parameters}

final_graph = self.set_arg_graph(graph=graph, parameters=best)

self.was_tuned = True

except Exception as ex:
self.log.warning(f'Exception {ex} occurred during tuning')
final_graph = graph
self.obtained_metric = self.init_metric

final_graph = self.adapter.restore(final_graph)
else:
final_graph = graph
return final_graph

def _search_near_initial_parameters(self, graph: OptGraph,
Expand Down
Loading

0 comments on commit afbaecf

Please sign in to comment.