fb-Hydra:如何让内部配置继承外部配置字段?

Mik*_*ike 5 python configuration config python-3.x fb-hydra

我正在尝试编写一个分层配置结构,以便内部目录中的配置文件继承外部目录中的配置文件。例如,在以下场景中

upper_config
|
|-middle_config
|   |
|   |-lower_config
Run Code Online (Sandbox Code Playgroud)

我希望middle_config能够继承和覆盖 的参数upper_config,并且lower_config能够继承和覆盖 和 的middle_config参数upper_config

一种解决方案是编写一个配置解析器,以便首先读取外部模块,然后在读取内部模块时覆盖外部模块中的字段。

但是,我想使用 Hydra(或其他一些工具,欢迎建议)来获得所有额外的便利。我已经从头到尾阅读了文档几次,虽然感觉配置组或包指令应该能够处理这个问题,但我无法完全将其拼凑在一起。

我相信这篇文章提出了一个非常相似的问题,但答案并没有启发我,而且提出问题的人似乎决定实现我上面描述的配置解析器的一个版本。

我希望有一种方法可以将内部配置文件的package指令更改为指向父配置,并以某种方式继承其默认列表。

Jas*_*sha 7

假设我们有以下文件:

my_app.py
outer/conf1.yaml
outer/middle/conf2.yaml
outer/middle/inner/conf3.yaml
Run Code Online (Sandbox Code Playgroud)

为了使事情具体化,以下是以下内容my_app.py

my_app.py
outer/conf1.yaml
outer/middle/conf2.yaml
outer/middle/inner/conf3.yaml
Run Code Online (Sandbox Code Playgroud)

总长DR

如果您的yaml文件仅包含纯数据(即没有默认列表或包指令),则在命令行动态组成配置的最灵活方法如下所示:

import hydra, omegaconf

@hydra.main(config_path="outer", config_name="conf1")
def my_app(cfg) -> None:
    print(omegaconf.OmegaConf.to_yaml(cfg))

my_app()
Run Code Online (Sandbox Code Playgroud)

这将合并outer/middle/conf2.yaml在 的顶部outer/conf1.yaml,然后合并outer/middle/inner/conf3.yaml在它的顶部。该@_global_关键字意味着输入配置应在顶层合并,而不是根据其包含目录的名称进行嵌套。

现在详细信息...

在回答这个问题时,我可能会使用 Hydra 1.1 的最新候选版本中的一些功能:

$ python my_app.py +middle@_global_=conf2 +middle/inner@_global_=conf3
Run Code Online (Sandbox Code Playgroud)

我们可以采取一些方法来用中间/内部配置覆盖我们的外部配置:

  • 使用默认列表来指定包。
  • 使用包头来指定包。
  • 使用命令行包覆盖来指定包(这是上面 TLDR 部分中使用的方法)

以下是每种方法的详细信息:

使用默认列表来指定包。

假设我们有以下内容: 在outer/conf1.yaml

>>> import hydra
>>> hydra.__version__
'1.1.0.rc1'
Run Code Online (Sandbox Code Playgroud)

outer/middle/conf1.yaml

defaults:
  - _self_
  - middle@_here_: conf2
a: 1
b: 2
Run Code Online (Sandbox Code Playgroud)

outer/middle/inner/conf3.yaml

defaults:
  - _self_
  - inner@_here_: conf3
b: 3
c: 4
Run Code Online (Sandbox Code Playgroud)

使用这些 yaml 文件,运行my_app.py会给出以下结果:

c: 5
d: 6
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,conf1被 覆盖conf2,而 又被 覆盖conf3。那么,这是如何运作的呢?默认列表用于指定每个配置对象的组成顺序。在 中conf1@_here_package 关键字用于指定应conf2合并当前配置组的信息,而不是包含在middle包中。这记录在默认列表包关键字中。同样有趣的是@_global_关键字。请注意,也可以直接写入- middle@foo: conf2而不是- middle@_here_: conf2在默认列表中,在这种情况下,输出配置中将出现一个键, 其下嵌套有"foo"的内容。conf2

正如在 中一样conf1.yamlconf2.yaml使用默认列表来指定conf3应合并到conf2而不是合并到名为的包中"inner"(这将是默认行为,如此处记录的 那样)。

关键字是- _self_做什么的?在默认列表中,此关键字允许控制当前配置与默认列表中指定的其他输入配置合并的顺序。例如,在conf2.yaml默认列表中,写- _self_ 在 before - inner@_here_: conf3可以确保conf3将被合并到 中 conf2,而不是相反。该_self_关键字记录 在此处。如果- _self_默认列表中未指定,则默认值与当前配置合并的顺序为:

  • 使用 Hydra 1.0:默认列表中的输入配置将合并到当前配置中
  • 使用 Hydra 1.1:当前配置最后合并,覆盖默认列表中指定的其他配置

作为参考,请参阅这些 从版本 1.0 迁移到 1.1 的迁移说明。

使用包头来指定包。

在 yaml 文件顶部使用package 指令可以实现类似的结果:

outer/conf1.yaml

$ python my_app.py
a: 1
b: 3
c: 5
d: 6
Run Code Online (Sandbox Code Playgroud)

outer/middle/conf2.yaml

defaults:
  - _self_
  - middle: conf2
a: 1
b: 2
Run Code Online (Sandbox Code Playgroud)

outer/middle/inner/conf3.yaml

# @package _global_
defaults:
  - _self_
  - inner: conf3
b: 3
c: 4
Run Code Online (Sandbox Code Playgroud)

# @package <PACKAGE>指令指定当前输入配置的内容应放置的位置。

# @package _global_
c: 5
d: 6
Run Code Online (Sandbox Code Playgroud)

这与在默认列表中使用关键字的工作方式非常相似@<PACKAGE>(如上一节所述),并且命令行的结果是相同的。这两种方法之间的一个区别是包头适用于给定输入配置的所有内容,而 @<PACKAGE>在默认列表中使用关键字可以更精细地控制哪些输入配置应放入哪些包中。

仍然需要使用- _self_默认列表中的关键字来确保合并以正确的顺序进行(请参阅上一节的注释_self_)。

Hydra 对包头的处理在 Hydra 1.0 和 1.1 中是不同的

使用命令行包覆盖来指定包

实现所需结果的最优雅、最灵活的方法是使用命令行包覆盖:如下outer/conf1.yaml所示:

$ python my_app.py
a: 1
b: 3
c: 5
d: 6
Run Code Online (Sandbox Code Playgroud)

因此outer/middle/conf2.yaml

a: 1
b: 2
Run Code Online (Sandbox Code Playgroud)

outer/middle/inner/conf3.yaml

b: 3
c: 4
Run Code Online (Sandbox Code Playgroud)

我们可以使用 Hydra 强大的命令行覆盖语法 来编写输出配置:

c: 5
d: 6
Run Code Online (Sandbox Code Playgroud)

_self_此方法不需要 使用关键字,因为它具有附加到默认列表(此处是参考)而不是前置+<group>@<package>=<option>的效果。