#!/usr/bin/env python
"""
Module running the optimization command
=======================================
Gets an experiment and iterates over it until one of the exit conditions is met
"""
from __future__ import annotations
import logging
import orion.core
from orion.client.experiment import ExperimentClient
from orion.core.cli import base as cli
from orion.core.cli import evc as evc_cli
from orion.core.io import experiment_builder
from orion.core.utils import sigterm_as_interrupt
from orion.core.utils.exceptions import (
BrokenExperiment,
InexecutableUserScript,
MissingResultFile,
)
from orion.core.utils.format_terminal import format_stats
from orion.core.worker.consumer import Consumer
log = logging.getLogger(__name__)
SHORT_DESCRIPTION = "Conducts hyperparameter optimization"
DESCRIPTION = """
This command starts hyperparameter optimization process for the user-provided model using the
configured optimization algorithm and search space.
"""
[docs]def add_subparser(parser):
"""Add the subparser that needs to be used for this command"""
hunt_parser = parser.add_parser(
"hunt", help=SHORT_DESCRIPTION, description=DESCRIPTION
)
orion_group = cli.get_basic_args_group(
hunt_parser, group_name="Hunt arguments", group_help=""
)
orion.core.config.experiment.add_arguments(
orion_group,
rename=dict(max_broken="--exp-max-broken", max_trials="--exp-max-trials"),
)
orion_group.add_argument(
"--max-trials",
type=int,
metavar="#",
help="(DEPRECATED) This argument will be removed in v0.3. Use --exp-max-trials instead",
)
orion_group.add_argument(
"--init-only",
default=False,
action="store_true",
help="Only create the experiment and register in database, but do not execute any trial.",
)
worker_args_group = hunt_parser.add_argument_group(
"Worker arguments (optional)",
description="Arguments to automatically resolved branching events.",
)
orion.core.config.worker.add_arguments(
worker_args_group,
rename=dict(max_broken="--worker-max-broken", max_trials="--worker-max-trials"),
)
evc_cli.get_branching_args_group(hunt_parser)
cli.get_user_args_group(hunt_parser)
hunt_parser.set_defaults(func=main)
hunt_parser.set_defaults(help_empty=True) # Print help if command is empty
return hunt_parser
COMPLETION_MESSAGE = """\
Hints
=====
Info
----
To get more information on the experiment, run the command
orion info --name {experiment.name} --version {experiment.version}
"""
NONCOMPLETED_MESSAGE = """\
Status
------
To get the status of the trials, run the command
orion status --name {experiment.name} --version {experiment.version}
For a detailed view with status of each trial listed, use the argument `--all`
orion status --name {experiment.name} --version {experiment.version} --all
"""
# pylint:disable=unused-argument
[docs]def on_error(client, trial, error, worker_broken_trials):
"""If the script is not executable, don't waste time and raise right away"""
if isinstance(error, (InexecutableUserScript, MissingResultFile)):
raise error
return True
# pylint:disable=too-many-arguments
[docs]def workon(
experiment,
n_workers=None,
pool_size=None,
max_trials=None,
max_broken=None,
max_idle_time=None,
heartbeat=None,
user_script_config=None,
interrupt_signal_code=None,
ignore_code_changes=None,
executor=None,
executor_configuration=None,
idle_timeout=None,
):
"""Try to find solution to the search problem defined in `experiment`."""
# NOTE: Remove in v0.3.0
if max_idle_time is not None and idle_timeout is None:
idle_timeout = max_idle_time
consumer = Consumer(
experiment,
user_script_config,
interrupt_signal_code,
ignore_code_changes,
)
client = ExperimentClient(experiment, heartbeat=heartbeat)
if executor is None:
executor = orion.core.config.worker.executor
if executor_configuration is None:
executor_configuration = orion.core.config.worker.executor_configuration
log.debug("Starting workers")
with client.tmp_executor(executor, n_workers=n_workers, **executor_configuration):
try:
client.workon(
consumer,
n_workers=n_workers,
pool_size=pool_size,
max_trials_per_worker=max_trials,
max_broken=max_broken,
trial_arg="trial",
on_error=on_error,
idle_timeout=idle_timeout,
)
except BrokenExperiment as e:
print(e)
if client.is_done:
print("Search finished successfully")
print("\n" + format_stats(client))
print("\n" + COMPLETION_MESSAGE.format(experiment=client))
if not experiment.is_done:
print(NONCOMPLETED_MESSAGE.format(experiment=client))
[docs]def main(args):
"""Build experiment and execute hunt command"""
args["root"] = None
args["leafs"] = []
# TODO: simplify when parameter parsing is refactored
experiment = experiment_builder.build_from_args(args)
if args["init_only"]:
return
config = experiment_builder.get_cmd_config(args)
worker_config = orion.core.config.worker.to_dict()
if config.get("worker"):
worker_config.update(config.get("worker"))
# If EVC is not enabled, we force Consumer to ignore code changes.
if not config["branching"].get("enable", orion.core.config.evc.enable):
ignore_code_changes = True
else:
ignore_code_changes = config["branching"].get("ignore_code_changes")
with sigterm_as_interrupt():
workon(experiment, ignore_code_changes=ignore_code_changes, **worker_config)