功能齐备的工作流编排工具: Flyte

2022 年什么会火?什么该学?本文正在参与“聊聊 2022 技术趋势”征文活动 」

原文:Batteries-Included Workflow Orchestration Tool: Flyte | by ODSC - Open Data Science | Medium 作者:Ketan Umare

机器学习(ML)已经被应用了几十年,在这个领域支持研究人员和工程师的工具还在不断发展。曾经被冠以“数据科学家”头衔的机器人现在已经成为数据工程师、机器学习工程师和机器学习研究人员,当然,数据科学家仍然在一个组织的数据团队中扮演着关键角色。

随着模型变得复杂,数据源变得多样化,基础设施(infrastructure)成为瓶颈。通常,数据科学家不得不陷入低层次的基础设施问题: Kubernetes、网络、 GPU、资源管理等。此外,ML实验要求能够快速地进行独立的实验,这就需要一种能够实现协作的方法。

如果基础设施与数据科学家的工作紧密相连,那么这个范围就太大了,无法处理。建立训练生产模型的 pipeline,以及运行日益复杂的模型和团队协作,将会产生大量的基础设施的争论。这是一个很大的领域,需要一个专门的团队或平台。

开发人员需要 DevOps; 同样,机器学习也需要 MLOps。对于大部分数据科学家来说,组合 MLOps 技术栈中的各种组件是一个需求,这导致了像 Flyte 这样的工具的开发。

关于 Flyte

Flyte 是一个开源的、容器原生的、支持结构化编程的和分布式的处理平台,支持构建高并发、可扩展和可维护的工作流,用于机器学习和数据处理。它使用户能够专注于业务逻辑,同时将基础设施管理外包给更合适的团队。它还允许维护平台的团队为他们的用户提供自助服务的平台。

下面是一个简单的 Flyte 代码示例,它使用 Python Flytekit API 定义了一个 Flyte 任务来计算总薪酬,输出结果是 pandas DataFrame:

import pandas as pd
from flytekit import Resources, task@task(limits=Resources(cpu="2", mem="150Mi"))
def total_pay(hourly_pay: float, hours_worked: int, df: pd.DataFrame) ->
pd.DataFrame:
     return df.assign(total_pay=hourly_pay * hours_worked)
复制代码

Flyte 的主要优点

  • k8s 原生的工作流自动化
  • Python,Java 和 Scala 的人性化的SDK
  • 版本控制和审计
  • 可复现的Pipeline
  • 强类型系统

Flyte 是一个功能丰富的平台。在这篇文章中,我们将了解 Flyte 的三个基本特性。

类型检查

  • Flytekit SDK 支持 Python、 Java 和 Scala。

总所周知 Python 的易用性很大程度上归功于它的动态性。然而最近这种观念发生了改变。Python 社区鼓励在 Python 代码中引入类型,因为它有助于编写无错误的代码,并提高代码的可读性。

Flyte 团队认为,类型是编写代码的一个重要方面; 因此,我们在 Flyte 中引入了一个原生类型系统。

Flytekit Python SDK 自动将 Python 类型提示映射到 flyte 理解的跨语言类型。可以参考文档查看从 Python 映射到 Flyte 类型系统的所有类型。

为了理解为什么我们重视在 Flyte 中的类型,让我们分析一个没有类型的 Python 函数。

def concat(a, b):
    return a + b
复制代码

在这个函数中,我们不能准确地预测类型:它们可以是整数、浮点数或字符串。因为它们都是可能的。当我们尝试在这个基础上开发代码时,我们可能需要重新检查这段代码,每当我们使用这个函数返回的输出时,都要在头脑中模拟一个类型系统,这相当繁琐。而且,当错误发生时,很难进行调试,因为我们可能要分析所有的数据类型来找到错误的来源。

总而言之,类型可以在一定程度上帮助消除这种繁琐的过程:

def concat(a: int, b: int) -> int:
    return a + b
复制代码

现在我们知道 a 和 b 是整数。我们可以轻易的解释函数输出是什么!它易于理解,易于调试!

出于这个原因,Flyte 需要使用类型。它支持强数据类型化,并且有一个健壮的类型系统来支持多种类型。

下面的例子展示了在 Flyte 中使用 dataclass json 作为类型:

import typing
from dataclasses import dataclass
from dataclasses_json import dataclass_json
from flytekit import task, workflow

@dataclass_json
@dataclass
class Datum(object):
     """     Example of a simple custom class that is modeled as a dataclass.
     """     x: int
     y: str
     z: typing.Dict[int, str]@task
def stringify(x: int) -> Datum:
     """
     A dataclass return will be regarded as a complex single JSON return.
     """
     return Datum(x=x, y=str(x), z={x: str(x)})@task
def add(x: Datum, y: Datum) -> Datum:
     """
     Flytekit will automatically convert the passed in JSON to a DataClass. If the structures do not match, it will raise a runtime failure.
     """
     x.z.update(y.z)
     return Datum(x=x.x + y.x, y=x.y + y.y, z=x.z)@workflow
def wf(x: int, y: int) -> Datum:
     """
     Dataclasses (JSON) can be returned from a workflow.
     """
     return add(x=stringify(x=x), y=stringify(x=y))if __name__ == "__main__":
     """
     This workflow can be run locally. During local execution, the dataclasses are marshalled to and from json.
     """
     wf(x=10, y=20)
复制代码

扩展类型系统是很容易的,Flyte有很多自定义类型。

下面是 FlyteFile 的例子,它是 Flyte 中的自定义文件类型。

from flytekit import task, workflow
from flytekit.types.file import FlyteFile@task
def t1(f: FlyteFile) -> str:
     with open(f) as fp:
          data = fp.readlines()
     return data[0]@workflow
def wf() -> str:
     return t1(
          f="https://raw.githubusercontent.com/mwaskom/seaborndata/master/iris.csv"
     )if __name__ == "__main__":
     print(f"Running {__file__} main...")
     print(f"Running wf(), first line of the file is '{wf()}'")
复制代码

FlyteFile 尝试下载远程的 CSV 文件; 然而,代码示例中的 URL 并不存在(实际的 URL 是raw.githubusercontent.com/mwaskom/sea… )。

在运行代码时,我们看到以下错误:

flytekit.common.exceptions.user.FlyteAssertion: Failed to get data from
https://raw.githubusercontent.com/mwaskom/seaborndata/master/iris.csv to
/var/folders/6r/9pdkgpkd5nx1t34ndh1f_3q80000gn/T/flyteabhpuq8t/20211011_163211/local_flytekit/d50f36f4119018dda42d601f76ea0999/iris.csv (recursive=False).Original exception: Value error! Received: 404. Request for data
@ https://raw.githubusercontent.com/mwaskom/seaborndata/master/iris.csv failed.
Expected status code 200
复制代码

因此,通过 Flyte 类型系统,我们可以通过简化调试过程和代码可读性来避免 pipeline 出错。

我们明白,不是所有的软件都能在一夜之间迁移到使用类型。因此,我们一直在努力为Flyte中的类型系统提供足够的灵活性,增加对任意 Python 数据类型的支持,这将在 Flytekit 0.24.0 中实现。所以我们仍然建议你使用类型以保证可维护性。

可重现性和容错性

可重现性

从 ML 或数据pipeline的角度看,回滚到过去的版本是非常重要的,因为有时候前一个版本比新版本更好。机器学习是一个很大的领域,在这里我们可以通过多个 pipeline 和算法处理海量的数据。同样,数据 pipeline 处理大量的数据处理。在这种情况下,我们应该精心管理我们的工作(至少是代码和数据) ,由于机器学习固有的复杂性,这项任务变得更加复杂。

Flyte 本质上支持可重现性。可重现性是关于“再现”或“重用”我们的工作的能力。在 Flyte 中包含可重现性的想法出自 Flyte 团队的创始成员在 Lyft 所面临的问题。我们观察到的一个值得注意的例子是,我们的一位同事离开了 Lyft。这位同事开发了一个系统,可以用 Geohash 量化接货点和卸货点,并估计往返所需的时间。由于当时机器学习的特殊性质,为了重现结果,团队花了几个月的时间才恢复了原来的算法。

这就是为什么 Flyte 是为了确定性计算而建立的,而确定性计算在机器学习中起着至关重要的作用。我们希望我们的算法在给定相同的输入集的情况下产生相同的输出集。为了实现这一点,我们需要将相关的可重复性机制插入我们的系统。

以下是 Flyte 的可重现性支持:

  • 每个任务都在一个独立的环境中运行;这使得任务之间不会相互影响。
  • 使用Docker、Protobuf和强大的版本控制系统,对任务代码捕获快照。

容错性

当突如其来故障中断工作流时,我们不希望丢失服务。容错就是容忍失败。它使服务能够持续运行而不会出现问题。

在机器学习和数据 pipeline 中,代码和计算的复杂性要求提供容错性。Flyte 完全理解这一点; 它有一些内置的容错机制。因此,Flyte 确保平台在设计上具有可恢复性。

以下是 Flyte 的容错性支持:

  • 用户和系统重试
  • 暂停
  • 缓存/持久化
  • 保证恢复任何过去的工作流执行到故障点

增量开发

增量开发是一个逐步开发系统的过程。当通过机器学习或数据管道构建复杂模型时,我们希望通过在不同模块上一步一步地构建一个模块来实现。

Flyte 坚定地遵循渐进式发展。以下是一些例子:

  • 从本地开始,在 Python 中运行代码,然后继续将模型部署到生产环境中
  • 在远程 Flyte 中运行单个任务,然后运行一个工作流
  • Flyte 支持使用域:开发、暂存和生产
  • 运行整个工作流程,缓存成功的步骤,并对失败的步骤进行迭代

缓存

缓存有助于更快地检索工作、更快地执行和最小化计算资源的浪费。当需要用相同的输入重复执行相同的任务时,缓存非常有用。

Flyte 有一个简洁的启用缓存的方法,下面是一个例子:

@task(cache=True, cache_version="1.0")
def fetch_dataset(...) -> ...:
复制代码

cache_version 字段表示任务功能已经改变。提升 cache_version 则类似于使 cache 失效。

总结

这篇文章只是略述了 Flyte 的能力。它是一个 K8s原生的平台,支持端到端的工作流编排,从将原始数据转换为可用的形式,到在生产中部署模型以服务用户。一旦部署到系统上,它就成为机器学习和数据处理 pipeline 的完美伙伴。

Supongo que te gusta

Origin juejin.im/post/7067188767383093278
Recomendado
Clasificación