Python Data Science Program
📓 Abrir notebook en GitHub

Clase 230 — Capstone 2: NLP o series de tiempo end-to-end

Parte: 8 — Capstones · Fuente: Hyndman & Athanasopoulos, Forecasting: Principles and Practice (3ª ed.) + Hugging Face NLP course. ⏱️ Duración estimada: 180 min.

🎯 Objetivo

Entregar un segundo capstone end-to-end eligiendo una de dos ramas: (A) NLP — clasificación de texto / NER / RAG con transformers y endpoint FastAPI, o (B) series de tiempo — forecasting con baselines + modelos modernos, backtesting honesto e intervalos de predicción. En ambas ramas: tracking con MLflow, contenedorización con Docker, reproducibilidad con uv lock + seeds, Model Card, y discusión de drift (texto o forecast).

📚 Resultados de aprendizaje

Al finalizar, el estudiante podrá:

🗺️ Fases del capstone

# Fase Entregable
1 Definición + dataset + Model Card v0 README.md del proyecto con problema, métrica primaria y rama elegida.
2 EDA específico del dominio NLP: distribución de longitudes, balance de clases, idioma. Series: STL, ACF/PACF, estacionariedad.
3 Split honesto + baselines NLP: TF-IDF + Logistic / clase mayoritaria. Series: naive, seasonal naive, ETS, SARIMA.
4 Modelo moderno + MLflow tracking NLP: fine-tune DistilBERT o sentence-transformers / RAG con FAISS. Series: NeuralProphet / Darts / Statsforecast (N-BEATS opcional).
5 Backtesting + intervalos NLP: slice analysis (longitud, idioma, clase). Series: expanding window + cuantiles P10/P90.
6 Empaquetado + endpoint + drift Docker + FastAPI (/predict o /forecast) + reporte de drift + Model Card final.

📖 Definiciones y características

📂 Dataset / recursos

🧪 Ejercicios

  1. Definición: escribir el problema en 5 líneas (qué se predice, para qué, métrica primaria, baseline aceptable, costo de errar). Elegir rama A o B.
  2. EDA del dominio: rama A → distribuciones de longitud y balance de clases por idioma; rama B → STL + ACF/PACF + test de estacionariedad ADF.
  3. Baselines: rama A → TF-IDF + LogisticRegression; rama B → naive + seasonal naive + ETS. Loggear todo a MLflow.
  4. Modelo moderno: rama A → fine-tune DistilBERT 2 epochs con transformers.Trainer; rama B → SARIMA o NeuralProphet con backtesting expanding window 5 folds.
  5. Endpoint + drift: FastAPI con /predict (NLP) o /forecast?horizon=14 (series), dockerizado. Reporte de drift entre primera y segunda mitad del test (Evidently o KS test manual).

📝 Homework verificable

Repositorio con:

  1. README.md del proyecto con problema, rama elegida (A o B), métrica primaria y resultado.
  2. Notebook de EDA + entrenamiento con seeds fijas (42) y MLflow tracking visible (screenshot o mlruns/ versionado).
  3. Dockerfile + compose.yml que levanten FastAPI + MLflow UI con un solo docker compose up.
  4. Endpoint funcional: curl -X POST localhost:8000/predict (NLP) o curl localhost:8000/forecast?horizon=14 (series) devuelve JSON válido.
  5. Model Card (1 página) con métricas globales y por slice/horizonte, datos de entrenamiento, sesgos conocidos, y plan de monitoreo de drift.

Criterio de aceptación: el modelo moderno supera al mejor baseline en la métrica primaria (NLP: ≥ +3 puntos de F1; series: MASE < 1.0 y sMAPE menor que seasonal naive), el endpoint responde en <500 ms, y la Model Card identifica al menos un slice/horizonte donde el modelo es débil.

⚠️ Errores comunes

Síntoma / mensaje Causa y cómo arreglar
sMAPE = 200% en algunos puntos y=0 con ŷ>0. Fix: usar sMAPE simétrica 2·|y−ŷ|/(|y|+|ŷ|+ε), o cambiar a MASE.
Modelo de forecast "perfecto" en test Hiciste train_test_split con shuffle=True. Fix: split temporal: train = df[:T₀], test = df[T₀:]. Nunca shuffle en series.
transformers no carga pesos en Docker Falta caché o no hay internet. Fix: descargar pesos en build (RUN python -c "from transformers import ...") o montar volumen ~/.cache/huggingface.
F1 global alto pero el cliente reclama No hiciste slice analysis. Fix: reportar F1 por longitud/idioma/clase; un slice débil explica las quejas.
Intervalos P10/P90 cubren <50% del test Modelo subestima la varianza. Fix: calibrar con conformal prediction o usar quantile regression con pinball loss directo.
OOM al fine-tunear DistilBERT en CPU Batch size demasiado grande. Fix: per_device_train_batch_size=8, gradient_accumulation_steps=4, o usar adapters/LoRA.

❓ Preguntas frecuentes

❓ ¿NLP o series? ¿Cuál es más fácil?

Series suele ser más rápido de prototipar (statsforecast entrena SARIMA sobre miles de series en minutos, sin GPU). NLP con transformers requiere GPU para fine-tuning serio, o aceptar fine-tuning lento en CPU con DistilBERT y max_length=128.

❓ ¿Puedo usar GPT-4/Claude vía API en vez de fine-tunear?

Sí — es válido como baseline en RAG o clasificación zero-shot, pero el capstone pide al menos un modelo propio entrenado y versionado en MLflow. La llamada a API puede ser el comparativo.

❓ ¿Por qué MASE además de sMAPE?

Porque MASE es comparable entre series (escala-invariante vs el naive estacional). Si reportás solo sMAPE, no podés saber si 12% es bueno o malo sin contexto. MASE < 1 ⇒ mejor que naive, en cualquier serie.

❓ ¿FastAPI o BentoML?

FastAPI por default (ya cubierto en Parte 4). BentoML si necesitás batch inference automática, model registry integrado, o despliegue a SageMaker/Vertex con menos código.

❓ ¿La Model Card es realmente necesaria?

Sí — Mitchell et al. (Google, 2019) la propusieron por una razón: sin ella, nadie sabe qué slices son débiles ni cuándo el modelo se rompe. Es lo primero que un evaluador externo (o auditor) pide.

🔗 Referencias

📥 Material descargable

➡️ Siguiente clase

Clase 231 — Capstone 3: visión por computadora con transfer learning