Skip to content

Latest commit

 

History

History
630 lines (499 loc) · 18.7 KB

File metadata and controls

630 lines (499 loc) · 18.7 KB

Ray Serve Puro - Predicción de Ganancias

Servicio completo de predicción de ganancias usando Ray Serve con preprocesamiento local, validación de esquemas y despliegue en Kubernetes.

📋 Descripción General

Esta arquitectura implementa un servicio de ML usando únicamente Ray Serve, donde todo el procesamiento (preprocesamiento e inferencia) se ejecuta dentro del mismo deployment de Ray.

Arquitectura

Cliente → Ray Serve (FastAPI) → Preprocesamiento → ONNX Runtime → Respuesta

Componentes:

  • Ray Serve: Framework de serving y orquestación
  • FastAPI: API REST con validación automática
  • Pydantic: Validación de esquemas de entrada
  • Scikit-learn: Preprocesamiento (encoders, scalers)
  • ONNX Runtime: Motor de inferencia del modelo

✅ Ventajas

  • Flexibilidad máxima: Fácil modificar el preprocesamiento en Python
  • Desarrollo rápido: Ciclo de desarrollo ágil, solo requiere Python
  • Validación robusta: Pydantic valida automáticamente las entradas
  • Despliegue simple: Un solo contenedor, menos complejidad operacional
  • Debugging fácil: Código Python estándar, fácil de depurar
  • Bajo costo de recursos: No requiere GPU ni servidores adicionales

⚠️ Consideraciones

  • Performance limitada: Menor throughput que Triton (~100 req/s vs ~1000 req/s)
  • Sin aceleración GPU: ONNX Runtime en CPU, no optimizado para GPU
  • Escalado simple: Escalado horizontal básico, sin batching dinámico sofisticado

🎯 Ideal Para

  • ✅ Equipos que trabajan principalmente con Python
  • ✅ Proyectos donde el preprocesamiento cambia frecuentemente
  • ✅ Fase de desarrollo y experimentación
  • ✅ Casos donde la flexibilidad es más importante que el máximo rendimiento
  • ✅ Infraestructura con recursos limitados

📦 Obtención de Modelos

Los modelos no están incluidos en este repositorio. Debes obtenerlos de una de estas formas:

Opción 1: Descargar Modelos Pre-entrenados

# Crear directorio de modelos
mkdir -p models

# Descargar desde tu almacenamiento (ejemplo con gsutil de GCP)
gsutil cp gs://tu-bucket/models/serving/model.onnx models/
gsutil cp gs://tu-bucket/models/serving/model.onnx.data models/
gsutil cp gs://tu-bucket/models/serving/preprocessing_info.pkl models/

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

Opción 2: Entrenar tu Propio Modelo

# Ejemplo: Entrenar y exportar modelo a ONNX
import pickle
import torch
import onnx
from sklearn.preprocessing import LabelEncoder, StandardScaler

# 1. Entrenar tu modelo (ejemplo con PyTorch/Scikit-learn)
# ... código de entrenamiento ...

# 2. Exportar a ONNX
torch.onnx.export(
    model,
    dummy_input,
    "models/model.onnx",
    export_params=True,
    opset_version=11,
    input_names=['input'],
    output_names=['output']
)

# 3. Guardar preprocessing_info.pkl
preprocessing_info = {
    'encoders': {
        'Region': region_encoder,
        'Country': country_encoder,
        'Item_Type': item_type_encoder,
        'Sales_Channel': sales_channel_encoder,
        'Order_Priority': order_priority_encoder
    },
    'scaler': scaler,
    'feature_columns': feature_columns_list,
    'categorical_columns': categorical_columns_list
}

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

Requisitos del Modelo

El modelo ONNX debe:

  • Input: Tensor de forma [-1, 15] (15 features preprocesadas)
  • Output: Tensor de forma [-1, 1] (predicción de profit)
  • Tipo de datos: FP32 (float32)

El archivo preprocessing_info.pkl debe contener:

  • encoders: Dict con LabelEncoders para columnas categóricas
  • scaler: StandardScaler para normalización
  • feature_columns: Lista con nombres de las 15 features
  • categorical_columns: Lista con nombres de columnas categóricas

📋 Descripción Técnica

Este servicio implementa un deployment avanzado de Ray Serve que:

  • Carga un modelo ONNX entrenado para predecir ganancias de ventas
  • Realiza preprocesamiento completo de datos (codificación categórica, normalización, feature engineering)
  • Valida las entradas usando FastAPI y Pydantic
  • Expone endpoints REST para predicciones y utilidades
  • Soporta despliegue tanto con Docker como con volúmenes de Kubernetes

🏗️ Arquitectura

  • Framework: Ray Serve 2.46.0
  • API: FastAPI con validación Pydantic
  • Modelo: ONNX con información de preprocesamiento
  • Preprocesamiento: Scikit-learn encoders y scalers
  • Contenedorización: Docker opcional
  • Orquestación: Kubernetes (usando RayService CRD)

📁 Estructura de Archivos

test-serve/
├── Dockerfile                    # Imagen Docker con modelo y código
├── serve.py                      # Deployment principal con preprocesamiento
├── schema.py                     # Esquemas Pydantic para validación
├── requirements.txt              # Dependencias Python
├── config-serve-docker.yaml      # Configuración K8s con imagen Docker
├── config-serve-volumen.yaml     # Configuración K8s con volúmenes/ConfigMap
└── models/
    ├── model.onnx                # Modelo ONNX entrenado
    ├── model.onnx.data           # Datos adicionales del modelo ONNX
    └── preprocessing_info.pkl    # Encoders, scalers y metadata de preprocesamiento

🔧 Componentes Principales

serve.py

Contiene el deployment ProfitPredictionService que:

  • Carga el modelo: ONNX desde /app/model.onnx
  • Carga preprocesamiento: Encoders, scalers y metadata desde preprocessing_info.pkl
  • Preprocesa datos:
    • Convierte fechas a características temporales (año, mes, día, día de semana)
    • Calcula días hasta envío
    • Codifica variables categóricas (Region, Country, Item_Type, etc.)
    • Normaliza características numéricas
  • Realiza predicciones: Usa onnxruntime para inferencia
  • Valida entradas: Usa Pydantic para validar el esquema de entrada
  • Expone endpoints:
    • POST /predict: Predicción principal de ganancias
    • GET /fake-email: Utilidad para generar emails falsos (usando Faker)

schema.py

Define el esquema SalesPredictionRequest con Pydantic:

  • Campos requeridos para predicción de ventas
  • Validación automática de tipos
  • Documentación integrada

Dockerfile

Construye una imagen Docker que:

  • Usa rayproject/ray:2.46.0-py311 como base
  • Instala todas las dependencias necesarias
  • Copia modelo, código y archivos de preprocesamiento
  • Configura el entorno de trabajo

config-serve-docker.yaml

Configuración de Kubernetes que:

  • Usa imagen Docker personalizada ray-serving-model:latest
  • Define recursos y escalado del cluster
  • Configura 2 réplicas del servicio

config-serve-volumen.yaml

Configuración alternativa que:

  • Usa imagen base de Ray (sin modelo empaquetado)
  • Monta código y modelo desde ConfigMap
  • Permite actualizar modelo sin reconstruir imagen
  • Instala dependencias en runtime usando runtime_env

🚀 Uso

1. Construir la imagen Docker (opcional)

docker build -t ray-serving-model:latest .

2. Publicar la imagen Docker

Opción A: Cargar en cluster local (kind)

kind load docker-image ray-serving-model:latest --name ray-cluster

Opción B: Push a Google Cloud Platform (GCP)

Usando Artifact Registry (recomendado)
  1. Configurar autenticación con GCP:

    # Instalar Google Cloud SDK si no lo tienes
    # https://cloud.google.com/sdk/docs/install
    
    # Autenticarse con tu cuenta de Google
    gcloud auth login
    
    # Configurar proyecto (reemplaza {PROJECT_ID} con tu proyecto)
    gcloud config set project {PROJECT_ID}
    
    # Configurar Docker para usar credenciales de gcloud
    # Para Artifact Registry
    gcloud auth configure-docker us-central1-docker.pkg.dev
    
    # O si usas otra región, reemplaza us-central1
    # gcloud auth configure-docker REGION-docker.pkg.dev
    
    # Verificar autenticación
    gcloud auth list
    gcloud config get-value project
  2. Crear un Artifact Registry (si no existe):

    # Crear repositorio en Artifact Registry
    gcloud artifacts repositories create ray-serving-repo \
      --repository-format=docker \
      --location=us-central1 \
      --description="Repositorio para imágenes de Ray Serve"
  3. Taggear la imagen con la URL de GCP:

    # Formato: REGION-docker.pkg.dev/PROYECTO-ID/REPOSITORIO/IMAGEN:TAG
    docker tag ray-serving-model:latest \
      us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:latest
    
    # O con versión específica
    docker tag ray-serving-model:latest \
      us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:v1.0.0
  4. Hacer push de la imagen:

    docker push us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:latest
Usando Container Registry (legacy)
  1. Configurar autenticación:

    gcloud auth login
    gcloud auth configure-docker
    gcloud config set project {PROJECT_ID}
  2. Taggear la imagen:

    # Formato: gcr.io/PROYECTO-ID/IMAGEN:TAG
    docker tag ray-serving-model:latest \
      gcr.io/{PROJECT_ID}/ray-serving-model:latest
  3. Hacer push:

    docker push gcr.io/{PROJECT_ID}/ray-serving-model:latest
  4. Actualizar config-serve-docker.yaml:

    Cambia la línea de la imagen en el archivo YAML:

    # Para Artifact Registry
    image: us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:latest
    imagePullPolicy: Always  # Cambiar de Never a Always
    
    # Para Container Registry
    image: gcr.io/{PROJECT_ID}/ray-serving-model:latest
    imagePullPolicy: Always
  5. Configurar acceso desde Kubernetes:

    Si tu cluster de Kubernetes está en GCP (GKE), asegúrate de que tenga permisos para acceder al registry:

    # Para GKE, el acceso suele estar configurado automáticamente
    # Si usas un cluster externo, necesitarás crear un Secret:
    kubectl create secret docker-registry gcp-registry-secret \
      --docker-server=us-central1-docker.pkg.dev \
      --docker-username=oauth2accesstoken \
      --docker-password=$(gcloud auth print-access-token) \
      --docker-email=TU-EMAIL

3. Desplegar en Kubernetes

Opción A: Usar imagen Docker

kubectl apply -f config-serve-docker.yaml

Opción B: Usar volúmenes/ConfigMap

# Crear ConfigMap con código y modelo
kubectl create configmap model-code \
  --from-file=serve.py \
  --from-file=schema.py \
  --from-file=models/

# Desplegar servicio
kubectl apply -f config-serve-volumen.yaml

4. Verificar el despliegue

# Ver pods
kubectl get pods

# Ver servicios
kubectl get services

# Ver logs
kubectl logs -f <pod-name>

5. Acceder al servicio

# Port-forward al servicio
kubectl port-forward service/rayservice-volumen-serve-svc 8000:8000

# Probar predicción
curl -X POST http://localhost:8000/predict \
  -H "Content-Type: application/json" \
  -d '{
    "Region": "Sub-Saharan Africa",
    "Country": "Mozambique",
    "Item_Type": "Office Supplies",
    "Sales_Channel": "Online",
    "Order_Priority": "M",
    "Order_Date": "2013-01-01",
    "Ship_Date": "2013-01-05",
    "Units_Sold": 1000.0,
    "Unit_Price": 9.33,
    "Unit_Cost": 6.92,
    "Total_Revenue": 9330.0,
    "Total_Cost": 6920.0
  }'

📡 Endpoints API

Endpoint Método Descripción
/predict POST Predicción de ganancias (principal)
/fake-email GET Generador de emails falsos para pruebas

POST /predict

Realiza una predicción de ganancias basada en datos de venta.

URL: http://localhost:8000/predict

Request Body:

{
  "Region": "Sub-Saharan Africa",
  "Country": "Mozambique",
  "Item_Type": "Office Supplies",
  "Sales_Channel": "Online",
  "Order_Priority": "M",
  "Order_Date": "2013-01-01",
  "Ship_Date": "2013-01-05",
  "Units_Sold": 1000.0,
  "Unit_Price": 9.33,
  "Unit_Cost": 6.92,
  "Total_Revenue": 9330.0,
  "Total_Cost": 6920.0
}

Response (Success):

{
  "status": "success",
  "prediction": {
    "total_profit": 2410.0,
    "currency": "USD"
  },
  "input_summary": {
    "region": "Sub-Saharan Africa",
    "country": "Mozambique",
    "item_type": "Office Supplies",
    "units_sold": 1000.0,
    "total_revenue": 9330.0,
    "total_cost": 6920.0,
    "actual_profit": 2410.0
  },
  "model_info": {
    "type": "ONNX",
    "features_used": 15
  }
}

Response (Error):

{
  "status": "error",
  "message": "Error description",
  "type": "ExceptionType"
}

Ejemplo con curl:

curl -X POST http://localhost:8000/predict \
  -H "Content-Type: application/json" \
  -d '{
    "Region": "Sub-Saharan Africa",
    "Country": "Mozambique",
    "Item_Type": "Office Supplies",
    "Sales_Channel": "Online",
    "Order_Priority": "M",
    "Order_Date": "2013-01-01",
    "Ship_Date": "2013-01-05",
    "Units_Sold": 1000,
    "Unit_Price": 9.33,
    "Unit_Cost": 6.92,
    "Total_Revenue": 9330.0,
    "Total_Cost": 6920.0
  }'

GET /fake-email

Genera un email falso usando la librería Faker (útil para testing).

URL: http://localhost:8000/fake-email

Response:

"john.doe@example.com"

Ejemplo con curl:

curl http://localhost:8000/fake-email

🔄 Flujo de Preprocesamiento

  1. Validación de entrada: Pydantic valida el esquema
  2. Conversión a DataFrame: Los datos se convierten a pandas DataFrame
  3. Feature engineering:
    • Extracción de características temporales de fechas
    • Cálculo de días hasta envío
  4. Codificación categórica: Variables categóricas se codifican usando LabelEncoders
  5. Normalización: Características numéricas se escalan usando StandardScaler
  6. Predicción: El modelo ONNX realiza la inferencia
  7. Respuesta: Se estructura y retorna la respuesta con metadatos

⚙️ Configuración

Recursos del Cluster

  • Head Node: 2 CPU, 2Gi memoria
  • Worker Nodes: 1 CPU, 2Gi memoria (escalable de 1 a 5 réplicas)

Deployment

  • Réplicas: 2 réplicas del servicio
  • CPU por réplica: 0.1 CPU (configurable)
  • Máximo por nodo: 1 réplica por nodo

Dependencias Runtime

Cuando se usa config-serve-volumen.yaml, las dependencias se instalan en runtime:

  • faker
  • torch
  • pandas
  • numpy
  • scikit-learn
  • onnx
  • onnxruntime
  • onnxscript
  • pydantic

🔍 Diferencias con onnx-base-serve

Este servicio es más completo que onnx-base-serve:

  • ✅ Incluye preprocesamiento completo de datos
  • ✅ Usa FastAPI para validación de esquemas
  • ✅ Tiene información de preprocesamiento persistida
  • ✅ Feature engineering avanzado (fechas, cálculos derivados)
  • ✅ Manejo robusto de errores y validaciones
  • ✅ Endpoints adicionales para utilidades

📝 Notas

  • El modelo debe estar entrenado y exportado a ONNX
  • El archivo preprocessing_info.pkl debe contener los mismos encoders y scalers usados durante el entrenamiento
  • Las fechas deben estar en formato ISO (YYYY-MM-DD)
  • El servicio maneja automáticamente valores categóricos desconocidos usando valores por defecto

🐛 Troubleshooting

Error: "Unauthenticated request" al hacer push a Artifact Registry

Si recibes el error:

denied: Unauthenticated request. Unauthenticated requests do not have permission 
"artifactregistry.repositories.uploadArtifacts"

Sigue estos pasos para resolverlo:

  1. Verificar autenticación actual:

    gcloud auth list
    gcloud config get-value project
  2. Re-autenticarse si es necesario:

    # Cerrar sesión y volver a autenticarse
    gcloud auth logout
    gcloud auth login
    
    # O usar una cuenta específica
    gcloud auth login TU-EMAIL@ejemplo.com
  3. Configurar Docker con la región correcta:

    # IMPORTANTE: Especificar la región exacta del repositorio
    gcloud auth configure-docker us-central1-docker.pkg.dev
    
    # Si tu repositorio está en otra región, usa esa región:
    # gcloud auth configure-docker europe-west1-docker.pkg.dev
    # gcloud auth configure-docker asia-east1-docker.pkg.dev
  4. Obtener token de acceso y configurar manualmente:

    # Obtener token de acceso
    gcloud auth print-access-token
    
    # Configurar Docker manualmente (si el método anterior no funciona)
    echo $(gcloud auth print-access-token) | docker login -u oauth2accesstoken --password-stdin us-central1-docker.pkg.dev
  5. Verificar permisos en el proyecto:

    # Verificar que tienes los permisos necesarios
    gcloud projects get-iam-policy {PROJECT_ID} --flatten="bindings[].members" --filter="bindings.members:user:TU-EMAIL@ejemplo.com"
    
    # Si no tienes permisos, necesitarás que un administrador te otorgue:
    # - Artifact Registry Writer
    # - O Storage Admin (para Container Registry)
  6. Usar Application Default Credentials (alternativa):

    # Configurar Application Default Credentials
    gcloud auth application-default login
    
    # Luego intentar el push nuevamente
    docker push us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:latest
  7. Verificar que el repositorio existe y tienes acceso:

    # Listar repositorios
    gcloud artifacts repositories list --location=us-central1
    
    # Ver detalles del repositorio
    gcloud artifacts repositories describe ray-serving-repo --location=us-central1

Error: "Permission denied" o "Access denied"

Si aún tienes problemas después de autenticarte:

  1. Verificar que el proyecto es correcto:

    gcloud config set project {PROJECT_ID}
    gcloud config get-value project
  2. Verificar que el repositorio existe en la región correcta:

    gcloud artifacts repositories list --location=us-central1 --format="table(name,format,location)"
  3. Contactar al administrador del proyecto para verificar que tienes los roles necesarios:

    • roles/artifactregistry.writer (para Artifact Registry)
    • roles/storage.admin (para Container Registry)

Error: "Value not in encoder"

  • El valor categórico no fue visto durante el entrenamiento
  • El servicio usa un valor por defecto, pero puede afectar la precisión

Error: "Model not found"

  • Verifica que el modelo esté en /app/model.onnx o ./models/model.onnx
  • Si usas ConfigMap, verifica que esté montado correctamente

Error: "Preprocessing info not found"

  • Verifica que preprocessing_info.pkl esté presente
  • Debe contener: encoders, scaler, feature_columns, categorical_columns