Hydra 组合
Hydra 是一个用于配置复杂应用程序的框架。DVC 支持 Hydra 的 配置组合,以此作为配置 实验运行 的方式。
你必须通过以下命令显式启用此功能:
$ dvc config hydra.enabled True
工作原理
在执行 dvc exp run
时,Hydra 将被用来在运行实验前组合生成单个 params.yaml
文件。该文件中的 参数 将用于配置包含你实验的 底层 DVC 流水线。
DVC 流水线 可以运行多个阶段,并使用不同的 shell 命令而非单一脚本,还提供诸如 模板化 和 foreach
阶段 等高级功能。
设置 Hydra
首先,你需要一个 conf/
目录来存放 Hydra 的 配置组。例如:
conf
├── config.yaml
├── dataset
│ ├── imagenette.yaml
│ └── imagewoof.yaml
└── train
├── baseline.yaml
├── model
│ ├── alexnet.yaml
│ ├── efficientnet.yaml
│ └── resnet.yaml
└── optimizer
├── adam.yaml
└── sgd.yaml
你还需创建一个 conf/config.yaml
文件,定义 Hydra 的 默认列表:
defaults:
- dataset: imagenette
- train/model: resnet
- train/optimizer: sgd
使用 dvc config hydra
选项可更改配置组目录和默认列表文件的默认位置。
现在我们来看一下基于上述设置生成的 params.yaml
文件可能是什么样子:
dataset:
url: https://s3.amazonaws.com/fast-ai-imageclas/imagenette2-160.tgz
output_folder: imagenette
train:
model:
name: ResNet
size: 50
weights: ResNet50_Weights.IMAGENET1K_V2
optimizer:
name: SGD
lr: 0.001
momentum: 0.9
运行实验
让我们构建一个包含两个阶段的 实验流水线。第一阶段下载数据集,并使用 params.yaml
中 dataset
部分定义的参数;第二阶段训练机器学习模型,并使用其余参数(整个 train
组)。
stages:
setup-dataset:
cmd:
- wget ${dataset.url} -O tmp.tgz
- mkdir -p ${dataset.output_folder}
- tar zxvf tmp.tgz -C ${dataset.output_folder}
- rm tmp.tgz
outs:
- ${dataset.output_folder}
train:
cmd: python train.py
deps:
- ${dataset.output_folder}
params:
- train
我们对上述 shell 命令(mkdir
、tar
、wget
)以及 输出 和 依赖项 路径(outs
、deps
)使用 模板化(${}
表达式)进行参数化。
你可以使用任意 YAML 解析库加载这些参数。在 Python 中,可以使用内置的 dvc.api.params_show()
或 Hydra 自带的 OmegaConf.load("params.yaml")
。
现在我们可以使用 dvc exp run --set-param
在运行时动态修改配置组合,例如从 train/model/efficientnet.yaml
加载模型配置(而不是来自 默认列表 的 resnet.yaml
):
$ dvc exp run --set-param 'train/model=efficientnet'
Stage 'setup-dataset' didn't change skipping
Running stage 'train':
> python train.py
--model.name 'EfficientNet' --model.size 'b0' --model.weights 'ResNet50_Weights.IMAGENET1K_V2'
--optimizer.name 'SGD' --optimizer.lr 0.001 --optimizer.momentum 0.9
...
如果需要 调试 组合后的值,请添加 -vv
参数(dvc exp run -vv --set-param 'train/model=efficientnet'
)。
我们也可以修改任意配置部分中的特定值:
$ dvc exp run --set-param 'train.optimizer.lr=0.1'
Stage 'setup-dataset' didn't change, skipping
Running stage 'train':
> python train.py
--model.name 'EfficientNet' --model.size 'b0' --model.weights 'ResNet50_Weights.IMAGENET1K_V2'
--optimizer.name 'SGD' --optimizer.lr 0.01 --optimizer.momentum 0.9
...
我们还可以在 实验队列 中加载多个 配置组,例如运行机器学习超参数的 网格搜索:
$ dvc exp run --queue \
-S 'train/optimizer=adam,sgd' \
-S 'train/model=resnet,efficientnet'
Queueing with overrides '{'params.yaml': ['optimizer=adam', 'model=resnet']}'.
Queued experiment 'ed3b4ef' for future execution.
Queueing with overrides '{'params.yaml': ['optimizer=adam', 'model=efficientnet']}'.
Queued experiment '7a10d54' for future execution.
Queueing with overrides '{'params.yaml': ['optimizer=sgd', 'model=resnet']}'.
Queued experiment '0b443d8' for future execution.
Queueing with overrides '{'params.yaml': ['optimizer=sgd', 'model=efficientnet']}'.
Queued experiment '0a5f20e' for future execution.
$ dvc queue start
...
请注意,DVC 会缓存所有运行记录,因此许多组合将无需实际运行实验即可完成。在上面的例子中,带有 ['optimizer=sgd', 'model=resnet']
的实验不会浪费计算时间,因为结果已存在于 运行缓存 中。你可以使用 dvc queue logs
来确认这一点:
$ dvc queue logs 0b443d8
Stage 'setup-dataset' didn't change, skipping
Stage 'train' didn't change, skipping
dvc exp run
每次运行时都会生成一个新的 params.yaml
,因此它不是可靠地复现已往实验的方法。相反,在需要复现先前运行的实验时,请使用 dvc repro
。
迁移 Hydra 项目
如果你已经配置了 Hydra 并希望开始同时使用 DVC,可能需要对代码进行轻微重构。DVC 不会将 Hydra 配置传递给 @hydra.main()
,因此应从代码中移除该装饰器。取而代之的是,DVC 会在你的代码运行前组合 Hydra 配置,并将结果写入 params.yaml
。
以上述示例为例,以下是使用 Hydra 但不使用 DVC 时 train.py
中 Python 代码的可能写法:
import hydra
from omegaconf import DictConfig
@hydra.main(version_base=None, config_path="conf", config_name="config")
def main(cfg: DictConfig) -> None:
# train model using cfg parameters
if __name__ == "__main__":
main()
将相同代码转换为使用 DVC 和启用 Hydra 配置组合的方式如下:
from omegaconf import OmegaConf
def main() -> None:
cfg = OmegaConf.load("params.yaml")
# train model using cfg parameters
if __name__ == "__main__":
main()
你现在不再需要在代码中导入 Hydra。本示例中包含了一个 main()
函数,因为这是一种良好的编程实践,但它并不是必需的。这种配置与代码的分离有助于调试,因为在实验开始之前,Hydra 生成的完整配置会被写入 params.yaml
。你可以选择是否使用 Hydra(或 DVC)来运行相同的代码。你还可以在 DVC 管道不同阶段的多个脚本之间复用 params.yaml
。
高级 Hydra 配置
你可以配置 DVC 与 Hydra 的协作方式。
默认情况下,DVC 会在 conf
目录中查找 Hydra 配置组,但你可以通过使用 dvc config hydra.config_dir other_dir
设置其他目录。这相当于 @hydra.main()
中的 config_path
参数。
在该目录内,DVC 会在 config.yaml
中查找 默认列表,但你可以通过使用 dvc config hydra.config_name other.yaml
设置不同的路径。这相当于 @hydra.main()
中的 config_name
参数。
Hydra 会自动发现 hydra_plugins
目录中的 插件。默认情况下,DVC 会在 DVC 仓库的根目录中查找 hydra_plugins
,但你可以通过 dvc config hydra.plugins_path other_path
设置其他路径。
自定义解析器
你可以通过将 OmegaConf 自定义解析器 编写为 hydra_plugins
目录内的文件,将其注册为插件。DVC 在组合 Hydra 配置时会使用这些自定义解析器。例如,在 hydra_plugins/my_resolver.py
中添加一个自定义解析器:
import os
from omegaconf import OmegaConf
OmegaConf.register_new_resolver('join', lambda x, y : os.path.join(x, y))
你可以在 Hydra 配置内部使用该自定义解析器:
dir: raw/data
relpath: dataset.csv
fullpath: ${join:${dir},${relpath}}
最终生成的 params.yaml
将如下所示:
dir: raw/data
relpath: dataset.csv
fullpath: raw/data/dataset.csv