Hydra是Facebook Research开发的开源Python配置管理框架,旨在解决复杂项目中配置混乱、多环境与多参数组合管理的难题。该框架采用分层配置与动态组合设计,支持以YAML文件实现结构化配置。Hydra尤其适用于简化机器学习实验、软件开发及其他复杂应用的配置管理。它的名字来源于希腊神话中的九头蛇,寓意其能够灵活管理多种配置组合。Hydra的核心特性包括支持多源分层配置组合、可通过命令行直接覆盖配置、提供动态命令补全功能,同时支持本地与远程运行,并能通过单命令执行批量参数作业。

Hydra的官方仓库地址为:hydra,详细文档可参阅:hydra-doc。Hydra功能全面,本文主要介绍其基本使用方法,更多高级功能请参考官方文档。截至本文撰写时,Hydra的稳定版本为1.3,该版本兼容Python 3.6至3.11,并全面支持Linux、macOS和Windows操作系统。安装命令如下:

pip install hydra-core --upgrade

1 基础教程

1.1 快速入门

简单示例

以下代码是一个简单的Hydra应用示例,它会打印出配置信息,其中my_app函数是编写业务逻辑的入口。

1

2

3

4

5

6

7

from omegaconf import DictConfig, OmegaConf

import hydra

@hydra.main(version_base=None)

def my_app(cfg: DictConfig) -> None:

    print(OmegaConf.to_yaml(cfg))

if __name__ == "__main__":

    my_app()

如果你直接执行这段代码(没有任何命令行参数),程序会输出一个空的配置对象:

1

{}

这是因为,当运行my_app.py时,@hydra.main装饰器会自动拦截对 my_app()的调用。此时Hydra会初始化一个空的DictConfig对象(类似于Python字典),并将其作为参数cfg 传递给函数。由于当前配置为空,OmegaConf.to_yaml(cfg)将其转换为YAML格式后,仅输出一个空对象。OmegaConf是Hydra的底层配置引擎,Hydra基于OmegaConf实现上层的复杂应用配置与运行管理,且OmegaConf可独立使用。

此外默认情况下,Hydra会创建以下目录结构以追踪和管理程序的运行结果:

1

2

3

4

5

6

7

8

9

outputs/

├── yyyy-mm-dd/          # 按日期分组

│   └── hh-mm-ss/        # 按时间精确到秒

│       └── .hydra/      # 保存本次运行的配置

│           ├── config.yaml    # 完整的配置

│           ├── hydra.yaml     # Hydra 自身的配置

│           └── overrides.yaml # 命令行覆盖的参数

│       └── my_app.log   # 日志文件(如果配置了日志)

│       └── 其他输出文件     # 你的程序生成的文件

可以通过以下方式为配置添加内容:

通过命令行添加:

1

2

# 不支持直接在 +key=value 语法中传入非 ASCII 字符

python my_app.py +name="zhangsan" +age=25

输出:

1

2

name: zhangsan

age: 25

创建配置文件:
创建一个config.yaml文件,然后运行:

1

python my_app.py --config-path=. --config-name=config

在代码中设置默认配置:
可以修改代码,为@hydra.main装饰器添加配置参数:

1

2

3

4

5

6

7

from omegaconf import DictConfig, OmegaConf

import hydra

@hydra.main(version_base=None, config_path=".", config_name="config")

def my_app(cfg):

    print(OmegaConf.to_yaml(cfg))

if __name__ == "__main__":

    my_app()

可以通过命令行覆盖已加载配置中的值,但是注意无需添加+前缀:

1

python my_app.py name="lisi"

使用++前缀可实现若配置中已存在该参数则覆盖,若不存在则新增:

1

python my_app.py ++name="wangwu" ++password=1234

要注意🤖:Hydra通过命令行修改配置时,仅会覆盖或新增程序运行时内存中的配置数据,不会改动磁盘上的原始配置文件,重启程序后仍会配置加载文件的原始配置。

配置对象使用

通过Hydra加载配置后,可通过属性或字典式访问或修改已有的配置项,访问不存在的配置项时会抛出异常:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

from omegaconf import DictConfig, OmegaConf

import hydra

@hydra.main(version_base=None, config_path=".", config_name="config")

def my_app(cfg: DictConfig):

    # 属性式访问配置值

    assert cfg.name == "张三"

    # 字典式访问配置值

    assert cfg["age"] == 25

    # 修改已有配置值

    cfg.name = "李四"         

    cfg["age"] = 30           

    assert cfg.name == "李四"

    assert cfg["age"] == 30

    # 访问缺失值会抛出异常

    try:

        cfg.birth_year

    except Exception as e:

        print("error !!")

        print(e)

if __name__ == "__main__":

    my_app()

之所以不允许访问不存在的配置键,仅能操作已有配置键,是因为Hydra默认启用了struct模式以严格结构化配置。如需新增或修改配置,可先关闭严格模式,允许动态新增键。但如果嵌套层级也未提前声明,则需要先创建空嵌套,再为其添加子项:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

from omegaconf import DictConfig, OmegaConf

import hydra

import os

@hydra.main(version_base=None, config_path=".", config_name="config")

def my_app(cfg: DictConfig):

    # 关闭struct模式,允许新增配置键

    OmegaConf.set_struct(cfg, False)

    # 同一级新增配置

    cfg.birth_year = 1995     

    cfg["hobby"] = ["篮球", "编程"]

    # 无法直接给不存在的嵌套层级链式赋值

    # cfg.address.city = "北京"         

    # 需要先创建嵌套层级,再赋值子键

    cfg.address = OmegaConf.create({})  # 显式创建空的嵌套

    cfg.address.city = "北京"        

    cfg.address["district"] = "朝阳区"

    # 验证写入结果

    assert cfg.birth_year == 1995 

    assert cfg.address.district == "朝阳区"

    print("配置写入验证通过!")

if __name__ == "__main__":

    my_app()

对配置文件进行分组

若希望分别使用CNN和Transformer模型对数据集进行训练基准测试,可通过配置组(Config Group)实现这一需求。配置组是一个带有名称的分组,包含一组有效的配置项。若选择不存在的配置项,系统会生成错误提示,并列出所有有效的配置项。

创建配置组时,需先新建一个目录(例如model),用于存放各模型配置项对应的文件。由于预计会创建多个配置组,建议提前将所有配置文件统一移至conf目录下管理。

目录结构如下:

1

2

3

4

5

├─ conf

│  └─ model

│      ├─ cnn.yaml

│      └─ transformer.yaml

└── my_app.py

model/cnn.yaml:

1

2

3

4

5

backbone: resnet50

learning_rate: 0.001

batch_size: 32

epochs: 20

dropout: 0.2

model/transformer.yaml:

1

2

3

4

5

backbone: vit_base

learning_rate: 0.0001

batch_size: 16

epochs: 30

attention_heads: 12

所有配置文件已统一存放至conf目录,需通过config_path参数告知Hydra该目录位置,并在代码中指定待加载的配置文件名config_name。若未明确指定具体配置文件名,Hydra无法自动推断加载目标,最终会输出空配置:

1

2

3

4

5

6

7

from omegaconf import DictConfig, OmegaConf

import hydra

@hydra.main(version_base=None, config_path="conf", config_name="model/cnn")

def my_app(cfg: DictConfig) -> None:

    print(OmegaConf.to_yaml(cfg))

if __name__ == "__main__":

    my_app()

也可以通过命令行从配置组中选择特定配置项,命令行使用+分组名=配置项的格式,例如:

1

python my_app.py +model=transformer

与常规用法一致,仍可覆盖最终配置中的单个参数值:

1

python my_app.py +model=transformer model.epochs=40

多文件处理

可以生成一个配置文件,在配置文件中用defaults参数添加默认配置列表。该列表用于指定Hydra组合最终配置对象的规则,按照约定,它需作为配置文件的首个配置项。如下所示:

1

2

defaults:

  - model: cnn

然后这个配置文件可以命名为任意名字,如conf文件夹下的config.yaml,这样运行会默认加载model对应的文件配置:

1

2

3

4

5

6

7

from omegaconf import DictConfig, OmegaConf

import hydra

@hydra.main(version_base=None, config_path="conf", config_name="config")

def my_app(cfg: DictConfig) -> None:

    print(OmegaConf.to_yaml(cfg))

if __name__ == "__main__":

    my_app()

默认配置列表支持叠加多个深度学习相关配置项。若同一配置组存在两个配置文件,系统会将这两个配置文件合并为一个新字典;当配置中出现相同键名时,后加载的配置项会覆盖先加载的配置项。示例默认配置如下:

1

2

3

4

defaults:

  - model:

    - cnn

    - transformer

若在配置文件夹conf下的dataset目录中,存在如下配置文件model/cifar10.yaml:

1

2

3

4

5

name: CIFAR-10

path: ./data/cifar10

num_classes: 10

learning_rate: 0.0001

augmentation: true  # 是否开启数据增强

当默认配置文件conf/config.yaml中默认配置列表的内容如下:

1

2

3

defaults:

  - model: cnn

  - dataset: cifar10

由于model和dataset分属不同的配置组,Hydra会将这两个配置组的默认配置进行独立合并。最终生成的完整配置结构中,会包含model和dataset两个一级配置项,各自保留对应配置组的完整参数:

1

2

3

4

model:

  # 此处为conf/model/cnn.yaml中的配置内容

dataset:

  # 此处为conf/dataset/cifar10.yaml中的配置内容

即使设置了默认配置,仍可手动确定参数并覆盖部分配置参数:

1

python my_app.py model=cnn model.epochs=30

在配置项前添加~前缀,可从默认配置列表中移除该默认项:

1

python my_app.py ~model

主配置的组合顺序

主配置文件中可同时包含配置参数和默认配置列表。在此情况下,若需调整默认配置列表与主配置之间的覆盖关系,可通过添加_self_关键字实现:将_self_置于默认配置列表末尾,则主配置参数将覆盖默认配置列表中的对应项;若将其置于列表开头,则默认配置列表中的参数将覆盖主配置中的内容。

需注意的是,从Hydra 1.1版本开始,默认行为为主配置覆盖默认配置列表中的配置;而在此之前的版本中,默认配置列表会覆盖主配置的参数。

例如默认配置文件config.yaml内容如下,会进行数据覆盖,也就是说配置文件里dataset部分会覆盖默认配置中的同名部分:

1

2

3

4

5

6

7

defaults:

  - model: cnn

  - dataset: cifar10

  - _self_

dataset:

  name: my_dataset

version: 1.0

1.2 整合应用

随着软件复杂度的不断提升,我们会采用模块化与组合化的设计思路来保证其可维护性。这种思路同样适用于配置文件的管理。假设我们需要为示例程序配置多类深度学习模型支持,且每个模型对应多种训练策略、搭配不同的数据预处理流程。使用Hydra时,既不必为模型、策略、预处理流程的各类组合编写独立类,也无需为其单独编写配置文件。我们可以借鉴底层软件开发的核心思路:通过组合化配置来解决这一问题。


 

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐