fb-hyda:如何获取内部配置继承外部配置字段?

问题描述

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

upper_config
|
|-middle_config
|   |
|   |-lower_config

我希望middle_config能够继承&;覆盖upper_config的参数,lower_config能够继承&;覆盖middle_configupper_config的参数。

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

但是,我想使用Hydra(或其他一些工具,欢迎建议)来获得所有增加的便利。我从头到尾读了几遍文档,虽然感觉配置组或包指令应该能够处理这个问题,但我不能把它们拼凑在一起。

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

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


解决方案

假设我们有以下文件:

my_app.py
outer/conf1.yaml
outer/middle/conf2.yaml
outer/middle/inner/conf3.yaml

为了让事情具体化,以下是my_app.py

的内容
import hydra, omegaconf

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

my_app()

TLDR

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

$ python my_app.py +middle@_global_=conf2 +middle/inner@_global_=conf3
这将在outer/conf1.yaml之上合并outer/middle/conf2.yaml,然后在此之上合并outer/middle/inner/conf3.yaml@_global_关键字表示输入配置应该在顶层进行合并,而不是根据其包含目录的名称进行嵌套。

立即了解详细信息.

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

>>> import hydra
>>> hydra.__version__
'1.1.0.rc1'

有几种方法可以用中间/内部配置覆盖外部配置:

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

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

使用"默认值"列表指定包。

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

defaults:
  - _self_
  - middle@_here_: conf2
a: 1
b: 2

outer/middle/conf1.yaml中:

defaults:
  - _self_
  - inner@_here_: conf3
b: 3
c: 4

outer/middle/inner/conf3.yaml中:

c: 5
d: 6

使用这些YAML文件,运行my_app.py会得到以下结果:

$ python my_app.py
a: 1
b: 3
c: 5
d: 6
如您所见,conf1正在被conf2覆盖,而conf2又被 被conf3覆盖。那么,这是怎么运作的呢?defaults list用于指定每个配置对象的顺序 是作曲的。在conf1中,使用@_here_Package关键字指定 应将conf2合并到当前配置组中,而不是 包括在middle包中。这在默认列表包中进行了记录 关键字。 同样令人感兴趣的是@_global_关键字。请注意,一个人也可以 在默认列表中写入- middle@foo: conf2而不是- middle@_here_: conf2,在这种情况下 "foo"键将出现在输出配置中,其内容为conf2 嵌套在它下面。

conf1.yaml中一样,conf2.yaml使用默认列表指定 conf3应该合并到conf2中,而不是合并到 名为"inner"的软件包(按照原样,这将是默认行为 记录在案 here)。

- _self_关键字的作用是什么? 在默认列表中,此关键字允许控制 当前配置与默认值中指定的其他输入配置合并 列表。例如,在conf2.yaml默认列表中,写入- _self_ 之前的- inner@_here_: conf3确保conf3将合并到 conf2,而不是反过来。此_self_关键字已记录 here。如果- _self_未在 默认设置列表,然后显示默认设置与当前 配置为:

  • 使用Hydra 1.0:从 默认列表将合并到当前配置
  • 使用Hydra 1.1:最后合并当前配置,覆盖默认列表中指定的其他配置
有关参考,请参阅这些迁移 说明 用于从1.0版迁移到1.1版。

使用包头指定包。

使用包 指令 在YAML文件的顶部可以获得类似的结果:

outer/conf1.yaml中:

defaults:
  - _self_
  - middle: conf2
a: 1
b: 2

outer/middle/conf2.yaml中:

# @package _global_
defaults:
  - _self_
  - inner: conf3
b: 3
c: 4

位于outer/middle/inner/conf3.yaml

# @package _global_
c: 5
d: 6
# @package <PACKAGE>指令指定 应放置当前输入配置。

$ python my_app.py
a: 1
b: 3
c: 5
d: 6
这与在DEFAULTS中使用@<PACKAGE>关键字的工作方式大致相同 列表(如上一节中详细描述的),命令行中的结果为 一模一样。这两种方法之间的一个区别是包头 应用于给定输入配置的所有内容,而使用 @<PACKAGE>默认列表中的关键字提供了更细粒度的信息 控制应将哪些输入配置放入哪些包。

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

Hydra对包头的处理方式在Hydra1.0和href="https://hydra.cc/docs/next/upgrades/0.11_to_1.0/adding_a_package_directive"中有所不同 1.1。

使用命令行包替代指定包

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

a: 1
b: 2

outer/middle/conf2.yaml因此:

b: 3
c: 4

outer/middle/inner/conf3.yaml

c: 5
d: 6
我们可以使用Hydra强大的命令行override syntax 要编写输出配置,请执行以下操作:

$ python my_app.py +middle@_global_=conf2 +middle/inner@_global_=conf3
a: 1
b: 3
c: 5
d: 6
此方法不需要使用_self_关键字,因为 +<group>@<package>=<option>的效果是将追加到默认值 列表(here是 引用),而不是前置。

相关文章