summarize_from_feedback/utils/experiments.py (66 lines of code) (raw):
"""
Utilities related to launching and managing experiments.
"""
from functools import partial, wraps
from typing import List, get_type_hints
from summarize_from_feedback.utils import jobs
from summarize_from_feedback.utils.combos import bind, combos
def get_annotation_of_only_argument(fn):
annotations = get_type_hints(fn).values()
if len(annotations) != 1:
raise ValueError(
f"fn {fn} has {len(annotations)} arguments, but we wanted 1: {annotations}"
)
ty, = annotations
return ty
def get_experiment_jobs(name, launch_fn, trials, hparam_class=None) -> List[jobs.Job]:
if hparam_class is None:
hparam_class = get_annotation_of_only_argument(launch_fn)
# Maps experiment def key to argument name for jobs.launch; these get pulled out
launch_kwarg_keys = dict(mpi="mpi", mode="mode")
to_launch = []
for trial in trials:
descriptors = []
trial_bindings = []
trial_bindings_dict = {} # only used for message
# Extract bindings & descriptors from the trial
for k, v, s in trial:
if k is not None:
if k in trial_bindings_dict:
print(f"NOTE: overriding key {k} from {trial_bindings_dict[k]} to {v}")
trial_bindings.append((k, v))
trial_bindings_dict[k] = v
if "descriptor" in s and s["descriptor"] is not "":
descriptors.append(str(s["descriptor"]))
# Pull out arguments for jobs.launch
launch_H = jobs.JobHParams()
launch_H.name = "/".join([name] + descriptors)
filtered_trial_bindings = []
dry_run = False
for k, v in trial_bindings:
if k in launch_kwarg_keys:
setattr(launch_H, launch_kwarg_keys[k], v)
elif k == "dry_run":
dry_run = v
else:
filtered_trial_bindings.append((k, v))
if dry_run:
print(f"{launch_H.name}: {filtered_trial_bindings}")
else:
hparams = hparam_class()
hparams.override_from_pairs(filtered_trial_bindings)
to_launch.append(jobs.Job(fn=partial(launch_fn, hparams), params=launch_H))
return to_launch
def experiment_fn_launcher(experiment_dict, fn):
def launcher(exp, name, **extra_args):
try:
trials = experiment_dict[exp]
except KeyError:
raise ValueError(f"Couldn't find experiment '{exp}'")
fn(name, trials, **extra_args)
return launcher
def experiment_def_launcher(experiment_dict, main_fn, **default_bindings):
"""
Use like this:
if __name__ == "__main__":
fire.Fire(
experiment_def_launcher(
experiment_dict=experiment_definitions(),
main_fn=train_rm.main,
)
)
"""
@wraps(main_fn)
def fn(name, trials, **extra_args):
# Bind remaining extra arguments from the defaults and from the command line
trials = combos(
*[bind(k, v) for k, v in default_bindings.items()],
trials,
*[bind(k, v) for k, v in extra_args.items()],
)
exp_jobs = get_experiment_jobs(name, main_fn, trials)
return jobs.multilaunch(exp_jobs)
return experiment_fn_launcher(experiment_dict, fn)