Triton Inference Server est un moteur d'inférence développé par NVIDIA. Il vous permet de déployer des modèles issus de divers frameworks d'IA, tels que TensorRT, TensorFlow, PyTorch et ONNX, en tant que services d'inférence en ligne, tout en prenant en charge la gestion multi-modèles et les backends personnalisés. Vous pouvez déployer rapidement des services d'inférence basés sur Triton sur PAI Elastic Algorithm Service (EAS).
Prérequis
Un compartiment Object Storage Service (OSS) situé dans la même région que PAI.
Des fichiers de modèles entraînés, tels que
.pt,.onnx,.planou.savedmodel.
Démarrage rapide : déploiement d'un service mono-modèle
Étape 1 : Préparer le référentiel de modèles
Triton impose une structure de répertoire spécifique au sein d'un compartiment Object Storage Service (OSS). Créez les répertoires en suivant le format ci-dessous. Pour plus d'instructions, consultez les rubriques Gérer les répertoires et Télécharger des fichiers.
oss://your-bucket/models/triton/
└── your_model_name/
├── 1/ # Version directory (must be a number)
│ └── model.pt # Model file
└── config.pbtxt # Model configuration file
Exigences clés :
Le nom du répertoire de version doit être un nombre, par exemple
1,2ou3.Un numéro plus élevé indique une version plus récente.
Chaque modèle nécessite un fichier de configuration
config.pbtxt.
Étape 2 : Créer le fichier de configuration du modèle
Créez un fichier config.pbtxt pour configurer les informations de base de votre modèle. Voici un exemple :
name: "your_model_name"
platform: "pytorch_libtorch"
max_batch_size: 128
input [
{
name: "INPUT__0"
data_type: TYPE_FP32
dims: [ 3, -1, -1 ]
}
]
output [
{
name: "OUTPUT__0"
data_type: TYPE_FP32
dims: [ 1000 ]
}
]
# Use a GPU for inference
# instance_group [
# {
# kind: KIND_GPU
# }
# ]
# Model version configuration
# Load only the latest version (default behavior)
# version_policy: { latest: { num_versions: 1 }}
# Load all versions
# version_policy: { all { }}
# Load the two latest versions
# version_policy: { latest: { num_versions: 2 }}
# Load specific versions
# version_policy: { specific: { versions: [1, 3] }}
Paramètres
|
Paramètre |
Obligatoire |
Description |
|
|
Non |
Nom du modèle. Si ce paramètre est spécifié, il doit correspondre au nom du répertoire du modèle. |
|
|
Oui |
Framework du modèle. Valeurs valides : |
|
|
Oui |
Alternative à |
|
|
Oui |
Taille maximale du lot. Définissez cette valeur sur |
|
|
Oui |
Configuration du tenseur d'entrée, incluant |
|
|
Oui |
Configuration du tenseur de sortie, incluant |
|
|
Non |
Spécifie le dispositif d'inférence : |
|
|
Non |
Contrôle les versions de modèle chargées. Pour un exemple de configuration, reportez-vous au fichier |
Vous devez spécifier soit le paramètre platform, soit le paramètre backend.
Étape 3 : Déployer le service
Connectez-vous à la PAI console et sélectionnez la région cible dans la barre de navigation supérieure.
Dans le volet de navigation de gauche, cliquez sur Elastic Algorithm Service (EAS), sélectionnez l'espace de travail cible, puis cliquez sur Deploy Service.
Dans la section Scenario-based Model Deployment, cliquez sur Triton Deployment.
-
Configurez les paramètres de déploiement :
Service Name : saisissez un nom personnalisé pour le service.
Model Configuration : pour le type de configuration, sélectionnez OSS, puis saisissez le chemin d'accès à votre référentiel de modèles, par exemple
oss://your-bucket/models/triton/.Number of Replicas et Resource Specification : sélectionnez les valeurs selon vos besoins. Pour obtenir de l'aide afin d'estimer la mémoire GPU requise, consultez la rubrique Estimer la mémoire GPU requise pour les grands modèles.
Cliquez sur Deploy et attendez le démarrage du service.
Étape 4 : Activer gRPC (facultatif)
Par défaut, Triton fournit un service HTTP sur le port 8000. Pour utiliser gRPC, procédez comme suit :
Dans l'angle supérieur droit de la page de configuration du service, cliquez sur Convert to Custom Deployment.
Dans la section Environment Information, définissez le Port Number sur
8001.Sous Features > Advanced Networking, activez l'option gRPC.
Cliquez sur Deploy.
Une fois le modèle déployé, vous pouvez appeler le service.
Déployer un service multi-modèles
Pour déployer plusieurs modèles dans une seule instance Triton, placez-les dans le même répertoire de référentiel de modèles :
oss://your-bucket/models/triton/
├── resnet50_pytorch/
│ ├── 1/
│ │ └── model.pt
│ └── config.pbtxt
├── densenet_onnx/
│ ├── 1/
│ │ └── model.onnx
│ └── config.pbtxt
└── classifier_tensorflow/
├── 1/
│ └── model.savedmodel/
│ ├── saved_model.pb
│ └── variables/
└── config.pbtxt
Les étapes de déploiement sont identiques à celles d'un modèle unique. Triton charge automatiquement tous les modèles présents dans le référentiel.
Backend Python pour l'inférence personnalisée
Lorsque vous devez personnaliser le prétraitement, le post-traitement ou la logique d'inférence, vous pouvez utiliser le backend Python de Triton.
Structure des répertoires
your_model_name/
├── 1/
│ ├── model.pt # Model file
│ └── model.py # Custom inference logic
└── config.pbtxt
Implémenter le backend Python
Créez un fichier model.py et définissez la classe TritonPythonModel :
import json
import os
import torch
from torch.utils.dlpack import from_dlpack, to_dlpack
import triton_python_backend_utils as pb_utils
class TritonPythonModel:
"""The class name must be 'TritonPythonModel'."""
def initialize(self, args):
"""
Triton calls this optional function once when it loads a model.
Use it to initialize information related to model properties and configurations.
Parameters
----------
args : A dictionary where both keys and values are strings. It includes:
* model_config: Model configuration information in JSON format.
* model_instance_kind: The device type.
* model_instance_device_id: The device ID.
* model_repository: The path to the model repository.
* model_version: The model version.
* model_name: The model name.
"""
# Parse the model configuration from the JSON string into a Python dictionary.
self.model_config = model_config = json.loads(args["model_config"])
# Get properties from the model configuration file.
output_config = pb_utils.get_output_config_by_name(model_config, "OUTPUT__0")
# Convert Triton types to NumPy types.
self.output_dtype = pb_utils.triton_string_to_numpy(output_config["data_type"])
# Get the path to the model repository.
self.model_directory = os.path.dirname(os.path.realpath(__file__))
# Get the device for model inference. This example uses a GPU.
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("device: ", self.device)
model_path = os.path.join(self.model_directory, "model.pt")
if not os.path.exists(model_path):
raise pb_utils.TritonModelException("Cannot find the pytorch model")
# Load the PyTorch model to the GPU by using .to(self.device).
self.model = torch.jit.load(model_path).to(self.device)
print("Initialized...")
def execute(self, requests):
"""
This required function executes the model. Triton calls it for every inference request.
If batching is enabled, you must implement the batch processing logic.
Parameters
----------
requests : A list of pb_utils.InferenceRequest objects.
Returns
-------
A list of pb_utils.InferenceResponse objects. The list must have the same length as the requests list.
"""
output_dtype = self.output_dtype
responses = []
# Iterate through the requests list and create a corresponding response for each request.
for request in requests:
# Get the input tensor.
input_tensor = pb_utils.get_input_tensor_by_name(request, "INPUT__0")
# Convert the Triton tensor to a Torch tensor.
pytorch_tensor = from_dlpack(input_tensor.to_dlpack())
if pytorch_tensor.shape[2] > 1000 or pytorch_tensor.shape[3] > 1000:
responses.append(
pb_utils.InferenceResponse(
output_tensors=[],
error=pb_utils.TritonError(
"Image shape should not be larger than 1000"
),
)
)
continue
# Run inference on the GPU.
prediction = self.model(pytorch_tensor.to(self.device))
# Convert the Torch output tensor to a Triton tensor.
out_tensor = pb_utils.Tensor.from_dlpack("OUTPUT__0", to_dlpack(prediction))
inference_response = pb_utils.InferenceResponse(output_tensors=[out_tensor])
responses.append(inference_response)
return responses
def finalize(self):
"""
This optional function is called when the model is unloaded. Use it for cleanup tasks.
"""
print("Cleaning up...")
Lorsque vous utilisez un backend Python, sachez que certains comportements de Triton changent :
max_batch_size est ignoré : le paramètre
max_batch_sizedans le fichierconfig.pbtxtn'active pas le regroupement dynamique par lots (dynamic batching) pour le backend Python. Vous devez parcourir la listerequestsdans votre méthodeexecuteet construire manuellement les lots pour l'inférence.instance_group est ignoré : le paramètre
instance_groupdansconfig.pbtxtne contrôle pas si le backend Python utilise un CPU ou un GPU. Vous devez déplacer explicitement le modèle et les données vers le dispositif cible dans vos méthodesinitializeetexecuteà l'aide de code tel quepytorch_tensor.to(torch.device("cuda")).
Mettre à jour le fichier de configuration
name: "resnet50_pt"
backend: "python"
max_batch_size: 128
input [
{
name: "INPUT__0"
data_type: TYPE_FP32
dims: [ 3, -1, -1 ]
}
]
output [
{
name: "OUTPUT__0"
data_type: TYPE_FP32
dims: [ 1000 ]
}
]
parameters: {
key: "FORCE_CPU_ONLY_INPUT_TENSORS"
value: {string_value: "no"}
}
Descriptions des paramètres clés :
backend : doit être défini sur
python.parameters : il s'agit d'une configuration facultative. Si vous exécutez l'inférence sur un GPU, définissez le paramètre FORCE_CPU_ONLY_INPUT_TENSORS sur
noafin d'éviter les frais généraux inutiles liés à la copie des tenseurs d'entrée entre le CPU et le GPU.
Déployer le service
Lors de l'utilisation du backend Python, vous devez configurer la mémoire partagée. Accédez à , saisissez la configuration JSON suivante, puis déployez le service.
{
"metadata": {
"name": "triton_server_test",
"instance": 1
},
"cloud": {
"computing": {
"instance_type": "ml.gu7i.c8m30.1-gu30",
"instances": null
}
},
"containers": [
{
"command": "tritonserver --model-repository=/models",
"image": "eas-registry-vpc.<region>.cr.aliyuncs.com/pai-eas/tritonserver:25.03-py3",
"port": 8000,
"prepare": {
"pythonRequirements": [
"torch==2.0.1"
]
}
}
],
"storage": [
{
"mount_path": "/models",
"oss": {
"path": "oss://oss-test/models/triton_backend/"
}
},
{
"empty_dir": {
"medium": "memory",
// Configure 1 GB of shared memory.
"size_limit": 1
},
"mount_path": "/dev/shm"
}
]
}
Paramètres de configuration JSON clés :
containers[0].image: image officielle Triton. Remplacez<region>par la région où se trouve votre service.containers[0].prepare.pythonRequirements: répertoriez ici vos dépendances Python. EAS les installe automatiquement avant le démarrage du service.-
storage: contient deux éléments de montage.Le premier élément monte le chemin d'accès de votre référentiel de modèles OSS vers le répertoire
/modelsdans le conteneur.Le second élément est une configuration obligatoire pour la mémoire partagée. Les processus Triton Server et du backend Python utilisent le chemin de mémoire partagée
/dev/shmpour transmettre les données des tenseurs sans copie (zero-copy), garantissant ainsi des performances optimales. La valeursize_limitest exprimée en Go. Estimez la taille requise en fonction de votre modèle et de la concurrence attendue.
Appeler le service
Obtenir le point de terminaison et le jeton du service
Accédez à la page Elastic Algorithm Service (EAS) et cliquez sur le nom de votre service.
Dans l'onglet Service Details, cliquez sur View Endpoint Information. Copiez le Internet Endpoint et le Token.
Envoyer une requête HTTP
Si le numéro de port est défini sur 8000, le service prend en charge les requêtes HTTP.
import numpy as np
# To install the tritonclient package, run the following command: pip install tritonclient
import tritonclient.http as httpclient
# The public endpoint generated after the service is deployed. Do not include the http:// prefix.
url = '1859257******.cn-hangzhou.pai-eas.aliyuncs.com/api/predict/triton_server_test'
triton_client = httpclient.InferenceServerClient(url=url)
image = np.ones((1,3,224,224))
image = image.astype(np.float32)
inputs = []
inputs.append(httpclient.InferInput('INPUT__0', image.shape, "FP32"))
inputs[0].set_data_from_numpy(image, binary_data=False)
outputs = []
outputs.append(httpclient.InferRequestedOutput('OUTPUT__0', binary_data=False)) # Get a 1000-dimensional vector.
# Specify the model name, request token, inputs, and outputs.
results = triton_client.infer(
model_name="<your-model-name>",
model_version="<version-num>",
inputs=inputs,
outputs=outputs,
headers={"Authorization": "<your-service-token>"},
)
output_data0 = results.as_numpy('OUTPUT__0')
print(output_data0.shape)
print(output_data0)
Envoyer une requête gRPC
Si vous définissez le port sur 8001 et activez gRPC, le service prend en charge les requêtes gRPC.
Le point de terminaison gRPC diffère du point de terminaison HTTP. Assurez-vous d'obtenir le point de terminaison correct depuis la page des détails du service.
#!/usr/bin/env python
import grpc
# To install the tritonclient package, run the following command: pip install tritonclient
from tritonclient.grpc import service_pb2, service_pb2_grpc
import numpy as np
if __name__ == "__main__":
# The service endpoint generated after service deployment. Do not include http://. Append :80 to the end.
host = (
"service_name.115770327099****.cn-beijing.pai-eas.aliyuncs.com:80"
)
# The service token. In a real application, you should use an actual token.
token = "<your-service-token>"
# The model name and version.
model_name = "<your-model-name>"
model_version = "<version-num>"
# Create gRPC metadata for token authentication.
metadata = (("authorization", token),)
# Create a gRPC channel and stub to communicate with the server.
channel = grpc.insecure_channel(host)
grpc_stub = service_pb2_grpc.GRPCInferenceServiceStub(channel)
# Build the inference request.
request = service_pb2.ModelInferRequest()
request.model_name = model_name
request.model_version = model_version
# Construct the input tensor, which corresponds to the input parameter defined in the model configuration file.
input = service_pb2.ModelInferRequest().InferInputTensor()
input.name = "INPUT__0"
input.datatype = "FP32"
input.shape.extend([1, 3, 224, 224])
# Construct the output tensor, which corresponds to the output parameter defined in the model configuration file.
output = service_pb2.ModelInferRequest().InferRequestedOutputTensor()
output.name = "OUTPUT__0"
# Add the input and output specifications to the request.
request.inputs.extend([input])
request.outputs.extend([output])
# Construct a random array and serialize it into a byte sequence as the input data.
request.raw_input_contents.append(np.random.rand(1, 3, 224, 224).astype(np.float32).tobytes())
# Send the inference request and receive the response.
response, _ = grpc_stub.ModelInfer.with_call(request, metadata=metadata)
# Extract the output tensor from the response.
output_contents = response.raw_output_contents[0] # Assume there is only one output tensor.
output_shape = [1, 1000] # Assume the output tensor shape is [1, 1000].
# Convert the output bytes to a NumPy array.
output_array = np.frombuffer(output_contents, dtype=np.float32)
output_array = output_array.reshape(output_shape)
# Print the model's output result.
print("Model output:\n", output_array)
Conseils de débogage
Activer la journalisation détaillée
Définissez verbose=True pour afficher les données JSON des requêtes et des réponses :
client = httpclient.InferenceServerClient(url=url, verbose=True)
Exemple de sortie :
POST /api/predict/triton_test/v2/models/resnet50_pt/versions/1/infer, headers {'Authorization': '************1ZDY3OTEzNA=='}
b'{"inputs":[{"name":"INPUT__0","shape":[1,3,32,32],"datatype":"FP32","data":[1.0,1.0,1.0,.....,1.0]}],"outputs":[{"name":"OUTPUT__0","parameters":{"binary_data":false}}]}'
Débogage en ligne
Vous pouvez tester le service directement à l'aide de la fonctionnalité de débogage en ligne de la console. Complétez l'URL de la requête avec /api/predict/triton_test/v2/models/resnet50_pt/versions/1/infer et utilisez les données de requête JSON issues des journaux détaillés comme corps de la requête.
Après l'envoi de la requête, le code d'état 200 est renvoyé. Le corps de la réponse contient model_version:"1", le nom de la sortie est OUTPUT__0, le type de données est FP32, la forme est [1,1000] et les données constituent un tableau de nombres à virgule flottante, ce qui indique que la requête d'inférence a abouti.
Effectuer un test de charge du service
Les étapes suivantes expliquent comment réaliser un test de charge en utilisant une seule entrée de données. Pour plus d'informations sur les tests de charge, consultez la rubrique Test de charge du service :
Dans l'onglet Benchmark Task, cliquez sur Create Stress Testing Task. Sélectionnez votre service Triton déployé et saisissez l'URL du test de charge.
-
Pour la source de données, sélectionnez Single Data Entry, puis utilisez le code suivant pour convertir le corps de la requête JSON en une chaîne encodée en Base64.
import base64 # An existing JSON request body string json_str = '{"inputs":[{"name":"INPUT__0","shape":[1,3,32,32],"datatype":"FP32","data":[1.0,1.0,.....,1.0]}]}' # Encode directly base64_str = base64.b64encode(json_str.encode('utf-8')).decode('ascii') print(base64_str)Dans la boîte de dialogue Create Stress Testing Task, configurez les paramètres suivants :
Service : sélectionnez le service Triton cible.
Stress Test URL : saisissez
/api/predict/triton_xxx/v2/models/resnet50_pt/versions/1/infercomme chemin.Single Data : collez le résultat encodé en Base64 obtenu avec le code ci-dessus dans la zone de texte.
Maximum Stress Test Duration : 300 secondes.
Maximum QPS : 64 000.
FAQ
Q : Erreur CUDA « no kernel image »
Cette erreur survient en raison d'un problème de compatibilité entre la version de l'image et le GPU. Vous pouvez essayer de changer de type d'instance GPU, par exemple en optant pour A10 ou T4.
Q : Erreur « url should not include the scheme »
Cette erreur est due à une URL de service incorrecte. Le format du point de terminaison indiqué dans la section « Obtenir le point de terminaison et le jeton du service » est http://17519301*******.cn-hangzhou.pai-eas.aliyuncs.com/api/predict/wen***** (notez qu'il diffère du point de terminaison gRPC). Vous devez supprimer le préfixe http://.
Q : Erreur gRPC « DNS resolution failed »
Cette erreur est causée par un hôte de service incorrect. Le format du point de terminaison indiqué dans la section « Obtenir le point de terminaison et le jeton du service » est http://we*****.1751930*****.cn-hangzhou.pai-eas.aliyuncs.com/ (notez qu'il diffère du point de terminaison HTTP). Vous devez supprimer le préfixe http:// et la barre oblique finale /, puis ajouter :80 à la fin. Le format final doit être we*****.1751930*****.cn-hangzhou.pai-eas.aliyuncs.com:80.
Documents connexes
Pour savoir comment déployer un service EAS à l'aide du moteur d'inférence TensorFlow Serving, consultez la rubrique Déploiement d'une image TensorFlow Serving.
Vous pouvez également développer une image personnalisée pour déployer un service EAS. Pour plus d'informations, consultez la rubrique Déployer un service à l'aide d'une image personnalisée.
Pour en savoir plus sur NVIDIA Triton, consultez la documentation officielle de Triton.