在本文中,我们讨论了 Torchserve 的性能调优,以便在生产环境中部署模型。机器学习项目生命周期中最大的挑战之一是将模型部署到生产环境中。这需要可靠的托管解决方案以及解决 MLOps 需求的方法。一个健壮的托管解决方案需要提供多模型托管、模型版本控制、指标记录、监控和扩展以应对高峰流量。本文将概述 Torchserve 及其性能调优方法,以便用于生产场景。我们还将讨论 Meta 的 Animated Drawings 应用程序,该程序可以将您的素描人物转换为动画,并探讨它如何使用 Torchserve 应对高峰流量。Animated Drawing 的流程如下。
https://ai.facebook.com/blog/using-ai-to-bring-childrens-drawings-to-life/
许多 AI 系统和工具旨在处理人类的真实图像,而儿童的画作则增加了复杂性和不可预测性,因为它们通常以抽象、幻想的方式构建。这类形态和风格的变异甚至可能让擅长在逼真图像和绘画中识别对象的顶尖 AI 系统感到困惑。Meta AI 研究人员正在努力克服这一挑战,以便 AI 系统能够更好地识别儿童以各种方式创作的描绘人类形象的画作。这篇精彩的博客文章提供了更多关于动画绘画和所采取方法的细节。
Torchserve
一旦训练好模型,就需要将其集成到更大的系统中,以便拥有完整的应用,我们使用“模型服务”一词来指代这种集成。基本上,模型服务就是使您的训练模型可供运行推理和后续使用。
Torchserve 是 PyTorch 推荐的模型生产部署解决方案。它是一个性能出色且可扩展的工具,可以将您的模型封装在 HTTP 或 HTTPS API 中。它具有一个用 Java 实现的用户界面,负责处理多个任务,包括为模型服务分配工作者以及处理客户端和服务器之间的连接。Torchserve 拥有一个 Python 后端,负责处理推理服务。
Torchserve 支持多模型服务以及版本控制,用于 A/B 测试,动态批处理,日志记录和指标。它公开了四个 API,用于推理、解释、管理和指标。
推理 API 默认监听 8080 端口,可通过 localhost 访问,这可以在 Torchserve 配置中进行配置,并启用从模型获取预测。
解释 API 在底层使用 Captum 提供被服务模型的解释,同样监听 8080 端口。
管理 API 允许注册或注销并描述模型。它还使用户能够根据需要扩展或缩减服务模型的工人数。
默认情况下,度量 API 监听 8082 端口,使我们能够监控正在服务的模型。
Torchserve 让您能够扩展模型服务,并通过支持批量推理和多个服务模型的工人数来处理峰值流量。扩展可以通过管理 API 和配置文件中的设置来完成。此外,度量 API 可以帮助您通过默认和可自定义的指标监控模型服务。
其他高级设置,如接收请求的队列长度、输入批次的最大等待时间以及许多其他属性,都可以通过传递给 Torchserve 的配置文件进行配置。
使用 Torchserve 部署你的模型步骤
- 安装 Torchserve、模型归档工具及其依赖项。
- 选择适合你任务的默认处理器(例如图像分类等)或编写自定义处理器。
- 使用 Torcharchive 将你的模型工件(训练好的模型检查点以及加载和运行模型所需的所有其他文件)和处理器打包成“.mar”文件,并将其放置在模型存储中。
- 开始部署您的模型。
- 运行推理。在这里我们将更详细地讨论模型处理器和指标。
模型处理器
Torchserve 在后台使用处理器来加载模型,预处理接收到的数据,运行推理和后处理响应。Torchserve 中的处理器是一个 Python 脚本,其中包含了所有模型初始化、预处理、推理和后处理逻辑。
Torchserve 为图像分类、分割、目标检测和文本分类等多种应用提供开箱即用的处理器。它还支持自定义处理器,以防默认处理器不支持您的用例。
它在自定义处理器方面提供了极大的灵活性,这使得 Torchserve 成为一个多框架的托管工具。自定义处理器允许您定义自定义逻辑来初始化一个模型,该模型还可以用于从其他框架(如 ONNX)加载模型。
Torchserve 处理器由四个主要功能组成:初始化、预处理、推理和后处理,每个功能都返回一个列表。下面的代码片段展示了自定义处理器的一个示例。自定义处理器继承自 Torchserve 中的 BaseHandler,并可以覆盖任何主要功能。以下是一个用于加载 Detectron2 模型进行图像检测的处理器示例,该模型已导出为 Torchscript,并使用 model.half()以 FP16 运行推理,详细信息将在本帖的另一部分中解释。
class MyModelHandler(BaseHandler):
def initialize(self, context):
self.manifest = ctx.manifest
properties = ctx.system_properties
model_dir = properties.get("model_dir")
serialized_file = self.manifest["model"]["serializedFile"]
model_pt_path = os.path.join(model_dir, serialized_file)
self.device = torch.device(
"cuda:" + str(properties.get("gpu_id"))
if torch.cuda.is_available() and properties.get("gpu_id") is not None
else "cpu"
)
self.model = torch.jit.load(model_pt_path, map_location=self.device)
self.model = self.model.half()
def preprocess(self, data):
inputs = []
for request in batch:
request_body = request.get("body")
input_ = io.BytesIO(request_body)
image = cv2.imdecode(np.fromstring(input_.read(), np.uint8), 1)
input = torch.Tensor(image).permute(2, 0, 1)
input = input.to(self.device)
input = input.half()
inputs.append({"image": input})
return inputs
def inference(self,inputs):
predictions = self.model(**inputs)
return predictions
def postprocess(self, output):
responses = []
for inference_output in inference_outputs:
responses_json = {
'classes': inference_output['pred_classes'].tolist(),
'scores': inference_output['scores'].tolist(),
"boxes": inference_output['pred_boxes'].tolist()
}
responses.append(json.dumps(responses_json))
return responses
指标
在生产环境中部署模型的一个关键组件是能够对其进行监控。Torchserve 定期收集系统级指标,并允许添加自定义指标。
系统级指标包括 CPU 利用率、主机机器上的可用和已用磁盘空间以及内存,以及不同响应代码(例如 200-300、400-500 以及 500 以上)的请求数量。如此处所述,可以添加自定义指标到指标中。TorchServe 将这两组指标记录到不同的日志文件中。默认情况下,指标收集如下:
- 系统指标 - log_directory/ts_metrics.log
- 自定义指标 - log_directory/model_metrics.log
如前所述,Torchserve 还暴露了指标 API,默认监听端口 8082,并允许用户查询和监控收集的指标。默认的指标端点返回 Prometheus 格式的指标。您可以使用 curl 请求查询指标,或将 Prometheus 服务器指向端点并使用 Grafana 进行仪表板操作。
在服务模型时,您可以使用以下 curl 请求查询指标:
curl http://127.0.0.1:8082/metrics
如果您正在考虑导出记录的指标,请参考以下示例,该示例使用 mtail 将指标导出到 Prometheus。在仪表板上跟踪这些指标允许您监控性能回归,这些回归可能在离线基准测试运行期间是零星的或难以发现。
调整生产中模型性能时需要考虑的因素
图 1 中建议的工作流程是关于如何在生产中使用 Torchserve 进行模型部署的一般思路。
在许多情况下,生产中模型服务的优化是基于吞吐量或延迟服务级别协议(SLA)。通常,实时应用更关注延迟,而离线应用可能更关注更高的吞吐量。
有许多主要因素会影响生产中模型服务的性能。特别是,我们在这里关注使用 Torchserve 提供 Pytorch 模型的服务,然而,这些因素中的大多数也适用于来自其他框架的所有模型。
- 模型优化:这是将模型部署到生产中的前置步骤。这是一个非常广泛的讨论,我们将在一系列未来的博客中深入探讨。这包括量化、剪枝以减小模型大小、使用中间表示(IR 图)如 Pytorch 中的 Torchscript、内核融合等多种技术。目前 torchprep 提供许多这些技术作为 CLI 工具。
- 批量推理:它指的是将多个输入输入到模型中,虽然在训练过程中至关重要,但在推理时间管理成本方面也非常有帮助。硬件加速器针对并行性进行了优化,批处理有助于充分利用计算能力,通常会导致更高的吞吐量。推理的主要区别在于,你不能等待太长时间从客户端填充一个批次,我们称之为动态批处理。
-
工作进程数量:Torchserve 使用工作进程来提供服务。Torchserve 工作进程是 Python 进程,它们持有模型权重的副本以进行推理。工作进程太少意味着你没有充分利用并行性,但太多可能会导致工作进程竞争并降低端到端性能。
- 硬件:根据模型、应用和延迟、吞吐量预算选择合适的硬件。这可能是 Torchserve 支持的硬件之一,CPU、GPU、AWS Inferentia。某些硬件配置旨在实现最佳性能,而其他配置则更适合成本效益推理。从我们的实验中我们发现,GPU 在较大的批量大小上表现最佳,而合适的 CPU 和 AWS Inferentia 对于较小的批量大小和低延迟来说则更加经济高效。
Torchserve 性能调优的最佳实践
为了在 Torchserve 上提供最佳性能,我们在此分享一些最佳实践。Torchserve 提供了一个基准测试套件,它提供了有价值的见解,有助于在不同选择上做出明智的决定,具体如下。
- 第一步优化您的模型,PyTorch 模型优化教程。模型优化选择也与所选硬件紧密相关。我们将在另一篇博客文章中详细讨论。
- 部署模型所需的硬件选择与延迟、吞吐量和每推理成本密切相关。根据模型大小和应用,这可能会发生变化,对于一些模型,如计算机视觉模型,在 CPU 上生产运行在历史上并不经济。然而,通过最近添加到 Torchserve 中的优化,如 IPEX,这已经变得更加经济实惠,并且具有成本效益,您可以在本调查案例研究中了解更多信息
-
Torchserve 中的工作者是 Python 进程,它们提供并行性,设置工作者的数量应谨慎进行。默认情况下,Torchserve 启动的工作者数量等于主机的 VCPUs 或可用的 GPU 数量,这可能会给 Torchserve 的启动增加相当多的时间
Torchserve 提供了一个配置属性来设置工作者的数量。为了通过多个工作者提供高效的并行性并避免它们在资源上竞争,我们建议以下设置作为基准,针对 CPU 和 GPU:
CPU:在处理器中,
torch.set_num_threads(1)
然后将工作者的数量设置为num physical cores / 2.
但最佳线程配置可以通过利用 Intel CPU 启动脚本来实现GPU:可用的 GPU 数量可以通过 config.properties 中的 number_gpus 进行设置。Torchserve 使用轮询算法分配工作者到 GPU。我们建议按照以下方式设置工作者数量。
Number of worker = (Number of available GPUs) / (Number of Unique Models).
注意,预 Ampere 的 GPU 在使用多实例 GPU 时无法提供任何资源隔离。 - 批处理大小可以直接影响延迟和吞吐量。为了更好地利用计算资源,批处理大小需要增加。然而,延迟和吞吐量之间存在权衡。较大的批处理大小可以提高吞吐量,但也会导致更高的延迟。在 Torchserve 中,可以通过两种方式设置批处理大小,一种是通过 config.properties 中的 model config,另一种是在使用管理 API 注册模型时。
在下一节中,我们将使用 Torchserve 基准测试套件来确定模型优化、硬件、工作者和批处理大小的最佳组合。
动画绘图性能调优
要使用 Torchserve 基准测试套件,首先我们需要一个存档文件,即上面讨论过的“.mar”文件,该文件包含模型、处理器以及所有其他用于加载和运行推理的工件。动画绘图使用 Detectron2 的 Mask-RCNN 实现作为目标检测模型。
如何运行基准测试套件
Torchserve 中的自动基准测试套件允许您使用不同的设置(包括批大小和工作者数量)对多个模型进行基准测试,并最终为您生成报告。要开始:
git clone https://github.com/pytorch/serve.git
cd serve/benchmarks
pip install -r requirements-ab.txt
apt-get install apache2-utils
模型级别的设置可以在一个类似于 yaml 的文件中进行配置
Model_name:
eager_mode:
benchmark_engine: "ab"
url: "Path to .mar file"
workers:
- 1
- 4
batch_delay: 100
batch_size:
- 1
- 2
- 4
- 8
requests: 10000
concurrency: 10
input: "Path to model input"
backend_profiling: False
exec_env: "local"
processors:
- "cpu"
- "gpus": "all"
此 yaml 文件将在 benchmark_config_template.yaml 文件中被引用,该文件包含生成报告的其他设置,它可以选择性地与 AWS 云监控日志一起工作。
python benchmarks/auto_benchmark.py --input benchmark_config_template.yaml
运行基准测试,结果将写入“csv”文件,该文件位于“_ /tmp/benchmark/ab_report.csv_”中,完整报告位于“/tmp/ts_benchmark/report.md”。它将包括 Torchserve 平均延迟、模型 P99 延迟、吞吐量、并发数、请求数、处理器时间以及其他一些指标。在这里,我们关注一些重要的跟踪性能调整的指标,包括并发数、模型 P99 延迟、吞吐量。我们特别关注这些数字与批处理大小、使用的设备、工作进程数以及是否进行了模型优化相结合。
此模型的延迟 SLA 已设置为 100 毫秒,这是一个实时应用程序,如我们之前讨论的,延迟更为关键,而吞吐量理想情况下应尽可能高,同时不违反延迟 SLA。
通过搜索空间,在不同批大小(1-32)、工作线程数量(1-16)和设备(CPU、GPU)的情况下,我们进行了一系列实验,并在下表中总结了最佳结果。
设备 | 并发 | # 请求 | #workers | 批处理大小 | 有效载荷/图像 | 优化 | 吞吐量 | 延迟 P99 |
CPU | 10 | 1000 | 1 | 1 | 小型 | 无 | 3.45 | 305.3 毫秒 |
CPU | 1 | 1000 | 1 | 1 | 小型 | 无 | 3.45 | 291.8 毫秒 |
GPU | 10 | 1000 | 1 | 1 | 小 | 无 | 41.05 | 25.48 毫秒 |
GPU | 1 | 1000 | 1 | 1 | 小 | 无 | 42.21 | 23.6 毫秒 |
GPU | 10 | 1000 | 1 | 4 | 小 | 无 | 54.78 | 73.62 毫秒 |
GPU | 10 | 1000 | 1 | 4 | 小 | model.half() | 78.62 | 50.69 毫秒 |
GPU | 10 | 1000 | 1 | 8 | 小型 | model.half() | 85.29 | 94.4 毫秒 |
在所有尝试过的批大小、并发性和工作者数量设置下,该模型在 CPU 上的延迟没有达到 SLA,实际上高出了约 13 倍。
将模型服务迁移到 GPU,可以立即将延迟提高约 13 倍,从 305 毫秒降低到 23.6 毫秒。
我们可以为该模型进行的简单优化之一是将它的精度降低到 fp16,这是一个单行代码(model.half()),可以将模型 P99 延迟降低 32%,并通过量提高几乎相同的吞吐量。
可能还有其他优化可以通过使用 Torchscripting 模型和优化推理或使用 ONNX 或 TensorRT 运行时优化等技巧来实现,这些优化方法涉及激进的融合,但超出了本文的范围。我们将在另一篇博文中讨论模型优化。
我们发现在 CPU 和 GPU 上,将**工作进程数设置为 1**在此情况下效果最佳。
- 将模型迁移到 GPU,使用工作进程数=1 和批量大小=1,与 CPU 相比,吞吐量提高了约 12 倍,延迟降低了约 13 倍。
- 将模型迁移到 GPU,使用 model.half(),工作进程数=1 和批量大小=8 在吞吐量和可接受的延迟方面取得了最佳结果。与 CPU 相比,吞吐量提高了约 25 倍,延迟仍然满足服务等级协议(94.4 毫秒)。
注意:如果您正在运行基准测试套件,请确保您设置了正确的 batch_delay
,并将请求的并发数设置为与您的批次大小成比例的数字。这里的并发数指的是同时发送到服务器的请求数量。
结论
在本文中,我们讨论了 Torchserve 暴露的考虑因素和旋钮,以调整生产中的性能。我们讨论了 Torchserve 基准测试套件作为调整性能和获取关于模型优化、硬件选择和成本的一般选择的见解的手段。我们使用了 Animated Drawings 应用程序作为案例研究,该应用程序使用 Detectron2 的 Mask-RCNN 模型,以展示使用基准测试套件进行性能调整。
如需了解 Torchserve 中性能调整的更多详细信息,请参阅我们的文档。如有任何进一步的问题和反馈,请随时在 Torchserve 仓库中提交工单。
致谢
我们想感谢 Somya Jain(Meta)、Christopher Gustave(Meta)在撰写本博客的许多步骤中提供的巨大支持和指导,以及为 Sketch Animator 工作流程提供见解。此外,还要特别感谢 AWS 的 Li Ning,他为使 Torchserve 的性能调优更加容易,投入了大量努力,并提供了自动基准测试套件。