Hydra 输出与工作目录完全指南:从 `hydra.job.chdir` 到 `hydra.output_subdir`
2026/9/15 23:18:07 网站建设 项目流程

Hydra 输出与工作目录完全指南:从hydra.job.chdirhydra.output_subdir

【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra

导读

本篇技术指南聚焦 Hydra 框架的"输出/工作目录"机制:为什么每次运行都需要一个新的输出目录、输出目录里到底存放了什么、如何让 Python 应用自动切换到该目录,以及如何在chdir开启后仍能访问原始工作目录。文章以官方教程 website/docs/tutorials/basic/running_your_app/3_working_directory.md 为核心骨架,并结合仓库中hydra/core/utils.pyhydra/conf/hydra/output/default.yaml、示例应用 examples/tutorials/basic/running_your_hydra_app/3_working_directory 等源码与配置,帮助你在实际项目中正确使用、定制与迁移 Hydra 的目录管理能力。


一、为什么需要"每个运行一个输出目录"

在没有 Hydra 时,每次运行实验你都要手动指定一个新的输出目录(--output_dir 2019-09-25/15-16-17之类的命令行参数),既繁琐又容易忘记覆盖上一次的结果。Hydra 的核心思路是:

为每一次运行自动创建一个新的输出目录,并把你的代码执行环境"放"到这个输出目录中。

每次运行应用,Hydra 都会生成一个新的输出目录;默认情况下,该目录同时用于存放 Hydra 自身产生的输出(配置快照、日志等)。这意味着你无需操心目录命名,也不必担心不同运行之间互相覆盖文件。

从源码结构看,这个能力由三层机制协同实现:

  1. 默认目录模板:默认的run.dirsweep.dir模板定义在 hydra/conf/hydra/output/default.yaml;
  2. 单次运行hydra.core.utils.run_job()负责计算输出目录、写入配置快照并(可选地)执行os.chdir(见 hydra/core/utils.py);
  3. 批量运行(multirun)BasicLauncher.launch()依据hydra.sweep.dirhydra.sweep.subdir为每个 job 计算目录(见 hydra/_internal/core_plugins/basic_launcher.py)。

下面先用一个最小示例观察它的实际行为。


二、最小示例:查看工作目录与输出目录

官方示例应用位于 examples/tutorials/basic/running_your_hydra_app/3_working_directory/my_app.py,内容如下:

import os from omegaconf import DictConfig import hydra from hydra.core.hydra_config import HydraConfig @hydra.main() def my_app(_cfg: DictConfig) -> None: print(f"Working directory : {os.getcwd()}") print(f"Output directory : {HydraConfig.get().runtime.output_dir}") if __name__ == "__main__": my_app()

运行两次,观察输出:

$ python my_app.py Working directory : /home/omry/dev/hydra Output directory : /home/omry/dev/hydra/outputs/2019-09-25/15-16-17 $ python my_app.py Working directory : /home/omry/dev/hydra Output directory : /home/omry/dev/hydra/outputs/2019-09-25/15-16-19

关键观察点:

  • 每次运行都会生成一个全新的输出目录,两次运行的时间戳目录不同(15-16-17vs15-16-19),互不干扰;
  • 在默认配置下(Hydra < 1.2 或chdir=False),当前工作目录不变,仍是你启动应用的目录;
  • 输出目录的完整路径可以通过Hydra Config 中的hydra.runtime.output_dir获取——这是应用内访问输出目录的标准方式。关于如何访问 Hydra Config,可参考 website/docs/configure_hydra/Intro.md 中 "Accessing the Hydra config" 一节。

提示:上述示例同时存放在 website/docs/tutorials/basic/running_your_app/3_working_directory.md 与示例目录中,两者是同一份代码的文档与实体两面。


三、输出目录里到底有什么

查看其中一个输出目录:

$ tree outputs/2019-09-25/15-16-17 outputs/2019-09-25/15-16-17 ├── .hydra │ ├── config.yaml │ ├── hydra.yaml │ └── overrides.yaml └── my_app.log

目录内容分两部分:

Hydra 输出子目录(默认名为.hydra

文件含义
config.yaml用户指定配置(合成后的完整配置)的 YAML 快照
hydra.yamlHydra 自身配置的 YAML 快照
overrides.yaml本次运行使用的命令行覆盖(overrides)列表

主输出目录

文件含义
my_app.log本次运行生成的日志文件

这套快照机制让每个输出目录自包含、可复现:只要保留outputs/...目录,事后就能精确得知"当时用了什么配置、覆盖了什么参数、Hydra 的配置是怎样的"。

从源码角度,这些文件由run_job()统一写入。在 hydra/core/utils.py 中可以看到实际写盘逻辑:

if config.hydra.output_subdir is not None: hydra_output = Path(config.hydra.runtime.output_dir) / Path( config.hydra.output_subdir ) _save_config(task_cfg, "config.yaml", hydra_output) _save_config(hydra_cfg, "hydra.yaml", hydra_output) _save_config(config.hydra.overrides.task, "overrides.yaml", hydra_output)

其中_save_config()(hydra/core/utils.py)会先mkdir(parents=True, exist_ok=True)再以OmegaConf.to_yaml()序列化写入。注意config.hydra.output_subdir若为None,则整个.hydra目录都不会被创建——这正是下一节"禁用输出子目录"的机制入口。


四、让应用自动切换到输出目录:hydra.job.chdir

很多应用希望把输出文件(如数据库 dump、模型 checkpoint)直接写在输出目录里,而不是手动拼接绝对路径。Hydra 从 v1.2 起提供了hydra.job.chdir开关。

4.1 行为对比

hydra.job.chdir的默认值定义在 hydra/conf/init.py:

# Change current working dir to the output dir. chdir: bool = False

Hydra v1.2 起默认False,工作目录保持不变。将其设为True后,@hydra.main装饰器会在调用你的主函数之前执行os.chdir,把 Python 的工作目录切换到本次运行的输出目录。实测效果:

# check current working dir $ pwd /home/jasha/dev/hydra # for Hydra >= 1.2, working dir remains unchanged by default $ python my_app.py Working directory : /home/jasha/dev/hydra Output directory : /home/jasha/dev/hydra/outputs/2023-04-18/13-43-24 # working dir changed to output dir $ python my_app.py hydra.job.chdir=True Working directory : /home/jasha/dev/hydra/outputs/2023-04-18/13-43-17 Output directory : /home/jasha/dev/hydra/outputs/2023-04-18/13-43-17 # output dir and files are still created even if `chdir` is disabled: $ tree -a outputs/2023-04-18/13-43-24/ outputs/2023-04-18/13-43-24/ ├── .hydra │ ├── config.yaml │ ├── hydra.yaml │ └── overrides.yaml └── my_app.log

三个要点:

  • chdir=True后,os.getcwd()hydra.runtime.output_dir指向同一个目录,此时直接写相对路径文件即可落在输出目录内;
  • 即使不开启chdir输出目录和其中的文件照常创建.hydramy_app.log依旧存在),只是你的进程还在原目录;
  • 该开关既可以在命令行以hydra.job.chdir=True传入,也可以写进配置文件的hydra.job节点。

4.2 源码实现

chdir的切换逻辑在 hydra/core/utils.py:

_chdir = hydra_cfg.hydra.job.chdir if _chdir: os.chdir(output_dir) ret.working_dir = output_dir else: ret.working_dir = os.getcwd()

并且使用try/finally保证任务结束(包括异常中断)后恢复原始工作目录(hydra/core/utils.py)。仓库测试也对chdir=True的场景做了覆盖验证,例如 hydra/test_utils/launcher_common_tests.py 中通过overrides + ["hydra.job.chdir=True"]断言os.getcwd()指向预期目录。

需要留意:切换工作目录的行为会影响应用对相对路径的解析。如果你在代码里读取了"启动时所在目录"下的资源文件,请参考下一节的两个工具函数,否则在chdir=True下会定位到错误位置。


五、访问原始工作目录:get_original_cwd()to_absolute_path()

hydra.job.chdir=True时,os.getcwd()已指向输出目录,但你依然可以拿到启动应用时所在的原始工作目录。官方示例 examples/tutorials/basic/running_your_hydra_app/3_working_directory/original_cwd.py 演示了这两种用法:

import os from omegaconf import DictConfig import hydra from hydra.utils import get_original_cwd, to_absolute_path @hydra.main() def my_app(_cfg: DictConfig) -> None: print(f"Current working directory : {os.getcwd()}") print(f"Orig working directory : {get_original_cwd()}") print(f"to_absolute_path('foo') : {to_absolute_path('foo')}") print(f"to_absolute_path('/foo') : {to_absolute_path('/foo')}") if __name__ == "__main__": my_app()

运行结果:

$ python examples/tutorial/8_working_directory/original_cwd.py Current working directory : /Users/omry/dev/hydra/outputs/2019-10-23/10-53-03 Original working directory : /Users/omry/dev/hydra to_absolute_path('foo') : /Users/omry/dev/hydra/foo to_absolute_path('/foo') : /foo

两个函数的语义(见 hydra/utils.py):

函数行为
get_original_cwd()返回启动 Hydra 应用时的原始工作目录(即HydraConfig.get().runtime.cwd,见 hydra/utils.py)。未初始化 HydraConfig 时调用会抛出ValueError
to_absolute_path(path)相对路径按原始工作目录为基准转为绝对路径;绝对路径原样返回(见 hydra/utils.py)

典型用法:应用需要读取位于项目根目录下的数据文件或预训练模型时,用to_absolute_path("data/foo")而非裸的相对路径,这样无论chdir是否开启都能正确定位。


六、改变或禁用 Hydra 输出子目录:hydra.output_subdir

默认情况下 Hydra 输出子目录名为.hydra。你可以通过覆盖hydra.output_subdir来改变或禁用它的创建:

# 改变子目录名(例如改为 custom_hydra_output) python my_app.py hydra.output_subdir=custom_hydra_output # 禁用子目录创建(不生成 .hydra 目录) python my_app.py hydra.output_subdir=null

结合上一节源码可以看到,run_job()在写快照前先判断config.hydra.output_subdir is not None(hydra/core/utils.py):为null时直接跳过整个.hydra目录的创建。

注意:禁用.hydra后,本次运行将不再保留config.yaml/hydra.yaml/overrides.yaml快照,会损失可复现性,请按需使用。


七、定制输出目录命名:hydra.run.dirhydra.sweep.dir

7.1 单次运行(run)目录模板

默认的单次运行模板定义在 hydra/conf/hydra/output/default.yaml:

run: dir: outputs/${now:%Y-%m-%d}/${now:%H-%M-%S}

outputs/日期/时分秒的树形结构。你可以在配置文件中覆盖hydra.run.dir来实现自己的命名策略,website/docs/configure_hydra/workdir.md 给出了三种常见模式:

按日期分组:

hydra: run: dir: ./outputs/${now:%Y-%m-%d}/${now:%H-%M-%S}

按 job 名称分组:

hydra: run: dir: outputs/${hydra.job.name}/${now:%Y-%m-%d_%H-%M-%S}

目录名中包含用户配置变量:

hydra: run: dir: outputs/${now:%Y-%m-%d_%H-%M-%S}/opt:${optimizer.type}

模板中支持${now:...}(时间格式化)、${hydra.job.name}(任务名)以及用户自定义配置项插值,实现"目录名自描述实验内容"的效果。

7.2 批量运行(multirun)目录模板

multirun 场景下涉及两个键:

默认值含义
hydra.sweep.dirmultirun/${now:%Y-%m-%d}/${now:%H-%M-%S}整个 sweep 的根目录
hydra.sweep.subdir${hydra.job.num}每个 job 的子目录(job 序号)

默认配置同样位于 hydra/conf/hydra/output/default.yaml。在底层,BasicLauncher.launch()会为每个 job 调用run_job()并传入job_dir_key="hydra.sweep.dir"job_subdir_key="hydra.sweep.subdir"(见 hydra/_internal/core_plugins/basic_launcher.py),而run_job()则在 hydra/core/utils.py 中先取sweep.dir,再延迟求值sweep.subdir(因为hydra.job.num这类值只在客户端执行时可用),最终拼成output_dir = os.path.join(dir, subdir)

因此,multirun 的目录同样支持时间、job 名等插值:

hydra: sweep: dir: ${hydra.job.name} subdir: ${hydra.job.num}

运行python my_app.py --multirun a=a1,a2,a3后将得到类似my_app/0my_app/1my_app/2的目录结构。

7.3 用hydra_override_dirname生成描述性目录名

如果希望子目录名直接体现本次 job 的命令行参数(如batch_size=32,learning_rate=0.1),可以使用内置的hydra_override_dirname解析器,它从命令行 override 推导目录名,通常与hydra.sweep.subdir配合使用(详见 website/docs/configure_hydra/workdir.md):

hydra: sweep: dir: multirun subdir: ${hydra_override_dirname:}

运行:

python my_app.py --multirun batch_size=32 learning_rate=0.1,0.01

会得到:

multirun ├── batch_size=32,learning_rate=0.01 └── batch_size=32,learning_rate=0.1

解析器还支持在调用处定制分隔符与排除键,例如将随机种子排除在目录名之外:

hydra: sweep: dir: multirun subdir: '${hydra_override_dirname:{exclude_keys: [seed]}}/seed=${seed}'

运行python my_app.py --multirun batch_size=32 learning_rate=0.1,0.01 seed=1,2将生成:

multirun ├── batch_size=32,learning_rate=0.01 │ ├── seed=1 │ └── seed=2 └── batch_size=32,learning_rate=0.1 ├── seed=1 └── seed=2

也可自定义键值分隔符kv_sep、条目分隔符item_sep,甚至通过element_resolver挂载一个自定义 OmegaConf 解析器对每个元素做预处理(例如把/\替换为_以保证目录名在不同平台可用)。相关示例配置与配套应用见仓库中的 examples/configure_hydra/job_override_dirname 目录。


八、配套资源与进一步阅读

  • 本文核心教程: website/docs/tutorials/basic/running_your_app/3_working_directory.md
  • 可运行的示例代码:examples/tutorials/basic/running_your_hydra_app/3_working_directory/my_app.py 与 original_cwd.py
  • 工作目录定制模式详解:website/docs/configure_hydra/workdir.md
  • 输出目录定制配套示例:examples/configure_hydra/workdir 与 examples/configure_hydra/job_override_dirname
  • Hydra Config 访问方式(HydraConfig.get()):website/docs/configure_hydra/Intro.md
  • 关键实现源码:hydra/core/utils.py(run_job/_save_config)、hydra/utils.py(get_original_cwd/to_absolute_path)、hydra/conf/init.py(hydra.job.chdir默认值)、hydra/conf/hydra/output/default.yaml(默认目录模板)
  • 相关测试:hydra/test_utils/launcher_common_tests.py(chdir行为验证)

使用建议:在 Hydra 1.2 及以上版本中,如果应用需要把产物写进输出目录,推荐显式开启hydra.job.chdir=True并使用get_original_cwd()/to_absolute_path()读取启动目录下的资源;同时保留默认的.hydra快照以维持可复现性。升级或迁移到新版本时,务必确认chdir默认值是否影响了你对相对路径的既有假设。

【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询