[译]使用 NVIDIA Triton 推理服务器为 TensorRT 模型提供服务
Published:
原文链接:Serving TensorRT Models with NVIDIA Triton Inference Server
原文作者:Tan Pengshi Alvin. Data Scientist, ML and Software Engineer. Shares codes, technical data science concepts and ideas.
在实时AI模型部署中,模型推理和硬件/GPU使用的效率至关重要。单个C/S架构的推理请求的速度取决于服务器的延迟和吞吐量。这是因为深度学习模型通常安装在服务器或一组服务器上,这些服务器将从公共客户端设备接收多个传入请求或数据。让我在这里描述一些定义:
延迟:通过客户端服务器连接进行单个请求响应循环所需的时间。假设互联网连接稳定,延迟将取决于模型推理的速度,数据包的传输和其他一些因素。
吞吐量:服务器可以在一个时间实例中处理的传入请求量。当传入的流量超过吞吐量时,排队了多余的请求,从而减慢了请求响应过程。
在本文中,我们将讨论通过TensorRT在单个服务器(GPU工作站,将通过网络接收其他设备的请求)上配置并推理模型,即NVIDIA TRITON推理服务器。
在Triton服务器上运行深度学习模型有几个优点,据报道,它比TFServing和Torchserve等其他框架优越。例如,它能够通过在多个请求上的模型推断中通过动态批次推理和并发来优化吞吐量。结合使用Tensorrt优化延迟,Triton Server能提供大量的快速推断。
先决条件
带有NVIDIA GPU的本地工作站/笔记本电脑
Docker容器和Linux终端的基础知识
了解Python的基础和深度学习库
1.tensorflow模型的示例
在Triton服务器上测试深度学习模型的实例中,我们选择经典的CNN模型 - RESNET50-在Imagenet数据集上预处理,如下所示。
import numpy as np
from tensorflow.keras.applications import ResNet50
from skimage import io
from skimage.transform import resize
from matplotlib import pyplot as plt
model = ResNet50(weights='imagenet')
url='https://images.dog.ceo/breeds/retriever-golden/n02099601_3004.jpg'
img = resize(io.imread(url), (224, 224))
single_batch = 255*np.expand_dims(np.array(img, dtype=np.float32), axis=0)
single_batch.shape
predictions = model.predict(single_batch) # warm up
np.argmax(predictions)
indices = (-predictions[0]).argsort()[:5]
print("Class | Probability (out of 1)")
list(zip(indices, predictions[0][indices]))
接下来,我们将将此TensorFlow模型优化为Tensorrt模型。
2.转换为ONNX模型
不同的深度学习框架有各自支持的的Tensorrt,如TensorFlow-Tensorrt和Onnx Tensorrt,但是NVIDIA Triton Server 仅采用了ONNX TensorRT框架。因此,我们需要首先将tensorflow模型转换为ONNX格式。首先需要在该目录 ${PWD}/models/tensorrt_fp16_model/1/model.onnx
中保存model.onnx
模型。
import onnx, tf2onnx
from tensorflow.keras.applications import ResNet50
BATCH_SIZE = 32
model = ResNet50(weights='imagenet')
model.save('keras_model')
!python -m tf2onnx.convert --saved-model keras_model --output temp.onnx
onnx_model = onnx.load_model('temp.onnx')
inputs = onnx_model.graph.input
for input in inputs:
dim1 = input.type.tensor_type.shape.dim[0]
dim1.dim_value = BATCH_SIZE
model_name = "model.onnx"
onnx.save_model(onnx_model, model_name)
3.使用Docker容器转换为Tensorrt模型
接下来,我们要将ONNX 模型 model.onnx
转换为Tensorrt模型model.plan
。如果本地安装了Tensorrt,则可以尝试尝试进行本地转换。但是,这是有问题的,因为Triton Server容器中的Tensorrt和CUDA软件在运行model.plan
文件时的行为可能有所不同。即使本地 TensorRT 的版本类似于Triton Tensorrt的版本,也是如此。
幸运的是,NVIDIA为Tensorrt提供了Docker映像,并具有与Triton Server搭配的版本标签。假设您在本地安装了Docker,请在终端上运行:docker pull nvcr.io/nvidia/tensorrt:22.11-py3
当我们获取到Docker映像后,在运行容器时,我们将进行卷(Volume)绑定。请注意,Volume绑定的参数必须是绝对路径。
docker run -it --gpus all --rm -v ${PWD}/models/tensorrt_fp16_model/1:/trt_optimize nvcr.io/nvidia/tensorrt:22.11-py3
这将在容器内启动一个终端,然后我们在容器内进行 TensorRT 转换以创建model.plan
,该文件也将由于绑定挂载而在本地可用。
4.将本地目录设置为Mirror Triton服务器
Triton Server软件类似于下载的Docker映像,然后我们还将进行卷的绑定。但是,在拉取docker镜像之前,我们希望遵守并初始化本地特定目录结构以及必要的config.pbtxt
模型配置文件。
目录结构应如下:
${PWD}/models
|
|-- tensorrt_fp16_model
| |
| |-- config.pbtxt
| |-- 1
| | |
| | |--model.plan
config.pbtxt文件的配置示例如下:
name: "tensorrt_fp16_model"
platform: "tensorrt_plan"
max_batch_size: 32
input [
{
name: "input_1"
data_type: TYPE_FP16
dims: [ 224, 224, 3 ]
}
]
output [
{
name: "predictions"
data_type: TYPE_FP16
dims: [ 1000 ]
}
]
5.设置Triton服务器容器 —
在确保Triton服务器的Docker镜像版本标签与Tensorrt的Docker镜像版本一样后,我们就可以下载:
docker pull nvcr.io/nvidia/tritonserver:22.11-py3
然后在附加模式下运行Docker容器:
docker run --gpus=all --rm --name triton_server -p 8000:8000 -p 8001:8001 -p 8002:8002 -v ${PWD}/models:/models nvcr.io/nvidia/tritonserver:22.11-py3 tritonserver --model-repository=/models --model-control-mode=poll --repository-poll-secs 30
如果正确设置了Triton服务器容器并准备好进行推断,则应在终端中看到以下输出,而不会在“状态”中看到任何错误消息:
6.在客户端进行推断
在这里,我将演示通过客户端调用Triton服务器运行推理的示例代码。当然,我们还需要在客户端工作站上安装TritonClient库:pip install tritonclient[all]
import tritonclient.http as tritonhttpclient
VERBOSE = False
input_name = 'input_1'
output_name = 'predictions'
model_name = 'tensorrt_fp16_model'
url = 'localhost:8000'
# if server and client are on different workstation, replace $localhost with IP address of the server
model_version = '1'
triton_client = tritonhttpclient.InferenceServerClient(url=url, verbose=VERBOSE)
model_metadata = triton_client.get_model_metadata(model_name=model_name, model_version=model_version)
model_config = triton_client.get_model_config(model_name=model_name, model_version=model_version)
target_dtype = np.float16
input_batch = input_batch.astype(target_dtype)
input0 = tritonhttpclient.InferInput(input_name, (32,224, 224, 3), 'FP16')
input0.set_data_from_numpy(input_batch,binary_data=True)
output = tritonhttpclient.InferRequestedOutput(output_name, binary_data=True)
response = triton_client.infer(model_name, model_version=model_version,
inputs=[input0], outputs=[output])
logits = response.as_numpy('predictions')
logits = np.asarray(logits, dtype=np.float32)
np.argmax(logits,axis=1)
response = triton_client.infer(model_name, model_version=model_version,
inputs=[input0], outputs=[output])
Triton TensorRT比本地 TensorRT 较慢
在结束文章之前,我不得不提到的是,由于优化了GPU使用和批处理推断,Triton Server在进行重大客户端流量进行推理时确实会很有效。
但是,当我们在Triton TensorRT 和Local TensorRT 之间运行单个局部推断时,由于Triton TensorRT 在网络上有其他开销推断,因此Local TensorRT 仍然会更快地推理速度。
结论
对于任何有抱负的AI工程师或机器学习工程师来说,部署具有可伸缩性的AI模型的是一项重要技能,并且了解 Nvidia Triton 服务器肯定会在竞争性数据科学领域中获得优势。否则,强大而准确的模型只能在Jupyter Notebooks 和 VS Code 后面埋没。
拓展阅读