AI模型部署:Triton Inference Server模型部署框架简介和快速实践
ztj100 2025-08-07 00:04 6 浏览 0 评论
关键词:Triton
前言
Triton Inference Server是由NVIDIA提供的一个开源推理框架,旨在为AI算法模型提供高效的部署和推理能力,目前已经成为主流的模型部署方案。本文对Triton Inference Server做简要介绍,并以一个简单的线性模型为例子来实践部署。
内容摘要
- Triton Inference Server简介
- Docker构建Triton Inference Server环境
- Triton Inference Server部署一个线性模型
Triton Inference Server简介
Triton Inference Server是一款开源的推理服务框架,它的核心库基于C++编写的,旨在在生产环境中提供快速且可扩展的AI推理能力,具有以下优势
- 支持多种深度学习框架:包括PyTorch,Tensorflow,TensorRT,ONNX,OpenVINO等产出的模型文件
- 至此多种机器学习框架:支持对树模型的部署,包括XGBoost,LightGBM等
- 支持多种推理协议:支持HTTP,GRPC推理协议
- 服务端支持模型前后处理:提供后端API,支持将数据的前处理和模型推理的后处理在服务端实现
- 支持模型并发推理:支持多个模型或者同一模型的多个实例在同一系统上并行执行
- 支持动态批处理(Dynamic batching):支持将一个或多个推理请求合并成一个批次,以最大化吞吐量
- 支持多模型的集成流水线:支持将多个模型进行连接组合,将其视作一个整体进行调度管理
Triton Inference Server架构如下图所示,从客户端请求开始,到模型调度处理,模型仓库管理和推理,响应返回,服务状态监控等。
Docker构建Triton Inference Server环境
Triton Inference Server官方推荐使用Docker来构建环境,在Docker镜像仓库下载已经构建好tritonserver镜像,根据cuda版本下载对应版本的镜像。笔者cuda版本为11.2,对应下载
nvcr.io/nvidia/tritonserver:21.02-py3镜像
docker pull nvcr.io/nvidia/tritonserver:21.02-py3
启动容器并进入,它以Python3作为服务端语言,tritonserver是推理服务的启动命令
root@de4977a12c14:/opt/tritonserver# which tritonserver
/opt/tritonserver/bin/tritonserver
root@de4977a12c14:/opt/tritonserver# which python3
/usr/bin/python3
模型部署还需要一些常用的Python算法包,为了后续测试,我们以该镜像作为源,安装PyTorch,transformers依赖到环境中形成一个新的镜像
# vim Dockerfile
From nvcr.io/nvidia/tritonserver:21.02-py3
Maintainer xxx
COPY ./torch-1.12.1+cu113-cp38-cp38-linux_x86_64.whl /home
RUN pip install /home/torch-1.12.1+cu113-cp38-cp38-linux_x86_64.whl -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip install transformers==4.28.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
构建新镜像triton_server:v1
docker build -t triton_server:v1 .
使用Triton Inference Server部署一个线性模型
本节实践使用Triton Inference Server部署一个线性模型成为一个API服务,包含PyTorch线性模型训练,Triton模型仓库构建,模型推理配置构建,服务端代码构建,服务端启动,客户端服务调用这六个步骤。
(1)PyTorch线性模型训练
我们使用PyTorch的nn.Linear直接构建一个线性层,舍去训练部分,直接使用初始化的权重保存为一个模型文件
import torch
import torch.nn as nn
model = nn.Linear(3, 1)
nn.init.xavier_normal_(model.weight.data)
torch.save(model, "./pytorch_model.bin")
(2)Triton模型仓库构建
Triton Inference Server将模型统一放置在一个目录下进行管理,该目录下每个记录了模型的名称,配置,后端代码,模型文件等,我们创建该目录为model_repository,并且在其下创建一个线性模型目录,命名为linear,该命名是自定义的,但是在后续的配置文件和请求中要保持命名统一。
mkdir model_repository
cd model_repository
mkdir linear
(3)模型推理配置构建
进入linear目录,创建一个文件夹命名为1,代表是linear的第一个版本,将步骤1中训练得到PyTorch模型文件放置在1目录的linear目录下
mkdir 1
cd 1
mkdir linear
cd linear
mv /home/model/pytorch_model.bin ./
回到1目录下创建模型的推理配置文件命名为config.pbtxt,所有版本共用该配置,配置如下
# vim config.pbtxt
name: "linear"
backend: "python"
max_batch_size: 4
input [
{
name: "x"
data_type: TYPE_FP32
dims: [ 3 ]
}
]
output [
{
name: "y"
data_type: TYPE_FP32
dims: [ 1 ]
}
]
该配置定义了模型的输入和输出,在此做简要的说明
- name:模型名称,必须和步骤二中的文件名一致
- max_batch_size:一个批次下的最大大小,4代表一次请求最大推理4条样本
- input:模型的输入信息,array格式,其中每个输入是一个json格式
- input-name:一个输入的名称,该名称自定义,但是在服务端代码必须和其保持一致
- input-data_type:一个输入的数据类型,本例中采用32位浮点
- input-data_dims:一个输入的维度,代表一条样本的维度,若max_batch_size不为0,它和max_batch_size一起构成了最终的输入大小,本例中最终输入最大是[4, 3]的矩阵,若max_batch_size为0,则dims就是最终的维度
此处输入的x维度为3,和我们训练PyTorch模型时的nn.Linear(3, 1)保持对应。
(4)服务端代码构建
服务端代码负责读取模型,并且在其中实现数据的前处理,模型推理,后处理的逻辑。Triton Inference Server提供了服务端代码模板TritonPythonModel,只需要略微修改即可,本例的服务端代码如下
import os
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:32'
os.environ['TRANSFORMERS_CACHE'] = os.path.dirname(os.path.abspath(__file__)) + "/work/"
os.environ['HF_MODULES_CACHE'] = os.path.dirname(os.path.abspath(__file__)) + "/work/"
import json
# triton_python_backend_utils is available in every Triton Python model. You
# need to use this module to create inference requests and responses. It also
# contains some utility functions for extracting information from model_config
# and converting Triton input/output types to numpy types.
import triton_python_backend_utils as pb_utils
import sys
import gc
import time
import logging
import torch
import numpy as np
gc.collect()
torch.cuda.empty_cache()
logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
level=logging.INFO)
class TritonPythonModel:
"""Your Python model must use the same class name. Every Python model
that is created must have "TritonPythonModel" as the class name.
"""
def initialize(self, args):
"""`initialize` is called only once when the model is being loaded.
Implementing `initialize` function is optional. This function allows
the model to intialize any state associated with this model.
Parameters
----------
args : dict
Both keys and values are strings. The dictionary keys and values are:
* model_config: A JSON string containing the model configuration
* model_instance_kind: A string containing model instance kind
* model_instance_device_id: A string containing model instance device ID
* model_repository: Model repository path
* model_version: Model version
* model_name: Model name
"""
# You must parse model_config. JSON string is not parsed here
self.model_config = json.loads(args['model_config'])
output_y_config = pb_utils.get_output_config_by_name(self.model_config, "y")
# Convert Triton types to numpy types
self.output_response_dtype = pb_utils.triton_string_to_numpy(output_y_config['data_type'])
# TODO 读取模型
model_path = os.path.dirname(os.path.abspath(__file__)) + "/linear/pytorch_model.bin"
self.model = torch.load(model_path)
self.model = self.model.eval()
logging.info("model init success")
def execute(self, requests):
"""`execute` MUST be implemented in every Python model. `execute`
function receives a list of pb_utils.InferenceRequest as the only
argument. This function is called when an inference request is made
for this model. Depending on the batching configuration (e.g. Dynamic
Batching) used, `requests` may contain multiple requests. Every
Python model, must create one pb_utils.InferenceResponse for every
pb_utils.InferenceRequest in `requests`. If there is an error, you can
set the error argument when creating a pb_utils.InferenceResponse
Parameters
----------
requests : list
A list of pb_utils.InferenceRequest
Returns
-------
list
A list of pb_utils.InferenceResponse. The length of this list must
be the same as `requests`
"""
# output_response_dtype = self.output_response_dtype
# output_history_dtype = self.output_history_dtype
# output_dtype = self.output_dtype
responses = []
# Every Python backend must iterate over everyone of the requests
# and create a pb_utils.InferenceResponse for each of them.
for request in requests:
# TODO 拿到输入
x = pb_utils.get_input_tensor_by_name(request, "x").as_numpy()
in_log_info = {
"x": x,
}
logging.info(in_log_info)
# TODO 推理结果
y = self.model(torch.tensor(x).float())
out_log_info = {
"y": y
}
logging.info(out_log_info)
y = y.detach().cpu().numpy()
y_tensor = pb_utils.Tensor("y", y.astype(self.output_response_dtype))
final_inference_response = pb_utils.InferenceResponse(
output_tensors=[y_tensor])
responses.append(final_inference_response)
# Create InferenceResponse. You can set an error here in case
# there was a problem with handling this inference request.
# Below is an example of how you can set errors in inference
# response:
#
# pb_utils.InferenceResponse(
# output_tensors=..., TritonError("An error occured"))
# You should return a list of pb_utils.InferenceResponse. Length
# of this list must match the length of `requests` list.
return responses
def finalize(self):
"""`finalize` is called only once when the model is being unloaded.
Implementing `finalize` function is OPTIONAL. This function allows
the model to perform any necessary clean ups before exit.
"""
print('Cleaning up...')
其中initialize方法实现了模型的初始化,execute方法实现了模型的推理逻辑,并且包转了返回。我们将该Python脚本保存命名为model.py,放置在文件1下。
服务端工作准备完成,此时linear目录结构如下
~/model_repository/linear$ tree
.
├── 1
│ ├── linear
│ │ └── pytorch_model.bin
│ └── model.py
└── config.pbtxt
(5)服务端启动
接下来通过Docker来启动Triton Inference Server镜像,将模型仓库目录model_repository整个挂载到容器内部
docker run --rm \
-p18999:8000 -p18998:8001 -p18997:8002 \
-v /home/model_repository/:/models \
triton_server:v1 \
tritonserver --model-repository=/models
服务分别占用三个端口,将宿主机的端口和其进行映射,其中8000端口用于客户端请求推理结果。tritonserver是容器中的服务启动命令,--model-repository是参数,指定了模型仓库位置。
容器启动日志如下
=============================
== Triton Inference Server ==
=============================
NVIDIA Release 21.02 (build 20174689)
Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
...
I0322 09:41:22.286694 1 server.cc:538]
+--------------+---------+--------+
| Model | Version | Status |
+--------------+---------+--------+
| linear | 1 | READY |
+--------------+---------+--------+
I0322 09:41:22.286937 1 tritonserver.cc:1642]
+----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
| Option | Value |
+----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
| server_id | triton |
| server_version | 2.7.0 |
| server_extensions | classification sequence model_repository schedule_policy model_configuration system_shared_memory cuda_shared_memory binary_tensor_data statistics |
| model_repository_path[0] | /models |
| model_control_mode | MODE_NONE |
| strict_model_config | 1 |
| pinned_memory_pool_byte_size | 268435456 |
| min_supported_compute_capability | 6.0 |
| strict_readiness | 1 |
| exit_timeout | 30 |
+----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
I0322 09:41:22.289210 1 grpc_server.cc:3979] Started GRPCInferenceService at 0.0.0.0:8001
I0322 09:41:22.289923 1 http_server.cc:2717] Started HTTPService at 0.0.0.0:8000
I0322 09:41:22.331775 1 http_server.cc:2736] Started Metrics Service at 0.0.0.0:8002
提示模型linear版本1已经READY,则启动成功。
(6)客户端服务调用
本例中客户端采用HTTP请求的方式进行调用,使用Python的requests即可进行请求,代码如下
import numpy as np
import requests
import json
if __name__ == "__main__":
model_name = "linear"
model_version = "1"
raw_data = {
"inputs": [
{
"name": "x",
"datatype": "FP32",
"shape": [3, 3],
"data": [[2.0, 3.0, 4.0], [1.1, 2.3, 10.3], [2.0, 3.0, 4.0]]
}
],
"outputs": [
{
"name": "y"
}
]
}
url = "http://0.0.0.0:18999/v2/models/linear/infer"
import requests
import json
response = requests.post(url=url,
data=json.dumps(raw_data, ensure_ascii=True),
headers={"Content_Type": "application/json"},
timeout=2000)
res = json.loads(response.text)
print(res)
在客户端我们创建了三条样本,形成[3, 3]的矩阵输入给Triton服务,请求url指定了要请求的模型,url中的模型名称linear必须和配置和文件夹命名一致。请求结果打印如下
{'model_name': 'linear', 'model_version': '1', 'outputs': [{'name': 'y', 'datatype': 'FP32', 'shape': [3, 1], 'data': [-0.21258652210235596, -1.3153640031814575, -0.21258652210235596]}]}
其中data就是模型推理的输出,维度和训练过程中的nn.Linear(3, 1)一致。同步的在服务端日志会打印出请求的数据和输出的数据
I0322 09:54:17.211954 1 grpc_server.cc:3979] Started GRPCInferenceService at 0.0.0.0:8001
I0322 09:54:17.212302 1 http_server.cc:2717] Started HTTPService at 0.0.0.0:8000
I0322 09:54:17.253904 1 http_server.cc:2736] Started Metrics Service at 0.0.0.0:8002
2024-03-22 09:54:24,068 - model.py[line:98] - INFO: {'x': array([[ 2. , 3. , 4. ],
[ 1.1, 2.3, 10.3],
[ 2. , 3. , 4. ]], dtype=float32)}
2024-03-22 09:54:24,073 - model.py[line:105] - INFO: {'y': tensor([[-0.2126],
[-1.3154],
[-0.2126]], grad_fn=<AddmmBackward0>)}
至此,基于Triton Inference Server搭建的一个简单的线性模型的预测推理服务完成。
相关推荐
- 其实TensorFlow真的很水无非就这30篇熬夜练
-
好的!以下是TensorFlow需要掌握的核心内容,用列表形式呈现,简洁清晰(含表情符号,<300字):1.基础概念与环境TensorFlow架构(计算图、会话->EagerE...
- 交叉验证和超参数调整:如何优化你的机器学习模型
-
准确预测Fitbit的睡眠得分在本文的前两部分中,我获取了Fitbit的睡眠数据并对其进行预处理,将这些数据分为训练集、验证集和测试集,除此之外,我还训练了三种不同的机器学习模型并比较了它们的性能。在...
- 机器学习交叉验证全指南:原理、类型与实战技巧
-
机器学习模型常常需要大量数据,但它们如何与实时新数据协同工作也同样关键。交叉验证是一种通过将数据集分成若干部分、在部分数据上训练模型、在其余数据上测试模型的方法,用来检验模型的表现。这有助于发现过拟合...
- 深度学习中的类别激活热图可视化
-
作者:ValentinaAlto编译:ronghuaiyang导读使用Keras实现图像分类中的激活热图的可视化,帮助更有针对性...
- 超强,必会的机器学习评估指标
-
大侠幸会,在下全网同名[算法金]0基础转AI上岸,多个算法赛Top[日更万日,让更多人享受智能乐趣]构建机器学习模型的关键步骤是检查其性能,这是通过使用验证指标来完成的。选择正确的验证指...
- 机器学习入门教程-第六课:监督学习与非监督学习
-
1.回顾与引入上节课我们谈到了机器学习的一些实战技巧,比如如何处理数据、选择模型以及调整参数。今天,我们将更深入地探讨机器学习的两大类:监督学习和非监督学习。2.监督学习监督学习就像是有老师的教学...
- Python 模型部署不用愁!容器化实战,5 分钟搞定环境配置
-
你是不是也遇到过这种糟心事:花了好几天训练出的Python模型,在自己电脑上跑得顺顺当当,一放到服务器就各种报错。要么是Python版本不对,要么是依赖库冲突,折腾半天还是用不了。别再喊“我...
- 神经网络与传统统计方法的简单对比
-
传统的统计方法如...
- 自回归滞后模型进行多变量时间序列预测
-
下图显示了关于不同类型葡萄酒销量的月度多元时间序列。每种葡萄酒类型都是时间序列中的一个变量。假设要预测其中一个变量。比如,sparklingwine。如何建立一个模型来进行预测呢?一种常见的方...
- 苹果AI策略:慢哲学——科技行业的“长期主义”试金石
-
苹果AI策略的深度原创分析,结合技术伦理、商业逻辑与行业博弈,揭示其“慢哲学”背后的战略智慧:一、反常之举:AI狂潮中的“逆行者”当科技巨头深陷AI军备竞赛,苹果的克制显得格格不入:功能延期:App...
- 时间序列预测全攻略,6大模型代码实操
-
如果你对数据分析感兴趣,希望学习更多的方法论,希望听听经验分享,欢迎移步宝藏公众号...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)