Skip to content

Latest commit

 

History

History

README.md

Triton Inference Server - Predicción de Ganancias

Sistema de predicción de ganancias usando NVIDIA Triton Inference Server con ensemble de preprocesamiento Python + inferencia ONNX optimizada.

📋 Descripción General

Esta arquitectura implementa un servidor de inferencia de alto rendimiento usando Triton Server, donde el preprocesamiento y la inferencia se ejecutan como un ensemble de modelos nativamente en Triton.

Arquitectura

Cliente → Triton (HTTP/gRPC) → Ensemble:
                                  ├─ Preprocesamiento (Python)
                                  └─ Inferencia (ONNX)
                               → Respuesta

Componentes:

  • Triton Server: Servidor de inferencia optimizado de NVIDIA
  • Modelo Python (preproc_profit): Preprocesamiento en Python dentro de Triton
  • Modelo ONNX (profit): Inferencia con ONNX Runtime optimizado
  • Ensemble (profit_ensemble): Orquestación del pipeline completo

✅ Ventajas

  • Alto rendimiento: Throughput muy superior (~1000 req/s)
  • Aceleración GPU: Soporte nativo para inferencia en GPU
  • Batching dinámico: Agrupa requests automáticamente para optimizar throughput
  • Múltiples backends: Soporta ONNX, TensorFlow, PyTorch, TensorRT
  • Métricas integradas: Prometheus metrics out-of-the-box
  • Ensemble nativo: Pipeline de modelos orquestado por Triton
  • Optimización automática: Múltiples instancias, scheduling inteligente

⚠️ Consideraciones

  • Menor flexibilidad: El preprocesamiento en Triton es menos flexible que en Python puro
  • Curva de aprendizaje: Requiere aprender configuración de Triton (config.pbtxt)
  • Debugging complejo: Más difícil debuggear que código Python estándar
  • Tamaño de imagen: Imágenes Docker más grandes (~10GB)

🎯 Ideal Para

  • ✅ Alto volumen de requests (>1000 req/s)
  • ✅ Inferencia con GPU para máximo rendimiento
  • ✅ Pipeline de modelos estable (preprocesamiento no cambia frecuentemente)
  • ✅ Necesidad de batching dinámico sofisticado
  • ✅ Producción con requisitos estrictos de latencia
  • ✅ Múltiples modelos que necesitan ser servidos simultáneamente

📦 Obtención de Modelos

Los modelos no están incluidos en este repositorio. Debes obtenerlos y colocarlos en la estructura correcta.

Estructura Requerida

model_repository/
├── preproc_profit/
│   ├── 1/
│   │   ├── model.py                    # ← Preprocesamiento (ya incluido)
│   │   └── preprocessing_info.pkl      # ← DESCARGAR
│   └── config.pbtxt                    # ← Ya incluido
├── profit/
│   ├── 1/
│   │   ├── model.onnx                  # ← DESCARGAR
│   │   └── model.onnx.data             # ← DESCARGAR (si aplica)
│   └── config.pbtxt                    # ← Ya incluido
└── profit_ensemble/
    ├── 1/                              # ← Vacío
    └── config.pbtxt                    # ← Ya incluido

Opción 1: Descargar Modelos Pre-entrenados

# Descargar modelo ONNX
gsutil cp gs://tu-bucket/models/serving/model.onnx \
    model_repository/serving/1/
gsutil cp gs://tu-bucket/models/serving/model.onnx.data \
    model_repository/serving/1/

# Descargar preprocessing info
gsutil cp gs://tu-bucket/models/serving/preprocessing_info.pkl \
    model_repository/preproc_profit/1/

# O desde S3 (AWS)
aws s3 cp s3://tu-bucket/models/serving/model.onnx \
    model_repository/serving/1/
aws s3 cp s3://tu-bucket/models/serving/preprocessing_info.pkl \
    model_repository/preproc_profit/1/

Opción 2: Entrenar tu Propio Modelo

# 1. Entrenar y exportar modelo a ONNX
import torch.onnx
import pickle

# Exportar modelo
torch.onnx.export(
    model,
    dummy_input,
    "model_repository/serving/1/model.onnx",
    input_names=['input'],
    output_names=['output'],
    dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}}
)

# 2. Guardar preprocessing_info.pkl
preprocessing_info = {
    'encoders': {...},      # LabelEncoders
    'scaler': scaler,       # StandardScaler
    'feature_columns': [...],
    'categorical_columns': [...]
}

with open('model_repository/preproc_profit/1/preprocessing_info.pkl', 'wb') as f:
    pickle.dump(preprocessing_info, f)

Requisitos del Modelo

Modelo ONNX (profit):

  • Input: input - Tensor FP32 de forma [-1, 15]
  • Output: output - Tensor FP32 de forma [-1, 1]
  • Backend: ONNX Runtime

Preprocessing Info (preproc_profit):

  • Debe contener encoders, scaler, feature_columns, categorical_columns
  • Compatible con el código en model_repository/preproc_profit/1/model.py

🚀 Arquitectura del Sistema

El sistema utiliza un pipeline de inferencia con tres componentes:

  1. preproc_profit: Modelo Python que preprocesa los datos raw (fechas, categorías, etc.)
  2. profit: Modelo ONNX que realiza la predicción basado en features procesadas
  3. profit_ensemble: Pipeline que orquesta el flujo de datos entre preprocesamiento y predicción
graph LR
    A[Datos Raw JSON] --> B[preproc_profit]
    B --> C[Features Procesadas]
    C --> D[profit ONNX]
    D --> E[Predicción]
Loading

📁 Estructura del Proyecto

profic-triton/
├── model_repository/              # Repositorio de modelos para Triton
│   ├── preproc_profit/           # Modelo de preprocesamiento Python
│   │   ├── 1/                    # Versión 1
│   │   │   ├── model.py          # Lógica de preprocesamiento
│   │   │   └── preprocessing_info.pkl  # Encoders y scalers
│   │   └── config.pbtxt          # Configuración de Triton
│   ├── profit/                   # Modelo ONNX de predicción
│   │   ├── 1/                    # Versión 1
│   │   │   └── model.onnx        # Modelo entrenado
│   │   └── config.pbtxt          # Configuración de Triton
│   └── profit_ensemble/          # Pipeline ensemble
│       ├── 1/                    # Versión 1 (vacío)
│       └── config.pbtxt          # Configuración del pipeline
├── test_client.py                # Cliente de prueba
├── requirements.txt              # Dependencias Python
├── Dockerfile                    # Imagen Docker personalizada
└── triton-deployment-docker.yaml # Despliegue en Kubernetes

🔧 Configuración Rápida

1. Instalar Dependencias

# Crear entorno virtual
python3 -m venv venv
source venv/bin/activate  # En Windows: venv\Scripts\activate

# Instalar dependencias
pip install -r requirements.txt

2. Ejecutar Triton Server con Docker

# Opción 1: Imagen oficial de NVIDIA
docker run --rm \
  -p 8000:8000 \
  -p 8001:8001 \
  -p 8002:8002 \
  -v $(pwd)/model_repository:/models \
  nvcr.io/nvidia/tritonserver:24.10-py3 \
  tritonserver --model-repository=/models

# Opción 2: Construir imagen personalizada (recomendado)
docker build -t profit-triton:latest .
docker run --rm \
  -p 8000:8000 \
  -p 8001:8001 \
  -p 8002:8002 \
  profit-triton:latest

3. Verificar el Servidor

# Verificar salud del servidor
curl -v http://localhost:8000/v2/health/ready

# Verificar modelos cargados
curl http://localhost:8000/v2/models

4. Ejecutar Cliente de Prueba

python test_client.py

📡 Endpoints API de Triton

Triton Server expone una API HTTP/REST en el puerto 8000 y gRPC en el puerto 8001.

Endpoints Principales

Endpoint Método Descripción
/v2/health/live GET Verificar si el servidor está vivo
/v2/health/ready GET Verificar si el servidor está listo
/v2/models GET Listar todos los modelos disponibles
/v2/models/{MODEL_NAME} GET Información de un modelo específico
/v2/models/{MODEL_NAME}/ready GET Verificar si un modelo está listo
/v2/models/{MODEL_NAME}/infer POST Realizar inferencia
/metrics GET Métricas Prometheus (puerto 8002)

GET /v2/health/live

Verifica si el servidor Triton está vivo.

URL: http://localhost:8000/v2/health/live

Response:

{
  "live": true
}

Ejemplo con curl:

curl http://localhost:8000/v2/health/live

GET /v2/health/ready

Verifica si el servidor está listo para recibir requests de inferencia.

URL: http://localhost:8000/v2/health/ready

Response:

{
  "ready": true
}

Ejemplo con curl:

curl http://localhost:8000/v2/health/ready

GET /v2/models

Lista todos los modelos cargados en el servidor.

URL: http://localhost:8000/v2/models

Response:

[
  {
    "name": "preproc_profit",
    "version": "1",
    "state": "READY"
  },
  {
    "name": "profit",
    "version": "1",
    "state": "READY"
  },
  {
    "name": "profit_ensemble",
    "version": "1",
    "state": "READY"
  }
]

Ejemplo con curl:

curl http://localhost:8000/v2/models

GET /v2/models/{MODEL_NAME}

Obtiene información detallada de un modelo específico.

URL: http://localhost:8000/v2/models/profit_ensemble

Response:

{
  "name": "profit_ensemble",
  "versions": ["1"],
  "platform": "ensemble",
  "inputs": [
    {
      "name": "raw_data",
      "datatype": "BYTES",
      "shape": [-1, 1]
    }
  ],
  "outputs": [
    {
      "name": "profit_prediction",
      "datatype": "FP32",
      "shape": [-1, 1]
    }
  ]
}

Ejemplo con curl:

curl http://localhost:8000/v2/models/profit_ensemble
curl http://localhost:8000/v2/models/profit
curl http://localhost:8000/v2/models/preproc_profit

POST /v2/models/{MODEL_NAME}/infer

Realiza inferencia con el modelo especificado.

URL: http://localhost:8000/v2/models/profit_ensemble/infer

Request Body (JSON):

{
  "inputs": [
    {
      "name": "raw_data",
      "shape": [1, 1],
      "datatype": "BYTES",
      "data": ["{\"Region\": \"Sub-Saharan Africa\", \"Country\": \"South Africa\", \"Item_Type\": \"Fruits\", \"Sales_Channel\": \"Online\", \"Order_Priority\": \"M\", \"Order_Date\": \"2024-01-15\", \"Ship_Date\": \"2024-01-20\", \"Units_Sold\": 1000, \"Unit_Price\": 9.33, \"Unit_Cost\": 6.92, \"Total_Revenue\": 9330.0, \"Total_Cost\": 6920.0}"]
    }
  ]
}

Response:

{
  "model_name": "profit_ensemble",
  "model_version": "1",
  "outputs": [
    {
      "name": "profit_prediction",
      "datatype": "FP32",
      "shape": [1, 1],
      "data": [2410.0]
    }
  ]
}

Ejemplo con curl:

curl -X POST http://localhost:8000/v2/models/profit_ensemble/infer \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{
      "name": "raw_data",
      "shape": [1, 1],
      "datatype": "BYTES",
      "data": ["{\"Region\": \"Sub-Saharan Africa\", \"Country\": \"South Africa\", \"Item_Type\": \"Fruits\", \"Sales_Channel\": \"Online\", \"Order_Priority\": \"M\", \"Order_Date\": \"2024-01-15\", \"Ship_Date\": \"2024-01-20\", \"Units_Sold\": 1000, \"Unit_Price\": 9.33, \"Unit_Cost\": 6.92, \"Total_Revenue\": 9330.0, \"Total_Cost\": 6920.0}"]
    }]
  }'

GET /metrics (Puerto 8002)

Obtiene métricas Prometheus del servidor Triton.

URL: http://localhost:8002/metrics

Response: Formato Prometheus text

# HELP nv_inference_request_success Number of successful inference requests
# TYPE nv_inference_request_success counter
nv_inference_request_success{model="profit_ensemble",version="1"} 42

# HELP nv_inference_request_duration_us Cumulative inference request duration in microseconds
# TYPE nv_inference_request_duration_us counter
nv_inference_request_duration_us{model="profit_ensemble",version="1"} 125000

...

Ejemplo con curl:

curl http://localhost:8002/metrics

📊 Modelos y Pipeline

preproc_profit (Python)

  • Tipo: Modelo Python personalizado
  • Input: raw_features - JSON string con datos de venta
  • Output: processed_features - Tensor FP32 con features procesadas
  • Funciones:
    • Extracción de características temporales (año, mes, día, día de semana)
    • Codificación de variables categóricas
    • Normalización de features numéricas
    • Manejo de valores faltantes

profit (ONNX)

  • Tipo: Modelo ONNX (LightGBM/XGBoost/NN)
  • Input: input - Features procesadas FP32
  • Output: output - Predicción de profit FP32
  • Optimizado para: CPU con ONNX Runtime

profit_ensemble

  • Tipo: Pipeline ensemble
  • Input: raw_data - JSON string con datos raw
  • Output: profit_prediction - Valor predicho de profit
  • Pipeline:
    1. raw_data → preproc_profit → processed_features
    2. processed_features → profit → profit_prediction

🔍 Formato de Datos de Entrada

El sistema espera datos en formato JSON con los siguientes campos:

{
    "Order_Date": "2024-01-15",
    "Ship_Date": "2024-01-18",
    "Order_ID": "ORD-2024-001",
    "Product_Name": "Office Chair",
    "Category": "Furniture",
    "Sub_Category": "Chairs",
    "Segment": "Corporate",
    "Country": "United States",
    "City": "New York",
    "State": "NY",
    "Postal_Code": "10001",
    "Region": "East",
    "Product_ID": "FUR-CH-10001",
    "Customer_ID": "CUST-001",
    "Customer_Name": "John Doe",
    "Ship_Mode": "Standard Class",
    "Quantity": 2,
    "Discount": 0.1,
    "Sales": 299.99
}

🧪 Testing y Validación

Test Individual del Preprocesador

import json
import numpy as np
import tritonclient.http as httpclient

client = httpclient.InferenceServerClient("localhost:8000")

# Crear datos de prueba
data = {"Order_Date": "2024-01-15", "Sales": 299.99, ...}
json_data = json.dumps(data)

# Crear input
inputs = [httpclient.InferInput("raw_features", [1, 1], "BYTES")]
inputs[0].set_data_from_numpy(np.array([[json_data]], dtype=object))

# Inferencia
results = client.infer("preproc_profit", inputs=inputs)
processed = results.as_numpy("processed_features")

Test del Pipeline Completo

# Usar el ensemble directamente
inputs = [httpclient.InferInput("raw_data", [1, 1], "BYTES")]
inputs[0].set_data_from_numpy(np.array([[json_data]], dtype=object))

results = client.infer("profit_ensemble", inputs=inputs)
prediction = results.as_numpy("profit_prediction")
print(f"Profit predicho: ${prediction[0][0]:.2f}")

☸️ Despliegue en Kubernetes

1. Aplicar Manifiestos

# Desplegar Triton Server
kubectl apply -f triton-deployment-docker.yaml

# Verificar pods
kubectl get pods -l app=triton-inference-server

# Ver logs
kubectl logs -f deployment/triton-inference-server

2. Port Forward para Pruebas

kubectl port-forward service/triton-inference-server 8000:8000 8001:8001

🔧 Personalización

Modificar el Preprocesamiento

Edita model_repository/preproc_profit/1/model.py para ajustar:

  • Manejo de nuevas características
  • Lógica de transformación
  • Validación de datos

Actualizar el Modelo ONNX

  1. Entrena tu nuevo modelo
  2. Exporta a ONNX
  3. Reemplaza model_repository/serving/1/model.onnx
  4. Actualiza preprocessing_info.pkl si cambian los encoders/scalers

Configurar Batching

Edita model_repository/serving/config.pbtxt:

dynamic_batching {
  preferred_batch_size: [ 1, 4, 8, 16, 32 ]
  max_queue_delay_microseconds: 100
}

🐛 Troubleshooting

Error: "preprocessing_info.pkl not found"

  • Verifica que el archivo existe en model_repository/preproc_profit/1/
  • Asegúrate de que contiene: encoders, scaler, feature_columns, categorical_columns

Error: "Model not ready"

  • Revisa logs: docker logs <container_id>
  • Verifica configuración: Nombres de input/output deben coincidir entre modelos

Error: "Shape mismatch"

  • El número de features procesadas debe coincidir con lo que espera el modelo ONNX
  • Usa onnx library para inspeccionar el modelo:
import onnx
model = onnx.load("model_repository/serving/1/model.onnx")
print(model.graph.input[0])

📈 Monitoreo y Métricas

Triton expone métricas en el puerto 8002:

# Ver métricas
curl http://localhost:8002/metrics

# Métricas importantes:
# - nv_inference_request_success
# - nv_inference_request_failure
# - nv_inference_count
# - nv_inference_exec_count
# - nv_inference_queue_duration_us

📚 Recursos Adicionales

📝 Notas Importantes

  • El preprocesador maneja automáticamente valores faltantes
  • Las variables categóricas desconocidas se manejan con valores por defecto
  • El pipeline es stateless - cada request es independiente
  • Para producción, considera usar GPU para el modelo ONNX si el volumen lo justifica