[python] 配置管理框架Hydra使用指北
1 基础教程1.1 快速入门简单示例以下代码是一个简单的Hydra应用示例它会打印出配置信息其中my_app函数是编写业务逻辑的入口。from omegaconf import DictConfig, OmegaConf import hydra hydra.main(version_baseNone) def my_app(cfg: DictConfig) - None: print(OmegaConf.to_yaml(cfg)) if __name__ __main__: my_app()如果你直接执行这段代码没有任何命令行参数程序会输出一个空的配置对象{}这是因为当运行my_app.py时hydra.main装饰器会自动拦截对my_app()的调用。此时Hydra会初始化一个空的DictConfig对象类似于Python字典并将其作为参数cfg传递给函数。由于当前配置为空OmegaConf.to_yaml(cfg)将其转换为YAML格式后仅输出一个空对象。OmegaConf是Hydra的底层配置引擎Hydra基于OmegaConf实现上层的复杂应用配置与运行管理且OmegaConf可独立使用。此外默认情况下Hydra会创建以下目录结构以追踪和管理程序的运行结果outputs/ ├── yyyy-mm-dd/ # 按日期分组 │ └── hh-mm-ss/ # 按时间精确到秒 │ └── .hydra/ # 保存本次运行的配置 │ ├── config.yaml # 完整的配置 │ ├── hydra.yaml # Hydra 自身的配置 │ └── overrides.yaml # 命令行覆盖的参数 │ └── my_app.log # 日志文件如果配置了日志 │ └── 其他输出文件 # 你的程序生成的文件可以通过以下方式为配置添加内容通过命令行添加# 不支持直接在 keyvalue 语法中传入非 ASCII 字符 python my_app.py namezhangsan age25输出name: zhangsan age: 25创建配置文件创建一个config.yaml文件然后运行python my_app.py --config-path. --config-nameconfig在代码中设置默认配置可以修改代码为hydra.main装饰器添加配置参数from omegaconf import DictConfig, OmegaConf import hydra hydra.main(version_baseNone, config_path., config_nameconfig) def my_app(cfg): print(OmegaConf.to_yaml(cfg)) if __name__ __main__: my_app()可以通过命令行覆盖已加载配置中的值但是注意无需添加前缀python my_app.py namelisi使用前缀可实现若配置中已存在该参数则覆盖若不存在则新增python my_app.py namewangwu password1234要注意Hydra通过命令行修改配置时仅会覆盖或新增程序运行时内存中的配置数据不会改动磁盘上的原始配置文件重启程序后仍会配置加载文件的原始配置。配置对象使用通过Hydra加载配置后可通过属性或字典式访问或修改已有的配置项访问不存在的配置项时会抛出异常from omegaconf import DictConfig, OmegaConf import hydra hydra.main(version_baseNone, config_path., config_nameconfig) def my_app(cfg: DictConfig): # 属性式访问配置值 assert cfg.name 张三 # 字典式访问配置值 assert cfg[age] 25 # 修改已有配置值 cfg.name 李四 cfg[age] 30 assert cfg.name 李四 assert cfg[age] 30 # 访问缺失值会抛出异常 try: cfg.birth_year except Exception as e: print(error !!) print(e) if __name__ __main__: my_app()之所以不允许访问不存在的配置键仅能操作已有配置键是因为Hydra默认启用了struct模式以严格结构化配置。如需新增或修改配置可先关闭严格模式允许动态新增键。但如果嵌套层级也未提前声明则需要先创建空嵌套再为其添加子项from omegaconf import DictConfig, OmegaConf import hydra import os hydra.main(version_baseNone, config_path., config_nameconfig) def my_app(cfg: DictConfig): # 关闭struct模式允许新增配置键 OmegaConf.set_struct(cfg, False) # 同一级新增配置 cfg.birth_year 1995 cfg[hobby] [篮球, 编程] # 无法直接给不存在的嵌套层级链式赋值 # cfg.address.city 北京 # 需要先创建嵌套层级再赋值子键 cfg.address OmegaConf.create({}) # 显式创建空的嵌套 cfg.address.city 北京 cfg.address[district] 朝阳区 # 验证写入结果 assert cfg.birth_year 1995 assert cfg.address.district 朝阳区 print(配置写入验证通过) if __name__ __main__: my_app()对配置文件进行分组若希望分别使用CNN和Transformer模型对数据集进行训练基准测试可通过配置组Config Group实现这一需求。配置组是一个带有名称的分组包含一组有效的配置项。若选择不存在的配置项系统会生成错误提示并列出所有有效的配置项。创建配置组时需先新建一个目录例如model用于存放各模型配置项对应的文件。由于预计会创建多个配置组建议提前将所有配置文件统一移至conf目录下管理。目录结构如下├─ conf │ └─ model │ ├─ cnn.yaml │ └─ transformer.yaml └── my_app.pymodel/cnn.yamlbackbone: resnet50 learning_rate: 0.001 batch_size: 32 epochs: 20 dropout: 0.2model/transformer.yamlbackbone: vit_base learning_rate: 0.0001 batch_size: 16 epochs: 30 attention_heads: 12所有配置文件已统一存放至conf目录需通过config_path参数告知Hydra该目录位置并在代码中指定待加载的配置文件名config_name。若未明确指定具体配置文件名Hydra无法自动推断加载目标最终会输出空配置:from omegaconf import DictConfig, OmegaConf import hydra hydra.main(version_baseNone, config_pathconf, config_namemodel/cnn) def my_app(cfg: DictConfig) - None: print(OmegaConf.to_yaml(cfg)) if __name__ __main__: my_app()也可以通过命令行从配置组中选择特定配置项命令行使用分组名配置项的格式例如python my_app.py modeltransformer与常规用法一致仍可覆盖最终配置中的单个参数值python my_app.py modeltransformer model.epochs40多文件处理可以生成一个配置文件在配置文件中用defaults参数添加默认配置列表。该列表用于指定Hydra组合最终配置对象的规则按照约定它需作为配置文件的首个配置项。如下所示defaults: - model: cnn然后这个配置文件可以命名为任意名字如conf文件夹下的config.yaml这样运行会默认加载model对应的文件配置from omegaconf import DictConfig, OmegaConf import hydra hydra.main(version_baseNone, config_pathconf, config_nameconfig) def my_app(cfg: DictConfig) - None: print(OmegaConf.to_yaml(cfg)) if __name__ __main__: my_app()默认配置列表支持叠加多个深度学习相关配置项。若同一配置组存在两个配置文件系统会将这两个配置文件合并为一个新字典当配置中出现相同键名时后加载的配置项会覆盖先加载的配置项。示例默认配置如下defaults: - model: - cnn - transformer若在配置文件夹conf下的dataset目录中存在如下配置文件model/cifar10.yamlname: CIFAR-10 path: ./data/cifar10 num_classes: 10 learning_rate: 0.0001 augmentation: true # 是否开启数据增强当默认配置文件conf/config.yaml中默认配置列表的内容如下defaults: - model: cnn - dataset: cifar10由于model和dataset分属不同的配置组Hydra会将这两个配置组的默认配置进行独立合并。最终生成的完整配置结构中会包含model和dataset两个一级配置项各自保留对应配置组的完整参数model: # 此处为conf/model/cnn.yaml中的配置内容 dataset: # 此处为conf/dataset/cifar10.yaml中的配置内容即使设置了默认配置仍可手动确定参数并覆盖部分配置参数python my_app.py modelcnn model.epochs30在配置项前添加~前缀可从默认配置列表中移除该默认项python my_app.py ~model主配置的组合顺序主配置文件中可同时包含配置参数和默认配置列表。在此情况下若需调整默认配置列表与主配置之间的覆盖关系可通过添加_self_关键字实现将_self_置于默认配置列表末尾则主配置参数将覆盖默认配置列表中的对应项若将其置于列表开头则默认配置列表中的参数将覆盖主配置中的内容。需注意的是从Hydra 1.1版本开始默认行为为主配置覆盖默认配置列表中的配置而在此之前的版本中默认配置列表会覆盖主配置的参数。例如默认配置文件config.yaml内容如下会进行数据覆盖也就是说配置文件里dataset部分会覆盖默认配置中的同名部分defaults: - model: cnn - dataset: cifar10 - _self_ dataset: name: my_dataset version: 1.01.2 整合应用随着软件复杂度的不断提升我们会采用模块化与组合化的设计思路来保证其可维护性。这种思路同样适用于配置文件的管理。假设我们需要为示例程序配置多类深度学习模型支持且每个模型对应多种训练策略、搭配不同的数据预处理流程。使用Hydra时既不必为模型、策略、预处理流程的各类组合编写独立类也无需为其单独编写配置文件。我们可以借鉴底层软件开发的核心思路通过组合化配置来解决这一问题。多轮运行Multi-run对于使用多套配置运行同一应用程序的场景可以通过命令行或配置文件两种方式为Hydra应用启用多轮运行功能。该功能自Hydra 1.2版本起引入通过设置hydra.mode配置项实现。hydra.mode的合法取值包括RUN单次运行和MULTIRUN多轮运行。若在输入配置中将hydra.mode设为MULTIRUN应用程序将默认以多轮运行模式启动。例如默认配置文件为defaults: - model: cnn - dataset: cifar10多轮运行命令如下python my_app.py hydra.modeMULTIRUN modelcnn,transformer datasetcifar10只要参数值用逗号分隔就会被Hydra识别为多取值参数Hydra会把所有带多个取值的参数做笛卡尔积全组合Hydra会把每个参数的取值两两配对生成以下多个任务依次运行python my_app.py hydra.modeMULTIRUN modelcnn,transformer datasetcifar10 # 本地启动2个任务 #0 : modelcnn datasetcifar10 #1 : modeltransformer datasetcifar10该命令可以用命令行参数简化python my_app.py --multirun modelcnn,transformer datasetcifar10 # 或 python my_app.py -m modelcnn,transformer datasetcifar10注意Hydra会在任务启动时延迟组合配置。若在启动任务参数遍历后修改代码或配置文件最终组合生成的配置可能会受影响。也可以在输入配置中通过覆盖hydra.sweeper.params来定义参数遍历规则并通过mode设置运行模式。沿用上述示例以下配置可实现完全相同的多轮运行效果defaults: - model: cnn - dataset: cifar10 hydra: mode: MULTIRUN # 设置运行模式 sweeper: params: dataset: cifar10 model: transformer, cnn直接运行程序不使用任何附加参数结果如下$ python my_app.py # 本地启动2个任务 #0 : modeltransformer datasetcifar10 #1 : modelcnn datasetcifar101.3 信息管理输出目录Hydra能够解决每次运行程序时需要手动指定新输出目录的问题它会为每次运行自动创建一个专属目录并在该输出目录中执行代码。默认情况下每次运行应用程序时都会生成一个全新的输出目录。可以通过读取Hydra配置来获取本次运行该输出目录的路径示例如下from omegaconf import DictConfig, OmegaConf import hydra import os hydra.main(version_baseNone, config_pathconf, config_nameconfig) def my_app(_cfg: DictConfig) - None: print(f工作目录{os.getcwd()}) print(f输出目录{hydra.core.hydra_config.HydraConfig.get().runtime.output_dir}) if __name__ __main__: my_app()通过设置hydra.job.chdirTrue可以让Hydra的hydra.main装饰器在执行用户的主函数前调用os.chdir将Python工作目录切换到输出目录python my_app.py hydra.job.chdirTrue可以通过覆盖配置项hydra.output_subdir将设为null则会完全禁用该子目录的创建。日志由于标准logging模块配置较为复杂为实现常规的日志功能通常需要编写较多代码且配置过程不够简便。Hydra能够自动完成Python logging的配置从而有效解决这一问题。默认情况下Hydra会以INFO级别向控制台输出日志同时在当前工作目录自动生成日志文件留存记录。以下为使用Hydra进行日志记录的示例# hydra_log_demo.py import logging from omegaconf import DictConfig import hydra # 为本文件创建日志器 log logging.getLogger(__name__) hydra.main(version_baseNone) def my_app(_cfg: DictConfig) - None: log.info(Info 级别日志消息) log.debug(Debug 级别日志消息) if __name__ __main__: my_app()可通过在命令行中指定hydra.verbose配置项来启用DEBUG级别的日志输出。该配置项支持布尔值、字符串或列表类型的取值开启全部或指定日志器的DEBUG级别输出如下python hydra_log_demo.py hydra.verbosetrue若要将特定函数对应日志器的级别设为DEBUG可使用如下命令python hydra_log_demo.py hydra.verbose[__main__,my_custom_logger]其效果等同于代码import logging logging.getLogger(NAME).setLevel(logging.DEBUG)如果不希望Hydra自动配置日志系统可以将hydra/job_logging对应程序的日志和hydra/hydra_logging对应Hydra框架自身的日志均设为nonepython my_app.py hydra/job_loggingNone hydra/hydra_loggingNone调试功能Hydra提供多种配置选项可有效提升程序的可调试性。在命令行中使用--cfg或-c参数即可在不运行目标函数的情况下打印应用程序的配置信息。该参数需配合一个选项来指定打印的配置范围job打印业务代码的配置hydra打印hydra框架自身的配置all打印完整配置内容即业务配置与hydra配置的合集仅打印业务配置指令如下$ python my_app.py --cfg job若只展示配置中的某一子集可搭配参数--package或简写-p使用python my_app.py --cfg hydra --package hydra.job默认情况下配置中的插值表达式不会被解析。若需打印解析后的最终配置可在--cfg参数基础上额外添加--resolve参数。信息查询功能使用--info参数可查询Hydra框架及应用程序的各类相关信息--info all默认模式打印所有可用信息--info config打印配置组合相关的辅助信息包括配置搜索路径、默认配置树、默认配置列表及最终生效的配置内容--info defaults打印最终的默认配置列表--info defaults-tree打印默认配置树结构--info plugins打印已安装的插件信息2 结构化配置在复杂项目中配置文件常面临类型模糊、配置错误难排查、缺少静态校验等问题。例如字段类型不明确易引发运行时异常、多层级配置的结构一致性难以保障、协作时难以通过工具提前发现配置冲突。为此Hydra基于Python数据类dataclasses定义了配置结构与类型其核心价值在于提供运行时类型检查与静态类型检查双重保障。它支持基础类型int、str、bool、float、Enum 等、嵌套结构、容器类型List、Dict以及可选字段但也存在部分限制例如仅部分支持联合类型且不支持自定义方法。Hydra中结构化配置主要有两种使用模式均完整保留其核心功能直接作为配置使用替代配置文件适合快速入门作为配置模式schema使用用于校验现有配置文件适合大型或协作项目。本教程将按此顺序依次详解两种模式。2.1 Hydra代码配置在后续的教程中我们将使用ConfigStore类把数据类dataclasses注册为Hydra中的输入配置。ConfigStore是一个在内存中存储配置的单例singleton对象与它交互的核心API是下文将要介绍的store方法。class ConfigStore(metaclassSingleton): def store( self, name: str, node: Any, group: Optional[str] None, package: Optional[str] _group_, provider: Optional[str] None, ) - None: 将配置节点存储至配置仓库中 :param name: 配置名称 :param node: 配置节点支持 DictConfig、ListConfig、 结构化配置Structured configs甚至普通的 dict 和 list 类型 :param group: 配置分组子分组分隔符为 / 例如 hydra/launcher :param package: 配置节点的父级层级结构。 子节点分隔符为 .例如 foo.bar.baz :param provider: 提供该配置的模块/应用名称 有助于调试排查问题。 ...ConfigStore具备与YAML输入配置完全一致的功能除此之外还提供类型校验能力。它既可单独使用也可与YAML配合使用。基础用例假设我们有一个简单的应用程序且存在一个包含cnn选项的model配置分组from omegaconf import DictConfig, OmegaConf import hydra hydra.main(version_baseNone, config_pathconf, config_namemodel/cnn) def my_app(cfg: DictConfig) - None: print(OmegaConf.to_yaml(cfg)) if __name__ __main__: my_app()目录结构├─ conf │ └─ model │ └─ cnn.yaml └── my_app.pymodel/cnn.yamlbackbone: resnet50 learning_rate: 0.001 batch_size: 32 epochs: 20 dropout: 0.2如果现在想要新增一个transformer选项该怎么做我们可以直接新增model/transformer.yaml配置分组文件但这并非唯一方式也可以通过ConfigStore为Hydra新增model配置分组的transformer选项。要实现这个需求只需在上述代码文件中添加几行代码from dataclasses import dataclass import hydra from omegaconf import DictConfig, OmegaConf from hydra.core.config_store import ConfigStore dataclass class TransformerConfig: optimizer: str sgd lr: float 0.0005 hidden_dim: int 256 cs ConfigStore.instance() # 将名为transformer的配置类注册至model配置分组 # 注意出现实体文件会报错 cs.store(nametransformer, groupmodel, nodeTransformerConfig) hydra.main(version_baseNone, config_pathconf) def my_app(cfg: DictConfig) - None: print(OmegaConf.to_yaml(cfg)) if __name__ __main__: my_app()上述代码不会生成实际的物理配置文件它仅用于在内存中注册配置类。现在应用程序已经能够识别model配置组中的两个选项您可以通过以下命令运行程序来验证效果python my_app.py modelcnn或python my_app.py modeltransformer在深度学习实验中管理多个模型配置时我们还可以借助ConfigStore支持的三种注册方式灵活控制配置节点实现不同方案间的快速切换from dataclasses import dataclass import hydra from omegaconf import DictConfig, OmegaConf from hydra.core.config_store import ConfigStore dataclass class TransformerConfig: optimizer: str sgd lr: float 0.0005 hidden_dim: int 256 cs ConfigStore.instance() # 直接使用类类型 cs.store(nameconfig1, nodeTransformerConfig) # 使用类实例覆盖部分默认值 cs.store(nameconfig2, nodeTransformerConfig(optimizerrmsprop, lr0.002)) # 使用字典会失去运行时类型安全保障 cs.store(nameconfig3, node{optimizer: adam, lr: 0.003, hidden_dim: 256}) # 3. Hydra主函数加载并打印配置 hydra.main(version_baseNone, config_nameconfig1) # 默认加载config1 def main(cfg: DictConfig) - None: print(当前加载的配置内容) print(cfg) if __name__ __main__: main()配置组在Hydra框架中配置组是一种用于组织互斥但相关配置项的机制。以深度学习场景为例训练CNN与Transformer属于不同的模型配置它们都属于模型配置这一大类但一次训练只能选择其中一种这就是配置组的典型应用。# 导入必要的库 from dataclasses import dataclass from typing import Any import hydra from hydra.core.config_store import ConfigStore from omegaconf import OmegaConf # 1. 定义CNN模型的配置 dataclass # 装饰器将普通类转为结构化配置类自动生成初始化、比较等方法 class CNNConfig: CNN模型的训练配置包含该模型特有的所有参数 model_type: str cnn batch_size: int 32 learning_rate: float 0.001 # 2. 定义Transformer模型的配置 dataclass class TransformerConfig: Transformer模型的训练配置包含该模型特有的所有参数 model_type: str transformer batch_size: int 16 learning_rate: float 0.0001 num_heads: int 8 dataclass class Config: 整个训练程序的主配置类 # model字段用于接收配置组中选择的模型配置暂时标注为Any类型 model: Any # 1. 获取配置存储库的单例实例整个程序只有一个ConfigStore cs ConfigStore.instance() # 2. 注册主配置名称为config对应后续hydra.main的config_name cs.store(nameconfig, nodeConfig) # 3. 注册配置组组名是model包含两个选项 cs.store(groupmodel, namecnn, nodeCNNConfig) cs.store(groupmodel, nametransformer, nodeTransformerConfig) # hydra.main装饰器标记程序入口指定配置名称为config hydra.main(version_baseNone, config_nameconfig) def train_model(cfg: Config) - None: 深度学习模型训练的主函数 print( 当前使用的训练配置 ) print(OmegaConf.to_yaml(cfg)) # 程序启动入口 if __name__ __main__: train_model()代码运行直接输出为model: ?????? 表示该字段本应有值但目前处于缺失状态。由于我们未给模型配置组设置默认值因此必须通过命令行显式指定要使用的模型配置。注意命令中的是必需的因为模型配置组没有默认值在这里表示添加并覆盖该配置字段python my_app.py modelcnn在上面实现中model字段被标注为Any类型这虽然不会阻碍程序运行但却把配置对象当作一个缺乏类型信息的黑箱字典使得IDE无法提供智能提示静态类型检查也完全失效从而降低了代码的可维护性和长期可靠性。要解决这一问题解决方法是将不同模型配置之间的公共字段进行抽象创建一个BaseModelConfig基础配置类from dataclasses import dataclass from typing import Any import hydra from omegaconf import MISSING # 标记字段“无默认值” from hydra.core.config_store import ConfigStore from omegaconf import OmegaConf dataclass class BaseModelConfig: 所有深度学习模型的基础配置抽离公共字段 model_type: str MISSING # 模型类型无默认值必须由子类指定 batch_size: int 32 # 公共字段默认批次大小子类可重写 learning_rate: float 0.001 # 公共字段默认学习率子类可重写 # 1. 定义CNN模型的配置 dataclass # 装饰器将普通类转为结构化配置类自动生成初始化、比较等方法 class CNNConfig(BaseModelConfig): CNN模型的训练配置包含该模型特有的所有参数 model_type: str cnn batch_size: int 32 learning_rate: float 0.001 # 2. 定义Transformer模型的配置 dataclass class TransformerConfig(BaseModelConfig): Transformer模型的训练配置包含该模型特有的所有参数 model_type: str transformer batch_size: int 16 learning_rate: float 0.0001 num_heads: int 8 dataclass class Config: 整个训练程序的主配置类 # 不再是Any而是BaseModelConfig model: BaseModelConfig # 1. 获取配置存储库的单例实例整个程序只有一个ConfigStore cs ConfigStore.instance() # 2. 注册主配置名称为config对应后续hydra.main的config_name cs.store(nameconfig, nodeConfig) # 3. 注册配置组组名是model包含两个选项 cs.store(groupmodel, namecnn, nodeCNNConfig) cs.store(groupmodel, nametransformer, nodeTransformerConfig) # hydra.main装饰器标记程序入口指定配置名称为config hydra.main(version_baseNone, config_nameconfig) def train_model(cfg: Config) - None: 深度学习模型训练的主函数 print( 当前使用的训练配置 ) print(OmegaConf.to_yaml(cfg)) # 程序启动入口 if __name__ __main__: train_model()可以在主结构化配置中设置默认值方法与在config.yaml配置文件中定义相似。以下是一个深度学习模型配置的示例新增了默认配置列表使其默认加载modelcnn。只需在代码中添加默认列表并相应修改配置类即可from dataclasses import dataclass, field from typing import Any, List # 定义默认配置列表从配置组model中加载名为cnn的配置 defaults [ {model: cnn} # 设为 MISSING则可强制用户在命令行中指定该参数的值。 # {model: MISSING} ] dataclass class Config: 整个训练程序的主配置类 # 受dataclass限制此处需通过field定义默认配置列表默认加载cnn配置 defaults: List[Any] field(default_factorylambda: defaults) # Hydra会根据默认配置列表自动填充该字段类型为BaseModelConfig model: BaseModelConfig MISSING你也可以通过命令行覆盖默认配置指定使用Transformer模型注意不要号因为这是覆盖操作python my_app.py modeltransformer2.2 配置模式Hydra的结构化配置本质是用代码定义的结构化规则来管理配置。它除了可以用代码定义的结构化配置替代传统的YAML配置文件外还能将结构化配置作为配置规则模板Schema用于校验已有YAML配置文件是否符合规范。Schema能够在程序启动时校验配置的合法性提前拦截所有配置错误。这对于保障大型项目的稳定性至关重要尤其适合多人协作的场景可以有效避免配置错写、漏写字段等问题。同一配置组内的Schema校验以深度学习训练场景的模型配置为例下文将拆解如何通过预设的Schema模板校验同一配置分组下各配置文件的字段、类型等是否符合规范要求。给定如下配置目录结构conf/ ├── config.yaml # 主配置包含训练、模型、数据等 └── model # 模型配置组 ├── resnet.yaml # ResNet模型配置 └── transformer.yaml # Transformer模型配置需为上述每个配置文件添加结构化配置Schema核心方式是在YAML文件的defaults列表中声明要继承的Schema模板并将这些Schema以base_config、model/resnet.yaml、model/transformer.yaml为名注册至Hydra配置仓库。各配置文件的defaults列表配置如下config.yamldefaults: - base_config # 继承基础配置Schema - model: resnet # 默认使用ResNet模型配置 - _self_ # 自身配置覆盖默认值 # 自定义训练参数 train: batch_size: 32 lr: 0.001 debug: truemodel/resnet.yamldefaults: - base_resnet # 继承ResNet基础Schema # ResNet专属参数 layers: 50 pretrained: true num_classes: 1000model/transformer.yamldefaults: - base_transformer # 继承Transformer基础Schema # Transformer专属参数 num_heads: 8 num_layers: 6 hidden_dim: 512 max_seq_len: 512通过Python dataclass定义各种Schema规则并注册到Hydra配置仓库使YAML配置文件可关联到对应的校验规则from dataclasses import dataclass from omegaconf import OmegaConf, MISSING import hydra from hydra.core.config_store import ConfigStore # -------------------------- 基础Schema定义 -------------------------- dataclass class BaseModelConfig: 所有模型的基础配置Schema model_type: str MISSING # 必选字段无默认值 device: str cuda # 可选字段默认值cuda dropout: float MISSING # 必选字段无默认值 dataclass class ResNetConfig(BaseModelConfig): ResNet模型专属Schema继承基础模型配置 model_type: str resnet # 固定值标识模型类型 dropout: float 0.1 # 覆盖默认值 layers: int MISSING # ResNet专属必选字段 pretrained: bool MISSING num_classes: int MISSING dataclass class TransformerConfig(BaseModelConfig): Transformer模型专属Schema继承基础模型配置 model_type: str transformer # 固定值 dropout: float 0.1 # 覆盖默认值 num_heads: int MISSING # Transformer专属必选字段 num_layers: int MISSING hidden_dim: int MISSING max_seq_len: int MISSING dataclass class TrainConfig: 训练配置Schema batch_size: int MISSING lr: float MISSING epochs: int 10 # 默认训练10轮 dataclass class Config: 整体配置Schema model: BaseModelConfig MISSING # 模型配置必选 train: TrainConfig MISSING # 训练配置必选 debug: bool False # 调试模式可选 # -------------------------- 注册Schema到配置仓库 -------------------------- cs ConfigStore.instance() cs.store(namebase_config, nodeConfig) # 注册主配置Schema cs.store(groupmodel, namebase_resnet, nodeResNetConfig) # 注册ResNet Schema cs.store(groupmodel, namebase_transformer, nodeTransformerConfig) # 注册Transformer Schema # -------------------------- 主函数 -------------------------- hydra.main(version_baseNone, config_pathconf, config_nameconfig) def train_app(cfg: Config) - None: 深度学习训练入口打印最终配置 print(最终训练配置) print(OmegaConf.to_yaml(cfg)) if __name__ __main__: train_app()运行代码时Hydra会先加载YAML配置文件再通过关联的Schema完成合法性校验若配置存在错误会立即抛出异常# 正常运行使用默认ResNet配置 python my_app.py # 模拟配置错误layers字段应为int类型传入字符串触发校验失败 python my_app.py model.layersattention跨配置组的Schema校验在前文的模型训练场景中Schema都定义在主程序里。但实际开发中常会遇到第三方库提供标准化的Schema我们需要跨配置组引用这些Schema来校验自己的配置文件而非把所有Schema都写在主程序中。假设存在一个公共的optimizer_lib库该库预先定义了所有模型的标准Schema并注册在独立配置组中本地conf/optimizer配置组下的YAML文件如sgd.yaml、adam.yaml仅需编写YAML配置文件并关联这个外部库的Schema完成校验无需重复定义模型规则配置目录结构如下# 项目整体目录 ├── my_app.py # 主程序 ├── optimizer_lib.py # 独立的优化器Schema库 └── conf/ ├── config.yaml # 主配置 └── optimizer/ # 本地优化器配置组 ├── sgd.yaml └── adam.yamloptimizer_lib.py代码如下from dataclasses import dataclass from omegaconf import MISSING from hydra.core.config_store import ConfigStore # -------------------------- 优化器基础Schema -------------------------- dataclass class BaseOptimizerConfig: 所有优化器的基础Schema独立库定义 opt_type: str MISSING # 必选字段优化器类型 lr: float MISSING # 必选字段学习率 weight_decay: float 0.0 # 可选字段权重衰减默认0 # -------------------------- 具体优化器Schema -------------------------- dataclass class SGDConfig(BaseOptimizerConfig): SGD优化器专属Schema opt_type: str sgd # 固定标识 momentum: float MISSING # SGD专属必选字段 nesterov: bool False # 可选字段是否使用Nesterov动量 dataclass class AdamConfig(BaseOptimizerConfig): Adam优化器专属Schema opt_type: str adam # 固定标识 betas: tuple[float, float] (0.9, 0.999) # 可选字段beta参数 eps: float MISSING # Adam专属必选字段 # -------------------------- 注册跨配置组的Schema -------------------------- def register_optimizer_configs() - None: cs ConfigStore.instance() # 注册到独立分组optimizer_lib/optimizer cs.store( groupoptimizer_lib/optimizer, namesgd, nodeSGDConfig ) cs.store( groupoptimizer_lib/optimizer, nameadam, nodeAdamConfig )主程序my_app.py中定义整体配置Schema并调用optimizer_lib的注册函数将跨配置组的Schema纳入Hydra配置仓库from dataclasses import dataclass from omegaconf import MISSING, OmegaConf import hydra from hydra.core.config_store import ConfigStore import optimizer_lib # 导入独立的优化器库 # -------------------------- 整体配置Schema -------------------------- dataclass class TrainConfig: 训练配置Schema batch_size: int 32 epochs: int 10 dataclass class Config: 主配置Schema optimizer: optimizer_lib.BaseOptimizerConfig MISSING # 引用独立库的Schema train: TrainConfig MISSING debug: bool False # -------------------------- 注册本地Schema并加载跨组Schema -------------------------- cs ConfigStore.instance() cs.store(namebase_config, nodeConfig) # 注册主配置Schema optimizer_lib.register_optimizer_configs() # 注册跨配置组的优化器Schema # -------------------------- 主函数 -------------------------- hydra.main(version_baseNone, config_pathconf, config_nameconfig) def train_app(cfg: Config) - None: 训练入口打印最终配置并触发Schema校验 print(最终训练配置含跨组优化器配置) print(OmegaConf.to_yaml(cfg)) if __name__ __main__: train_app()本地conf/optimizer下的YAML文件需要通过绝对路径引用optimizer_lib中的Schema并通过_here_指定包路径确保Schema的校验作用域与当前配置一致。conf/optimizer/sgd.yamldefaults: - /optimizer_lib/optimizer/sgd_here_ # 绝对路径引用跨组Schema_here_统一包作用域 # SGD专属配置需符合SGDConfig的Schema规则 lr: 0.01 momentum: 0.9 weight_decay: 0.0001conf/optimizer/adam.yamldefaults: - /optimizer_lib/optimizer/adam_here_ # 绝对路径引用跨组Schema - _self_ # 自身配置覆盖Schema默认值组合顺序Schema先加载自身配置后覆盖 # Adam专属配置需符合AdamConfig的Schema规则 lr: 0.001 betas: (0.9, 0.999) eps: 1e-08 weight_decay: 0.0005主配置conf/config.yamldefaults: - base_config # 主配置Schema - optimizer: sgd # 默认使用SGD优化器配置 - _self_ # 自定义训练参数 train: batch_size: 64 epochs: 20 debug: true运行代码时Hydra会先加载第三方库的Schema再校验本地YAML配置# 正常运行SGD配置符合Schema规则 python my_app.py # 配置错误momentum应为float传入字符串触发校验失败 python my_app.py optimizer.momentumhigh # 配置错误Adam必选字段eps缺失启动时直接报错 python my_app.py optimizeradam optimizer.epsnone3 参考
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2444819.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!