【已开源】字节跳动模型大规模部署实战(文末相关岗位放出)
1.背景介绍
在字节跳动,基于深度学习的应用遍地开花,工程师关注模型效果的同时也需要关注线上服务一致性和性能,早期这通常需要算法专家和工程专家分工合作并紧密配合来完成,这种模式存在比较高的 diff 排查验证等成本。
随着 PyTorch/TensorFlow 框架的流行,深度学习模型训练和在线推理完成了统一,开发者仅需要关注具体算法逻辑,调用框架的 Python API 完成训练验证过程即可,之后模型可以很方便的序列化导出,并由统一的高性能 C++ 引擎完成推理工作。提升了开发者训练到部署的体验。
然而,完整的服务通常还存在大量的预处理/后处理等业务逻辑,这类逻辑通常是把各种输入经过加工处理转变为 Tensor,再输入到模型,之后模型的输出 Tensor 再加工成目标格式,一些典型的场景如下:
- Bert
Resnet
- 性能:此方案大多基于虚拟机实现,虚拟机方案灵活并且非常可控,但深度学习框架中的虚拟机大多通常性能不够优良。补充说明一下,框架早期都是为 Tensor 计算设计,数组计算每个算子成本很高,虚拟机的派发和调度成本可以忽略。但是,移植到程序语言编程层面开销难以忽略,代码写多了就会成为性能瓶颈。据测试,TorchScript 解释器性能只有 Python 的 1/5 左右,tf.function 性能更差一些。
- 功能不全:事实上应用到真实场景中,我们仍然可以找出很多 tf.function/TorchScript 不支持的重要功能,比如:自定义的资源不能打包,只能序列化内置类型;字符串只能做 bytes 处理,中文等 unicode 会造成 diff;容器必须同构,不支持自定义类型等等...
最底层是纯 C++/CUDA 的基础库,由高性能算子专家开发。
在基础库之上,准守约定封装出来 Python 的 库,可以用在 training 过程中。
需要 inferencing 时,利用 MATXScript 可以把 Python 代码,翻译成对等的 C++ 代码,编译成动态链接库,加上模型及其他依赖的资源,一起打包发布即可。
此处以最简单的英文文本预处理为例,展示一下 MATXScript 如何使用。
编写一个基本的查字典的逻辑
classText2Ids:
def__init__(self) -> None:
self.table: Dict[str, int] = {
"hello": 0,
"world": 1,
"[UNK]": 2,
}
deflookup(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]
编写 Pipeline
import matx
classWorkFlow:
def__init__(self):
# 此处会进行代码编译,Python 代码自动编译封装为 Callable 对象
self.text2ids = matx.script(Text2Ids)()
defprocess(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<string>
#include<vector>
#include<map>
#include<iostream>
#include<matxscript/pipeline/tx_session.h>
usingnamespace ::matxscript::runtime;
intmain()
{
// test case
std::unordered_map<std::string, RTValue> feed_dict;
feed_dict.emplace("texts", Unicode(U"hello world"));
std::vector<std::pair<std::string, RTValue>> result;
constchar* module_path = "./my_dir";
constchar* 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;
}
}
return0;
}
a. text
from typing import List, Dict, Tuple
import libcut
import matx
classVocabulary:
...
defutf8_decoder(s: List[bytes]):
return [x.decode() for x in s]
classTextNDArrayBuilder:
...
classTextPipeline:
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)
defprocess(self, text: List[bytes]):
# List[bytes] 是对齐 C++ 的 vector<string>
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
classVisionPipeline:
def__init__(self,
device_id: int = 0,
mode: str = "eval",
image_size: int = 224,):
self.is_training = mode == 'train'
self.mode = mode
...
defprocess(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()
classMultimodalEvalPipeline:
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)
defeval(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')
#include<string>
#include<vector>
#include<map>
#include<iostream>
#include<matxscript/pipeline/tx_session.h>
usingnamespace ::matxscript::runtime;
intmain()
{
// test case
std::unordered_map<std::string, RTValue> feed_dict;
feed_dict.emplace("texts", List({String("hello world")}));
feed_dict.emplace("images", List({String("......")}));
std::vector<std::pair<std::string, RTValue>> result;
constchar* module_path = "/path/to/my_multimodal";
constchar* 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;
}
}
return0;
}
联邦学习和隐私计算 推荐广告大规模分布式训练系统(包括 GPU 训练系统) 推荐广告大规模 GPU 推理系统 融合多种硬件和训练框架的编译器研究(ByteIR) 自动推理性能优化(TVM, XPerf) 分布式训练系统中的通信模块(BytePS) 分布式机器学习调度系统 通用机器学习平台 推荐/广告/搜索的算法研究:更大的算力,更复杂的模型 大规模多模态预训练 图神经网络 AutoML/NAS 分子动力学模拟和应用 计算机视觉:OCR 等
了 解 更 多
点击「阅读原文」,加入字节跳动 AML 团队
最新评论
推荐文章
作者最新文章
你可能感兴趣的文章
Copyright Disclaimer: The copyright of contents (including texts, images, videos and audios) posted above belong to the User who shared or the third-party website which the User shared from. If you found your copyright have been infringed, please send a DMCA takedown notice to [email protected]. For more detail of the source, please click on the button "Read Original Post" below. For other communications, please send to [email protected].
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。