Servir con FastAPI
FastAPI envuelve modelos de ML en endpoints HTTP tipados con documentación OpenAPI automática. Carga el modelo una vez al inicio, valida las entradas con Pydantic y expón comprobaciones de salud para la orquestación.
Receta
from fastapi import FastAPI
from pydantic import BaseModel
import joblib
app = FastAPI()
model = joblib.load("model.joblib")
class PredictRequest(BaseModel):
features: list[float]
@app.post("/predict")
def predict(req: PredictRequest) -> dict:
pred = model.predict([req.features])
return {"prediction": pred.tolist()}Ejemplo de Trabajo
"""serving_fastapi.py - API de modelo lista para producción."""
from __future__ import annotations
from contextlib import asynccontextmanager
from typing import Any
import joblib
import numpy as np
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
MODEL_PATH = "models/churn_pipeline.joblib"
MODEL_VERSION = "1.2.0"
model: Any = None
@asynccontextmanager
async def lifespan(app: FastAPI):
global model
model = joblib.load(MODEL_PATH)
yield
model = None
app = FastAPI(title="Churn Predictor", version=MODEL_VERSION, lifespan=lifespan)
class PredictRequest(BaseModel):
tenure: float = Field(ge=0)
monthly_charges: float = Field(ge=0)
total_charges: float = Field(ge=0)
class PredictResponse(BaseModel):
churn_probability: float
churn_prediction: bool
model_version: str
@app.get("/health")
def health() -> dict:
return {"status": "ok", "model_loaded": model is not None}
@app.post("/predict", response_model=PredictResponse)
def predict(req: PredictRequest) -> PredictResponse:
if model is None:
raise HTTPException(503, "Model not loaded")
features = np.array([[req.tenure, req.monthly_charges, req.total_charges]])
proba = model.predict_proba(features)[0][1]
return PredictResponse(
churn_probability=round(float(proba), 4),
churn_prediction=proba >= 0.5,
model_version=MODEL_VERSION,
)Ejecutar: uvicorn serving_fastapi:app --host 0.0.0.0 --port 8000
Errores Comunes
- Cargar modelo por solicitud - lento y consume mucha memoria. Solución: cargar una vez en
lifespan/inicio. - Sin validación de entrada - características incorrectas provocan errores en
predict. Solución: modelos Pydantic con restricciones. - Sin comprobación de salud - el orquestador no puede detectar fallos. Solución: endpoint
/health. - Orden incorrecto de características - predicciones incorrectas silenciosas. Solución: campos con nombre, no listas crudas.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| FastAPI | Modelos Python, lógica personalizada | Rendimiento máximo (usar Triton) |
| BentoML | Servir modelos empaquetados | API simple de un solo endpoint |
| Flask | Aplicaciones heredadas | Nuevos proyectos (usar FastAPI) |
| AWS Lambda + container | Bajo tráfico, serverless | Inferencia GPU |
Preguntas Frecuentes
¿Endpoints síncronos o asíncronos?
Síncrono está bien para inferencia de CPU; asíncrono para llamadas I/O-bound de LLM.¿Cómo sirvo PyTorch?
Cargar modelo en `lifespan`; predecir en un pool de hilos para trabajo CPU-bound.¿Predicciones por lotes?
Aceptar lista de solicitudes; predecir en lote para rendimiento.¿Autenticación?
Middleware de clave API u OAuth2 a través de dependencias de FastAPI.¿Límites de tasa?
Límites de tasa de `slowapi` o proxy inverso.¿Contenerizar?
Dockerfile con artefacto del modelo + `CMD` de uvicorn.¿Múltiples modelos?
Endpoints separados o nombre del modelo en el cuerpo de la solicitud.¿Servir con GPU?
Cargar modelo a CUDA en `lifespan`; un proceso por GPU.¿Registrar predicciones?
Middleware registra hash de entrada, predicción, latencia (no PII cruda).¿Documentación OpenAPI?
Generada automáticamente en `/docs` - deshabilitar en producción si es necesario.¿Cargar desde MLflow?
`mlflow.pyfunc.load_model("models:/name/Production")` en `lifespan`.¿Probar?
`TestClient` de `fastapi.testclient` para pruebas de integración.Relacionado
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.