用 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,然后 ...
随机推荐
- flask 中的request【转载】
每个框架中都有处理请求的机制(request),但是每个框架的处理方式和机制是不同的,为了了解flask的request中都有什么东西,首先我们要写一个前后端的交互 基于HTML+Flask 写一段前 ...
- (Redis基础教程之七)如何使用Redis中的Hashes
如何在ubuntu18.04上安装和保护redis 如何连接到Redis数据库 如何管理Redis数据库和Keys 如何在Redis中管理副本和客户端 如何在Redis中管理字符串 如何在Redis中 ...
- Lock Less Java Object Pool
It has been a while since I wrote anything, I have been busy with my new job that involves doing som ...
- PTA 那就别担心了
PTA 那就别担心了 给定一个有向无环图,给出起点\(st\)和终点\(ed\),问从起点出发的所有路径是否都能到达终点,并且让你求出从起点到终点的不同路径数量 \(DFS\)记忆化搜索 对于第一个问 ...
- Input报错“Form elements must have labels: Element has no title attribute Element has no placeholde”
喵~ 项目开发难免会遇到些不解的问题,以下总结的是简化版,重在复现问题,解决问题. 写表单时,如果只是单独写了input元素,发现在后台管理会飘红.感觉很奇怪,明明没有写错语法,为什么会飘红呢? 1. ...
- fastadmin-自定义
1.样式重置 渲染完毕后再展示 [v-cloak]{ display: none; } <div id="app" v-cloak></div> 去除内置的 ...
- [NET,C# ] Nuget包发布流程
1.新建一个.NET Core类库 2.新增一个方法,并编译项目 3.下载Nuget.exe,与刚才新建的类库放在同一目录下 下载地址:https://www.nuget.org/downloads ...
- 论文解读《LightRAG: Simple and Fast Retrieval-Augmented Generation》
博客:https://learnopencv.com/lightrag 视频:https://www.youtube.com/watch?v=oageL-1I0GE 代码:https://github ...
- Nginx转发解析长域名多路径域名为一级域名
Nginx解析短域名,例如:访问 http://192.168.1.23 可直接跳转到 http://192.168.1.23/webroot/decision server { listen 90 ...
- WinForm 通用权限框架,简单实用支持二次开发
前言 开发一个安全.灵活且易于维护的应用程序是至关重要的.特别是在企业级应用中,权限管理不仅涉及到用户访问控制,还关系到数据的安全性和系统的稳定性. 推荐一款 WinForm 通用.完整的权限架构开发 ...