facebookresearch / hydra

Hydra is a framework for elegantly configuring complex applications
https://hydra.cc
MIT License
8.81k stars 634 forks source link

[Feature Request] Expand options to transform special characters for dir name #1832

Open alexeib opened 3 years ago

alexeib commented 3 years ago

Currently, when using "override_dirname" in sweep path, hydra will construct a directory name with the parameters unless excluded in the hydra.job.config.override_dirname option. Unfortunately when sweeping on parameters which are lists, dictionaries, or have commas in them, you end up with a directory name that while valid, usually breaks shell integrations and has to be quoted everywhere later. I hacked this in my local hydra install by modifying hydra/core/utils.py and adding

working_dir = working_dir.replace("[", "").replace("]", "").replace("{", "").replace("}", "").replace(",", "_")

as the third line in run_job() function

it works for me, but obviously it is not a great solution. Any idea if there is existing support to do something like this, or if it can be added in future versions, maybe through expanding options in hydra.job.config.override_dirname?

Thanks!

jieru-hu commented 3 years ago

thanks @alexeib we will look into this

danielgafni commented 2 years ago

Also, when one of the overridden parameters is a path (containing /), it totally messes up using override_dirname for other directories (eg multirun subdir)

Jasha10 commented 2 years ago

Good point!

Jasha10 commented 2 years ago

One workaround is to use an OmegaConf custom resolver to come up with the output directory's name. For example:

# my_app.py
import hydra
from omegaconf import OmegaConf, ListConfig

def my_override_dirname(overrides: ListConfig) -> str:
    """Process the overrides passed to the app and return a single string"""
    task_overrides: ListConfig = overrides.task
    ret: str = "_".join(task_overrides)
    ret = ret.replace("{", "")
    ret = ret.replace("}", "")
    ret = ret.replace("[", "")
    ret = ret.replace("]", "")
    ret = ret.replace(",", "_")
    ret = ret.replace("/", "_")
    ret = ret.replace("=", "-")
    return ret

OmegaConf.register_new_resolver("my_override_dirname", my_override_dirname)

@hydra.main(config_path=".", config_name="config")
def app(cfg):
    from hydra.core.hydra_config import HydraConfig
    print(HydraConfig.get().runtime.output_dir)
    ...

app()
# config.yaml
hydra:
  run:
    dir: outputs/${now:%Y-%m-%d}_${my_override_dirname:${hydra.overrides}}
  sweep:
    dir: multirun/${now:%Y-%m-%d}
    subdir: ${my_override_dirname:${hydra.overrides}}

Launching a single run:

$ python my_app.py +foo=bar
...
$ ls outputs/
2022-01-13_+foo-bar

Launching with --multirun:

$ python my_app.py --multirun +foo=bar,baz
...
$ ls multirun/2022-01-13/
+foo-bar  +foo-baz   multirun.yaml

Note that instead of passing hydra.overrides to the custom resolver, one could just-as-well pass hydra.override_dirname to a custom resolver for postprocessing:

...
    dir: outputs/${now:%Y-%m-%d}_${my_override_dirname_postprocessing:${hydra.override_dirname}}