Diseño de la Arquitectura del Proyecto

Lectura
20 min~5 min lectura

Concepto clave

Diseñar la arquitectura de una API de Machine Learning en producción es como planificar la construcción de un puente: no solo necesitas que sea funcional hoy, sino que debe soportar el tráfico futuro, mantenerse estable bajo condiciones variables y permitir reparaciones sin interrumpir el servicio. En este contexto, arquitectura se refiere a la estructura organizada de componentes (modelo, API, base de datos, monitoreo) y cómo interactúan para servir predicciones de manera confiable.

Una arquitectura bien diseñada para FastAPI y ML debe considerar tres pilares: escalabilidad (manejar más peticiones sin degradación), mantenibilidad (facilitar actualizaciones del modelo) y observabilidad (monitorear rendimiento y errores). Imagina un restaurante de comida rápida: la cocina (modelo) prepara platos (predicciones), los camareros (API) toman pedidos y entregan resultados, y los gerentes (sistemas de monitoreo) supervisan que todo funcione sin problemas. Sin esta estructura, aumentar la clientela o cambiar el menú sería caótico.

Cómo funciona en la práctica

Vamos a desglosar el diseño paso a paso para un proyecto típico de API ML con FastAPI. Primero, identifica los componentes esenciales: 1) Modelo ML entrenado y serializado (ej., con pickle o joblib), 2) API FastAPI con endpoints para predicciones y salud, 3) Validación de datos usando Pydantic para asegurar entradas correctas, 4) Logging y monitoreo con herramientas como Prometheus o logs estructurados, y 5) Configuración mediante variables de entorno para diferentes entornos (desarrollo, producción).

En la práctica, comienza definiendo la estructura de carpetas. Por ejemplo, crea directorios como app/ para el código de la API, models/ para los archivos del modelo, y tests/ para pruebas. Luego, configura FastAPI con routers para organizar endpoints (ej., /predict para predicciones, /health para verificar estado). Integra el modelo cargándolo al iniciar la aplicación para evitar latencias en cada petición. Finalmente, añade middlewares para logging y manejo de errores, asegurando que cada paso sea trazable.

Codigo en accion

Aquí tienes un ejemplo funcional de la estructura inicial y un endpoint básico. Antes de refactorizar, un código simple podría verse desorganizado; después, aplicamos mejores prácticas.

Antes: Código monolítico en un solo archivo, sin validación clara.

from fastapi import FastAPI
import pickle

app = FastAPI()
model = pickle.load(open("model.pkl", "rb"))

@app.post("/predict")
async def predict(data: dict):
    # Sin validación, propenso a errores
    result = model.predict([data["features"]])
    return {"prediction": result.tolist()}

Después: Arquitectura modular con validación y carga eficiente del modelo.

# app/main.py
from fastapi import FastAPI
from app.routers import predict, health
from app.models.load_model import load_ml_model
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(title="ML API en Producción")
ml_model = load_ml_model()  # Carga al inicio

app.include_router(predict.router)
app.include_router(health.router)

@app.on_event("startup")
async def startup_event():
    logger.info("API ML iniciada con modelo cargado")

Errores comunes

  • Error 1: Cargar el modelo en cada petición, lo que causa latencia alta. Solución: Carga el modelo una vez al iniciar la aplicación, como en el ejemplo anterior con load_ml_model().
  • Error 2: Falta de validación de entradas, llevando a predicciones erróneas o caídas. Solución: Usa Pydantic para definir esquemas estrictos; por ejemplo, crea una clase PredictionRequest con tipos y rangos válidos.
  • Error 3: No implementar logging o monitoreo, dificultando la depuración en producción. Solución: Integra logging estructurado (ej., con JSON) y métricas básicas como tasa de predicciones y errores.
  • Error 4: Mezclar configuración hardcodeada, imposibilitando despliegues en distintos entornos. Solución: Usa variables de entorno con librerías como pydantic-settings para gestionar configuraciones.
  • Error 5: Diseñar endpoints sin versionado, complicando actualizaciones sin romper clientes existentes. Solución: Incluye versión en la ruta, como /api/v1/predict, para futuros cambios.

Checklist de dominio

  1. ¿Has definido una estructura de carpetas clara (ej., app/, models/, tests/)?
  2. ¿Cargas el modelo ML una sola vez al inicio para optimizar rendimiento?
  3. ¿Usas Pydantic para validar todas las entradas de la API?
  4. ¿Has implementado logging básico para rastrear peticiones y errores?
  5. ¿Configuras la aplicación con variables de entorno, no con valores hardcodeados?
  6. ¿Incluyes endpoints de salud (/health) para monitoreo?
  7. ¿Planificas la escalabilidad, como usar workers con Gunicorn para producción?

Diseña y configura la estructura base de tu API ML

Sigue estos pasos para crear la arquitectura inicial de un proyecto FastAPI para ML en producción. Asegúrate de tener Python 3.8+ instalado.

  1. Paso 1: Crea una carpeta para el proyecto, por ejemplo ml_api_project, y dentro, las subcarpetas: app, models, tests.
  2. Paso 2: En app/, crea los archivos: main.py (punto de entrada), routers/predict.py y routers/health.py para los endpoints, y models/load_model.py para cargar el modelo.
  3. Paso 3: En models/load_model.py, escribe una función load_ml_model() que simule la carga de un modelo (puedes usar un placeholder como un diccionario si no tienes un modelo real).
  4. Paso 4: En app/routers/predict.py, define un router FastAPI con un endpoint POST /predict que use Pydantic para validar una entrada con al menos dos características numéricas.
  5. Paso 5: Configura app/main.py para incluir los routers, cargar el modelo al inicio, y añadir logging básico. Prueba ejecutando con uvicorn app.main:app --reload.
Pistas
  • Usa from pydantic import BaseModel para crear clases de validación en los routers.
  • En load_ml_model(), puedes devolver un objeto simple como {"model": "placeholder"} para simular, enfocándote en la estructura.
  • Asegúrate de que las importaciones en main.py referencien correctamente los módulos dentro de app/.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.