负载测试TensorFlow Serving的REST接口

223 阅读8分钟

在这篇文章中,我们将分享在众多部署配置中对一个图像分类模型进行负载测试的经验教训和发现。这些配置涉及基于REST的部署与TensorFlow Serving。通过这种方式,我们旨在让读者对这些配置之间的差异有一个全面的了解。

这篇文章较少涉及代码,更多的是关于我们为执行部署而不得不做出的架构决定。我们将首先提供一个关于我们设置的概述,包括技术规格。我们还将分享我们对所做的设计选择及其影响的评论。

技术设置

TensorFlow服务功能丰富,并在其设计中嵌入了有针对性的规范(后面会有更多介绍)。对于在线预测场景,模型通常作为某种服务暴露。

为了执行我们的测试,我们使用了一个预先训练好的ResNet50模型,它可以将各种图像分类到不同的类别。然后我们以如下方式为这个模型服务:

我们的部署平台(Kubernetes集群上的节点)是基于CPU的。我们在流程的任何阶段都不采用GPU。为此,我们可以建立一个CPU优化的TensorFlow服务镜像,并利用其他一些选项,可以减少延迟,提高系统的整体吞吐量。我们将在后面的文章中讨论这些。

你可以在这个资源库中找到所有的代码并了解如何进行部署的。在这里,你可以找到示例笔记本和详细的设置说明,以便玩弄这些代码。因此,我们不会逐行讨论代码,而是在必要时对最重要的部分进行说明。

在这篇文章的其余部分,我们将讨论与TensorFlow服务相关的部署实验的关键考虑因素,包括其动机、限制和我们的实验结果。

随着Vertex AI等无服务器产品的出现,部署模型并安全可靠地扩展它们变得前所未有的简单。这些服务有助于极大地缩短产品上市时间,提高开发人员的整体生产力。也就是说,在某些情况下,你可能仍然希望对事情进行更细化的控制。这也是我们首先想做这些实验的原因之一。

考虑因素

TensorFlow服务有它自己的一套约束和设计选择,可以影响部署。在这一节中,我们提供了这些考虑因素的简明概述。

部署基础设施:我们选择GKE是因为Kubernetes是使用GCP时的标准部署平台,而且GKE让我们专注于ML部分而不用担心基础设施,因为它是一个完全管理的谷歌云平台服务。我们的主要兴趣是如何为基于CPU的环境部署模型,所以我们准备了一个CPU优化的TensorFlow服务镜像。

在更多或更少的服务器之间进行权衡:我们从最简单的配备2vCPU和4GB内存的虚拟机开始进行TensorFlow Serving设置的实验,然后我们逐渐将规格升级到8vCPU和64GB内存。另一方面,我们将Kubernetes集群中的节点数量从8个减少到2个,因为部署更便宜的服务器和更少的昂贵服务器是一种成本的权衡。

有利于多核环境的选项:我们想看看高端虚拟机是否能在节点较少的情况下,通过选项利用多核环境的优势来超越简单的虚拟机。为此,我们试验了不同数量的 [inter_op_parallelism](https://www.tensorflow.org/api_docs/python/tf/config/threading/set_inter_op_parallelism_threads)[intra_op_parallelism](https://www.tensorflow.org/api_docs/python/tf/config/threading/set_intra_op_parallelism_threads)线程的TensorFlow服务部署集,根据CPU核心的数量。

动态批处理和其他考虑:现代的ML框架,如TensorFlow Serving,通常支持动态批处理,初始模型预热,不同模型的多个版本的多次部署,以及更多的开箱即用。对于我们在线预测的目的,我们没有仔细测试这些功能。然而,根据官方文件,动态批处理能力也是值得探索的,以提高性能。我们已经看到,默认的批处理配置可以减少一点延迟,尽管其结果没有包括在这篇博文中。

实验

我们准备了以下环境。在TensorFlow Serving中,intra_op_parallelism_threads 的数量被设置为等于CPU核心的数量,而inter_op_parallelism_threads 的数量被设置为2到8,以达到实验的目的,因为它控制线程的数量来并行化独立操作的执行。下面我们将详细介绍我们对每个Kubernetes集群的vCPU数量、RAM大小和节点数量所做的调整。请注意,vCPU的数量和RAM的大小是单独适用于集群节点的。

负载测试是使用Locust进行的。我们在每个负载测试中都运行了5分钟。请求的数量由用户的数量控制,它取决于客户端的情况。我们每秒钟增加一个用户,直到150个,我们发现处理的请求数达到了高原,请求是每秒钟产生的,以了解TensorFlow服务的行为方式。所以你可以假设请求/秒并不能反映真实世界的情况,即客户在任何时候都试图发送请求。

我们在Kubernetes集群上实验了以下节点配置。这些配置是这样读取的。{num_vcpus_per_node}-{ram}_{num_nodes}:

  • 2vCPUs, 4GB RAM, 8个节点
  • 4vCPUs, 8GB RAM, 4个节点
  • 8vCPUs, 16GB RAM, 2个节点
  • 8vCPUs, 64GB RAM, 2个节点

你可以在上述资源库中找到用于实验这些不同配置的代码。每个实验的部署是通过Kustomize提供的,以覆盖基本配置,基于文件的配置是通过ConfigMap注入的。

结果

本节介绍了上述每个配置的结果,并根据我们考虑的环境提出了哪个配置是最好的。根据图1,最佳配置和环境设置被观察为2个节点,8个intra_op_parallelism_threads ,8个inter_op_parallelism_threads ,8个vCPUs,16GB RAM:

通过挑选最佳方案,我们观察到以下几个方面:

  • TensorFlow Serving部署在更少、更大(更多的CPU和RAM)的机器上时,效率更高,但RAM容量对处理更多的请求没有太大影响。通过实验找到合适的inter_op_parallelism_threads ,这一点很重要。随着数量的增加,即使节点配备了高容量的硬件,也不一定能保证更好的性能。

TensorFlow服务更注重于可靠性而不是吞吐量性能。我们认为它牺牲了一些吞吐量性能来实现可靠性,但这是TensorFlow Serving的预期行为,正如官方文件中所述。即使处理尽可能多的请求是很重要的,但在处理生产系统时,保持服务器尽可能的可靠也是实质性的重要。

在性能和可靠性之间有一个权衡,所以你必须小心选择正确的。然而,TensorFlow Serving的吞吐量性能似乎与FastAPI等其他框架的结果足够接近,如果你想考虑更丰富的功能,如动态批处理和在模型之间有效共享GPU资源,我们相信TensorFlow Serving是正确的选择。

关于gRPC和TensorFlow Serving的说明

我们正在处理一个图像分类模型的部署,模型的输入将包括图像。因此,请求有效载荷的大小会根据图像的分辨率和保真度而螺旋上升。因此,确保消息传输尽可能地轻巧是特别重要的。一般来说,gRPC中的消息传输比REST快很多。篇文章对REST和gRPC APIs之间的主要区别进行了很好的讨论。

TensorFlow Serving可以用gRPC无缝地服务于一个模型,但是比较gRPC API和REST API的性能是不容易的。这就是为什么我们没有在这篇文章中包括这一点。有兴趣的读者可以查看这个资源库,它遵循类似的设置,但使用gRPC服务器来代替。

成本

我们为此使用了GCP成本估算器。每个实验配置的定价被假定为每月24小时的实时性(这对我们的实验来说是足够的)。

机器配置(E2系列)

价格(美元)

2vCPUs, 4GB RAM, 8个节点

11.15

4vCPUs, 8GB RAM, 4个节点

11.15

8vCPUs, 16GB RAM, 2 节点

11.15

8vCPUs, 64GB RAM, 2个节点

18.21

总结

在这篇文章中,我们讨论了从我们对标准图像分类模型进行负载测试的经验中得到的一些重要教训。我们考虑了将模型暴露给终端用户的行业级框架--TensorFlow服务。虽然我们进行负载测试的设置可能不完全类似于在野外发生的情况,但我们希望我们的发现至少可以作为社区的一个良好的起点。尽管帖子用一个图像分类模型展示了我们的方法,但这些方法应该是相当具有任务相关性的。