1. 大背景如是说
在二进制颤动,如前所述广度自学的应用领域Engilbert,技师高度关注数学模型效用的同时也须要高度关注圣戈当斯区服务项目连续性和操控性,晚期这一般来说须要演算法研究者和工程建设研究者相互配合并相互配合来顺利完成,这种商业模式存有较为高的 diff 摸查校正等生产成本。
随著 PyTorch/TensorFlow 架构的盛行,广度自学数学模型体能训练和新浪网方法论推理顺利完成了标准化,合作开发人员仅须要高度关注具体内容演算法方法论,初始化架构的 Python API 顺利完成体能训练校正操作过程方可,后数学模型可以很方便快捷的文件格式化求出,并由标准化的高效能 C++ 发动机顺利完成方法论推理工作。提高了合作开发人员体能训练到布署的新体验。
不过,完备的服务项目一般来说还存有大批的后处置/后处置等销售业务方法论,这类方法论一般来说是把各种输入历经研磨处置变革为 Tensor,再输入到数学模型,后数学模型的输入 Tensor 再研磨成最终目标文件格式,一些众所周知的情景如下表所示:
BertResnet我们的最终目标是为以上起新端操作过程,提供更多智能化且标准化的体能训练、方法论推理计划,减低育苗合作开发方法论推理操作过程、翻转 diff 等一连串难题,同时实现小规模的标准化布署计划。
2. 核心理念难题
PyTorch/TensorFlow 等架构相较已经化解了数学模型的体能训练/方法论推理标准化的难题,因此数学模型排序这类不存有训推多功能的问题了(微分操控性强化无此此次探讨覆盖范围)。
核心理念要化解的难题是:后处置和后处置须要提供更多高效能训推多功能的计划。
对于此类方法论,TensorFlow 2.x 提供更多了 tf.function(还不完善),PyTorch 提供更多了 TorchScript,其无一例外都是选择了原生 Python 语法子集。 但即使强大如此,仍然存有不可忽略的难题:
操控性:此计划大多如前所述虚拟机同时实现,虚拟机计划灵活并且非常可控,但广度学习架构中的虚拟机大多一般来说操控性不够优良。补充说明一下,架构晚期都是为 Tensor 排序设计,数组排序每个微分生产成本很高,虚拟机的派发和调度生产成本可以忽略。但是,移植到程序语言编程层面开销难以忽略,代码写多了就会成为操控性瓶颈。据测试,TorchScript 解释器操控性只有 Python 的 1/5 左右,tf.function 操控性更差一些。功能不全:事实上应用领域到真实情景中,我们仍然可以找出很多 tf.function/TorchScript 不支持的重要功能,比如:自定义的资源不能打包,只能文件格式化内置类型;字符串只能做 bytes 处置,中文等 unicode 会造成 diff;容器必须同构,不支持自定义类型等等…再者,还有很多非广度自学任务,比如在自然语言处置中仍然有很多非广度自学的应用领域或者子任务,如序列标注,语言数学模型解码,树数学模型的育苗特征构造等任务,这些一般来说具有更灵活的特征范式,但同时都没有完备同时实现起新端训推多功能计划,仍然有大批的合作开发以及正确性校验工作。
为了化解上述难题,我们合作开发了一套如前所述编译的后处置计划:MATXScript!
3. 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 强化,在二进制颤动,此计划得到了小规模布署校正!
4. 小试牛刀
此处以最简单的英文文本后处置为例,展示一下 MATXScript 如何使用。
最终目标:把一段英文文本转成 indexes
1.编写一个基本的查字典的方法论
class Text2Ids: def __init__(self) -> None: self.table: Dict[str, int] = { “hello”: 0, “world”: 1, “[UNK]”: 2, } def lookup(self, word: str) -> int: return self.table.get(word, 2) def __call__ (self, words: List[str]) -> List[int]: return [self.lookup(w) for w in words]
2.编写 Pipeline
import 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]
3.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”}))
4.C++ 加载
#include #include #include #include #include using namespace ::matxscript::runtime; int main() { // test case std::unordered_map feed_dict; feed_dict.emplace(“texts”, Unicode(U”hello 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 << std::endl; } } return 0; }
完备的代码见:https://github.com/bytedance/matxscript/tree/main/examples/text2ids
小结:以上是一个非常简单的纯 Python 同时实现的后处置方法论,且能被一段通用的 C++ 代码加载运行,下面我们结合数学模型展示一个实际的多模态起新端案例!
5. 多模态案例
此处以图文多模态(Bert+Resnet)为例,数学模型使用 PyTorch 编写,展示体能训练和布署中实际的工作。
配置环境a. 配置 gcc/cuda 等基础设施(一般来说是运维同学已经搞定)b. 安装 MATXScript 及如前所述此合作开发的基础库(text、vision等)编写数学模型代码a. 此处省略,大家可以参考论文或其他开源同时实现自行搞定编写后处置代码a. text
from typing import List, Dict, Tuple import libcut import matx class Vocabulary: … def utf8_decoder(s: List[bytes]): return [x.decode() for x in s] class TextNDArrayBuilder: … class TextPipeline: def __init__(self, mode: str = “eval”): self.mode = mode self.cut_engine = libcut.Cutter(/path/to/cut_models, …) self.vocab = matx.script(Vocabulary)(/path/to/vocab.txt) self.decoder = matx.script(utf8_decoder) self.input_builder = matx.script(TextNDArrayBuilder)(self.vocab) def process(self, text: List[bytes]): # List[bytes] 是翻转 C++ 的 vector text: List[str] = self.decoder(text) words: List[List[str]] = self.cut_engine(text) batch_ids: List[List[int]] = self.vocab(words) input_ids, segment_ids, mask_ids = self.input_builder(batch_ids, 32) if self.mode == “train”: return input_ids.torch(), segment_ids.torch(), mask_ids.torch() return input_ids, segment_ids, mask_ids
b. vision
from typing import List, Dict, Tuple import matx from matx import vision class VisionPipeline: def __init__(self, device_id: int = 0, mode: str = “eval”, image_size: int = 224,): self.is_training = mode == train self.mode = mode … def process(self, image,): if self.is_training: decode_nds = self.random_crop_decode(image) flip_nds = self.random_flip(decode_nds) resize_nds = self.resize(flip_nds) transpose_nd = self.transpose_norm(resize_nds, vision.SYNC) else: decode_nds = self.decode(image) resize_nds = self.resize(decode_nds) crop_nds = self.center_crop(resize_nds) transpose_nd = self.transpose_norm(crop_nds, vision.SYNC) if self.mode == “trace”: return transpose_nd return transpose_nd.torch()
4.接入 DataLoadera. TextPipeline 可以当成一个正常的 Python Class 接入 Dataset 方可b. VisionPipeline 涉及到 GPU 后处置,更适合按 batch 进行处置,须要自己单独构造一个 DataLoader(这里埋个点,后会开源二进制颤动内部如前所述多线程的 DataLoader)
5.加上数学模型代码,开始体能训练吧
6.求出起新端 Inference Model
class MultimodalEvalPipeline: def __init__(self): self.text_pipe = TextPipeline(mode=”eval”, …) self.vision_pipe = VisionPipeline(mode=”eval”, …) self.torch_model = torch.jit.load(/path/to/multimodal.jit, map_location=cuda:0) self.tx_model_op = matx.script(self.torch_model, device=0) def eval(self, texts: List[bytes], images: List[bytes]) -> List[float]: input_ids, segment_ids, mask_ids = self.text_pipe.process(texts) images = self.vision_pipe.process(images) scores = self.tx_model_op(input_ids, segment_ids, mask_ids, images) return scores # examples example_batch_size = 8 text_examples = [hello, world.encode()] * example_batch_size with open(/path/image.jpg, rb) as f: image_example = f.read() image_examples = [image_example] * example_batch_size # pipeline instance pipe = MultimodalEvalPipeline(…) mod = matx.trace(pipe.eval, text_examples, image_examples) # test print(mod.run({“texts”: text_examples, “images”: image_examples})) # save mod.save(/path/to/my_multimodal)
小结:历经以上步骤,我们方可顺利完成起新端体能训练&发布工作,且整个操作过程是纯 Python 代码顺利完成的,可以完全由演算法同学自己控制。当然,如果数学模型排序这类还有操控性难题,也是可以在背后通过自动改图强化工作顺利完成。
注:完备代码示例见 https://github.com/bytedance/matxscript/tree/main/examples/e2e_multi_modal
6. 标准化Server
在上个章节,我们得到了一个演算法同学发布的数学模型包,本章节论述如果用标准化的服务项目进行加载和运行。
完备的 Server 包括:IDL 协议、Batching 策略、进/线程调度和排布、数学模型方法论推理…
这里,我们只探讨数学模型方法论推理这块,其他的都是可以按约定合作开发方可。我们以一个 main 函数来示例数学模型加载和运行的操作过程:
#include #include #include #include #include using namespace ::matxscript::runtime; int main() { // test case std::unordered_map feed_dict; feed_dict.emplace(“texts”, List({String(“hello world”)})); feed_dict.emplace(“images”, List({String(“……”)})); std::vector> result; const char* module_path = “/path/to/my_multimodal”; const char* module_name = “model.spec.json”; { // cuda:0 auto sess = TXSession::Load(module_path, module_name, 0); auto result = sess->Run(feed_dict); for (auto& r : result) { std::cout << “key: ” << r.first << “, value: ” << r.second << std::endl; } } return 0; }
以上代码是最简单的一个 C++ 加载多模态数学模型的案例,对 Server 合作开发的同学来说,只需进行简单的抽象和约定,方可把上述代码改造成一个标准化的 C++ 数学模型服务项目架构。
7. 更多信息
我们是二进制颤动-AML-机器自学系统团队,致力于为公司提供更多标准化的高效能训推多功能化架构,同时也会通过火山发动机机器自学平台服务项目于合作企业,火山发动机机器自学平台预计 2023 年起提供更多 MATX 的相关支持,包括预置镜像环境、常用情景的公开样例、企业接入和使用操作过程中的技术保障等,可以达到体能训练和方法论推理情景低生产成本加速和多功能化的效用。欢迎在 https://www.volcengine.com/product/ml-platform 详细了解我们的产品。
更多信息,可访问 Github:https://github.com/bytedance/matxscript
