用 Facebook Hydra 参数配置框架来简化程序配置
用 Facebook Hydra 参数配置框架来简化程序配置
0x00 摘要
Facebook Hydra 允许开发人员通过编写和覆盖配置来简化 Python 应用程序(尤其是机器学习方面)的开发。开发人员可以借助Hydra,通过更改配置文件来更改产品的行为方式,而不是通过更改代码来适应新的用例。
本文通过几个示例为大家展示如何使用。
0x01 问题描述
在机器学习的开发中,经常会遇到各种调整参数,各种比较性能的情况。所以开发者经常会迷惑:
- 我现在这两个模型都使用的是什么参数来着?
- 我需要添加几个参数,又要修改代码,应该如何防止搞乱代码?
- 可以使用配置文件,但是如果希望新添加一个参数,则各个配置文件之间很难同步,我如何处理配置文件?
- 我今天跑了十几个模型,一不小心把他们的输出给冲掉了,我该怎么办?
- 十几个模型的log也容易被误删除,如何防止彼此冲突?
- 我在哪里?我在做什么?
这些问题,Facebook的开发人员早已遭遇过,深受其害的他们于是开发出来了 Hydra 来解决这些问题。
0x02 概述
Hydra提供了一种灵活的方法来开发和维护代码及配置,从而加快了机器学习研究等领域中复杂应用程序的开发。 它允许开发人员从命令行或配置文件“组合”应用程序的配置。这解决了在修改配置时可能出现的问题,例如:
- 维护配置的稍微不同的副本或添加逻辑以覆盖配置值。
- 可以在运行应用程序之前就组成和覆盖配置。
- 动态命令行选项卡完成功能可帮助开发人员发现复杂配置并减少错误。
- 可以在本地或远程启动应用程序,使用户可以利用更多的本地资源。
Hydra承诺的其他好处包括:
- 使为新用例和需求的项目添加功能变得更加容易,而无需重写大量代码。
- 减少了复杂应用程序中常见的一些样板代码,例如处理配置文件,配置日志记录和定义命令行标志。
下面我们通过几个简单例子给大家演示下如何使用。
0x03 使用
3.1 安装
项目地址位于:https://github.com/facebookresearch/hydra
安装方式如下:
pip install --upgrade hydra-core -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com
3.2 示例
3.2.1 示例代码
示例代码如下
import hydra
@hydra.main()
def app(cfg):
print(cfg.pretty())
print("The user is : " + cfg.user)
if __name__ == "__main__":
app()
运行如下:
python3 test_hydra.py +user=ua +pwd=pa
输出如下:
Use OmegaConf.to_yaml(cfg)
category=UserWarning,
user: ua
pwd: pa
The user is : ua
3.2.2 简化参数处理
常见的一个机器学习程序中,是用如下代码来处理输入和各种参数。
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=10, metavar='N',
help='number of epochs to train (default: 10)')
parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
help='learning rate (default: 0.01)')
通过示例代码我们可以看出来,在 hydra 之中,我们直接使用 cfg.user 就可以。
而且还可以通过配置文件来直接处理参数,比如:
@hydra.main(config_path="conf", config_name="config")
def my_app(cfg: DictConfig) -> None:
print(OmegaConf.to_yaml(cfg))
3.2.3 输出目录
人们在做研究时经常遇到的一个问题是如何保存输出。典型的解决方案是传入一个指定输出目录的命令行标志,但这很快会变得乏味。当你希望同时运行多项任务,并且必须为每个任务传递不同的输出目录时,这尤其令人恼火。
Hydra 通过为每次运行生成输出目录,并在运行代码之前更改当前工作目录来解决此问题。这样可以很好地将来自同一 sweep 的任务分组在一起,同时保持每个任务与其他任务的输出分离。
我们可以简单的来看看目录的变化,可以看到,在当前目录下生成了一个 outputs 目录。
其内部组织是按照时间来进行,把每次运行的输出,log 和 配置都归类在一起。
├── outputs
│ └── 2021-03-21
│ ├── 11-52-35
│ │ ├── .hydra
│ │ │ ├── config.yaml
│ │ │ ├── hydra.yaml
│ │ │ └── overrides.yaml
│ │ └── test_hydra.log
│ └── 11-57-55
│ ├── .hydra
│ │ ├── config.yaml
│ │ ├── hydra.yaml
│ │ └── overrides.yaml
│ └── test_hydra.log
├── test_hydra.py
3.2.4 配置所在
我们分别打开两个.hydra目录下的config.yaml文件看看。
可以看到,每次运行时候,对应的参数配置都保存在其中。这样极大的方便了用户的比对和分析。
$ cat outputs/2021-03-21/11-52-35/.hydra/config.yaml
user: ua
pwd: pa
$ cat outputs/2021-03-21/11-57-55/.hydra/config.yaml
user: ub
pwd: pb
0x04 Multirun 处理组合情况
Multirun 是 Hydra 的一种功能,它可以多次运行你的函数,每次都组成一个不同的配置对象。这是一个自然的扩展,可以轻松地组合复杂的配置,并且非常方便地进行参数扫描,而无需编写冗长的脚本。
例如,对于两种参数,我们可以扫描所有 4 个组合,一个命令就是会完成所有组合的执行:
python test_hydra.py --multirun user=ua,ub pwd=pa,pb
得到输出如下:
[2021-03-27 11:57:54,435][HYDRA] Launching 4 jobs locally
[2021-03-27 11:57:54,435][HYDRA] #0 : +user=ua +pwd=pa
user: ua
pwd: pa
[2021-03-27 11:57:54,723][HYDRA] #1 : +user=ua +pwd=pb
user: ua
pwd: pb
[2021-03-27 11:57:54,992][HYDRA] #2 : +user=ub +pwd=pa
user: ub
pwd: pa
[2021-03-27 11:57:55,248][HYDRA] #3 : +user=ub +pwd=pb
user: ub
pwd: pb
可以看到生成如下目录树,每个参数组合对应了一个目录。
├── multirun
│ └── 2021-03-27
│ └── 11-57-53
│ ├── 0
│ │ ├── .hydra
│ │ │ ├── config.yaml
│ │ │ ├── hydra.yaml
│ │ │ └── overrides.yaml
│ │ └── test_hydra.log
│ ├── 1
│ │ ├── .hydra
│ │ │ ├── config.yaml
│ │ │ ├── hydra.yaml
│ │ │ └── overrides.yaml
│ │ └── test_hydra.log
│ ├── 2
│ │ ├── .hydra
│ │ │ ├── config.yaml
│ │ │ ├── hydra.yaml
│ │ │ └── overrides.yaml
│ │ └── test_hydra.log
│ ├── 3
│ │ ├── .hydra
│ │ │ ├── config.yaml
│ │ │ ├── hydra.yaml
│ │ │ └── overrides.yaml
│ │ └── test_hydra.log
│ └── multirun.yaml
0x05 处理复杂情况
对于一般的机器学习运行和普通python程序,hydra是非常好用的,因为可以使用 装饰器 来直接作用于 python 函数。
但是如果遇到了复杂情况,比如spark-submit,我们该如何处理?因为 spark-submit 是没办法用 hydra 来装饰。
比如:
spark-submit cut_words.py
这样就hydra就没办法截取 spark 的输入,输出。
遇到这个情况,我是使用 python 文件内部 调用 linux命令行,然后在spark-submit之前就处理其参数,在 spark 运行时候 转发程序输出的办法来解决(如果哪位同学有更好的办法,可以告诉我,谢谢)。
5.1 Python subprocess
Python subprocess 允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。
subprocess模块中定义了一个Popen类,通过它可以来创建进程,并与其进行复杂的交互。Popen 是 subprocess的核心,子进程的创建和管理都靠它处理。
构造函数:
class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0,restore_signals=True, start_new_session=False, pass_fds=(),*, encoding=None, errors=None)
常用参数:
- args:shell命令,可以是字符串或者序列类型(如:list,元组)
- bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认-1。
0:不使用缓冲区
1:表示行缓冲,仅当universal_newlines=True时可用,也就是文本模式
正数:表示缓冲区大小
负数:表示使用系统默认的缓冲区大小。 - stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- preexec_fn:只在 Unix 平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
- shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
- cwd:用于设置子进程的当前目录。
- env:用于指定子进程的环境变量。如果 env = None,子进程的环境变量将从父进程中继承。
5.2 具体例子
下面例子很简陋,不能直接运行,只是给大家演示下大致思路,还请根据具体情况做相关调整。
- 我们通过subprocess.Popen启动了spark;
- hydra 的输入 可以转换为 spark 和 python 的输入;
- 然后读取子进程的stdout;
- 逐次使用log.info来打印转发的stdout,这样spark的输出就被转发到了hydra的输出之中;
这样,spark的输出就可以被hydra捕获,从而整合到hydra log体系之中。
import shlex
import subprocess
import hydra
import logging
log = logging.getLogger(__name__)
@hydra.main()
def app(cfg):
# 可以在这里事先处理参数,被hydra处理之后,也成为 spark 和 python 的输入,进行处理
shell_cmd = 'spark-submit cut_words.py' + cfg.xxxxxx # 假如cut_words有参数
cmd = shlex.split(shell_cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while p.poll() is None:
line = p.stdout.readline()
line = line.strip()
if line:
log.info('Subprogram output: [{}]'.format(line))
if p.returncode == 0:
log.info('Subprogram success')
else:
log.info('Subprogram failed')
if __name__ == '__main__':
app()
5.3 流程示例
以下就是我采取办法的流程示例。
- Input 由 hydra 处理之后,由 python 父进程 转发给 spark 和 我们的python 商业逻辑;
- 具体spark 的输出,由 python 父进程转发给 Hydra logging;
具体如下图:
Input Input
Hydra +----------+ +------------------------v
| ^ |
| | |
| | |
+-------------------------+ v
| | | | +------+-------------+
| v +----------> | | Spark |
| | | |
| Parent Python Process | | Business Python |
| | | |
| +<-----------^ | | |
| | | | | |
+-------------------------+ +------+-------------+
| | |
| | |
| | |
Hydra <---------<+ +------------------------+
Logging Output
5.4 期待
现在 Hydra 统一保存 配置 到独立的配置文件之中。如果可以把某些输出也按照统一格式保存在配置文件中就更好了。这样我们就可以把这些配置文件统一处理,比较,图形化。直接把配置和输出结合起来,更加直观。
0x06 总结
这里只是简单给出了三个例子,Hydra还有众多的用法等待大家去探究。相信大家的很多痛点都可以用它来解决,赶紧试试吧。
0xEE 个人信息
★★★★★★关于生活和技术的思考★★★★★★
微信公众账号:罗西的思考
如果您想及时得到个人撰写文章的消息推送,或者想看看个人推荐的技术资料,敬请关注。

0xFF 参考
机器学习项目配置太复杂怎么办?Facebook 开发了 Hydra 来帮你
Python 从subprocess运行的子进程中实时获取输出的例子
用 Facebook Hydra 参数配置框架来简化程序配置的更多相关文章
- Liferay7 BPM门户开发之25: Liferay7应用程序配置(APPLICATION CONFIGURATION)
首先有几个概念需要明确.1.第一个概念是这里的应用程序配置不是写XML之类的配置文件,是类似字典的类型化配置这意味着应用程序配置不只是一个字符串键值对的列表.值还可以有类型,如整数列表,字符串列表,一 ...
- [ASP.NET Core 3框架揭秘] Options[2]: 配置选项的正确使用方式[下篇]
四.直接初始化Options对象 前面演示的几个实例具有一个共同的特征,即都采用配置系统来提供绑定Options对象的原始数据,实际上,Options框架具有一个完全独立的模型,可以称为Options ...
- 八:SpringBoot-集成JPA持久层框架,简化数据库操作
SpringBoot-集成JPA持久层框架,简化数据库操作 1.JPA框架简介 1.1 JPA与Hibernate的关系: 2.SpringBoot整合JPA Spring Data JPA概述: S ...
- OEL上使用yum install oracle-validated 简化主机配置工作
环境:OEL 5.7 + Oracle 10.2.0.5 RAC 如果你正在用OEL(Oracle Enterprise Linux)系统部署Oracle,那么可以使用yum安装oracle-vali ...
- 新书出版《.NET框架设计—模式、配置、工具》感恩回馈社区!
很高兴我的第一本书由图灵出版社出版.本书总结了我这些年来对框架学习.研究的总结,里面纯干货,无半句废话. 书的详情请看互动网的销售页面:http://product.china-pub.com/377 ...
- 如何在 ETL 项目中统一管理上百个 SSIS 包的日志和包配置框架
一直准备写这么一篇有关 SSIS 日志系统的文章,但是发现很难一次写的很完整.因为这篇文章的内容可扩展的性太强,每多扩展一部分就意味着需要更多代码,示例和理论支撑.因此,我选择我觉得比较通用的 LOG ...
- IDM主机上安装融合应用程序配置框架
IDM主机上安装融合应用程序配置框架 安装Oracle融合应用程序>设置>身份和访问管理节点安装融合应用程序配置框架 由于我们使用Oracle VirtualBox虚拟机这一次,我们在 ...
- 自己写的日志框架--linkinLog4j--框架可配置+提性能
OK,上一篇博客我们已经实现了日志框架的基本的功能,但是还有一个最大的问题就是日志输出地不能重定向,然后一些输出也不可控.那现在我们来实现一个比较完整的日志框架. 设计思路如下: 1,定义一堆常量Li ...
- maven项目配置框架
任何一个maven项目都会继承一个默认的父pom配置:Super POM,详见:https://maven.apache.org/guides/introduction/introduction-to ...
- springmvc(二) ssm框架整合的各种配置
ssm:springmvc.spring.mybatis这三个框架的整合,有耐心一步步走. --WH 一.SSM框架整合 1.1.整合思路 从底层整合起,也就是先整合mybatis与spring,然后 ...
随机推荐
- 设计模式(17)-Chain of Responsibility Pattern
行为模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化.行为模式不仅仅是关于类和对象的,而且是关于它们之间的相互作用的. 行为模式分为类的行为模式和对象的行为模式两 ...
- Java 并发编程实战学习笔记——寻找可强化的并行性
寻找可强化的并行性 1.串行执行所有任务 代码 复制 - 运行 package net.jcip.examples; import java.util.*; /** * SingleThreadRen ...
- Prometheus 3.0.0 升级中遇到的 `--storage.tsdb.retention` 错误的修复方法
在将 Prometheus 升级到 3.0.0 后,许多用户会遇到以下错误: Error parsing command line arguments: unknown long flag '--st ...
- 圆梦:借助云开发 CloudBase实现你的游戏开发梦想
最近我发现AI产品在不断涌现新动向,尤其是一些技术巨头推出的创新产品.例如,今天我们要探讨的是腾讯云开发的云开发 CloudBase,如果你之前没有听说过这个名字,那可能还记得腾讯云推出的另一个产品- ...
- MAC清理
今日分享 Mac清理 有很多三方软件可以清理,以前用过腾讯的柠檬lite,每次就清个几百兆,系统数据感觉还是得自己手动清理才行 今天电脑又在提醒储存空间不足了,一看占用发现系统数据占了100多个G,学 ...
- 买苹果MacBook Pro ,有必要买care吗?
先了解一下AppleCare+ for Mac的服务范围:将原先的一年保修延长至三年,并且提供两次收取服务费的意外保修服务,以及当电池寿命低于80%时免费的电池更换.其中,意外保修服务依然是要收费的, ...
- JVM实战—2.JVM内存设置与对象分配流转
大纲 1.JVM内存划分的原理细节 2.对象在JVM内存中如何分配如何流转 3.部署线上系统时如何设置JVM内存大小 4.如何设置JVM堆内存大小 5.如何设置JVM栈内存与永久代大小 6.问题汇总 ...
- IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结
本文由巩鹏军分享,原题"IM兼容性基建",本文有修订. 1.引言 一个成熟的IM成品,在运营过程中随着时间的推移,会发布不同的版本,但为了用户体验并不能强制要求用户必须升级到最新版 ...
- 移动端开源 IM 框架 MobileIMSDK v6.0 发布!
一.更新内容简介 本次为主要版本更新(本次更新内容见文末"MobileIMSDK v6.0更新内容 "一节),强势升级,将同时支持TCP.UDP.WebSocket三种协议,精心封 ...
- Linux C语言面试考点
数组 数组初始化方法 /* 以下为自动类型 *//* 一维数组 */int arr[] = {1, 3, 5}; //不指定长度,由编译器自动计算int arr[5] = {0, }; //指定长度 ...