Python Hydra库使用介绍

本文最后更新于:2022年7月21日 下午

Python Hydra库使用介绍

Python的Hydra库是一个开源的配置管理库,能够动态创建一个可继承的的配置系统,十分灵活,能够用来管理深度学习中许多的参数。

Hydra库由Facebook开发,文档:Getting started | Hydra,GitHub:facebookresearch/hydra

为什么要使用Hydra

一般在深度学习中,需要处理很多的配置参数,比如数据集路径、优化器、学习率、模型架构等,目前有的人使用原生的argparse库来从命令行读入配置、有的人使用json或者yaml格式的配置文件、有的人在源代码中写一个类来保存配置、有的人直接使用.py文件来写配置、还有的大型库(如openmmlab)自己开发一套配置系统。下面来分析各种方式的优缺点:

方式 优点 缺点
argparse Python内置,使用方便 一旦需要很多的配置,那命令就会非常长,可能需要额外一个.sh文件来运行
json、yaml配置文件 通用配置文件,也拥有对应的内置库,使用方便 若要对某个超参数进行消融实验,则要复制一堆整个的文件,然后对某一项进行修改
源代码中写一个class、py文件当配置 不需要额外操作,直接是python的数据结构,甚至还能使用一些高级语法,也可以通过继承来结构化 要进行消融实验还是很麻烦,并且会让代码变得更长,继承也不是很灵活
OpenMMLab的Config 精心设计,功能强大,支持继承,拓展方便 不适合用来构建自己的库,需要阅读很多的源代码
Hydra 支持继承,可通过yaml和命令行混合配置,还支持一些动态插值的语法…… 因为是第三方库,需要进行一定的学习

安装

1
pip install hydra-core

一个简单的使用例子

这个例子改自官方文档,新建一个conf/config.yaml文件,内容如下:

1
2
3
4
db:
driver: mysql
user: omry
pass: secret

然后代码里通过装饰器可以得到数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import hydra
from omegaconf import DictConfig, OmegaConf
# 装饰器config_path参数表示配置文件的目录,config_name表示配置文件的名字
@hydra.main(version_base=None, config_path="conf", config_name="config")
def my_app(cfg: DictConfig) -> None:
print(OmegaConf.to_yaml(cfg)) # 可视化配置
print(cfg.db.user) # object style
print(cfg['db']['user']) # dict style
cfg.db.user = 'kamino' # 可以重新赋值
print(cfg['db'].user) # 混合 style
other_func(cfg) # 可以调用其他函数来传参

def other_func(cfg):
print(cfg['db'].user)

if __name__ == "__main__":
my_app()

输出如下:

1
2
3
4
5
6
7
8
9
db:
driver: mysql
user: omry
pass: secret

omry
omry
kamino
kamino

简单吧😄

使用继承的简单例子

如图所示构建了一个深度学习中可能出现的例子,在配置文件夹conf下有一个config2.yaml是组合的配置文件。一个深度学习代码可能包含backbonedatasethead等方面的配置,假设我们要使用mnist数据集,用resnet模型,并自定义一个10分类2层的一个分类器,然后指定学习率为1e-3,那在backbone文件夹下就新建一个resnet.yaml文件,其余同理。

在主文件中要在一开始就写如图中的defaults配置项,表示继承下来的部分,其中key是文件夹的名字,value是具体配置文件的名字,_self_表示本文件,当某个值存在冲突的时候会根据顺序处理冲突,假如同一个值被多个配置定义了,那更后的一个会覆盖,假如有多个相同的key,那最终key下的配置会被合并。

💚 defaults中的key还可以是一个目录的格式,比如- backbone/v1/v2: vgg,解析后也是需要.backbone.v1.v2.<proprety>来获取值。

💚defaults中也可以忽略key,则不添加层次关系。比如- run,就表示加载进run.yaml下的配置。

把上面的配置输出如下:

1
2
3
4
5
6
7
backbone:
layer: 18
head:
hidden_size: 256
dataset:
data_dir: ./data
learning_rate: 0.001

从命令行中获取配置参数

  • 假设我在backbone添加了一个新的模型vgg,那就是在backbone文件夹下新建一个vgg.yaml,之后可以通过更改默认配置文件来切换模型——但是,我们也可以直接在命令行中进行切换:

    1
    python main2.py backbone=vgg
  • 假如在backbone里提供多个模型,但是不提供默认的模型,需要强制用户手动选择,则不用填defaults,而是在命令行中用+来进行选择。(若已有默认,则会报错)

    1
    python main2.py +backbone=vgg
  • 假如要覆盖某个具体数值,可以直接在命令行中赋值:

    1
    2
    python main2.py learning_rate=1e-4
    python main2.py backbone.layer=30
  • 假如不确定某个配置项在配置文件中是否存在,则用++,若配置项存在则覆盖,若配置项不存在则追加

    1
    python main2.py ++lr=1e-2

Yaml配置中可使用的动态语法

正如官方的这个例子:

1
2
3
4
5
node:                         # Config is hierarchical
loompa: 10 # Simple value
zippity: ${node.loompa} # 直接插值
do: "oompa ${node.loompa}" # 字符串插值
waldo: ??? # 缺失值,在使用前需要先赋值否则报错

可以用${}来引用其他配置中的值,要注意这个引用的值是绝对路径

还可以用???来表示需要用户手动配置的必填值,缺失会报错omegaconf.errors.MissingMandatoryValue

需要注意的是,使用这些语法后,只有在使用值的时候才会实时算出对应值来,用print(OmegaConf.to_yaml(cfg)) # 可视化配置这种语句可视化会发现值还是???或者${}