00325 Hydra - Basic Tutorial 学习笔记


前言

基本教程涵盖了基本的Hydra概念。

Operating System: Ubuntu 22.04.4 LTS

参考文档

  1. Tutorials intro
  2. Basic Tutorial

一个简单的命令行应用程序

link: https://hydra.cc/docs/tutorials/basic/your_first_app/simple_cli/

这是一个简单的Hydra应用程序,打印您的配置。my_app函数是您的代码的占位符。我们将慢慢发展这个例子,以展示更多的Hydra功能。

本教程中的示例可在此处获得。

# my_app.py
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()

在此示例中,Hydra创建一个空的cfg对象并将其传递给用@hydra. main注释的函数。

您可以通过命令行添加配置值。+表示该字段是新的。

$ python my_app.py +db.driver=mysql +db.user=omry +db.password=secret
db:
  driver: mysql
  user: omry
  password: secret

有关version_base参数的详细信息,请参阅version_base页

有关命令行的更多信息,请参阅Hydra的命令行标志基本覆盖语法

指定配置文件

link: https://hydra.cc/docs/tutorials/basic/your_first_app/config_file/

键入所有这些命令行参数可能会变得乏味。您可以通过在my_app.py旁边创建一个配置文件来解决这个问题。Hydra配置文件是yaml文件,应该具有.yaml文件扩展名。

# config. yaml
db: 
  driver: mysql
  user: omry
  password: secret

通过向@hydra.main()装饰器传递一个config_name参数来指定配置名称。请注意,您应该省略.yaml扩展名。Hydra还需要知道在哪里找到您的配置。通过传递config_path来指定其相对于应用程序的目录。

# my_app.py
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()

config. yaml会在您运行应用程序时自动加载。

$ python my_app.py
db:
  driver: mysql
  user: omry
  password: secret

您可以从命令行覆盖加载的配置中的值。请注意缺少+前缀。

$ python my_app.py db.user=root db.password=1234
db:
  driver: mysql
  user: root
  password: 1234

如果配置值已经在配置中,请使用++覆盖配置值,或者添加它。例如:

# Override an existing item
$ python my_app.py ++db.password=1234

# Add a new item
$ python my_app.py ++db.timeout=5

您可以为您的Hydra应用程序启用Tab完成

使用配置对象

link: https://hydra.cc/docs/tutorials/basic/your_first_app/using_config/

以下是Hydra配置对象的一些基本功能:

# config.yaml
node:                         # Config is hierarchical
  loompa: 10                  # Simple value
  zippity: ${node.loompa}     # Value interpolation
  do: "oompa ${node.loompa}"  # String interpolation
  waldo: ???                  # Missing value, must be populated prior to access
# main.py
from omegaconf import DictConfig, OmegaConf
import hydra

@hydra.main(version_base=None, config_path=".", config_name="config")
def my_app(cfg: DictConfig):
    assert cfg.node.loompa == 10          # attribute style access
    assert cfg["node"]["loompa"] == 10    # dictionary style access

    assert cfg.node.zippity == 10         # Value interpolation
    assert isinstance(cfg.node.zippity, int)  # Value interpolation type
    assert cfg.node.do == "oompa 10"      # string interpolation

    cfg.node.waldo                        # raises an exception

if __name__ == "__main__":
    my_app()

输出:

$ python my_app.py 
Traceback (most recent call last):
  File "my_app.py", line 32, in my_app
    cfg.node.waldo
omegaconf.errors.MissingMandatoryValue: Missing mandatory value: node.waldo
    full_key: node.waldo
    object_type=dict

Hydra的配置对象是OmegaConf的DictConfig的一个实例。您可以在此处了解有关OmegaConf的更多信息。

分组配置文件

link: https://hydra.cc/docs/tutorials/basic/your_first_app/config_groups/

假设您想在每个PostgreSQL和MySQL上对您的应用程序进行基准测试。为此,请使用配置组。

配置组是具有一组有效选项的命名组。选择不存在的配置选项会生成包含有效选项的错误消息。

创建配置组

要创建配置组,请创建一个目录,例如db,以保存每个数据库配置选项的文件。由于我们期望有多个配置组,我们将主动将所有配置文件移动到conf目录中。

# Directory layout
├─ conf
│  └─ db
│      ├─ mysql.yaml
│      └─ postgresql.yaml
└── my_app.py
# db/mysql.yaml
driver: mysql
user: omry
password: secret
# db/postgresql.yaml
driver: postgresql
user: postgres_user
password: drowssap
timeout: 10

使用配置组

由于我们将所有配置移动到conf目录中,我们需要使用config_path参数告诉Hydra在哪里可以找到它们。config_path是相对于my_app.py的目录。

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

@hydra.main(version_base=None, config_path="conf")
def my_app(cfg: DictConfig) -> None:
    print(OmegaConf.to_yaml(cfg))

if __name__ == "__main__":
    my_app()

运行my_app.py而不请求配置将打印一个空配置。

$ python my_app.py
{}

使用 +GROUP=OPTION 从配置组中选择一个项目,例如:

$ python my_app.py +db=postgresql
db:
  driver: postgresql
  pass: drowssap
  timeout: 10
  user: postgres_user

默认情况下,配置组决定最终配置对象中配置内容的放置位置。在Hydra中,配置内容的路径被称为配置包。db/postgresql.yaml的包名为db。

和之前一样,你仍然可以覆盖生成的配置中的单个值:

$ python my_app.py +db=postgresql db.timeout=20
db:
  driver: postgresql
  pass: drowssap
  timeout: 20
  user: postgres_user

高级主题

选择默认配置

link: https://hydra.cc/docs/tutorials/basic/your_first_app/defaults/

在办公室政治之后,您决定默认使用MySQL。您不再希望每次运行应用程序时都输入+db=mysql。

您可以将默认列表添加到配置文件中。默认列表是一个列表,告诉Hydra如何组合最终的配置对象。按照惯例,它是配置中的第一项。

配置组默认值

# config.yaml
defaults:
  - db: mysql

请记住指定config_name:

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()

当您运行更新的应用程序时,默认情况下会加载MySQL。

$ python my_app.py
db:
  driver: mysql
  pass: secret
  user: omry

您可以在默认列表中包含多个项目,例如。

defaults:
 - db: mysql
 - db/mysql/engine: innodb

默认值按顺序排列:

  • 如果多个配置定义相同的值,则最后一个获胜。
  • 如果多个配置为同一个字典做出贡献,则结果是组合字典。

覆盖配置组默认值

您仍然可以加载PostgreSQL并覆盖单个值。

$ python my_app.py db=postgresql db.timeout=20
db:
  driver: postgresql
  pass: drowssap
  timeout: 20
  user: postgres_user

你可以通过在默认列表中为默认条目添加前缀 ~ 来将其删除:

$ python my_app.py ~db
{}

主配置的组成顺序

你的主配置文件可以同时包含配置值和默认列表。在这种情况下,你应该在默认列表中添加 _self_ 关键字,以指定配置文件相对于默认列表中项目的组合顺序。

  • 如果你想让你的主配置文件覆盖默认列表中的配置值,可以在默认列表的末尾添加 _self_
  • 如果你想让默认列表中的配置覆盖主配置中的值,请在默认列表中将 _self_ 作为第一个项目插入。
# config.yaml
defaults:
  - db: mysql
  - _self_

db:
  user: root
# Result config: db.user from config.yaml
db:
  driver: mysql  # db/mysql.yaml
  pass: secret   # db/mysql.yaml 
  user: root     # config.yaml

# config.yaml
defaults:
  - _self_
  - db: mysql

db:
  user: root
# Result config: All values from db/mysql
db:
  driver: mysql # db/mysql.yaml
  pass: secret  # db/mysql.yaml
  user: omry    # db/mysql.yaml

有关详细信息,请参阅组合顺序

Hydra 1.0和Hydra 1.1之间的默认组合顺序发生了变化。

  • Hydra 1.0:默认列表中的配置正在覆盖主要配置。

  • Hydra 1.1:一个配置正在覆盖默认列表中的配置。

为了减少混乱,Hydra 1.1会发出警告,如果主配置文件中同时包含默认列表和配置值,并且在默认列表中没有指定_self_。

如果你根据期望的行为将 self 添加到默认列表中,警告就会消失。

非配置组默认值

有时候配置文件不属于任何配置组。你仍然可以默认加载它。以下是 some_file.yaml 的一个例子。

defaults:
  - some_file

不属于配置组的配置文件将始终被加载。它们不能被覆盖。更喜欢使用配置组。

有关默认列表的更多信息,请参阅参考手册/默认列表

把这一切放在一起

link: https://hydra.cc/docs/tutorials/basic/your_first_app/composition/

example: https://github.com/facebookresearch/hydra/blob/main/examples/tutorials/basic/your_first_hydra_app/6_composition

随着软件变得越来越复杂,我们使用模块化和组合来保持其可管理性。我们在配置方面也可以这样做。假设我们希望我们的工作示例支持多个数据库,每个数据库有多个模式,以及不同的用户界面。我们不会为每个数据库、模式和用户界面的组合写一个单独的类,所以我们也不应该写单独的配置。在配置方面,我们使用了与编写底层软件相同的解决方案:组合。

在 Hydra 中执行此操作时,我们首先添加一个模式和一个 UI 配置组:

# Directory layout
├── conf
│   ├── config.yaml
│   ├── db
│   │   ├── mysql.yaml
│   │   └── postgresql.yaml
│   ├── schema
│   │   ├── school.yaml
│   │   ├── support.yaml
│   │   └── warehouse.yaml
│   └── ui
│       ├── full.yaml
│       └── view.yaml
└── my_app.py

有了这些配置,我们已经有12种可能的组合。如果没有组合,我们就需要12个单独的配置。一个简单的更改,比如将db.user重命名为db.username,就需要编辑全部12个。这简直是维护的噩梦。

使用组合可以解决这个问题。与其创建 12 个不同的配置文件,完全指定每个配置,不如创建一个单一的配置文件,指定不同的配置维度,以及每个维度的默认值。

# config.yaml
defaults:
  - db: mysql
  - ui: full
  - schema: school

最终的配置是由MySQL数据库、完整的用户界面和学校模式(我们在这里首次看到)组成的:

$ python my_app.py
db:
  driver: mysql
  user: omry
  pass: secret
ui:
  windows:
    create_db: true
    view: true
schema:
  database: school
  tables:
  - name: students
    fields:
    - name: string
    - class: int
  - name: exams
    fields:
    - profession: string
    - time: data
    - class: int

请继续关注如何自动运行所有组合(多运行)。

总结

  • 添加每个新数据库、模式或用户界面只需要一个文件。
  • 每个配置组都可以在默认列表中指定一个默认值。
  • 可以通过在默认列表或命令行中从每个配置组中选择所需的选项来组成任何组合。

多运行

link: https://hydra.cc/docs/tutorials/basic/running_your_app/multi-run/

有时候你想用多个不同的配置运行同一个应用程序。
例如,对每个数据库的每个模式运行性能测试。

你可以通过命令行或配置文件来多运行 Hydra 应用程序:

配置 hydra.mode(Hydra 1.2 的新功能)

你可以以任何支持的方式配置 hydra.mode。合法的值是 RUN 和 MULTIRUN。以下是如何从命令行覆盖并遍历所有数据库和模式的组合。在你的输入配置中设置 hydra.mode=MULTIRUN 将使你的应用程序默认启用多运行。

$ python my_app.py hydra.mode=MULTIRUN db=mysql,postgresql schema=warehouse,support,school
[2021-01-20 17:25:03,317][HYDRA] Launching 6 jobs locally
[2021-01-20 17:25:03,318][HYDRA]        #0 : db=mysql schema=warehouse
[2021-01-20 17:25:03,458][HYDRA]        #1 : db=mysql schema=support
[2021-01-20 17:25:03,602][HYDRA]        #2 : db=mysql schema=school
[2021-01-20 17:25:03,755][HYDRA]        #3 : db=postgresql schema=warehouse
[2021-01-20 17:25:03,895][HYDRA]        #4 : db=postgresql schema=support
[2021-01-20 17:25:04,040][HYDRA]        #5 : db=postgresql schema=school

为简洁起见,省略了打印的配置。

从命令行使用 –multirun (-m) 选项

您也可以从命令行实现上述操作:

python my_app.py --multirun db=mysql,postgresql schema=warehouse,support,school

或者

python my_app.py -m db=mysql,postgresql schema=warehouse,support,school

你可以在运行时访问 hydra.mode 来确定应用程序是处于 RUN 模式还是 MULTIRUN 模式。在这里查看如何在运行时访问 Hydra 配置。

如果出现冲突(例如,hydra.mode=RUN,而应用程序是使用–multirun运行的),Hydra将在运行时确定hydra.mode的值。下表显示了在不同输入配置和命令行组合下,运行时hydra.mode的值。

No multirun commandline flag –multirun ( -m)
hydra.mode=RUN RunMode.RUN RunMode.MULTIRUN (with UserWarning)
hydra.mode=MULTIRUN RunMode.MULTIRUN RunMode.MULTIRUN
hydra.mode=None (default) RunMode.RUN RunMode.MULTIRUN

Hydra在作业启动时懒散地组合配置。如果你在启动作业/扫描后更改代码或配置,最终组合的配置可能会受到影响。

通过 hydra.sweeper.params 进行扫描

你也可以通过重写 hydra.sweeper.params 在输入配置中定义扫描。使用上述示例,相同的多运行可以通过以下配置来实现。

hydra:
  sweeper:
    params:
      db: mysql,postgresql
      schema: warehouse,support,school

输入配置和命令行覆盖的语法是一致的。如果在输入配置和命令行中都指定了扫描,则命令行扫描将优先于输入配置中定义的扫描。如果我们使用上述输入配置和新的命令行覆盖运行相同的应用程序:

$ python my_app.py -m db=mysql
[2021-01-20 17:25:03,317][HYDRA] Launching 3 jobs locally
[2021-01-20 17:25:03,318][HYDRA]        #0 : db=mysql schema=warehouse
[2021-01-20 17:25:03,458][HYDRA]        #1 : db=mysql schema=support
[2021-01-20 17:25:03,602][HYDRA]        #2 : db=mysql schema=school

以上配置方法目前仅适用于Hydra的默认BasicSweeper。对于其他扫描器,请查看相应的文档。

其他扫描类型

Hydra支持其他类型的扫描,例如:

x=range(1,10)                  # 1-9
schema=glob(*)                 # warehouse,support,school
schema=glob(*,exclude=w*)      # support,school

有关详细信息,请参阅扩展覆盖语法

Sweeper

Hydra 内置了默认的扫描逻辑。还有一些额外的扫描器可作为插件使用。例如,Ax Sweeper 可以自动寻找最佳的参数组合!

Launcher

默认情况下,Hydra在本地串行运行您的多运行作业。其他启动器可作为插件提供,用于并行启动和在不同集群上运行。例如,JobLib启动器可以使用多处理在您的本地机器上并行执行不同的参数组合。

输出/工作目录

link: https://hydra.cc/docs/tutorials/basic/running_your_app/working_directory/

Hydra可以解决每次运行时需要指定新输出目录的问题,方法是为每次运行创建一个目录,并在该输出目录中执行您的代码。默认情况下,此输出目录用于存储该运行的Hydra输出(配置、日志等)。

每次运行应用程序时,都会创建一个新的输出目录。您可以通过检查Hydra配置来获取输出目录的路径,如下面的示例所示。

# my_app.py
import os
from omegaconf import DictConfig
import hydra

@hydra.main(version_base=None)
def my_app(_cfg: DictConfig) -> None:
    print(f"Working directory : {os.getcwd()}")
    print(f"Output directory  : {hydra.core.hydra_config.HydraConfig.get().runtime.output_dir}")
$ 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

让我们看一下其中一个输出目录:

$ 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

结语

第三百二十五篇博文写完,开心!!!!

今天,也是充满希望的一天。


文章作者: LuYF-Lemon-love
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 LuYF-Lemon-love !
  目录