如何使用枚举重新加载 hydra 配置
How to reload hydra config with enumerations
是否有更好的方法从枚举实验中重新加载 hydra 配置?现在我像这样重新加载它:
initialize_config_dir(config_dir=exp_dir, ".hydra"), job_name=config_name)
cfg = compose(config_name, overrides=overrides)
print(cfg.enum)
>>> ENUM1
但ENUM1实际上是一个枚举,通常加载为
>>> <SomeEnumClass.ENUM1: 'enum1'>
我可以通过向实验 hydra 文件添加默认配置存储来解决此问题:
defaults:
- base_config_cs
现在结果是
initialize_config_dir(config_dir=exp_dir, ".hydra"), job_name=config_name)
cfg = compose(config_name, overrides=overrides)
print(cfg.enum)
>>> <SomeEnumClass.ENUM1: 'enum1'>
有没有更好的方法来做到这一点而不添加这个?或者我可以在 python 代码中添加默认值吗?
这是一个很好的问题——从以前的 Hydra 运行s 可靠地重新加载配置是一个可以改进的领域。
正如您所发现的,加载保存的文件 config.yaml
直接导致未类型化的 DictConfig 对象。
下面的解决方案涉及一个名为 reload.py
的脚本,该脚本使用默认列表创建一个配置节点,该列表加载架构 base_config_cs
和保存的文件 config.yaml
.
在这个 post 的最后,我还给出了一个简单的解决方案,涉及将 .hydra/overrides.yaml
加载到 re-run 配置组合过程。
假设您 运行 具有以下设置的 Hydra 作业:
# app.py
from dataclasses import dataclass
from enum import Enum
import hydra
from hydra.core.config_store import ConfigStore
from omegaconf import DictConfig
class SomeEnumClass(Enum):
ENUM1 = 1
ENUM2 = 2
@dataclass
class Schema:
enum: SomeEnumClass
x: int = 123
y: str = "abc"
def store_schema() -> None:
cs = ConfigStore.instance()
cs.store(name="base_config_cs", node=Schema)
@hydra.main(config_path=".", config_name="foo")
def app(cfg: DictConfig) -> None:
print(cfg)
if __name__ == "__main__":
store_schema()
app()
# foo.yaml
defaults:
- base_config_cs
- _self_
enum: ENUM1
x: 456
$ python app.py y=xyz
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
在 运行 宁 app.py
之后,存在一个目录 outputs/2022-02-05/06-42-42/.hydra
包含保存的文件 config.yaml
.
正如您在问题中正确指出的那样,要重新加载保存的配置,您必须将架构 base_config_cs
与 config.yaml
的内容合并。这是实现该目的的模式:
# reload.py
import os
from hydra import compose, initialize_config_dir
from hydra.core.config_store import ConfigStore
from app import store_schema
config_name = "config"
exp_dir = os.path.abspath("outputs/2022-02-05/07-19-56")
saved_cfg_dir = os.path.join(exp_dir, ".hydra")
assert os.path.exists(f"{saved_cfg_dir}/{config_name}.yaml")
store_schema() # stores `base_config_cs`
cs = ConfigStore.instance()
cs.store(
name="reload_conf",
node={
"defaults": [
"base_config_cs",
config_name,
]
},
)
with initialize_config_dir(config_dir=saved_cfg_dir):
cfg = compose("reload_conf")
print(cfg)
$ python reload.py
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
在上面的python文件reload.py
中,我们在ConfigStore中存储了一个名为reload_conf
的节点。以这种方式存储 reload_conf
相当于创建一个名为 reload_conf.yaml
的文件,Hydra 在配置搜索路径上可以发现该文件。此 reload_conf
节点有一个默认列表,可加载架构 base_config_cs
和 config
。为此,必须满足以下两个条件:
- 架构
base_config_cs
必须存储在 ConfigStore 中。这是通过调用我们从 app.py
. 导入的 store_schema
函数来完成的
- 名称由变量
config_name
指定的配置节点,即本例中的 config.yaml
,必须可被 Hydra 发现(此处通过调用 initialize_config_dir
处理) .
请注意,在 foo.yaml
中,我们有一个默认列表 ["base_config_cs", "_self_"]
,它在加载 foo
的内容 _self_
之前加载架构 base_config_cs
。为了让 reload_conf
以相同的合并顺序重建应用程序的配置,base_config_cs
应该在属于 reload_conf
.
的默认列表中出现在 config_name
之前
通过从 foo.yaml
中删除默认列表并使用 cs.store
确保在应用程序和重新加载脚本中使用相同的默认列表
# app2.py
from dataclasses import dataclass
from enum import Enum
from typing import Any, List
import hydra
from hydra.core.config_store import ConfigStore
from omegaconf import MISSING, DictConfig
class SomeEnumClass(Enum):
ENUM1 = 1
ENUM2 = 2
@dataclass
class RootConfig:
defaults: List[Any] = MISSING
enum: SomeEnumClass = MISSING
x: int = 123
y: str = "abc"
def store_root_config(primary_config_name: str) -> None:
cs = ConfigStore.instance()
# defaults list defined here:
cs.store(
name="root_config", node=RootConfig(defaults=["_self_", primary_config_name])
)
@hydra.main(config_path=".", config_name="root_config")
def app(cfg: DictConfig) -> None:
print(cfg)
if __name__ == "__main__":
store_root_config("foo2")
app()
# foo2.yaml (note NO DEFAULTS LIST)
enum: ENUM1
x: 456
$ python app2.py hydra.job.chdir=false y=xyz
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
# reload2.py
import os
from hydra import compose, initialize_config_dir
from hydra.core.config_store import ConfigStore
from app2 import store_root_config
config_name = "config"
exp_dir = os.path.abspath("outputs/2022-02-05/07-45-43")
saved_cfg_dir = os.path.join(exp_dir, ".hydra")
assert os.path.exists(f"{saved_cfg_dir}/{config_name}.yaml")
store_root_config("config")
with initialize_config_dir(config_dir=saved_cfg_dir):
cfg = compose("root_config")
print(cfg)
$ python reload2.py
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
一种更简单的替代方法是使用 .hydra/overrides.yaml
根据最初传递给 Hydra 的覆盖重构应用的配置:
# reload3.py
import os
import yaml
from hydra import compose, initialize
from app import store_schema
config_name = "config"
exp_dir = os.path.abspath("outputs/2022-02-05/07-19-56")
saved_cfg_dir = os.path.join(exp_dir, ".hydra")
overrides_path = f"{saved_cfg_dir}/overrides.yaml"
assert os.path.exists(overrides_path)
overrides = yaml.unsafe_load(open(overrides_path, "r"))
print(f"{overrides=}")
store_schema()
with initialize(config_path="."):
cfg = compose("foo", overrides=overrides)
print(cfg)
$ python reload3.py
overrides=['y=xyz']
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
这种方法有其缺点:如果您的应用程序的配置涉及某些 non-hermetic 操作,例如查询时间戳(例如通过 Hydra 的 now
resolver) or looking up an environment variable (e.g. via the oc.env
解析器),则由 reload.py
组成的配置可能是与 app.py
.
中加载的原始版本不同
是否有更好的方法从枚举实验中重新加载 hydra 配置?现在我像这样重新加载它:
initialize_config_dir(config_dir=exp_dir, ".hydra"), job_name=config_name)
cfg = compose(config_name, overrides=overrides)
print(cfg.enum)
>>> ENUM1
但ENUM1实际上是一个枚举,通常加载为
>>> <SomeEnumClass.ENUM1: 'enum1'>
我可以通过向实验 hydra 文件添加默认配置存储来解决此问题:
defaults:
- base_config_cs
现在结果是
initialize_config_dir(config_dir=exp_dir, ".hydra"), job_name=config_name)
cfg = compose(config_name, overrides=overrides)
print(cfg.enum)
>>> <SomeEnumClass.ENUM1: 'enum1'>
有没有更好的方法来做到这一点而不添加这个?或者我可以在 python 代码中添加默认值吗?
这是一个很好的问题——从以前的 Hydra 运行s 可靠地重新加载配置是一个可以改进的领域。
正如您所发现的,加载保存的文件 config.yaml
直接导致未类型化的 DictConfig 对象。
下面的解决方案涉及一个名为 reload.py
的脚本,该脚本使用默认列表创建一个配置节点,该列表加载架构 base_config_cs
和保存的文件 config.yaml
.
在这个 post 的最后,我还给出了一个简单的解决方案,涉及将 .hydra/overrides.yaml
加载到 re-run 配置组合过程。
假设您 运行 具有以下设置的 Hydra 作业:
# app.py
from dataclasses import dataclass
from enum import Enum
import hydra
from hydra.core.config_store import ConfigStore
from omegaconf import DictConfig
class SomeEnumClass(Enum):
ENUM1 = 1
ENUM2 = 2
@dataclass
class Schema:
enum: SomeEnumClass
x: int = 123
y: str = "abc"
def store_schema() -> None:
cs = ConfigStore.instance()
cs.store(name="base_config_cs", node=Schema)
@hydra.main(config_path=".", config_name="foo")
def app(cfg: DictConfig) -> None:
print(cfg)
if __name__ == "__main__":
store_schema()
app()
# foo.yaml
defaults:
- base_config_cs
- _self_
enum: ENUM1
x: 456
$ python app.py y=xyz
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
在 运行 宁 app.py
之后,存在一个目录 outputs/2022-02-05/06-42-42/.hydra
包含保存的文件 config.yaml
.
正如您在问题中正确指出的那样,要重新加载保存的配置,您必须将架构 base_config_cs
与 config.yaml
的内容合并。这是实现该目的的模式:
# reload.py
import os
from hydra import compose, initialize_config_dir
from hydra.core.config_store import ConfigStore
from app import store_schema
config_name = "config"
exp_dir = os.path.abspath("outputs/2022-02-05/07-19-56")
saved_cfg_dir = os.path.join(exp_dir, ".hydra")
assert os.path.exists(f"{saved_cfg_dir}/{config_name}.yaml")
store_schema() # stores `base_config_cs`
cs = ConfigStore.instance()
cs.store(
name="reload_conf",
node={
"defaults": [
"base_config_cs",
config_name,
]
},
)
with initialize_config_dir(config_dir=saved_cfg_dir):
cfg = compose("reload_conf")
print(cfg)
$ python reload.py
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
在上面的python文件reload.py
中,我们在ConfigStore中存储了一个名为reload_conf
的节点。以这种方式存储 reload_conf
相当于创建一个名为 reload_conf.yaml
的文件,Hydra 在配置搜索路径上可以发现该文件。此 reload_conf
节点有一个默认列表,可加载架构 base_config_cs
和 config
。为此,必须满足以下两个条件:
- 架构
base_config_cs
必须存储在 ConfigStore 中。这是通过调用我们从app.py
. 导入的 - 名称由变量
config_name
指定的配置节点,即本例中的config.yaml
,必须可被 Hydra 发现(此处通过调用initialize_config_dir
处理) .
store_schema
函数来完成的
请注意,在 foo.yaml
中,我们有一个默认列表 ["base_config_cs", "_self_"]
,它在加载 foo
的内容 _self_
之前加载架构 base_config_cs
。为了让 reload_conf
以相同的合并顺序重建应用程序的配置,base_config_cs
应该在属于 reload_conf
.
config_name
之前
通过从 foo.yaml
中删除默认列表并使用 cs.store
确保在应用程序和重新加载脚本中使用相同的默认列表
# app2.py
from dataclasses import dataclass
from enum import Enum
from typing import Any, List
import hydra
from hydra.core.config_store import ConfigStore
from omegaconf import MISSING, DictConfig
class SomeEnumClass(Enum):
ENUM1 = 1
ENUM2 = 2
@dataclass
class RootConfig:
defaults: List[Any] = MISSING
enum: SomeEnumClass = MISSING
x: int = 123
y: str = "abc"
def store_root_config(primary_config_name: str) -> None:
cs = ConfigStore.instance()
# defaults list defined here:
cs.store(
name="root_config", node=RootConfig(defaults=["_self_", primary_config_name])
)
@hydra.main(config_path=".", config_name="root_config")
def app(cfg: DictConfig) -> None:
print(cfg)
if __name__ == "__main__":
store_root_config("foo2")
app()
# foo2.yaml (note NO DEFAULTS LIST)
enum: ENUM1
x: 456
$ python app2.py hydra.job.chdir=false y=xyz
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
# reload2.py
import os
from hydra import compose, initialize_config_dir
from hydra.core.config_store import ConfigStore
from app2 import store_root_config
config_name = "config"
exp_dir = os.path.abspath("outputs/2022-02-05/07-45-43")
saved_cfg_dir = os.path.join(exp_dir, ".hydra")
assert os.path.exists(f"{saved_cfg_dir}/{config_name}.yaml")
store_root_config("config")
with initialize_config_dir(config_dir=saved_cfg_dir):
cfg = compose("root_config")
print(cfg)
$ python reload2.py
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
一种更简单的替代方法是使用 .hydra/overrides.yaml
根据最初传递给 Hydra 的覆盖重构应用的配置:
# reload3.py
import os
import yaml
from hydra import compose, initialize
from app import store_schema
config_name = "config"
exp_dir = os.path.abspath("outputs/2022-02-05/07-19-56")
saved_cfg_dir = os.path.join(exp_dir, ".hydra")
overrides_path = f"{saved_cfg_dir}/overrides.yaml"
assert os.path.exists(overrides_path)
overrides = yaml.unsafe_load(open(overrides_path, "r"))
print(f"{overrides=}")
store_schema()
with initialize(config_path="."):
cfg = compose("foo", overrides=overrides)
print(cfg)
$ python reload3.py
overrides=['y=xyz']
{'enum': <SomeEnumClass.ENUM1: 1>, 'x': 456, 'y': 'xyz'}
这种方法有其缺点:如果您的应用程序的配置涉及某些 non-hermetic 操作,例如查询时间戳(例如通过 Hydra 的 now
resolver) or looking up an environment variable (e.g. via the oc.env
解析器),则由 reload.py
组成的配置可能是与 app.py
.