查看原文
其他

韩言博:探索为dflow用户提供更便捷的操作方式——dflow语法糖的尝试

韩言博 深度势能 2024-06-16


DeepModeling Hackathon 2.0 于8月31日圆满落下帷幕,最终5位选手在决赛中脱颖而出,充分展示了各自的风采。未来,选手们也将携手DeepModeling社区,用创意和代码为更多人创造价值。


今天,小编为大家带来工作流套件:dflow赛道一等奖韩言博的获奖作品展示,一起来看看吧~


工作流套件:dflow赛道一等奖

选手姓名:韩言博

院校年级:西安交通大学读博士研究

研究方向:功能碳纳米材料理论研究

曾获荣誉:在首届 DeepModeling Hackathon 大赛中获得科学计算赛道二等奖



01

项目介绍


在本次 Hackathon 决赛中,开发了一些便于使用 dflow 工作流的语法糖,旨在探索为dflow用户提供更便捷的操作方式,主要包括:

  • 隐式检查 Step/OP 出入参定义

  • 省略 .add() 的 Workflow 上下文

  • 探索省略 Slice() 参数的 ArgoRange 功能(略)


02

项目内容详解


问题背景

目前在 dflow 中定义 Python OP 的方式是通过定义一个 dflow.OP 类的子类,并实现其get_input_sign、get_output_sign和 execute 方法,定义工作流的方式是初始化一个  Workflow 类之后,通过调用 .add() 方法将初始化的各个 Step 加入到 Workflow 中,例如:


from dflow.python import OP, OPIO, Artifact, OPIOSign, PythonOPTemplatefrom pathlib import Path
class MyOP(OP): @classmethod def get_input_sign(self): return OPIOSign({ 'msg': str, 'num': int, 'foo': Artifact(Path), 'idir': Artifact(Path), }) @classmethod def get_output_sign(self): return OPIOSign({ 'msg': str, 'bar': Artifact(Path), 'odir': Artifact(Path), }) @OP.exec_sign_check def execute( self, op_in: OPIO,) -> OPIO: # do something and return dict op_out like defined in `get_output_sign` return op_out # upload some artifacts
wf = dflow.Workflow()wf.add( name="step1", template=PythonOPTemplate( MyOP, image="python:3.8" ),)
wf.submit()


问题分析
  1. 在当前的使用方式下,用户需要小心地控制 get_input_sign、get_output_sign 方法的内容,保证其与 execute 的输入输出分别对应;当定义的 OP 较多时,还需要仔细保证每个 OP 的输入输出签名都不与 execute 中的执行逻辑冲突。

  2. 用户定义 Step 和将 Step 添加到Workflow的过程是分离的,复杂工作流中大量的 .add() 语句会影响程序的可读性,也不利于多个Workflow的管理。

解决结果

为简化OP定义和工作流定义的过程,本项目为 dflow 添加了一些语法糖,使用户可以省略 get_input_sign 和 get_output_sign 方法的定义、省略`add`方法的调用,使用户使用dflow时将精力集中于`execute`的逻辑上,并有更好的可读性。对应于上面的例子,语法糖的用法如下:


from dflow.python import OP, Artifact, PythonOPTemplatefrom pathlib import Path
@OP.functiondef MyOP( msg: str, num: int, foo: Artifact(Path), idir: Artifact(Path),) -> { 'msg': str, 'bar': Artifact(Path), 'odir': Artifact(Path), }: # do something and return dict just as other python functions do return { 'msg': msg_out, 'bar': Path(bar), 'odir': Path(odir), } # upload some artifacts
with Workflow(name='python-sugar') as wf: step = Step( name='step', template=PythonOPTemplate( MyOP, image='python:3.8' ), parameters={ 'msg': 'Hello', 'num': 3}, artifacts={'foo': artifact0, 'idir': artifact1}, )wf.submit()


在改进后的方式中,用户仅需要显式定义好OP的关键逻辑,dflow 需要的输入输出参数的类型和名称将通过 Python annotation 的方式自动推断。基于函数的定义也可以近乎无痛地(仅需要添加适合于dflow 的 annotation 和一个装饰器语句 @OP.function)与正在使用的其他代码相互迁移。同时在定义工作流的过程中,用户可以省略 add 方法的调用,使工作流的定义更加简洁。


实现思路和细节

对于 dflow 的用户而言,将精力集中于工作流 OP 的定义是最关键的需求;dflow 此前的 OP 定义方式提供了极高的自由度,但并不总是能帮助开发者定义自己所需的程序逻辑。


参考 argoproj/Hera 等项目的语法,其“便利性”都在于让用户关注工作流的核心逻辑,对于 dflow 而言也就是 OP 类中 execute 方法实现的逻辑。因此本项目在设计时考虑要让用户的使用流程尽可能靠近“定义工作OP逻辑→工作流的环境准备工作(配置和文件上传)→组装工作流→提交工作流”的方式,并且保证每个步骤都清晰明了。


定义工作OP逻辑的过程中,通常用户是将已经调试好的Python函数嵌入到工作流中,发挥Python 装饰器的作用能极大地帮助这项工作,因此本项目为 OP 类编写了一个 function 装饰器用于将用户函数转化为 dflow 的OP类。该装饰器的程序逻辑主要是:


  • 提取被装饰函数的注解(即Python3中可以使用的函数对象的__annotations__属性)

  • 创建一个OP的新的子类,与函数同名并存入OP的 subclass 属性以保证子类的行为和直接定义OP子类相同(这部分是在各种实现中选择出的最能保证 dflow 现有逻辑的方式,也属难点)

  • 根据函数注解的转入参数和传出参数,组装子类的get_input_sign、get_output_sign 方法,并适时检查和报错提醒使用者。

  • 组装子类的 execute 方法,使之调用被装饰的函数

  • 为产生 argo 脚本段落提供适用于此装饰器条件下使用的 script 变量,并引入必要的依赖。(其实我觉得可能还有更简单的方式完成这个部分_(:з)∠)_)

  • 返回子类代替被装饰的函数名


从而在后续的调用中,被装饰函数的变量名将被同名的 OP 子类取代,因而其行为将与 dflow 现有的定义一致,但简化了用户的使用成本。同时由于装饰器发挥了 Python 注解的特性,大部分IDE也能及时地提醒使用者输入变量使用中存在的问题;此外如果使用 typing.TypedDict 定义输出参数,输出参数也将得到检查。不过这部分程序还有进一步提升的空间,例如使用户无需指明类型地定义 OP 逻辑、返回值不再限制为字典类型等。


组装工作流的过程中,本项目主要是使用户省略反复调用.add() ,这部分功能的实现较为简单,主要是为 Workflow 类提供 __enter__ 和 __exit__ 方法的逻辑。并通过一个单例对象来管理上下文状态。考虑到 Workflow 没有嵌套的必要,而且定义多个 workflow 实例后仍能使用 __enter__ 返回的变量进行管理,因此这种设计是合理的。该部分的程序逻辑是:


  • 为 Workflow 类实现 __enter__ 和 __exit__ 方法逻辑,使用with Workflow() as wf: 语句时,单例 GLOBAL_CONTEXT 转入工作流 wf 的“环境”状态。

  • 在Step实例化的过程中进行GLOBAL_CONTEXT的检查,检查是否在某工作流的“环境”中,如果在,则调用.add() 来添加该 Step 。


无论是否使用装饰器定义 OP,或是否使用 workflow 上下文,都不会影响 dflow 现有的工作流定义行为,并且可以混合使用,在保留灵活性的同时,为 dflow 的用户提供了更易快速上手的使用方式:


  • 新用户将有更低的学习成本,如果了解Python的类型注解特性的话,只需要了解 dflow 的类型注解含义即可很快定义好自己的 OP

  • 用户可以将精力集中到工作流的业务逻辑上

  • 帮助用户将不同工作流放在对应的上下文中定义,提高代码可读性

  • 引入的定义方式和 Python 特性足够兼容,学习成本低

  • 不影响已有工作流的定义,可以逐步迁移或选择性使用


受限于比赛时间和个人能力,本次Hackathon的项目实现方案可能还有进一步改善的空间,希望今后能在 DeepModeling 社区中通过与其他开发者、用户的合作交流,共同将 dflow 做的更适合科学计算领域用户使用!


03

导师评价


导师简介

刘歆子建


2016年于北京大学获得材料化学学士学位和数学双学位,2021年博士毕业于北京大学理论与计算化学、量子动力学方向,2019年获得国家奖学金和"化学之星"称号。现工作于深势科技技术部门,为dflow工作流套件主要开发者之一。




韩言博作为计算化学方向二年级博士生,展示出了优秀的软件工程功底和编码能力,并体现了其对最佳架构的思考。在初赛中,他为dflow开发了ray集群执行器,在计算资源层面上对接了ray集群,为dflow和ray两个生态的互动提供了一个不错的出发点。在决赛中,他致力于优化dflow的用户体验,设计了一种更接近python原生的OP定义方式,缩短了用户定义OP的篇幅;通过python上下文的运用,省略了将step或task显式添加到workflow的语句,并为slices设计了一种语法糖,初步实现了可行解决方案。比赛过程中他独立思考、调研学习了业界相关软件的经验、而后代码实现了自己的想法,展示出个人的思维和行动力,若比赛中更多地与dflow开发者交流讨论,可能在完成比赛的同时更有利于dflow的发展。互联网业界的优秀经验,在不远的将来必将影响科学计算领域,如果言博对二者碰撞带来的可能感兴趣的话,希望更多了解云原生、MLOps等领域,与DeepModeling社区紧密交流,一道设计和引领dflow及其他基础工具和套件的发展。



- End -

(如需转载图文请与公众号后台联系)

-------------------------------

推荐阅读

我们又在美国禁令中被自动过滤掉了。

DPA-1: 共建覆盖元素周期表的预训练大模型

超新星成长记——哥伦布训练营超新星计划进行时

继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存