假设我们有以下文件:
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/middle/conf2.yaml
在 之上outer/conf1.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
,而后者又被 覆盖conf3
。那么,这是如何工作的呢?默认列表用于指定每个配置对象的组成顺序。在conf1
中,@_here_
package 关键字用于指定conf2
应该合并到当前配置组的信息,而不是包含在middle
包中。这记录在默认列表包关键字中。同样有趣的是@_global_
关键字。请注意,也可以写- middle@foo: conf2
而不是- middle@_here_: conf2
在默认列表中,在这种情况下,一个"foo"
键将出现在输出配置中,其内容为conf2
嵌套在其下。
就像在conf1.yaml
,conf2.yaml
中一样,使用默认列表来指定conf3
应该合并到conf2
而不是合并到名为的包中"inner"
(这将是默认行为,如此处所述
)。
- _self_
关键字在做什么?在默认列表中,此关键字允许控制当前配置与默认列表中指定的其他输入配置合并的顺序。例如,在conf2.yaml
默认列表中,写- _self_
在之前 - inner@_here_: conf3
确保conf3
将被合并到
conf2
,而不是相反。此处记录了此_self_
关键字
。如果未在默认列表中指定,则默认与当前配置合并的顺序为:- _self_
- 使用 Hydra 1.0:默认列表中的输入配置被合并到当前配置中
- 使用 Hydra 1.1:当前配置最后合并,覆盖默认列表中指定的其他配置
有关参考,请参阅从 1.0 版迁移到 1.1 版的这些迁移说明
。
使用包头来指定包。
在 yaml 文件顶部使用package 指令
可以获得类似的结果:
在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
这与在默认列表中使用关键字的方式非常相似@<PACKAGE>
(如上一节所述),并且在命令行中的结果是相同的。这两种方法之间的一个区别是包头适用于给定输入配置的所有内容,而
@<PACKAGE>
在默认列表中使用关键字可以更精细地控制哪些输入配置应放入哪些包中。
在默认列表中使用- _self_
关键字仍然是必要的,以确保合并以正确的顺序发生(请参阅上一节的注释_self_
)。
Hydra 对包头的处理在 Hydra 1.0 和 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 强大的命令行覆盖语法
来组成输出配置:
$ python my_app.py +middle@_global_=conf2 +middle/inner@_global_=conf3
a: 1
b: 3
c: 5
d: 6
_self_
这种方法不需要
使用关键字,因为它+<group>@<package>=<option>
具有附加到默认列表(这里是一个参考)而不是前置的效果。