Renamed to slurm_tuner, added support for statistical significance intermediaries
This commit is contained in:
parent
4c4d44546c
commit
fa004278aa
5 changed files with 25 additions and 15 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "hyperparameter-tuner"
|
name = "slurm-tuner"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["C4 Patino <c4patino@gmail.com>"]
|
authors = ["C4 Patino <c4patino@gmail.com>"]
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,6 @@
|
||||||
max-line-length = 130
|
max-line-length = 130
|
||||||
ignore = E402, W503
|
ignore = E402, W503
|
||||||
|
|
||||||
[pydocstyle]
|
|
||||||
ignore = D100, D103, D203, D212, D406, D407, D411, D413
|
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
max-complexity = 25
|
max-complexity = 25
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
"""SLURM job submission and result handling for Optuna optimization."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import Dict, Tuple, Callable
|
from typing import Dict, Tuple, Callable
|
||||||
from optuna.trial import Trial
|
from optuna.trial import Trial
|
||||||
|
|
@ -10,9 +11,9 @@ import subprocess
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import optuna
|
import optuna
|
||||||
|
|
||||||
from hyperparameter_tuner.loss import Loss
|
from slurm_tuner.loss import Loss
|
||||||
|
|
||||||
logger = logging.getLogger('main')
|
slurm_logger = logging.getLogger('slurm_tuner')
|
||||||
|
|
||||||
|
|
||||||
def create_objective(
|
def create_objective(
|
||||||
|
|
@ -20,6 +21,8 @@ def create_objective(
|
||||||
results_path: str,
|
results_path: str,
|
||||||
loss: Loss,
|
loss: Loss,
|
||||||
trial_param_types: Dict[str, Tuple[str, Tuple, Dict]],
|
trial_param_types: Dict[str, Tuple[str, Tuple, Dict]],
|
||||||
|
return_average_on_prune: bool = False,
|
||||||
|
log_trial_id_with_intermediate: bool = False,
|
||||||
) -> Callable[[Trial], float]:
|
) -> Callable[[Trial], float]:
|
||||||
"""
|
"""
|
||||||
Create an objective function for Optuna to optimize, which submits SLURM jobs and waits for results.
|
Create an objective function for Optuna to optimize, which submits SLURM jobs and waits for results.
|
||||||
|
|
@ -35,6 +38,8 @@ def create_objective(
|
||||||
- str - The parameter type ('int', 'float', or 'categorical').
|
- str - The parameter type ('int', 'float', or 'categorical').
|
||||||
- Tuple - Positional arguments for the parameter's sampling method.
|
- Tuple - Positional arguments for the parameter's sampling method.
|
||||||
- Dict[str, Any] - Keyword arguments for the parameter's sampling method.
|
- Dict[str, Any] - Keyword arguments for the parameter's sampling method.
|
||||||
|
return_average_on_prune: bool - Whether to return the average loss of the all intermediate steps when a trial is pruned.
|
||||||
|
log_trial_id_with_intermediate: bool - Whether to return the trial ID with the intermediate value instead of current step.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def objective(trial: Trial) -> float:
|
def objective(trial: Trial) -> float:
|
||||||
|
|
@ -59,7 +64,7 @@ def create_objective(
|
||||||
if arg_type in param_methods:
|
if arg_type in param_methods:
|
||||||
trial_params[param_name] = param_methods[arg_type](param_name, *args, **kwargs)
|
trial_params[param_name] = param_methods[arg_type](param_name, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
logger.error(f'Invalid parameter type: {arg_type}')
|
slurm_logger.error(f'Invalid parameter type: {arg_type}')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
command = f'sbatch {slurm_script} {results_path} {trial_id} {" ".join(str(v) for v in trial_params.values())}'
|
command = f'sbatch {slurm_script} {results_path} {trial_id} {" ".join(str(v) for v in trial_params.values())}'
|
||||||
|
|
@ -70,10 +75,10 @@ def create_objective(
|
||||||
match = re.search(regex, output.stdout)
|
match = re.search(regex, output.stdout)
|
||||||
job_id = int(match.group(1))
|
job_id = int(match.group(1))
|
||||||
|
|
||||||
logger.info(f'SLURM job {job_id} submitted with trial ID: {trial_id}')
|
slurm_logger.info(f'SLURM job {job_id} submitted with trial ID: {trial_id}')
|
||||||
logger.info(f'Paramters: {trial_params}')
|
slurm_logger.info(f'Paramters: {trial_params}')
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
logger.error('Error submitting SLURM job')
|
slurm_logger.error('Error submitting SLURM job')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
while not os.path.isfile(results_path):
|
while not os.path.isfile(results_path):
|
||||||
|
|
@ -102,16 +107,24 @@ def create_objective(
|
||||||
row_data = step_data.iloc[0]
|
row_data = step_data.iloc[0]
|
||||||
intermediate_value = loss(row_data, trial.params)
|
intermediate_value = loss(row_data, trial.params)
|
||||||
|
|
||||||
trial.report(intermediate_value, current_step)
|
# Determine whether the designated pruners say that we should prune based off of intermediate values
|
||||||
|
trial.report(intermediate_value, current_step if not log_trial_id_with_intermediate else trial_id)
|
||||||
if trial.should_prune():
|
if trial.should_prune():
|
||||||
cancel_command = f'scancel {job_id}'
|
cancel_command = f'scancel {job_id}'
|
||||||
try:
|
try:
|
||||||
|
# Cancel the slurm job associated with that task
|
||||||
subprocess.run(cancel_command, shell=True, check=True)
|
subprocess.run(cancel_command, shell=True, check=True)
|
||||||
logger.info(f'SLURM job {job_id} cancelled due to pruning')
|
slurm_logger.info(f'SLURM job {job_id} cancelled due to pruning')
|
||||||
raise optuna.TrialPruned()
|
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
logger.error('Error cancelling SLURM job')
|
slurm_logger.error('Error cancelling SLURM job, resources have to be release manually')
|
||||||
raise
|
finally:
|
||||||
|
if return_average_on_prune:
|
||||||
|
# Return average intermediate values (useful for Wilcoxon signed rank test)
|
||||||
|
losses = step_data.apply(lambda row: loss(row, trial.params), axis=1)
|
||||||
|
return losses.mean()
|
||||||
|
else:
|
||||||
|
# Completely abort the trial
|
||||||
|
raise optuna.TrialPruned()
|
||||||
|
|
||||||
current_step += 1
|
current_step += 1
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue