随着 pytorch/tensorflow 框架的流行,深度学习模型训练和在线推理完成了统一,开发者仅需要关注具体算法逻辑,调用框架的 python api 完成训练验证过程即可,之后模型可以很方便的序列化导出,并由统一的高性能 c++ 引擎完成推理工作。提升了开发者训练到部署的体验。
然而,完整的服务通常还存在大量的预处理/后处理等业务逻辑,这类逻辑通常是把各种输入经过加工处理转变为 tensor,再输入到模型,之后模型的输出 tensor 再加工成目标格式,一些典型的场景如下:
bertresnet
我们的目标就是为以上端到端的过程,提供自动化且统一的训练、推理方案,减轻人工开发推理过程、对齐 diff 等一系列问题,实现大规模的统一部署方案。
二. 核心问题pytorch/tensorflow 等框架相对已经解决了模型的训练/推理统一的问题,因此模型计算本身不存在训推一体的问题了(算子性能优化不在本次讨论范围)。
核心要解决的问题就是:预处理和后处理需要提供高性能训推一体的方案。
对于此类逻辑,tensorflow 2.x 提供了 tf.function(还不完善),pytorch 提供了 torchscript,其无一例外都是选择了原生 python 语法子集。 但即使强大如此,仍然存在不可忽略的问题:
性能:此方案大多基于虚拟机实现,虚拟机方案灵活并且非常可控,但深度学习框架中的虚拟机大多通常性能不够优良。补充说明一下,框架早期都是为 tensor 计算设计,数组计算每个算子成本很高,虚拟机的派发和调度成本可以忽略。但是,移植到程序语言编程层面开销难以忽略,代码写多了就会成为性能瓶颈。据测试,torchscript 解释器性能只有 python 的 1/5 左右,tf.function 性能更差一些。功能不全:事实上应用到真实场景中,我们仍然可以找出很多 tf.function/torchscript 不支持的重要功能,比如:自定义的资源不能打包,只能序列化内置类型;字符串只能做 bytes 处理,中文等 unicode 会造成 diff;容器必须同构,不支持自定义类型等等...再者,还有很多非深度学习任务,比如在自然语言处理中仍然有很多非深度学习的应用或者子任务,如序列标注,语言模型解码,树模型的人工特征构造等任务,这些通常具有更灵活的特征范式,但同时都没有完整实现端到端的训推一体方案,仍然有大量的开发以及正确性校验工作。
为了解决上述问题,我们开发了一套基于编译的预处理方案:matxscript!
三. matxscript在深度学习算法开发中,开发者通常使用 python 进行快速迭代和实验,同时使用 c++ 开发高性能的线上服务,其中正确性校验和服务开发都会成为较重负担!
matxscript(https://github.com/bytedance/matxscript) 是一个 python 子语言的 aot 编译器,可以自动化将 python 翻译成 c++,并提供一键打包发布功能。使用 matxscript 可以让开发者快速进行模型迭代的同时以较低成本完成高性能服务的部署。
核心架构如下:
最底层是纯 c++/cuda 的基础库,由高性能算子专家开发。在基础库之上,准守约定封装出来 python 的 库,可以用在 training 过程中。需要 inferencing 时,利用 matxscript 可以把 python 代码,翻译成对等的 c++ 代码,编译成动态链接库,加上模型及其他依赖的资源,一起打包发布即可。其中,编译器作用非常关键,其核心流程如下:
通过以上流程,用户所编写的预处理代码,可以被编译成 pipeline 中的一个 jitop,为了把前后处理和模型联动,我们还开发了 tracing 系统(接口设计上参考了 pytorch),架构如下:
基于 matxscript,我们可以训练和推理使用同一套代码,大大降低了模型部署的成本。同时,架构和算法得到了解耦,算法同学完全使用 python 工作即可,架构同学专注于编译器开发及 runtime 优化,在字节跳动,此方案得到了大规模部署验证!
四. 小试牛刀此处以最简单的英文文本预处理为例,展示一下 matxscript 如何使用。
目标:把一段英文文本转成 indexes
编写一个基本的查字典的逻辑class text2ids: def __init__(self) -> none: self.table: dict[str, int] = { hello: 0, world: 1, [unk]: 2, } def lookup(self, word: str) return self.table.get(word, 2) def__call__ (self, words: list[str]) return [self.lookup(w) for w in words]
编写 pipelineimport matx class workflow: def __init__(self): # 此处会进行代码编译,python 代码自动编译封装为 callable 对象 self.text2ids = matx.script(text2ids)() def process(self, texts): ids = self.text2ids(texts) return ids # test handler = workflow() print(handler.process(hello world unknown)) # output: [0, 1, 2]
trace 导出到 磁盘# dump mod = matx.trace(handler.process, hello world) print(mod.run({texts: hello world})) mod.save('./my_dir') # load mod = matx.load('./my_dir', -1) print(mod.run({texts: hello world}))
c++ 加载#include #include #include #include #include using namespace ::matxscript::runtime; int main() { // test case std::unordered_map feed_dict; feed_dict.emplace(texts, unicode(uhello world)); std::vector result; const char* module_path = ./my_dir; const char* module_name = model.spec.json; { // -1 mean cpu auto sess = txsession::load(module_path, module_name, -1); auto result = sess->run(feed_dict); for (auto& r : result) { std::cout << key: << r.first << , value: << r.second
七. 更多信息我们是字节跳动-aml-机器学习系统团队,致力于为公司提供统一的高性能训推一体化框架,同时也会通过火山引擎机器学习平台服务于合作企业,火山引擎机器学习平台预计 2023 年起提供 matx 的相关支持,包括预置镜像环境、常用场景的公开样例、企业接入和使用过程中的技术保障等,可以达到训练和推理场景低成本加速和一体化的效果。欢迎在 https://www.volcengine.com/product/ml-platform 详细了解我们的产品。
以上就是字节跳动模型大规模部署实战的详细内容。