Python通过Hydra来简化配置文件
Hydra 是一个开源 Python 框架,可简化研究和其他复杂应用程序的开发。关键特性是能够通过 组合动态创建分层配置,并通过配置文件和命令行覆盖它 。在机器学习项目中,我们有多个版本的模型参数和架构,并且一些训练参数如文件链接,学习率,版本等信息更希望通过命令行来进行指定,以前的方法是需要自己实现argparse模块的读取命令行参数,自行进行解析和使用。使用Hydra就可以很好的解决上述麻烦。下面将结合官方文档对这个模块的使用进行介绍。
注意:我们导入包的时候使用 import hydra ,但pip下载包时需要使用 pip install hydra-core
1、常规使用
我们使用hydra需要使用装饰器 @hydra.main() ,被这个装饰器装饰的函数需要接收一个参数DictConfig对象(来自于omegaconf包),这个对象就是我们配置类对象了。
@hydra.main(version_base,config_path,config_name)这个装饰器函数常用3个传参:
- version_base:表示应用程序的版本号。通常情况下,这个字符串应该是一个语义化版本号,例如 "1.0.0"
- confige_path: 一个字符串,表示 Hydra 应该在哪里查找配置文件。这个参数通常是一个相对文件路径,一般是文件夹,例如 "conf"
- config_name: 表示 Hydra 应该使用哪个配置文件。这个参数通常是一个 YAML 文件的名称,例如 "config.yaml"
下面这个例子就是打印出.\config文件夹\config.yaml配置文件中的内容
import hydra
from omegaconf import DictConfig, OmegaConf
@hydra.main(version_base=None, config_path="config文件夹", config_name="config.yaml")
def my_app(cfg: DictConfig) -> None:
print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
my_app()
运行后的结果是:
1.1、命令行更改配置 / 增加配置
除了上述提到常规使用外,我们还可以在运行时更改配置文件中的值,或者增加一个配置文件。
更改配置文件的值直接在命令行后面跟值即可例如下面例子中把db.user的值更改为其他值,并且还增加dp.name这个配置(增加配置需要使用+)。代码还是上面的示例代码,不过我们启动时的命令行改一下即可,并不需要我们去额外编写什么解析方式(但是需要注意使用命令行来对配置文件进行覆盖重写是不支持里面包含中文的)
1.2、属性访问以及配置文集的特殊配置
从上文中我们可以很容易的推导出,使用@hydra.main()得到配置对象的类型是DictConfig,因此我们可以使用DictConfig的所有方式去访问相关的属性值。比如 cfg.db 得到的也是DictConfig对象,还可以使用 cfg["db"] 去获取同样的对象。
我们除了在程序中可以很容易的访问配置对象,我们在配置.yaml文件中也可以很容易的复用值(使用${}即可)。例如我们下面的这个配置config_dev.yaml
下面是示例代码
import hydra
@hydra.main(version_base=None, config_path="config文件夹", config_name="config_dev.yaml")
def my_app(cfg) -> None:
print(f"引用值:{cfg.node.sister_age}")
print(f"引用字符传:{cfg.node.sister_name}")
print(cfg.node.missing) # 可以通过cfg.node.missing = 2进行赋值后再使用,或者在命令行中赋值
if __name__ == "__main__":
my_app()
运行后的结果是
2、配置分组使用
在实际使用中我们常常会将文件分解成多个配置文件,比如dev环境,pro环境的配置是不一样的,因此我们可以使用配置组的方式去加载配置文件,我们需要一个主配置文件,这个主配置文件的路径应该是传入@hydra.main(config_name="主配置文件config.yaml")中的文件,并且在主配置文件中我们需要defaults:来指定配置文件的加载顺序。为了演示我创建了以下的文件结构以及配置文件内容:
########### 主配置文件config.yaml ##########
defaults:
- _self_
- config_dev.yaml
- other配置: config_other.yaml
db:
driver: mysql
user: omry
pass: secret
common_field: 主配置_公共字段 # 公用字段
########## config_dev.yaml ############
dev:
name: dev_配置文件
common_field: dev_公共字段 # 公用字段
########## other配置\config_other.yaml #########
other: 其他配置文件
common_field: other_公共字段 # 公用字段
我们在三个配置文件中都设置了一个commom_field用来演示覆盖模式,需要提到的是在同一层级下,根据defaults载入的先后顺序,后载入的同名配置会覆盖先载入的同名配置。我们可以看到在主配置文件的defaults中有一个 _self_,这个表示主配置文件本身。因此根据主配置文件中的defaults我们可以知道,配置文件的载入顺序是:主配置文件,config_dev文件,other配置/config_other.yaml文件,并且后载入的同名配置属性会覆盖前面的属性。运行后来看看我们的猜想对不对:
import hydra
from omegaconf import OmegaConf
@hydra.main(version_base=None, config_path="config文件夹", config_name="主配置文件config.yaml")
def my_app(cfg) -> None:
print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
my_app()
运行结果: