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

我们有 Hydra 输出目录(默认是 .hydra),以及应用程序日志文件。在 Hydra 输出目录中,我们有:

  • config.yaml:用户指定配置的转储文件
  • Hydra.yaml:Hydra配置的转储文件
  • overrides.yaml:使用的命令行覆盖设置

在主输出目录中:

  • my_app.log:为本次运行创建的日志文件

你可以使用自定义工作目录模式来配置输出目录的名称。

自动将当前工作目录更改为作业的输出目录

通过设置 hydra.job.chdir=True,您可以配置 Hydra 的 @hydra.main 装饰器在将控制权传递给用户的装饰主函数之前调用 os.chdir 来更改 Python 的工作目录。截至 Hydra v1.2,hydra.job.chdir 的默认值为 False。设置 hydra.job.chdir=True 可以方便地使用输出目录来存储应用程序的输出(例如,数据库转储文件)。

# 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

更改或禁用Hydra的输出子目录

你可以通过覆盖 hydra.output_subdir 来更改 .hydra 子目录的名称。你也可以通过将 hydra.output_subdir 设置为 null 来禁用它的创建。

访问应用程序中的原始工作目录

如果启用了 hydra.job.chdir=True,你仍然可以通过在 hydra.utils 中导入 get_original_cwd() 和 to_absolute_path() 来访问原始的工作目录:

from hydra.utils import get_original_cwd, to_absolute_path

@hydra.main(version_base=None)
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

可以自定义生成的工作目录的名称。

Logging

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

由于设置成本,人们通常不使用Python日志记录。Hydra通过为你配置Python日志记录来解决这个问题。

默认情况下,Hydra 会在控制台和自动工作目录中的日志文件中记录 INFO 级别的信息。

使用Hydra进行日志记录的示例:

import logging
from omegaconf import DictConfig
import hydra

# A logger for this file
log = logging.getLogger(__name__)

@hydra.main()
def my_app(_cfg: DictConfig) -> None:
    log.info("Info level message")
    log.debug("Debug level message")

if __name__ == "__main__":
    my_app()

$ python my_app.py
[2019-06-27 00:52:46,653][__main__][INFO] - Info level message

你可以通过覆盖 hydra.verbose 来在命令行启用 DEBUG 级别的日志记录。

Hydra.verbose可以是布尔值、字符串或列表:

例子:

  • Hydra.verbose=true:将所有日志记录器的日志级别设置为 DEBUG。
  • Hydra.verbose=NAME:将记录器 NAME 的日志级别设置为 DEBUG。相当于 import logging; logging.getLogger(NAME).setLevel(logging.DEBUG)。
  • Hydra.verbose=[NAME1,NAME2]:将记录器 NAME1 和 NAME2 的日志级别设置为 DEBUG。

示例输出:

$ python my_app.py hydra.verbose=[__main__,hydra]
[2019-09-29 13:06:00,880] - Installed Hydra Plugins
[2019-09-29 13:06:00,880] - ***********************
...
[2019-09-29 13:06:00,896][__main__][INFO] - Info level message
[2019-09-29 13:06:00,896][__main__][DEBUG] - Debug level message

你可以通过将 hydra/job_logging 设置为 disabled 来禁用日志输出。

$ python my_app.py hydra/job_logging=disabled
<NO OUTPUT>

如果你不想让Hydra配置日志记录,你也可以设置hydra/job_logging=none和hydra/hydra_logging=none。

可以自定义日志记录。

Debugging

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

Hydra提供了一些选项来提高可调试性。

打印配置

在命令行中添加 –cfg 或 -c 参数,即可在不运行函数的情况下打印应用程序的配置。

–cfg 选项接受一个参数,指定要打印配置文件的哪个部分:

  • job:你的配置
  • hydra:Hydra的配置
  • all:完整的配置,这是job和hydra的结合。
# A normal run:
$ python my_app.py
MySQL connecting to localhost with user=root and password=1234

# just show the config without running your function:
$ python my_app.py --cfg job
db:
  host: localhost
  user: root
  password: 1234

打印的配置包括通过命令行完成的任何修改:

$ python my_app.py db.host=10.0.0.1 --cfg job
db:
  host: 10.0.0.1
  user: root
  password: 1234

你可以使用 –package 或 -p 来显示配置的一部分:

python my_app.py --cfg hydra --package hydra.job
# @package hydra.job
name: my_app
config_name: config
...

默认情况下,配置插值不会被解析。要打印已解析的配置,请在使用 –cfg 标志的同时使用 –resolve 标志。

Info

–info 标志可以提供有关 Hydra 和您的应用程序的各个方面的信息:

  • –info all:默认行为,输出所有信息
  • –info config:打印有助于理解配置组成的信息:配置搜索路径、默认树、默认列表以及最终的配置。
  • –info defaults:打印最终默认设置列表
  • –info defaults-tree:打印默认设置树
  • –info plugins:显示已安装插件的信息

Tab completion

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

使用Tab键可以完成配置组、配置节点和值的输入。若要完成路径的输入,请以/或./开头。

结语

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

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


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