谁说 Python 搞不定 AI 模型微服务?!Towhee 来了

作者 | 郭人通

“从用 Python 定义流水线,到生成 Docker 镜像,再到启动服务并调用执行,一共不到 30 行代码!?”想要模型落地,有一连串大坑躲都躲不开:

模型转不了 ONNX,TensorRT。

模型推理搞的飞快之后,发现预处理、后处理才是瓶颈。

除了需要搜寻各种英伟达的算法库,还需要学习各种高性能计算和 CUDA 编程技术,对着图片解码、视频解码、tensor transform、分词之类的操作一顿猛搞。

发现英伟达老爷的东西“又高又硬”,根本装不起来。

不仅要写一堆脚本程序,努力的把一整条处理流水线串起来,更要设计好服务的调用接口。

串完发现流水线各级速度不匹配,想着把慢的阶段起多实例并行,又扯上了负载均衡和流水线优化。

流水线上的算法实例起多了,物理资源又不够了。

一切内容终于准备就绪了,竟然又要去学写 Dockerfile ......

为了解决上面一连串的问题,我们发起了一个开源项目 Towhee,任何用户都能够从 Python 代码一键构建面向生产的高性能推理流水线。这个项目提供了一套优雅的函数式 Python 编程接口,以及一组覆盖日常工作所需要的工具集,只需要几行代码,就能够自动的解决上述问题:将推理流水线内出现的代码(模型、算法、数据处理过程等)转换成对应的高性能实现,组织端到端的推理服务代码,一键生成 Docker 镜像。

下面举一个小例子演示如何使用 Towhee:用 CLIP 做图片 embedding 特征提取。从定义流水线到生成 Docker 镜像,再到启动服务并调用执行,一共不到 30 行代码。

首先,用 Python 定义一个“流水线”:

流水线接受图片作为输入,输出图片对应的 embedding 向量。这个流水线包含两个算子 image_decode.cv2_rgb 和 image_text_embedding.clip_image,分别负责图片的解码以及 CLIP 的图片特征提取。

例子里对流水线进行了配置,通过 pipeline_config 的 format_priority 参数给到 Towhee 一个提示,告诉 Towhee 优先把模型转到 TensorRT 的格式去做推理,如果失败就尝试走 ONNX。

image_decode.cv2_rgb 的算子配置是 num_instance=4,让 Towhee 在流水线中起4个图片解码的实例提高并发。image_text_embedding.clip_image 的算子配置是 dev_id=0,让 Towhee 在 GPU0 上跑 CLIP 的推理。如果指定了使用 GPU 进行模型推理,Towhee 会自动的选择 GPU 版本的模型前后处理过程,并在同一块 GPU 设备上执行前处理、模型推理、后处理。更多流水线配置能力详见官方文档(https://docs.towhee.io/)。

接下来,我们通过 Towhee 把这个流水线制作成 Docker 镜像,这里选用 Nvidia Triton 作为高性能推理服务框架:

命令执行完毕,我们将得到一个名为 clip_image_embedding:v1 的镜像,使用下面的命令启动服务实例:

在完成了模型服务启动之后,我们在本地编写一个简单的测试程序,通过调用刚才启动的服务,对当前目录下 jpg 图片做 embedding 特征提取:

如果你想进一步做一个基于 CLIP 的 “文本-图像跨模态” 召回服务,我们还可以再添加一个用 “CLIP 提取文本 embedding 特征” 的流水线,并进一步丰富服务的调用代码,完整程序不会超过 100 行 Python 代码。(这里因为和上面的示例代码比较接近,就不展开赘述了,感兴趣的同学可以自行尝试,遇到问题欢迎到 Slack 或微信反馈,链接见文章末尾)

除了对大量工程细节的极致封装,Towhee 在性能方面也有不俗的表现。在我自己的机器上(64核CPU,3080单卡),使用 HuggingFace 的 CLIP 预训练模型和配套工具库实现了同样功能的流水线,并和 Towhee 的流水线进行了性能对比:

HuggingFace 与 Towhee 的流水线性能对比

经过多次实验,HuggingFace 流水线的平均 QPS 67,Towhee 流水线的平均 QPS 328。Towhee 的流水线有接近 5 倍的提速。也就是说,通过使用 Towhee,仅用一块卡就可以达到之前五块卡的服务能力,两个字,省钱!

接下来,继续分享一些 Towhee 中的重要组件和实用工具:Towhee Operator Hub、DataCollection 编程接口、高性能推理服务,以及项目的 Roadmap。

Towhee Operator Hub

Towhee 的 Hub 不叫 Model Hub,而是叫 Operator Hub。主要原因在于,Towhee 是面向端到端的流水线构建,不仅仅是聚焦神经网络模型,还更加关注算法模块功能的丰富度、高性能推理、标准的任务接口。

算法模块的丰富度:Towhee 中的算法模块,包括神经网络模型,也包括各种五花八门的数据处理方法,如音视频编解码、图片裁切、文本分词、向量降维、数据库操作等。

Towhee 关注的算法来源主要有四类:

应用场景所需的通用算法或数据处理方法;

各类顶会以及arXiv上受到广泛关注的前沿模型;

Paper with Code 关键领域的 benchmark 上榜模型;

业务领域常用的算法库、模型库集成。

目前,Towhee 已经覆盖计算机视觉、自然语言处理、音频、生物医药、多模态五大领域的十多项任务,120多种模型结构,700多个预训练模型。查看 Towhee 支持的 Operator 列表(https://github.com/towhee-io/towhee)。

高性能推理:Towhee Hub 上的算法模块除了提供预训练模型以及Fine-tuning 能力,会更加聚焦流水线落地后的推理性能。Towhee 会为常用算法或前沿模型提供对应的高性能实现,根据 Python 侧流水线的定义,在推理服务的构建期进行自动化替换。这些加速支持包括:

模型转 ONNX、TensorRT;

前后处理的多线程、GPU 加速;

常见的数据处理过程加速,如图片、音频、视频解码加速等。

标准的任务接口:Towhee 会按照任务类型对算子进行模块化组织。任务类型确定了标准的调用接口,并通过算子提供这些接口的不同实现。

在 Towhee Hub 中 Operator 的组织方式类似 PapersWithCode,相同任务的 Operator 会组织在一个分类下,结构目录清晰、方便阅读和使用。目前,Towhee 社区也在尝试逐步覆盖 Papers with Code 上的重点任务,支持用户对 SOTA 模型的快速试用,避免用户在 “读懂论文-看懂论文作者的初版实现-面向工程落地的代码重构-PoC-性能优化” 这个链条上的重复造轮子。Towhee 会对已支持的模型添加 Papers with Code 链接。如果在 Papers with Code 模型页面的 'Code' 部分看到 towhee-io/towhee,就说明在 Towhee 可以对该模型进行试用。例如跨模态视频召回的 CLIP4Clip 模型。

Towhee Hub 上的每一个 Operator repo,既是一个模型的代码仓库,也是一个直接可调用的模块。如前面的例子中用到的 image_text_embedding.clip_image 算子,对应 Hub 上 image-text-embedding/clip_image 这个 repo。这个仓库的用户名和仓库名分别与流水线调用中的包名 image_text_embedding、算子名 clip_image 对应。Towhee 规定了一套 Operator 代码仓库的接口协议。只要代码仓库的组织方式遵守这套协议规范,就可以通过 Towhee 提供的工具,将仓库代码自动化打包,并可以通过 "用户名.仓库名" 的方式对算法模块直接加载调用。

任务接口标准化有一个非常大的优势是可以灵活切换模型,而不用担心引入额外的工程量。这对于快速 PoC 以及流水线升级都很重要。在原型阶段,可以根据业务逻辑确定需要哪些类型的任务,由于相同任务下的不同算子具有一致的调用接口,因此可以乐高积木式的自由组合,进行快速的效果尝试。在生产环境如果需要对流水线的某个 Operator 升级,也只需要在流水线的定义中替换相应的 Operator,通过 Towhee 重新构建 docker image 即可。

目前 Towhee 社区也在收集用户的算子需求,在每个版本中进行汇总并更新实现。有这方面需求的同学可以加项目的Slack/微信群进行讨论或提 github issue。相关链接在文章末尾。

DataCollection 编程接口

Operator 是乐高积木,DataCollection 接口则是强力胶,用来连接积木。通过 DataCollecton 接口,Towhee 用户可以轻松构建推理流水线。DataCollection 在逻辑上的定义是一个带 Schema 的非结构化数据表格,每一行对应一个数据实体(Entity),每一列对应一种数据属性。一个流水线对应着一组数据属性操作,每个 Operator 接受一到多个数据属性作为输入,并产生新的数据属性作为输出。

DataCollection 主要有三个作用:

在原型阶段,用于构建可直接本地执行的流水线;

用于定义面向生产部署的 DAG;

作为 driver program 连通业务逻辑与流水线服务。

在文章一开始 CLIP 的例子中,已经简单演示了后两个用法。这里对流水线的本地快速原型构建做一个简单的介绍。CLIP 这个例子的本地可执行版本写出来是这样:

代码中方括号的部分指定了 schema 的相关信息。这个例子中 3 个 Operator 够成了一个收尾相连的链,当然,还可以通过 DataCollection 定义一些比 operator-chain 更复杂的 DAG:

这个例子中,我们先通过 YOLO v5 检测图片中出现的 objects,然后在原图中将这些 objects 裁切出来,并使用 CLIP 分别对这些 objects 的图片进行 embedding feature 的提取。这个 DAG 画出来是这个样子:

CLIP 示例对应的 DAG

高性能推理服务

Towhee 项目的目标不是打造一款全新的推理引擎,而是聚焦于让现有的高性能推理引擎更加落地。目前,Towhee 的推理服务适配了 Nvidia Triton 推理框架,主要特性包括:

推理流水线 DAG。相比常见的推理引擎只支持 “单纯的神经网络模型推理”,或 “前处理-模型推理-后处理 三阶段流水线”,DAG 型的流水线可以覆盖更丰富的数据处理组合逻辑。

异构的推理后端。DAG 中的每一个算子节点可以在不同的后端引擎上执行,既包括神经网络推理引擎,如 Pytorch(TorchScript),ONNXRuntime, TensorRT;也包括通用的数据处理过程,如 Python 函数,DALI,或自定义Python/C++后端;还包括数据库/索引连接器,如 Milvus,FAISS。

极致的推理性能。推理服务可在 NVIDIA GPU,x86/ARM CPU 上高效运行,支持多 GPU,支持多模型实例,支持实时、流式、批处理、动态批处理请求,支持流水线自动并行。

Docker 与 Kubernetes 部署。推理流水线都以 Docker 镜像的方式进行封装,对外提供 grpc 与 http 接口,隐藏大量内部复杂性。可通过 Kubernetes 轻松部署推理微服务。

在支持上述特性的同时,Towhee 为用户完成了大量自动化工作,包括逻辑图到物理图的映射,物理图到异构执行后端的映射,以及运行期调度。

DAG 的映射与自动优化

上图给出了 CLIP 这个例子从流水线定义到生成推理流水线服务的过程。从上到下,整个自动化构建与执行过程共分三个阶段,分别是逻辑图(编译期),物理图(编译期),以及执行后端(运行期)。逻辑图是通过对 DataCollection 进行 Trace 所获得的关于图定义的完整元信息。物理图会对逻辑图中各个算子节点进行物理实现的绑定,如 image_decode 节点在物理层会以 Python model 的方式执行 OpenCV 程序,CLIP 对应的图片前处理过程在物理层会绑定 DALI 实现,CLIP 模型在物理层会绑定 TensorRT 实现。同时,这些算子节点会在物理层完成 ensemble 的相关配置、实例个数配置、以及物理资源的分配。在运行期,会根据物理图上各个节点的物理绑定,适配执行后端,以及物理内存与计算资源,并根据图上各个算子节点的依赖关系,驱动流水线的自动化执行。

Roadmap

Towhee 项目的 Roadmap 可以在官网(https://towhee.io/)查看。目前,Towhee 项目还在快速迭代与重度研发的阶段。项目 Roadmap 上的关键内容有:

前沿算法模型

模型方面,近期会聚焦 CVPR 2022 新出炉的前沿成果,如 CV 大神何恺明的 MAE,香港大学、UC Berkeley、腾讯合作的 MCQ 等。同时,会集成 AutoEncoder ,提供向量降维能力。对于中文跨模态搜索、多模态召回、代码等数据上的任务类型也会有模型更新。

进展详情请关注项目的 Model ChangeLog。

计算机视觉:

MAE:Masked Autoencoders Are Scalable Vision Learners CVPR2022 (backbone)

SVT:Self-supervised Video Transformer (action recognition)

TransRAC:Encoding Multi-scale Temporal Correlation with Transformers for Repetitive Action Counting (repetitive action counting)

CoFormer:Collaborative Transformers for Grounded Situation Recognition (action recognition/grounded action counting)

MCQ:Bridging Video-text Retrieval with Multiple Choice Question (text video retrieval)

STRM:Spatio-temporal Relation Modeling for Few-shot Action Recognition (action recognition)

多模态:

CoOp:Conditional Prompt Learning for Vision-Language Models (visual language task)

CVNet:Correlation Verification for Image Retrieval (image retrieval)

代码文件:

CodeBERT:A Pre-Trained Model for Programming and Natural Languages

CodeGen:A Conversational Paradigm for Program Synthesis

Embedding 向量降维:

Autoencoder

编程接口与工具

更加统一的 DataCollection 批处理接口与流处理接口。

支持自定义的算子。Towhee 目前给出了比较丰富的标准算子模块,但在常见的业务流程中,通常会引入业务逻辑,自定义算子可以很好的满足这部分需求。Towhee 的自定义算子将会包含两种形态,一种是通过 python 的 lambda 表达式生成,另一种是通过继承 Operator 基类生成。

算子的打包与安装工具升级。支持基于 pip 的打包与安装,并提供基于 AWS S3 的模型参数下载服务,优化本地模型参数缓存。

推理服务优化

支持视频与音频的推理流水线。长视频或长音频的处理往往会耗费大量物理资源,针对这类场景,Towhee 将不再一次性展开帧数据,而是提供流式的帧数据处理,以降低内存/显存需求。

在推理服务构建中完整支持 Schema。

基于 numba 的 python 算子自动优化 (实验阶段)。Towhee 通过自定义算子支持业务相关的逻辑,我们鼓励用户使用 python 来快速构建这些逻辑,同时,Towhee 会在底层对这些 python 代码进行尽可能的优化:基于 numba,使用LLVM编译技术解释字节码,优化 IR 并翻译到目标代码进行执行。

本地执行优化

支持基于 Apache Arrow 的列存数据,提高本地内存利用率,支持连续内存上的向量化执行。

Transformer 模型结构的执行性能优化(实验阶段)。

Github:

https://github.com/towhee-io/towhee

项目主页:

https://towhee.io/tasks/operator

Slack:

https://slack.towhee.io/

Towhee 团队 2023 秋季招聘已经启动

原文地址:https://www.toutiao.com/article/7130112109807206920/

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Python中的函数(二) 在上一篇文章中提到了Python中函数的定义和使用,在这篇文章里我们来讨论下关于函数的一些更深的话题。在学习C语言函数的时候,遇到的问题主要有形参实参的区别、参数的传递和改变、变量的作用域。同样在Python中,关于对函数的理解和使用也存在这些问题。下面来逐一讲解。一.函
Python中的字符串 可能大多数人在学习C语言的时候,最先接触的数据类型就是字符串,因为大多教程都是以"Hello world"这个程序作为入门程序,这个程序中要打印的"Hello world"就是字符串。如果你做过自然语言处理方面的研究,并且用Python
Python 面向对象编程(一) 虽然Python是解释性语言,但是它是面向对象的,能够进行对象编程。下面就来了解一下如何在Python中进行对象编程。一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法。 类是对现实世界中一些事物的封装,
Python面向对象编程(二) 在前面一篇文章中谈到了类的基本定义和使用方法,这只体现了面向对象编程的三大特点之一:封装。下面就来了解一下另外两大特征:继承和多态。 在Python中,如果需要的话,可以让一个类去继承一个类,被继承的类称为父类或者超类、也可以称作基类,继承的类称为子类。并且Pytho
Python中的函数(一) 接触过C语言的朋友对函数这个词肯定非常熟悉,无论在哪门编程语言当中,函数(当然在某些语言里称作方法,意义是相同的)都扮演着至关重要的角色。今天就来了解一下Python中的函数用法。一.函数的定义 在某些编程语言当中,函数声明和函数定义是区分开的(在这些编程语言当中函数声明
在windows下如何快速搭建web.py开发框架 用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方便和顺手,就是web.py。它由一名黑客所创建,但是不幸的是这位创建者于2013年自杀了。据说现在由
将Sublime Text 2搭建成一个好用的IDE 说起编辑器,可能大部分人要推荐的是Vim和Emacs,本人用过Vim,功能确实强大,但是不是很习惯,之前一直有朋友推荐SUblime Text 2这款编辑器,然后这段时间就试了一下,就深深地喜欢上这款编辑器了...
Python中的模块 有过C语言编程经验的朋友都知道在C语言中如果要引用sqrt这个函数,必须用语句"#include<math.h>"引入math.h这个头文件,否则是无法正常进行调用的。那么在Python中,如果要引用一些内置的函数,该怎么处理呢?在Python中
Python的基础语法 在对Python有了基础的认识之后,下面来了解一下Python的基础语法,看看它和C语言、java之间的基础语法差异。一.变量、表达式和语句 Python中的语句也称作命令,比如print "hello python"这就是一条语句。 表达式,顾名思义,是
Eclipse+PyDevʽjango+Mysql搭建Python web开发环境 Python的web框架有很多,目前主流的有Django、Tornado、Web.py等,最流行的要属Django了,也是被大家最看好的框架之一。下面就来讲讲如何搭建Django的开发环境。一.准备工作 需要下载的
在windows下安装配置Ulipad 今天推荐一款轻便的文本编辑器Ulipad,用来写一些小的Python脚本非常方便。 Ulipad下载地址: https://github.com/limodou/ulipad http://files.cnblogs.com/dolphin0520/u...
Python中的函数(三) 在前面两篇文章中已经探讨了函数的一些相关用法,下面一起来了解一下函数参数类型的问题。在C语言中,调用函数时必须依照函数定义时的参数个数以及类型来传递参数,否则将会发生错误,这个是严格进行规定的。然而在Python中函数参数定义和传递的方式相比而言就灵活多了。一.函数参数的
在Notepad++中搭配Python开发环境 Python在最近几年一度成为最流行的语言之一,不仅仅是因为它简洁明了,更在于它的功能之强大。它不仅能够完成一般脚本语言所能做的事情,还能很方便快捷地进行大规模的项目开发。在学习Python之前我们来看一下Python的历史由来,"Pytho
Python中的条件选择和循环语句 同C语言、Java一样,Python中也存在条件选择和循环语句,其风格和C语言、java的很类似,但是在写法和用法上还是有一些区别。今天就让我们一起来了解一下。一.条件选择语句 Python中条件选择语句的关键字为:if 、elif 、else这三个。其基本形式如
关于raw_input( )和sys.stdin.readline( )的区别 之前一直认为用raw_input( )和sys.stdin.readline( )来获取输入的效果完全相同,但是最近在写程序时有类似这样一段代码:import sysline = sys.stdin.readline()
初识Python 跟学习所有的编程语言一样,首先得了解这门语言的编程风格和最基础的语法。下面就让我们一起来了解一下Python的编程风格。1.逻辑行与物理行 在Python中有逻辑行和物理行这个概念,物理行是指在编辑器中实际看到的一行,逻辑行是指一条Python语句。在Python中提倡一个物理行只
当我们的代码是有访问网络相关的操作时,比如http请求或者访问远程数据库,经常可能会发生一些错误,有些错误可能重新去发送请求就会成功,本文分析常见可能需要重试的场景,并最后给出python代码实现。
1.经典迭代器 2.将Sentence中的__iter__改成生成器函数 改成生成器后用法不变,但更加简洁。 3.惰性实现 当列表比较大,占内存较大时,我们可以采用惰性实现,每次只读取一个元素到内存。 或者使用更简洁的生成器表达式 4.yield from itertools模块含有大量生成器函数可
本文介绍简单介绍socket的常用函数,并以python-kafka中的源码socketpair为例,来讲解python socket的运用
python实践中经常出现编码相关的异常,大多网上找资料而没有理解原理,导致一次次重复错误。本文对常用Unicode、UTF-8、GB2312编码的原理进行介绍,接着介绍了python字符类型unicode和str以及常见编解码错误UnicodeEncodeError和UnicodeDEcodeEr