Capítulo 1: Introducción al Machine Learning profesional¶
Módulo 02 — MACHINE LEARNING · AI Master Academy
"En el módulo 01 aprendiste cómo aprende una máquina. En este módulo aprenderás el oficio de hacer que ese aprendizaje resuelva problemas reales, con dinero real, en empresas reales."
Índice¶
- Recapitulación y puente: de la teoría al oficio
- El workflow completo de un proyecto ML profesional
- 2.1. Diagrama general del workflow
- 2.2. Fase 1: Definición del problema de negocio
- 2.3. Fase 2: ¿Es esto un problema de ML?
- 2.4. Fase 3: Métrica de negocio vs. métrica técnica
- 2.5. Fase 4: Datos
- 2.6. Fase 5: Baseline
- 2.7. Fase 6: Iteración de modelos
- 2.8. Fase 7: Evaluación
- 2.9. Fase 8: Despliegue
- 2.10. Fase 9: Monitorización
- 2.11. Tabla resumen de fases y entregables
- La regla del baseline: empieza siempre por el modelo más tonto
- scikit-learn a fondo: la herramienta del oficio
- 4.1. La filosofía de la API
- 4.2. Anatomía de un estimator
- 4.3. Datasets de ejemplo
- 4.4. Tabla de módulos principales
- Tu primer modelo completo end-to-end
- 5.1. Versión sencilla: Iris en 20 líneas
- 5.2. train_test_split y stratify, explicados de verdad
- 5.3. Versión intermedia: dataset de negocio sintético con preprocesado
- 5.4. Versión avanzada: Pipeline profesional
- Conceptos transversales del oficio
- 6.1. El tradeoff bias-variance a fondo
- 6.2. Underfitting y overfitting revisitados: curvas de validación
- 6.3. La maldición de la dimensionalidad
- Taxonomía de problemas de Machine Learning
- ML clásico vs. Deep Learning vs. LLMs en 2026
- Caso empresarial completo: predicción de demanda en un e-commerce
- Buenas prácticas
- Malas prácticas
- Errores comunes
- FAQ — Preguntas frecuentes
- Resumen del capítulo
- Bibliografía y recursos
1. Recapitulación y puente: de la teoría al oficio¶
Bienvenido al módulo de Machine Learning. Antes de avanzar, hagamos inventario de lo que ya dominas del módulo 01:
- Python, NumPy y Pandas: sabes manipular arrays, DataFrames, hacer slicing, agrupaciones y limpieza básica de datos.
- Gradiente descendente: entiendes que entrenar un modelo es minimizar una función de coste ajustando parámetros paso a paso en la dirección opuesta al gradiente.
- Train/test split: sabes que evaluar un modelo con los mismos datos con los que se entrenó es hacerse trampas al solitario.
- Regresión lineal desde cero: implementaste
y = w·x + b, calculaste el error cuadrático medio y actualizastewybcon gradiente descendente. Viste una máquina aprender con tus propios ojos.
Eso es la mecánica del aprendizaje automático. Este módulo trata del oficio.
La diferencia es la misma que hay entre saber cómo funciona un motor de combustión y ser ingeniero de automoción. El motor (gradiente descendente, funciones de coste) es una pieza; el oficio incluye decidir qué coche construir, para quién, con qué presupuesto, cómo probarlo antes de venderlo y cómo mantenerlo cuando ya circula por la carretera.
En la práctica profesional, los porcentajes de tiempo sorprenden a casi todos los recién llegados:
Tiempo real de un proyecto de ML en una empresa (aproximado)
─────────────────────────────────────────────────────────────
Definir el problema con negocio ████████ ~15%
Conseguir y limpiar datos ████████████████ ~35%
Ingeniería de características ████████ ~15%
Entrenar y ajustar modelos █████ ~10%
Evaluación y validación con negocio █████ ~10%
Despliegue y monitorización ████████ ~15%
Nota
La parte que ocupa los titulares —entrenar modelos— es apenas un 10% del trabajo. Un profesional de ML es, ante todo, alguien que traduce problemas de negocio a problemas de datos y que trata los datos con el mismo rigor con el que un contable trata el dinero.
Tres cambios de mentalidad que marcan el salto de estudiante a profesional:
- El modelo no es el producto; la decisión es el producto. A nadie le importa tu 94% de accuracy. Le importa cuántos euros se ahorran, cuántos clientes se retienen, cuántas horas se liberan.
- Los datos mandan. Un modelo mediocre con datos excelentes casi siempre gana a un modelo excelente con datos mediocres. Esta asimetría gobierna dónde inviertes tu tiempo.
- Todo se compara contra un baseline. Sin una referencia trivial contra la que medir, cualquier número que produzcas es ruido. Volveremos a esto con fuerza en la sección 3.
Consejo profesional
Guarda esta frase y repítela en cada proyecto: "¿Qué decisión va a cambiar gracias a este modelo, quién la toma hoy, y cómo la toma hoy?". Si no puedes responderla, no tienes un proyecto de ML: tienes un experimento académico.
2. El workflow completo de un proyecto ML profesional¶
Este es el núcleo del capítulo. Todo lo que aprendas en el resto del módulo (regresión, clasificación, árboles, boosting, validación cruzada...) encaja en alguna casilla de este workflow. Apréndelo bien: es el mapa del territorio.
2.1. Diagrama general del workflow¶
flowchart TD
A["1. Definición del problema<br/>de negocio"] --> B{"2. ¿Es un problema<br/>de ML?"}
B -- "No"--> B1["Solución con reglas,<br/>SQL, dashboard o proceso.<br/>FIN (y es un éxito)"]
B -- "Sí"--> C["3. Definir métricas:<br/>negocio + técnica"]
C --> D["4. Datos:<br/>obtención, exploración,<br/>limpieza, split"]
D --> D1{"¿Datos suficientes<br/>y de calidad?"}
D1 -- "No"--> D2["Recolectar más datos /<br/>renegociar alcance"]
D2 --> D
D1 -- "Sí"--> E["5. Baseline:<br/>el modelo más tonto posible"]
E --> F["6. Iteración de modelos:<br/>features → modelo → ajuste"]
F --> G{"7. Evaluación:<br/>¿supera al baseline y<br/>al umbral de negocio?"}
G -- "No, y quedan ideas"--> F
G -- "No, y no quedan ideas"--> G1["Informar honestamente.<br/>Replantear o cancelar"]
G -- "Sí"--> H["8. Despliegue:<br/>API, batch o embebido"]
H --> I["9. Monitorización:<br/>rendimiento, drift, negocio"]
I --> I1{"¿Degradación<br/>detectada?"}
I1 -- "Sí"--> D
I1 -- "No"--> I
style A fill:#1565c0,color:#fff
style E fill:#2e7d32,color:#fff
style G1 fill:#b71c1c,color:#fff
style B1 fill:#6a1b9a,color:#fff
style I fill:#e65100,color:#fff
Advertencia
El diagrama tiene flechas de vuelta atrás por todas partes y no es casualidad. Un proyecto de ML no es lineal: es un ciclo con retrocesos constantes. Si tu plan de proyecto no contempla volver a la fase de datos tres o cuatro veces, tu plan es ficción.
Veamos cada fase en profundidad. Para cada una: qué se hace, quién la hace, errores típicos y entregable.
2.2. Fase 1: Definición del problema de negocio¶
Qué se hace. Se traduce una frustración o una oportunidad de negocio ("perdemos clientes", "compramos stock de más", "el equipo de soporte no da abasto") en una pregunta concreta y accionable. Se identifica: la decisión que se quiere mejorar, quién la toma hoy, con qué información, con qué frecuencia y cuánto cuesta equivocarse en cada dirección (falso positivo vs. falso negativo tienen costes distintos casi siempre).
Quién la hace. El product manager o responsable de negocio, junto con el científico de datos. El error clásico es que el equipo de datos se lo invente solo. El científico de datos aporta aquí una habilidad crítica: detectar cuándo la petición de negocio ("queremos IA") no tiene una decisión detrás.
Errores típicos: - Empezar por la tecnología ("usemos deep learning") en lugar de por la decisión. - Aceptar objetivos vagos: "mejorar la experiencia del cliente" no es medible; "reducir el churn mensual del 4% al 3%" sí. - No cuantificar el statu quo: si no sabes cómo se decide hoy y con qué acierto, no podrás demostrar mejora. - Ignorar restricciones desde el inicio: latencia máxima, regulación (GDPR, sector financiero/sanitario), presupuesto de infraestructura.
Entregable. Un documento de una o dos páginas (a menudo llamado ML problem statement o one-pager) con: decisión a mejorar, usuario de la predicción, frecuencia, coste de los errores, restricciones y criterio de éxito de negocio.
2.3. Fase 2: ¿Es esto un problema de ML?¶
Qué se hace. Se somete el problema a un filtro honesto. ML tiene sentido cuando: (a) existe un patrón que aprender, (b) hay datos históricos que lo contienen, (c) el patrón es demasiado complejo o cambiante para escribirlo como reglas, y (d) una predicción imperfecta aporta valor. Si un IF stock < 10 THEN reponer resuelve el 95% del problema, esa es la solución correcta.
Quién la hace. El científico de datos senior o el ML engineer, con veto argumentado. Es la fase donde más valor aporta la experiencia: decir "no" a tiempo ahorra meses.
Errores típicos: - Usar ML por prestigio ("el comité quiere IA en la memoria anual"). - No comprobar si ya existe una heurística que funciona razonablemente bien. - Confundir "tenemos muchos datos" con "tenemos los datos que contienen el patrón". Terabytes de logs no sirven si la variable objetivo no está registrada. - Ignorar que la predicción debe llegar a tiempo para la decisión: predecir la demanda de mañana no sirve si el pedido al proveedor se hace con tres semanas de antelación.
Entregable. Una decisión documentada: "sí es ML" (y de qué tipo: ver taxonomía), "no es ML" (y qué alternativa se propone), o "todavía no" (qué datos habría que empezar a recoger hoy para que lo sea en seis meses).
Consejo profesional
Rechazar un proyecto de ML con una alternativa más simple que funciona te da más credibilidad profesional que entregar un modelo complejo que nadie usa. Los mejores equipos de datos presumen de los proyectos que no hicieron.
2.4. Fase 3: Métrica de negocio vs. métrica técnica¶
Qué se hace. Se definen dos métricas y el puente entre ellas:
- Métrica de negocio: lo que le importa al que paga. Euros ahorrados, churn reducido, tickets desviados, stock inmovilizado. Se mide en producción, a menudo con A/B test.
- Métrica técnica: lo que optimiza el modelo y puedes medir offline. MAE, RMSE, F1, AUC, precision@k...
El puente es la parte difícil: "un punto menos de MAE en la predicción de demanda equivale aproximadamente a X euros menos de stock inmovilizado". Este puente casi nunca es exacto, pero debe existir aunque sea como estimación.
Quién la hace. Científico de datos y negocio, juntos, por escrito, antes de entrenar nada. Definir la métrica después de ver los resultados es el equivalente estadístico de disparar la flecha y pintar la diana alrededor.
Errores típicos: - Optimizar accuracy en un problema desbalanceado (99% de transacciones legítimas → un modelo que dice "todo legítimo" tiene 99% de accuracy y es inútil). - Elegir una métrica simétrica (MAE) cuando los costes son asimétricos (quedarse sin stock cuesta 5 veces más que sobrar stock). - No fijar el umbral de éxito de antemano: ¿a partir de qué valor de la métrica el proyecto se considera un éxito y se despliega?
Entregable. Documento de métricas: métrica técnica principal, métricas secundarias de control, métrica de negocio, puente estimado entre ambas y umbral mínimo de despliegue.
2.5. Fase 4: Datos¶
Qué se hace. Es la fase más larga. Incluye:
- Inventario y acceso: qué tablas/fuentes existen, quién es su dueño, qué permisos hacen falta.
- Análisis exploratorio (EDA): distribuciones, valores faltantes, outliers, correlaciones, fugas de información potenciales.
- Definición de la unidad de observación y la variable objetivo: ¿una fila es un cliente, un cliente-mes, una transacción? ¿El target está bien definido y disponible en el momento de la predicción?
- Limpieza y construcción del dataset: joins, imputación, tratamiento de outliers, codificación.
- Particionado: train/validation/test (y si hay componente temporal, split temporal, nunca aleatorio).
Quién la hace. El científico de datos con el data engineer (que construye y mantiene las tuberías de datos) y, crucialmente, con los expertos de dominio, que son quienes saben que "la columna fecha_alta se rellenaba a mano hasta 2023 y no es fiable".
Errores típicos: - Data leakage (fuga de datos): incluir en las features información que no estará disponible en el momento de predecir. Es el error más caro y más frecuente del oficio. Ejemplo clásico: predecir impago usando una columna que solo se rellena después de que el cliente impague. - Hacer split aleatorio en datos temporales (entrenas con el futuro, evalúas con el pasado: resultados de fantasía). - Limpiar los datos mirando el conjunto completo (incluido test): las estadísticas de imputación y escalado deben calcularse solo con train. - No versionar el dataset: seis meses después nadie sabe con qué datos exactos se entrenó el modelo en producción.
Entregable. Dataset versionado y documentado (diccionario de datos), informe de EDA, y los splits reproducibles (con semilla o con fechas de corte documentadas).
2.6. Fase 5: Baseline¶
Qué se hace. Antes de tocar cualquier modelo real, se construye la predicción más tonta que se te ocurra: la media, la clase mayoritaria, "mañana venderé lo mismo que hoy", o la regla heurística que negocio ya usa. Se evalúa con la métrica técnica acordada. Ese número es el suelo: todo lo que hagas después se compara contra él.
Quién la hace. El científico de datos. Debería llevar horas, no días.
Errores típicos: saltársela (el más común), o construir un baseline artificialmente malo para que el modelo luzca. La sección 3 desarrolla esto a fondo con código.
Entregable. El valor del baseline en la métrica acordada, en el mismo conjunto de test que usarán todos los modelos posteriores.
2.7. Fase 6: Iteración de modelos¶
Qué se hace. El bucle central del trabajo técnico:
┌────────────────────────────────────────────────┐
│ hipótesis → cambio (feature o modelo) → │
│ entrenar → medir en validación → anotar → │
│ ¿mejora? ── sí → conservar cambio │
│ └─ no → descartar y anotar por qué │
└────────────────────────────────────────────────┘
Se avanza en escalera de complejidad: modelo lineal regularizado → árbol → random forest → gradient boosting → (solo si está justificado) redes neuronales. En cada peldaño, la pregunta no es "¿es mejor?" sino "¿la mejora compensa la complejidad extra en mantenimiento, latencia e interpretabilidad?".
Quién la hace. Científico de datos / ML engineer. En equipos maduros, con experiment tracking (MLflow, Weights & Biases) para que cada experimento quede registrado con sus datos, código, hiperparámetros y resultado.
Errores típicos: - Cambiar tres cosas a la vez: si mejora, no sabes cuál de las tres fue. - Ajustar hiperparámetros mirando el conjunto de test (el test se toca una vez, al final; para iterar se usa validación o validación cruzada). - Perseguir décimas de mejora en la métrica técnica cuando el cuello de botella está en los datos. - No registrar experimentos: "¿este 0.87 era con o sin la feature de estacionalidad?".
Entregable. Registro de experimentos, modelo candidato con sus hiperparámetros, y código de entrenamiento reproducible.
2.8. Fase 7: Evaluación¶
Qué se hace. El modelo candidato se evalúa una sola vez contra el conjunto de test intacto, y contra el baseline. Pero la evaluación profesional va más allá del número global:
- Análisis por segmentos: ¿el modelo funciona igual de bien para clientes nuevos que antiguos? ¿Para productos de alta y baja rotación? Un buen promedio puede esconder un desastre en el segmento que más le importa a negocio.
- Análisis de errores: mirar con lupa los 50 casos donde el modelo más se equivoca. Ahí suelen aparecer bugs de datos, fugas o segmentos problemáticos.
- Chequeo de equidad y sesgos cuando las predicciones afectan a personas (crédito, contratación, salud): rendimiento por grupo demográfico y cumplimiento regulatorio (en la UE, el AI Act clasifica muchos de estos usos como alto riesgo).
- Traducción a negocio: convertir la métrica técnica al puente definido en la fase 3 y presentarlo en euros/horas/clientes.
Quién la hace. El científico de datos presenta; negocio y (si aplica) riesgo/cumplimiento validan. La decisión de desplegar no la toma el equipo de datos en solitario.
Errores típicos: - Evaluar varias veces contra test y quedarse con la mejor pasada (el test deja de ser una estimación honesta). - Presentar solo la métrica global sin segmentos ni intervalos de confianza. - Declarar victoria offline sin plan de validación online (A/B test o despliegue gradual).
Entregable. Informe de evaluación: métrica en test vs. baseline, análisis por segmentos, análisis de errores, riesgos identificados y recomendación de despliegue (o de no despliegue).
2.9. Fase 8: Despliegue¶
Qué se hace. El modelo pasa de un notebook a un sistema. Los tres patrones principales:
| Patrón | Cómo funciona | Cuándo usarlo | Ejemplo |
|---|---|---|---|
| Batch | El modelo se ejecuta cada noche/semana y escribe predicciones en una tabla | La decisión no es en tiempo real | Predicción de demanda semanal, scoring de churn mensual |
| API en tiempo real | El modelo se sirve tras un endpoint HTTP; cada petición recibe una predicción en milisegundos | La decisión ocurre durante una interacción | Detección de fraude en el pago, recomendación en la web |
| Embebido (edge) | El modelo se empaqueta dentro de la aplicación o dispositivo | Sin conexión, latencia extrema o privacidad | Detección de defectos en cámara de fábrica, teclado predictivo |
Además: contenedores (Docker), CI/CD para el modelo, pruebas de carga, plan de rollback (si el modelo nuevo falla, se vuelve al anterior o a la heurística en un clic) y despliegue gradual (canary o shadow mode: el modelo predice en paralelo sin actuar, y se comparan sus decisiones con las reales antes de darle el mando).
Quién la hace. ML engineer y equipo de plataforma/DevOps. El científico de datos participa definiendo el contrato de entrada/salida y las validaciones.
Errores típicos:
- Reimplementar el preprocesado en el código de producción a mano (divergencia entrenamiento-servicio o training-serving skew). La solución es el Pipeline de sklearn, que veremos en la sección 5.4: el preprocesado viaja dentro del artefacto del modelo.
- No validar los datos de entrada en producción (llega un NaN o una categoría nueva y el servicio revienta a las 3 de la mañana).
- Desplegar al 100% de usuarios el primer día.
Entregable. Servicio o job en producción, versionado, con tests, plan de rollback y documentación de la API o de la tabla de salida.
2.10. Fase 9: Monitorización¶
Qué se hace. El trabajo NO termina en el despliegue; empieza otra fase permanente. Se monitorizan tres capas:
- Salud del sistema: latencia, errores, disponibilidad (esto es ingeniería estándar).
- Salud de los datos y del modelo: distribución de las features de entrada (¿ha cambiado respecto al entrenamiento? → data drift), distribución de las predicciones, y —cuando el valor real llega con retraso— la métrica técnica real (concept drift: la relación entre X e y cambió; el mundo se movió y el modelo se quedó atrás).
- Salud de negocio: ¿la métrica de negocio mejora de verdad? ¿El equipo usa las predicciones o las ignora?
Se definen alertas con umbrales y un calendario de reentrenamiento (periódico o disparado por drift).
Quién la hace. ML engineer con soporte del científico de datos. En organizaciones maduras existe un rol específico (MLOps).
Errores típicos: - Monitorizar solo la infraestructura: el servicio responde en 20 ms... predicciones cada vez peores. - No monitorizar el uso: el modelo funciona perfecto pero el equipo de compras dejó de mirar la tabla hace tres meses. - Reentrenar a ciegas sin re-evaluar: un reentrenamiento con datos corruptos puede empeorar el modelo silenciosamente.
Entregable. Dashboard de monitorización, alertas configuradas, runbook de incidencias y política de reentrenamiento documentada.
2.11. Tabla resumen de fases y entregables¶
| # | Fase | Pregunta clave | Responsable principal | Entregable | Error más caro |
|---|---|---|---|---|---|
| 1 | Problema de negocio | ¿Qué decisión mejoramos? | Negocio + DS | One-pager del problema | Empezar por la tecnología |
| 2 | ¿Es ML? | ¿Hay patrón, datos y valor en predecir? | DS senior | Decisión go/no-go argumentada | Hacer ML por prestigio |
| 3 | Métricas | ¿Cómo sabremos que funciona? | Negocio + DS | Doc de métricas y umbral | Métrica elegida a posteriori |
| 4 | Datos | ¿Tenemos la señal, limpia y sin fugas? | DS + Data Eng + dominio | Dataset versionado + EDA | Data leakage |
| 5 | Baseline | ¿Cuál es el suelo a batir? | DS | Métrica del modelo trivial | Saltársela |
| 6 | Iteración | ¿Qué cambio mejora validación? | DS / ML Eng | Registro de experimentos + candidato | Ajustar contra test |
| 7 | Evaluación | ¿Supera baseline y umbral, en todos los segmentos? | DS + negocio | Informe y recomendación | Evaluar test varias veces |
| 8 | Despliegue | ¿Cómo llega la predicción a la decisión? | ML Eng + DevOps | Servicio/job con rollback | Training-serving skew |
| 9 | Monitorización | ¿Sigue funcionando? ¿Se usa? | MLOps / ML Eng | Dashboard + alertas + política de reentrenamiento | Monitorizar solo infraestructura |
Ejercicio rápido 1¶
Tu director comercial te dice: "Quiero un modelo de IA que me diga qué clientes van a dejar de comprarnos". Antes de escribir una línea de código, formula las cinco preguntas que le harías para convertir esa frase en un problema de ML bien definido.
Ver solución
Preguntas mínimas (hay más válidas): 1. **¿Qué significa exactamente "dejar de comprarnos"?** → Definir la variable objetivo: ¿90 días sin compra? ¿Cancelar contrato? Sin definición operativa del churn, no hay target. 2. **¿Qué harás con la lista de clientes en riesgo?** → La decisión: ¿llamada del comercial? ¿descuento? Si no hay acción posible, la predicción no vale nada. 3. **¿Con cuánta antelación necesitas saberlo?** → Horizonte de predicción. Predecir el churn el día antes no deja margen para actuar. 4. **¿Cuánto cuesta cada tipo de error?** → Llamar a un cliente que no iba a irse (coste bajo) vs. no detectar a uno que se va (coste alto). Esto define la métrica (probablemente recall-orientada) y el umbral. 5. **¿Cómo lo hacéis hoy y qué tal funciona?** → El statu quo es el baseline de negocio. Si el comercial ya acierta el 70% con su intuición, ese es el número a batir. Pregunta extra de profesional: *¿tenemos histórico de clientes que se fueron, con sus datos de los meses previos?* Sin eso, no hay dataset.3. La regla del baseline: empieza siempre por el modelo más tonto¶
Si de este capítulo solo retuvieras una regla operativa, que sea esta:
Nunca entrenes un modelo complejo sin haber medido antes qué consigue el modelo más tonto posible.
¿Qué es un baseline?¶
Un baseline es una predicción trivial que no aprende nada (o casi nada) de los datos:
- Regresión: predecir siempre la media (o la mediana) del target. En series temporales: "mañana = hoy" (baseline naive, sorprendentemente difícil de batir).
- Clasificación: predecir siempre la clase mayoritaria, o predecir al azar respetando las proporciones.
- Reglas de negocio existentes: la heurística que el equipo ya usa ("reponer cuando el stock baja de X") es también un baseline, y a menudo el más importante.
¿Por qué existe esta regla? Cuatro razones¶
- Da contexto a cualquier número. "Mi modelo tiene MAE de 12" no significa nada. "El baseline tiene MAE de 13.1 y mi modelo 12" significa que meses de trabajo compraron un 8% de mejora, y negocio puede decidir si eso paga el proyecto.
- Detecta problemas triviales. Si tu red neuronal apenas supera a "predecir la media", o el problema no tiene señal, o tus datos están rotos. Mejor descubrirlo el día 2 que el mes 4.
- Detecta fugas de datos. Si tu primer modelo saca 99.8% de accuracy y el baseline 52%, no descorches el champán: casi seguro tienes data leakage. Los resultados demasiado buenos son una alarma, no una alegría.
- Es un modelo desplegable. Si el proyecto se cancela, el baseline (o la heurística mejorada que descubriste al construirlo) puede desplegarse igualmente. Cuesta un céntimo y a veces captura el 80% del valor.
Código: baselines con scikit-learn¶
scikit-learn trae los baselines de serie: DummyRegressor y DummyClassifier. Vamos a verlos funcionando de verdad.
# ============================================================
# BASELINES CON SCIKIT-LEARN: DummyRegressor y DummyClassifier
# ============================================================
# Importamos NumPy para generar datos sintéticos reproducibles
import numpy as np
# DummyRegressor y DummyClassifier viven en sklearn.dummy:
# son "modelos" que ignoran las features y predicen constantes
from sklearn.dummy import DummyRegressor, DummyClassifier
# train_test_split divide los datos en entrenamiento y test
from sklearn.model_selection import train_test_split
# Métricas: error absoluto medio (regresión) y accuracy (clasificación)
from sklearn.metrics import mean_absolute_error, accuracy_score
# Fijamos la semilla del generador para que el ejemplo sea reproducible
rng = np.random.default_rng(42)
# ---------- PARTE 1: BASELINE DE REGRESIÓN ----------
# Simulamos 1000 días de ventas de una tienda:
# X = matriz de features (día de la semana codificado 0-6, temperatura)
n = 1000 # número de observaciones
dia_semana = rng.integers(0, 7, size=n) # día de la semana aleatorio
temperatura = rng.normal(20, 8, size=n) # temperatura media diaria
# Las ventas dependen del día (más en fin de semana) y algo de la temperatura,
# más ruido aleatorio: así hay señal real pero imperfecta, como en la vida
ventas = (
200 # ventas base
+ 40 * (dia_semana >= 5) # +40 unidades sábado y domingo
+ 1.5 * temperatura # efecto suave de la temperatura
+ rng.normal(0, 25, size=n) # ruido irreducible
)
# Apilamos las features como columnas de una matriz (n_muestras, n_features)
X = np.column_stack([dia_semana, temperatura])
y = ventas # el target es un vector 1D
# Dividimos 80/20 con semilla fija: el test queda intocable hasta el final
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Creamos el baseline: strategy="mean" → predice SIEMPRE la media de y_train
dummy_reg = DummyRegressor(strategy="mean")
# fit() en un Dummy solo calcula y guarda la media del train; ignora X
dummy_reg.fit(X_train, y_train)
# predict() devuelve la media repetida, una vez por fila de X_test
pred_baseline = dummy_reg.predict(X_test)
# Medimos el MAE: error absoluto medio en unidades del target (ventas)
mae_baseline = mean_absolute_error(y_test, pred_baseline)
print(f"MAE baseline (media): {mae_baseline:.2f} unidades")
# Salida típica: MAE baseline (media): 31.55 unidades
# ---------- PARTE 2: BASELINE DE CLASIFICACIÓN ----------
# Simulamos un problema desbalanceado: 5% de clientes cometen fraude
y_clf = (rng.random(n) < 0.05).astype(int) # 1 = fraude (~5%), 0 = legítimo
# Features irrelevantes a propósito: queremos ver qué hace el baseline solo
X_clf = rng.normal(size=(n, 3))
# Mismo split 80/20; stratify=y_clf mantiene el 5% de fraude en ambas partes
Xc_tr, Xc_te, yc_tr, yc_te = train_test_split(
X_clf, y_clf, test_size=0.2, random_state=42, stratify=y_clf
)
# strategy="most_frequent" → predice SIEMPRE la clase mayoritaria (0)
dummy_clf = DummyClassifier(strategy="most_frequent")
# fit() solo cuenta las clases en el train y memoriza la más frecuente
dummy_clf.fit(Xc_tr, yc_tr)
# accuracy del baseline: ¡~95% sin aprender absolutamente nada!
acc_baseline = accuracy_score(yc_te, dummy_clf.predict(Xc_te))
print(f"Accuracy baseline (clase mayoritaria): {acc_baseline:.3f}")
# Salida típica: Accuracy baseline (clase mayoritaria): 0.950
Fíjate en el segundo resultado: un clasificador que dice "nadie comete fraude, jamás" acierta el 95% de las veces. Este es el argumento definitivo contra usar accuracy a la ligera, y la demostración de por qué el baseline te protege: cualquier modelo de fraude que presuma de "95% de accuracy" está exactamente al nivel de no hacer nada.
Nota
DummyRegressor acepta strategy="mean", "median", "quantile" y "constant". DummyClassifier acepta "most_frequent", "prior", "stratified" (azar respetando proporciones), "uniform" y "constant". La documentación oficial: sklearn.dummy.
Consejo profesional
En tu informe de resultados, la primera fila de la tabla de modelos debe ser siempre el baseline. Un lector que no vea baseline en tu tabla tiene derecho a desconfiar de todo lo demás.
4. scikit-learn a fondo: la herramienta del oficio¶
En el módulo 01 implementaste regresión lineal desde cero con NumPy. Fue el equivalente a fabricar tu propio martillo para entender la física del clavo. A partir de ahora usarás herramientas industriales, y la herramienta central del ML clásico en Python es scikit-learn (sklearn): una biblioteca de código abierto nacida en 2007, mantenida por una comunidad enorme, y estándar de facto en la industria para datos tabulares.
4.1. La filosofía de la API¶
Lo más valioso de sklearn no son sus algoritmos (que también), sino su diseño de API. Toda la biblioteca gira en torno a un contrato uniforme con tres conceptos:
- Estimator (estimador): cualquier objeto que aprende de datos. Todos tienen el método
fit(X, y): recibe datos y aprende de ellos, guardando lo aprendido en atributos internos.fitsiempre devuelveself, lo que permite encadenar:model.fit(X, y).predict(X_new). - Predictor: todo estimator que además sabe producir predicciones tiene
predict(X)(y en clasificación, normalmentepredict_proba(X)para probabilidades). También suelen tenerscore(X, y), que devuelve una métrica por defecto (R² en regresión, accuracy en clasificación). - Transformer (transformador): todo estimator que en lugar de predecir transforma datos (escalar, imputar, codificar) tiene
transform(X), y por comodidadfit_transform(X)que hace ambas cosas seguidas.
¿Por qué esta uniformidad es genial? Por cuatro motivos concretos:
- Intercambiabilidad total. Cambiar de regresión lineal a un random forest es cambiar UNA línea (
LinearRegression()→RandomForestRegressor()). El resto del código —split, fit, predict, métricas— no se toca. Esto convierte "probar 5 modelos" en un bucle de 5 iteraciones. - Composición. Como todos los transformers hablan el mismo idioma, se pueden encadenar en un
Pipeline(sección 5.4), y el pipeline entero se comporta a su vez como un único estimator. Es LEGO estadístico. - Herramientas genéricas.
cross_val_score,GridSearchCV,validation_curve... funcionan con cualquier estimator, incluidos los tuyos propios si respetas el contrato. Las herramientas se escriben una vez y sirven para todo. - Curva de aprendizaje plana. Aprendida la API con un modelo, la sabes para los ~200 que trae la biblioteca. Compara con el mundo pre-sklearn, donde cada algoritmo tenía su propia interfaz incompatible.
Advertencia
La convención de datos de sklearn es estricta: X es siempre una matriz 2D de forma (n_muestras, n_features) —aunque tengas una sola feature, debe ser (n, 1), no (n,)— e y es un vector 1D de forma (n_muestras,). El error Expected 2D array, got 1D array instead que verás mil veces en tu carrera se arregla con X.reshape(-1, 1).
4.2. Anatomía de un estimator¶
Diseccionemos un estimator para entender qué guarda y dónde:
# Importamos un modelo lineal clásico
from sklearn.linear_model import LinearRegression
# 1) CONSTRUCCIÓN: aquí solo se fijan HIPERPARÁMETROS (decisiones del humano).
# Nada se aprende todavía; el objeto está "vacío".
model = LinearRegression(fit_intercept=True) # hiperparámetro: ¿ajustar b?
# 2) Los hiperparámetros quedan accesibles como atributos normales
print(model.fit_intercept) # → True
# 3) ENTRENAMIENTO: fit() aprende los PARÁMETROS a partir de los datos
model.fit(X_train, y_train) # aprende w y b internamente
# 4) CONVENCIÓN CLAVE: todo atributo aprendido termina en guion bajo "_".
# Así distingues a simple vista lo que decidiste tú (sin _) de lo que
# aprendió el modelo (con _).
print(model.coef_) # → los pesos w aprendidos (uno por feature)
print(model.intercept_) # → el sesgo b aprendido
# 5) Si llamas a predict() ANTES de fit(), sklearn lanza NotFittedError:
# el contrato protege contra usar un modelo sin entrenar.
Esta convención del guion bajo final (coef_, intercept_, feature_importances_, classes_...) es universal en sklearn y te permite inspeccionar cualquier modelo entrenado sin leer su documentación completa.
Hiperparámetro vs. parámetro, la distinción que lo organiza todo:
| Parámetro | Hiperparámetro | |
|---|---|---|
| ¿Quién lo fija? | El algoritmo, durante fit() |
El humano, en el constructor |
| Ejemplo | Los pesos w de una regresión |
La profundidad máxima de un árbol |
| ¿Dónde vive en sklearn? | Atributos con _ final |
Argumentos del constructor |
| ¿Cómo se elige el mejor? | Optimización (gradiente, etc.) | Búsqueda con validación (GridSearchCV) |
4.3. Datasets de ejemplo¶
sklearn incluye datasets pequeños para aprender y prototipar, y generadores sintéticos:
# Datasets clásicos empaquetados (pequeños, cargan al instante)
from sklearn.datasets import load_iris, load_diabetes, load_wine
# Descargables más grandes (se bajan la primera vez y se cachean)
from sklearn.datasets import fetch_california_housing
# Generadores sintéticos: creas datasets a medida para experimentar
from sklearn.datasets import make_classification, make_regression
# load_iris con as_frame=True devuelve DataFrames de pandas (más cómodo)
iris = load_iris(as_frame=True)
# El objeto devuelto es un Bunch: un diccionario con atributos
print(iris.data.shape) # (150, 4) → 150 flores, 4 medidas
print(iris.target_names) # ['setosa' 'versicolor' 'virginica']
print(iris.frame.head()) # DataFrame completo con features + target
Nota
Iris (150 flores de 3 especies, medidas por Edgar Anderson y popularizadas por Ronald Fisher en 1936) es el "Hola mundo" del ML. Es perfecto para aprender la mecánica y pésimo para simular la realidad: es minúsculo, está limpio y es casi linealmente separable. Por eso en la sección 5 lo complementaremos con un dataset de negocio sintético y sucio.
4.4. Tabla de módulos principales¶
| Módulo | Qué contiene | Ejemplos que usarás pronto |
|---|---|---|
sklearn.linear_model |
Modelos lineales | LinearRegression, Ridge, Lasso, LogisticRegression |
sklearn.tree |
Árboles de decisión | DecisionTreeClassifier, DecisionTreeRegressor |
sklearn.ensemble |
Combinaciones de modelos | RandomForestClassifier, GradientBoostingRegressor, HistGradientBoostingClassifier |
sklearn.neighbors |
Métodos por vecindad | KNeighborsClassifier |
sklearn.svm |
Máquinas de vectores soporte | SVC, SVR |
sklearn.cluster |
Clustering (no supervisado) | KMeans, DBSCAN |
sklearn.decomposition |
Reducción de dimensionalidad | PCA |
sklearn.preprocessing |
Transformadores de features | StandardScaler, OneHotEncoder, MinMaxScaler |
sklearn.impute |
Imputación de valores faltantes | SimpleImputer, KNNImputer |
sklearn.model_selection |
Splits, validación y búsqueda | train_test_split, cross_val_score, GridSearchCV, validation_curve |
sklearn.metrics |
Métricas de evaluación | mean_absolute_error, accuracy_score, f1_score, roc_auc_score, confusion_matrix |
sklearn.pipeline |
Composición de pasos | Pipeline, make_pipeline |
sklearn.compose |
Preprocesado por columnas | ColumnTransformer |
sklearn.dummy |
Baselines | DummyClassifier, DummyRegressor |
sklearn.datasets |
Datos de ejemplo | load_iris, make_classification |
Qué NO hace sklearn (importante para no pedirle peras al olmo): no hace deep learning (para eso PyTorch/TensorFlow/JAX), no está pensado para datasets que no caben en memoria (para eso Spark ML, Dask-ML o muestreo), no entrena en GPU en general, y sus modelos de series temporales son limitados (statsmodels, sktime, Prophet, Nixtla cubren ese hueco). Para gradient boosting de máxima potencia en tabular, la industria complementa sklearn con XGBoost, LightGBM y CatBoost, que respetan la misma API — otra victoria del diseño uniforme.
Ejercicio rápido 2¶
Sin ejecutar código: ¿cuál de estas llamadas viola el contrato de la API de sklearn y por qué? (a) StandardScaler().fit_transform(X_train); (b) scaler.fit(X_test) después de haber entrenado el modelo con X_train escalado; (c) LinearRegression().predict(X_test).
Ver solución
- **(a) Correcta.** `fit_transform` sobre train es el uso canónico: aprende media y desviación del train y lo transforma. - **(b) Incorrecta conceptualmente (aunque ejecuta sin error).** Nunca se hace `fit` del scaler sobre test: las estadísticas de escalado deben venir del train (`scaler.transform(X_test)`), porque en producción no tendrás "el test" para recalcularlas y porque estarías filtrando información del test al proceso. Es data leakage de manual. - **(c) Incorrecta y además lanza excepción.** Se llama `predict` sobre un estimator sin `fit` previo → `NotFittedError`. El contrato de sklearn exige entrenar antes de predecir.5. Tu primer modelo completo end-to-end¶
Vamos a recorrer tres versiones del mismo viaje —cargar → dividir → entrenar → predecir → evaluar— con complejidad creciente. La versión sencilla te enseña la mecánica; la intermedia añade el preprocesado del mundo real; la avanzada lo empaqueta como lo haría un profesional.
5.1. Versión sencilla: Iris en 20 líneas¶
# ============================================================
# VERSIÓN SENCILLA: clasificar flores Iris de principio a fin
# ============================================================
# Cargador del dataset Iris incluido en sklearn
from sklearn.datasets import load_iris
# División train/test que ya conoces del módulo 01
from sklearn.model_selection import train_test_split
# Regresión logística: clasificador lineal simple y robusto.
# (Pese al nombre, es un CLASIFICADOR; la historia del nombre, en el cap. 3)
from sklearn.linear_model import LogisticRegression
# Baseline de clasificación y métrica
from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score
# ---- 1. CARGAR ----
# return_X_y=True devuelve directamente la matriz X y el vector y
X, y = load_iris(return_X_y=True)
# X: (150, 4) → largo/ancho de sépalo y pétalo en cm
# y: (150,) → especie codificada como 0, 1, 2
# ---- 2. DIVIDIR ----
# 25% para test; stratify=y garantiza la misma proporción de especies
# en train y test (lo explicamos a fondo en la sección siguiente)
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.25, # 25% de las 150 flores quedan apartadas
random_state=42, # semilla fija → split reproducible
stratify=y, # respeta las proporciones de las 3 clases
)
# ---- 3. BASELINE PRIMERO, SIEMPRE ----
baseline = DummyClassifier(strategy="most_frequent") # clase mayoritaria
baseline.fit(X_train, y_train) # "aprende" contando
acc_base = accuracy_score(y_test, baseline.predict(X_test))
# ---- 4. ENTRENAR EL MODELO REAL ----
model = LogisticRegression(max_iter=1000) # más iteraciones para converger
model.fit(X_train, y_train) # aprende los pesos con X e y
# ---- 5. PREDECIR ----
y_pred = model.predict(X_test) # una especie predicha por flor
# ---- 6. EVALUAR (contra el baseline) ----
acc_model = accuracy_score(y_test, y_pred)
print(f"Baseline: {acc_base:.3f} | Modelo: {acc_model:.3f}")
# Salida típica: Baseline: 0.342 | Modelo: 0.974
El modelo pasa de un 34% (baseline: Iris tiene 3 clases equilibradas, la mayoritaria del split ronda 1/3) a un 97%. Esa distancia —no el 97% aislado— es la evidencia de que el modelo aprendió algo.
5.2. train_test_split y stratify, explicados de verdad¶
Ya conoces el porqué del split del módulo 01. Ahora los detalles profesionales:
test_size: fracción (0.2 = 20%) o número absoluto de muestras. Valores típicos: 0.2–0.25. Con millones de filas, un 1% de test puede bastar; con 200 filas, quizá necesites validación cruzada en lugar de un único split (capítulo de validación).random_state: fija la semilla del barajado. Sin ella, cada ejecución da un split distinto y tus resultados no son reproducibles. Fíjala siempre y anótala.shuffle=True(por defecto): baraja antes de partir. Excepción crítica: en datos temporales se poneshuffle=Falseo, mejor, se usa un corte por fecha — entrenar con datos del futuro para predecir el pasado es la fuga temporal clásica.stratify=y: el protagonista. Hace un muestreo estratificado: garantiza que la proporción de cada clase en train y test sea (casi) idéntica a la del dataset completo.
¿Por qué importa stratify? Imagina 1000 clientes con 5% de fraude (50 casos). Un split aleatorio del 20% se lleva en promedio 10 fraudes al test, pero por puro azar podría llevarse 3... o 18:
Sin stratify (mala suerte posible): Con stratify (garantizado):
───────────────────────────────── ───────────────────────────
Train: 953 legit / 47 fraude (4.7%) Train: 760 legit / 40 fraude (5.0%)
Test: 190 legit / 3 fraude (1.6%) ❌ Test: 190 legit / 10 fraude (5.0%) ✅
→ evaluación sobre 3 fraudes: ruido → evaluación representativa
Con solo 3 fraudes en test, tu estimación de recall se mueve a saltos del 33%: es una lotería, no una evaluación. stratify=y elimina esa fuente de varianza gratis. Regla práctica: en clasificación, usa stratify=y siempre, y con más razón cuanto más desbalanceadas estén las clases o más pequeño sea el dataset. En regresión no aplica directamente (el target es continuo); si necesitas algo análogo se estratifica por bins del target, algo que verás en el capítulo de validación.
5.3. Versión intermedia: dataset de negocio sintético con preprocesado¶
Iris viene limpio y numérico. La realidad no. Construyamos un dataset sintético de un problema clásico de negocio —predicción de churn en una empresa de telecomunicaciones— con los tres estorbos habituales: categorías de texto, valores faltantes y escalas dispares.
# ============================================================
# VERSIÓN INTERMEDIA: churn sintético con preprocesado manual
# ============================================================
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score, f1_score
# ---------- 1. CREAR EL DATASET SINTÉTICO ----------
rng = np.random.default_rng(7) # generador con semilla fija
n = 2000 # 2000 clientes
# Antigüedad en meses: uniforme entre 1 y 72
antiguedad = rng.integers(1, 73, size=n).astype(float)
# Factura mensual en euros: normal alrededor de 45€
factura = rng.normal(45, 15, size=n).clip(10, 120)
# Tipo de contrato: variable CATEGÓRICA de texto (como en la vida real)
contrato = rng.choice(
["mensual", "anual", "dos_años"], size=n, p=[0.55, 0.30, 0.15]
)
# Número de incidencias de soporte en el último año
incidencias = rng.poisson(1.5, size=n).astype(float)
# El churn real depende de: contrato mensual, factura alta,
# poca antigüedad y muchas incidencias (+ ruido)
logit = (
-2.0
+ 1.4 * (contrato == "mensual") # el contrato mensual dispara el churn
+ 0.03 * (factura - 45) # facturas altas empujan a irse
- 0.02 * antiguedad # la antigüedad fideliza
+ 0.35 * incidencias # cada incidencia quema al cliente
)
p_churn = 1 / (1 + np.exp(-logit)) # sigmoide → probabilidad
churn = (rng.random(n) < p_churn).astype(int) # 1 = se va, 0 = se queda
# Ensamblamos el DataFrame y ensuciamos: 8% de facturas faltantes
df = pd.DataFrame({
"antiguedad_meses": antiguedad,
"factura_mensual": factura,
"tipo_contrato": contrato,
"incidencias": incidencias,
"churn": churn,
})
mask_nan = rng.random(n) < 0.08 # 8% de posiciones al azar
df.loc[mask_nan, "factura_mensual"] = np.nan # las volvemos NaN
print(df["churn"].mean()) # tasa de churn ≈ 0.29
print(df.isna().sum()) # ~160 NaN en factura_mensual
# ---------- 2. SEPARAR FEATURES Y TARGET, Y DIVIDIR ----------
X = df.drop(columns="churn") # todo menos el target
y = df["churn"] # el target
# stratify=y: misma tasa de churn en train y test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# ---------- 3. PREPROCESADO MANUAL (fit SOLO con train) ----------
num_cols = ["antiguedad_meses", "factura_mensual", "incidencias"]
cat_cols = ["tipo_contrato"]
# (a) IMPUTACIÓN: rellenar NaN con la MEDIANA calculada en el TRAIN.
imputer = SimpleImputer(strategy="median")
X_train_num = imputer.fit_transform(X_train[num_cols]) # fit + transform
X_test_num = imputer.transform(X_test[num_cols]) # SOLO transform
# (b) ESCALADO: media 0 y desviación 1, con estadísticas del TRAIN.
# La regresión logística converge mejor con features comparables.
scaler = StandardScaler()
X_train_num = scaler.fit_transform(X_train_num) # fit en train
X_test_num = scaler.transform(X_test_num) # aplicar a test
# (c) ONE-HOT: convertir texto en columnas binarias.
# handle_unknown="ignore": si en producción aparece una categoría
# nueva, se codifica como todo ceros en vez de reventar.
encoder = OneHotEncoder(handle_unknown="ignore", sparse_output=False)
X_train_cat = encoder.fit_transform(X_train[cat_cols]) # aprende categorías
X_test_cat = encoder.transform(X_test[cat_cols]) # las aplica
# (d) Unimos numéricas escaladas + categóricas codificadas
X_train_ready = np.hstack([X_train_num, X_train_cat])
X_test_ready = np.hstack([X_test_num, X_test_cat])
# ---------- 4. BASELINE Y MODELO ----------
base = DummyClassifier(strategy="most_frequent").fit(X_train_ready, y_train)
model = LogisticRegression(max_iter=1000).fit(X_train_ready, y_train)
# ---------- 5. EVALUAR: accuracy Y f1 (clases desbalanceadas) ----------
for nombre, est in [("Baseline", base), ("LogReg", model)]:
pred = est.predict(X_test_ready)
print(f"{nombre:9s} acc={accuracy_score(y_test, pred):.3f} "
f"f1={f1_score(y_test, pred):.3f}")
# Salida típica:
# Baseline acc=0.715 f1=0.000 ← ¡f1 desenmascara al baseline!
# LogReg acc=0.782 f1=0.554
Tres lecciones de este código:
- El patrón
fiten train /transformen test aparece tres veces (imputer, scaler, encoder). Es sagrado: cualquier estadística usada para transformar debe calcularse solo con datos de entrenamiento. - F1 desenmascara al baseline. Con 71.5% de clientes que se quedan, el baseline logra 71.5% de accuracy... y F1 = 0, porque jamás detecta un solo churner. La métrica correcta cambia la historia por completo.
- El preprocesado manual es frágil. Llevamos ya 6 objetos que hay que aplicar en el orden correcto, sin equivocarse, también en producción. Esa fragilidad es exactamente lo que resuelve la versión avanzada.
5.4. Versión avanzada: Pipeline profesional¶
Un Pipeline encadena transformadores y un modelo final en un único objeto que respeta la API de sklearn (fit, predict). Un ColumnTransformer aplica preprocesados distintos a columnas distintas. Juntos eliminan de raíz dos de los errores más caros del oficio: el data leakage en el preprocesado y el training-serving skew.
flowchart LR
subgraph P["Pipeline (un único estimator: .fit / .predict)"]
direction LR
subgraph CT["ColumnTransformer (preprocesado)"]
direction TB
N["Columnas numéricas:<br/>antigüedad, factura,<br/>incidencias"] --> NI["SimpleImputer<br/>(mediana)"] --> NS["StandardScaler"]
C["Columna categórica:<br/>tipo_contrato"] --> CE["OneHotEncoder<br/>(handle_unknown)"]
end
CT --> M["LogisticRegression"]
end
D["DataFrame crudo<br/>(con NaN y texto)"] --> P
P --> S["Predicción de churn"]
style P fill:#1565c0,color:#fff
style CT fill:#00838f,color:#fff
# ============================================================
# VERSIÓN AVANZADA: el mismo problema, empaquetado en Pipeline
# ============================================================
# Pipeline encadena pasos; ColumnTransformer reparte por columnas
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.metrics import classification_report
# ---------- 1. SUB-PIPELINE PARA COLUMNAS NUMÉRICAS ----------
# Cada paso es una tupla (nombre, transformador); el orden importa:
# primero imputar los NaN, después escalar.
numeric_pipe = Pipeline(steps=[
("imputer", SimpleImputer(strategy="median")), # NaN → mediana del train
("scaler", StandardScaler()), # media 0, desviación 1
])
# ---------- 2. COLUMNTRANSFORMER: cada tipo de columna, su receta ----------
preprocessor = ColumnTransformer(transformers=[
# (nombre, transformador, lista de columnas a las que aplica)
("num", numeric_pipe, ["antiguedad_meses", "factura_mensual",
"incidencias"]),
("cat", OneHotEncoder(handle_unknown="ignore"), ["tipo_contrato"]),
])
# ---------- 3. PIPELINE COMPLETO: preprocesado + modelo ----------
clf = Pipeline(steps=[
("prep", preprocessor), # todo el preprocesado
("model", LogisticRegression(max_iter=1000)), # el clasificador final
])
# ---------- 4. ENTRENAR: UNA sola llamada hace todo en orden ----------
# fit() del pipeline = fit_transform de cada paso con el train,
# y fit del modelo final. Imposible filtrar test por accidente.
clf.fit(X_train, y_train) # ¡X_train es el DataFrame CRUDO!
# ---------- 5. PREDECIR: el pipeline aplica el preprocesado solo ----------
y_pred = clf.predict(X_test) # imputa + escala + codifica + predice
# classification_report: precision, recall y f1 por clase, de un vistazo
print(classification_report(y_test, y_pred, target_names=["queda", "se va"]))
# ---------- 6. BONUS: validación cruzada sobre el pipeline entero ----------
# cross_val_score re-entrena el pipeline COMPLETO en cada fold:
# el preprocesado se recalcula con el train de cada fold → cero leakage.
scores = cross_val_score(clf, X_train, y_train, cv=5, scoring="f1")
print(f"F1 en 5-fold CV: {scores.mean():.3f} ± {scores.std():.3f}")
# ---------- 7. PERSISTIR: el pipeline entero es UN artefacto ----------
import joblib # serialización eficiente
joblib.dump(clf, "modelo_churn_v1.joblib") # preprocesado + modelo juntos
# En producción: clf = joblib.load(...); clf.predict(df_nuevo)
# El servicio recibe datos CRUDOS y el pipeline hace el resto.
Por qué esto es lo profesional, punto por punto:
- Cero leakage estructural: el pipeline garantiza por construcción que ninguna estadística de test contamina el entrenamiento, incluso dentro de la validación cruzada (donde hacerlo a mano es casi imposible sin errores).
- Un solo artefacto desplegable:
joblib.dumpguarda preprocesado y modelo juntos. Producción carga un objeto y le pasa datos crudos. Se acabó reimplementar el escalado en otro lenguaje "a ojo". - Búsqueda de hiperparámetros unificada: con
GridSearchCVsobre el pipeline puedes ajustar a la vez hiperparámetros del modelo (model__C) y del preprocesado (prep__num__imputer__strategy) — la sintaxis de doble guion bajo navega los pasos. Lo explotaremos en el capítulo de validación y ajuste. - Legibilidad: el pipeline ES la documentación del flujo de datos. En Jupyter, además, se dibuja como diagrama interactivo automáticamente.
Consejo profesional
Adopta esta regla desde hoy: si hay cualquier preprocesado, va dentro de un Pipeline. Sin excepciones. Los notebooks con fit_transform sueltos por celdas son la principal fábrica de resultados irreproducibles de la industria.
Ejercicio rápido 3¶
En la versión intermedia, un compañero propone "simplificar" así: imputar y escalar todo el DataFrame primero (imputer.fit_transform(X)) y hacer el train_test_split después. El código queda más corto y da resultados parecidos. ¿Qué está mal y por qué "resultados parecidos" no es un argumento?
Ver solución
Está cometiendo **data leakage de preprocesado**: la mediana de imputación y la media/desviación del escalado se calculan usando también las filas que luego serán test. El test deja de ser "datos nunca vistos": información suya (sus estadísticas) se filtró al entrenamiento. Por qué "da parecido" no es defensa: 1. **La magnitud del daño depende del caso.** Con 2000 filas i.i.d. y transformaciones suaves, el efecto es pequeño. Con datasets pequeños, outliers fuertes, o transformaciones más agresivas (selección de features por correlación con el target, por ejemplo), el leakage infla la métrica de forma seria. No sabes de antemano cuánto: por eso la práctica se prohíbe siempre. 2. **En producción es directamente imposible:** cuando llegue un cliente nuevo no existirá "el conjunto completo" para recalcular estadísticas. El preprocesado debe poder aplicarse a UNA fila nueva usando solo lo aprendido en train — exactamente lo que garantiza `fit` en train / `transform` en el resto. 3. **La solución sin coste existe:** el `Pipeline` da el código corto Y correcto. No hay tradeoff que discutir.6. Conceptos transversales del oficio¶
Hay tres ideas que atravesarán todos los capítulos de este módulo. Merecen ser entendidas a fondo ahora, porque explican el porqué de casi todas las técnicas que vienen (regularización, validación cruzada, ensembles, selección de features).
6.1. El tradeoff bias-variance a fondo¶
Cuando un modelo falla en datos nuevos, su error proviene de tres fuentes que conviene separar mentalmente:
- Bias (sesgo): error por suposiciones demasiado rígidas del modelo. Una recta intentando ajustar una curva tiene bias alto: por muchos datos que le des, su forma no puede capturar el patrón. El bias es un error sistemático: falla siempre en la misma dirección.
- Varianza: error por sensibilidad excesiva a los datos concretos de entrenamiento. Un árbol profundísimo memoriza cada fila, ruido incluido; si le hubiera tocado otra muestra de entrenamiento, habría aprendido un modelo muy distinto. La varianza es un error errático: falla en direcciones distintas según la muestra.
- Ruido irreducible: la parte del fenómeno que ninguna feature disponible explica (la venta que dependió de que ese día llovió y el dato de lluvia no está). Ningún modelo puede bajar de ahí.
La analogía de las dianas. Imagina que entrenas el mismo tipo de modelo sobre 4 muestras de entrenamiento distintas y cada modelo dispara su predicción a una diana (el centro = el valor verdadero):
BIAS BAJO BIAS ALTO
VARIANZA BAJA VARIANZA BAJA
(el objetivo) (underfitting)
┌───────────┐ ┌───────────┐
│ ··· │ │ │
│ · ┌─┐ · │ │ ┌─┐ │
│ · │●│●· │ │ │ │ ●│●
│ · │●│●· │ │ └─┘ ●│●
│ · └─┘ · │ │ │
│ ··· │ │ │
└───────────┘ └───────────┘
Tiros juntos Y centrados Tiros juntos pero
lejos del centro:
error sistemático
BIAS BAJO BIAS ALTO
VARIANZA ALTA VARIANZA ALTA
(overfitting) (lo peor de ambos)
┌───────────┐ ┌───────────┐
│ ● ··· │ │ ● │
│ · ┌─┐ ·● │ │ ┌─┐ │
│ · │ │ · │ │ ● │ │ │
│ ●· │ │ · │ │ └─┘ ●│
│ · └─┘ · │ │ │
│ ··● │ │ ● │
└───────────┘ └───────────┘
Centrados EN PROMEDIO, Dispersos Y
pero cada tiro a un sitio: descentrados
sensible a la muestra
El tradeoff: al aumentar la complejidad del modelo (más profundidad, más features, menos regularización), el bias baja (el modelo puede representar patrones más ricos) pero la varianza sube (tiene más libertad para memorizar ruido). El punto dulce está en medio, y encontrarlo es, literalmente, el trabajo de ajustar modelos:
Error
│ ▲
│ \ ......
│ \ Error en validación ...
│ \ ▼ ....
│ `-. _____ ....
│ `-.. ..--' ``--..
│ `----' ← punto dulce
│ ``--..
│ ``--... Error en train
│ ```---......_______
└────────────────────────────────────────────▶
simple Complejidad del modelo complejo
(bias alto, (bias bajo,
varianza baja) varianza alta)
UNDERFITTING ◀──────────────────▶ OVERFITTING
Tabla de diagnóstico: síntomas y remedios
| Diagnóstico | Síntoma en las métricas | Causa | Remedios (por orden de coste) |
|---|---|---|---|
| Underfitting (bias alto) | Error de train ALTO y error de validación ALTO, muy próximos entre sí | Modelo demasiado simple para el patrón, o features pobres | Modelo más expresivo; añadir features o interacciones; reducir regularización; entrenar más (si aplica) |
| Overfitting (varianza alta) | Error de train BAJO y error de validación mucho más ALTO (hueco grande) | Modelo demasiado flexible para los datos disponibles | Más datos; regularización; simplificar el modelo (menos profundidad, menos features); ensembles tipo bagging; early stopping |
| Punto dulce | Ambos errores bajos y próximos | Complejidad adecuada | Nada: documenta, congela y despliega |
| Techo de ruido | Ambos errores estancados aunque añadas datos y complejidad | Falta señal en las features | Mejores datos/features (hablar con dominio), no mejores modelos |
Nota
la primera pregunta ante cualquier modelo que rinde mal es siempre la misma: ¿cuál es su error en train? Si ya es malo en train, tienes bias: darle más datos no ayudará. Si es perfecto en train y malo en validación, tienes varianza: complicar el modelo empeorará las cosas. Este diagnóstico de 10 segundos evita semanas de trabajo en la dirección equivocada.
6.2. Underfitting y overfitting revisitados: curvas de validación¶
En el módulo 01 viste estos conceptos con la regresión lineal. Ahora vamos a verlos con una herramienta profesional: la curva de validación, que dibuja el error de train y validación en función de un hiperparámetro de complejidad. Usaremos la profundidad de un árbol de decisión (los árboles llegan en su propio capítulo; hoy solo necesitas saber que max_depth controla su complejidad).
# ============================================================
# CURVA DE VALIDACIÓN: ver el tradeoff bias-variance en vivo
# ============================================================
import numpy as np
import matplotlib.pyplot as plt # para dibujar la curva
# validation_curve entrena el modelo para cada valor del hiperparámetro,
# con validación cruzada, y devuelve scores de train y de validación
from sklearn.model_selection import validation_curve
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_classification
# ---- 1. Dataset sintético con ruido: 2000 muestras, 20 features ----
X, y = make_classification(
n_samples=2000, # tamaño moderado: el overfitting se notará
n_features=20, # 20 features en total...
n_informative=8, # ...pero solo 8 contienen señal real
n_redundant=4, # 4 son combinaciones de las informativas
flip_y=0.08, # 8% de etiquetas volteadas = ruido irreducible
random_state=42, # reproducibilidad
)
# ---- 2. Rango del hiperparámetro de complejidad a explorar ----
profundidades = np.arange(1, 21) # árboles de profundidad 1 a 20
# ---- 3. validation_curve hace TODO el trabajo pesado ----
train_scores, val_scores = validation_curve(
DecisionTreeClassifier(random_state=42), # el estimator a estudiar
X, y, # los datos
param_name="max_depth", # hiperparámetro a variar
param_range=profundidades, # sus valores
cv=5, # validación cruzada 5-fold
scoring="accuracy", # métrica a registrar
)
# Formas resultantes: (20 profundidades, 5 folds)
# ---- 4. Promediamos los 5 folds para cada profundidad ----
train_mean = train_scores.mean(axis=1) # media de accuracy en train
val_mean = val_scores.mean(axis=1) # media de accuracy en validación
# ---- 5. Dibujamos las dos curvas juntas ----
plt.figure(figsize=(9, 5))
plt.plot(profundidades, train_mean, "o-", label="Train")
plt.plot(profundidades, val_mean, "s-", label="Validación")
plt.axvline(profundidades[val_mean.argmax()], ls="--", color="gray",
label=f"Mejor profundidad = {profundidades[val_mean.argmax()]}")
plt.xlabel("max_depth (complejidad del árbol)")
plt.ylabel("Accuracy")
plt.title("Curva de validación: bias-variance en acción")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
# ---- 6. Lectura numérica de las tres zonas ----
for d in [1, profundidades[val_mean.argmax()], 20]:
i = d - 1 # índice en los arrays
print(f"depth={d:2d} train={train_mean[i]:.3f} "
f"val={val_mean[i]:.3f} hueco={train_mean[i]-val_mean[i]:.3f}")
# Salida típica:
# depth= 1 train=0.734 val=0.731 hueco=0.003 ← UNDERFIT: ambos mal
# depth= 6 train=0.907 val=0.859 hueco=0.048 ← punto dulce
# depth=20 train=1.000 val=0.820 hueco=0.180 ← OVERFIT: train perfecto,
# validación cae
La lectura de la curva es el diagnóstico de la tabla anterior hecho gráfico:
- Profundidad 1–2: train y validación bajos y pegados → underfitting (bias alto). El árbol es un tocón que no puede representar el patrón.
- Profundidad ~5–7: validación en su máximo, hueco moderado → punto dulce.
- Profundidad 15–20: train llega a 1.000 (memorización perfecta, incluido el 8% de etiquetas ruidosas que nosotros mismos volteamos) mientras validación cae. El árbol aprendió el ruido → overfitting (varianza alta).
Advertencia
train = 100% no es un logro; es un síntoma. Un modelo que clava el train ha tenido capacidad de sobra para memorizar; la única pregunta es cuánto ruido memorizó por el camino. Míralo siempre junto a validación.
6.3. La maldición de la dimensionalidad¶
Última idea transversal, más sutil: añadir features no es gratis. La intuición geométrica:
En 1 dimensión, 100 puntos uniformes en el intervalo [0,1] están apretados: la distancia media entre vecinos es ~0.01. En 2 dimensiones, esos mismos 100 puntos deben cubrir un cuadrado: ya se dispersan. En 10 dimensiones, cubren un hipercubo de 10 dimensiones y están desesperadamente solos: para mantener la misma densidad de 1D necesitarías 100¹⁰ = 10²⁰ puntos.
Cobertura con los MISMOS 100 puntos:
1D: ●●●●●●●●●●●●●●●●●●●●●●●●●●●●● denso (vecinos a ~0.01)
2D: ┌─────────────┐
│ ● ● ● │
│ ● ● │ se dispersan (vecinos a ~0.1)
│ ● ● ● │
└─────────────┘
10D: [ hipercubo 10-dimensional ] prácticamente vacío:
cada punto está lejísimos
de todos los demás
Consecuencias prácticas que sí notarás:
- Las distancias pierden significado. En alta dimensión, la distancia al vecino más cercano y al más lejano se vuelven casi iguales. Los métodos basados en distancia (k-NN, clustering con K-Means) se degradan.
- Se necesitan exponencialmente más datos. Cada feature nueva multiplica el volumen del espacio que el modelo debe "mapear". Con features irrelevantes, el modelo tiene más sitio donde encontrar patrones espurios → más varianza → más overfitting con los mismos datos.
- Más features irrelevantes = más ruido que memorizar. Añadir 200 columnas "por si acaso" a un dataset de 1000 filas es una receta directa de overfitting.
Matices honestos (la maldición tiene letra pequeña): los datos reales rara vez llenan el espacio completo — suelen vivir cerca de una estructura de dimensión mucho menor (manifold), y por eso el ML funciona en absoluto con imágenes de millones de píxeles. Además, algunos modelos son más resistentes (los árboles ignoran features irrelevantes bastante bien; los modelos regularizados como Lasso las apagan). Las defensas prácticas: seleccionar features con criterio de dominio, regularización, y reducción de dimensionalidad (PCA y compañía, capítulo de no supervisado).
Consejo profesional
ante la duda "¿meto esta feature?", la pregunta correcta no es "¿podría ayudar?" sino "¿tengo una razón de dominio para creer que contiene señal, y estará disponible en producción en el momento de predecir?". Las features se ganan su plaza, no entran por defecto.
Ejercicio rápido 4¶
Tu modelo de scoring da accuracy 0.99 en train y 0.71 en validación. Tu jefe propone: "consigamos un servidor más potente y entrenemos un modelo más grande". ¿Qué diagnóstico haces, qué opinas de la propuesta y qué tres acciones propondrías en su lugar?
Ver solución
**Diagnóstico:** hueco enorme entre train (0.99) y validación (0.71) → **overfitting / varianza alta**. El modelo memoriza el train pero no generaliza. **La propuesta empeora el problema:** un modelo más grande tiene aún más capacidad de memorizar; el hueco crecería. Más potencia de cálculo ataca el bias (permite modelos más expresivos), y aquí el bias no es el problema — el train ya está casi perfecto. **Tres acciones correctas (varianza alta → reducir varianza):** 1. **Regularizar o simplificar:** limitar profundidad/complejidad, subir la penalización L1/L2, early stopping. 2. **Más datos de entrenamiento** (si es viable conseguirlos): la varianza cae al crecer la muestra. 3. **Auditar las features:** eliminar las irrelevantes o de dudosa disponibilidad (maldición de la dimensionalidad) y, de paso, comprobar que ese 0.99 en train no esconda una fuga de datos — un train casi perfecto siempre merece esa sospecha. Bonus: usar validación cruzada para que el diagnóstico no dependa de un único split.7. Taxonomía de problemas de Machine Learning¶
Parte del oficio es, ante un problema nuevo, clasificarlo correctamente en la taxonomía: de esa clasificación se derivan el tipo de modelo, la métrica y la forma de evaluar. El mapa de decisión:
flowchart TD
Q0{"¿Tienes ejemplos históricos<br/>con la respuesta correcta<br/>(target etiquetado)?"}
Q0 -- "Sí → SUPERVISADO"--> Q1{"¿Qué tipo de<br/>respuesta predices?"}
Q0 -- "No → NO SUPERVISADO"--> Q5{"¿Qué buscas<br/>en los datos?"}
Q1 -- "Un número continuo"--> R["REGRESIÓN<br/>MAE, RMSE, R²"]
Q1 -- "Una categoría"--> Q2{"¿Cuántas categorías<br/>posibles y cuántas<br/>por ejemplo?"}
Q1 -- "Un orden"--> RK["RANKING<br/>NDCG, MAP"]
Q2 -- "2 clases, 1 por ejemplo"--> CB["CLASIFICACIÓN BINARIA<br/>F1, AUC, precision/recall"]
Q2 -- ">2 clases, 1 por ejemplo"--> CM["MULTICLASE<br/>accuracy, F1 macro"]
Q2 -- "varias por ejemplo"--> ML["MULTILABEL<br/>F1 por etiqueta, Hamming"]
R --> Q4{"¿El orden temporal<br/>es esencial?"}
Q4 -- "Sí"--> TS["SERIES TEMPORALES<br/>MAE/MAPE con<br/>validación temporal"]
Q5 -- "Grupos naturales"--> CL["CLUSTERING<br/>silhouette + validación<br/>de negocio"]
Q5 -- "Lo raro / anómalo"--> AN["DETECCIÓN DE ANOMALÍAS<br/>precision@k, tasa de falsas alarmas"]
style Q0 fill:#1565c0,color:#fff
style R fill:#2e7d32,color:#fff
style CB fill:#2e7d32,color:#fff
style CM fill:#2e7d32,color:#fff
style ML fill:#2e7d32,color:#fff
style TS fill:#e65100,color:#fff
style CL fill:#6a1b9a,color:#fff
style AN fill:#6a1b9a,color:#fff
style RK fill:#e65100,color:#fff
Tabla de decisión con ejemplos empresariales:
| Tipo de problema | Pregunta que responde | Ejemplo empresarial | Target | Métrica típica | Capítulo |
|---|---|---|---|---|---|
| Regresión | ¿Cuánto? | Predecir el precio de venta de un piso; previsión de ingresos | Número continuo | MAE, RMSE, R², MAPE | Cap. 2 |
| Clasificación binaria | ¿Sí o no? | ¿Este cliente impagará? ¿Este email es spam? | 0/1 | F1, AUC-ROC, precision, recall | Cap. 3 |
| Clasificación multiclase | ¿Cuál de estas N? | Clasificar tickets de soporte en 8 departamentos | Una clase de N | Accuracy, F1 macro/weighted | Cap. 3 |
| Clasificación multilabel | ¿Cuáles de estas N? | Etiquetar un artículo con varios temas a la vez | Varias clases por ejemplo | F1 por etiqueta, Hamming loss | Cap. 3 |
| Clustering | ¿Qué grupos hay? | Segmentar clientes para campañas diferenciadas | No hay (no supervisado) | Silhouette + interpretación de negocio | Cap. no supervisado |
| Detección de anomalías | ¿Qué es raro aquí? | Transacciones fraudulentas; fallos de máquina en fábrica | Raro/normal (a menudo sin etiquetas) | Precision@k, recall con pocas etiquetas | Cap. no supervisado |
| Ranking | ¿En qué orden? | Ordenar resultados de búsqueda del catálogo; priorizar leads | Orden de relevancia | NDCG, MAP, MRR | (avanzado) |
| Series temporales | ¿Cuánto, mañana? | Demanda de producto por semana; carga del call center | Número futuro | MAE, MAPE, con backtesting temporal | Cap. propio |
Tres avisos de clasificación de problemas que evitan errores caros:
- Muchos problemas de "clasificación" son en realidad de ranking o de probabilidad. A negocio rara vez le sirve "impagará: sí/no"; le sirve "los 500 clientes con mayor probabilidad de impago, ordenados". Eso cambia la métrica (precision@500, AUC) y el umbral deja de ser 0.5 por decreto.
- Las series temporales se disfrazan de regresión. Si el orden temporal importa, el split aleatorio queda prohibido y la evaluación se hace con cortes temporales (backtesting). Tratar una serie temporal como regresión i.i.d. es de los errores más frecuentes en principiantes.
- El clustering no tiene respuesta correcta. Sin target, la "bondad" de una segmentación la valida negocio ("¿estos grupos me sirven para algo?"), no solo una métrica interna. Desconfía de cualquier informe de clustering sin interpretación de dominio.
8. ML clásico vs. Deep Learning vs. LLMs en 2026¶
Estamos en 2026 y los LLMs dominan los titulares. Es legítimo preguntarse: ¿para qué aprender scikit-learn si existe GPT/Claude/Gemini? La respuesta honesta del oficio: porque cada familia de técnicas domina un territorio distinto, y el territorio de los datos tabulares de empresa —que es donde vive la mayoría del ML que genera dinero— sigue perteneciendo al ML clásico, en particular al gradient boosting.
Tabla de decisión honesta (2026):
| Situación | Mejor primera opción | Por qué | Cuándo reconsiderar |
|---|---|---|---|
| Datos tabulares (filas y columnas: clientes, transacciones, sensores agregados) | Gradient boosting (XGBoost/LightGBM/CatBoost) o ML clásico de sklearn | En tabular, el boosting sigue ganando o empatando con redes en la gran mayoría de benchmarks y competiciones, con una fracción del coste, y con interpretabilidad razonable | Con volúmenes gigantes y features muy complejas, probar también redes tabulares; pero exige demostración, no fe |
| Texto (clasificar, extraer, resumir, conversar) | LLM (API o modelo abierto ajustado) | Los LLMs entienden lenguaje con un nivel que ningún método clásico alcanza; con few-shot resuelven sin dataset de entrenamiento | Tareas de texto masivas, simples y sensibles a coste/latencia: un modelo pequeño ajustado (o TF-IDF + clásico) puede ser 100 veces más barato |
| Imágenes, audio, vídeo | Deep learning (CNNs, transformers de visión; a menudo modelos preentrenados ajustados) | La representación jerárquica aprendida es insustituible en datos perceptuales | Casi nunca vuelve el ML clásico aquí, salvo features artesanales en visión industrial muy controlada |
| Pocos datos etiquetados (decenas–cientos) | ML clásico regularizado, o LLM few-shot si el problema es de lenguaje | Los modelos grandes desde cero se mueren de hambre con pocos datos; los clásicos regularizados y los LLMs preentrenados no | Si puedes etiquetar más datos barato, hazlo: suele rendir más que cualquier truco |
| Regulación estricta (crédito, salud, seguros) | ML clásico interpretable (lineales, árboles, boosting con SHAP) | Los reguladores exigen explicar cada decisión individual; con LLMs es hoy imposible al nivel requerido | Los LLMs pueden ayudar alrededor del proceso (informes, revisión documental), no en la decisión regulada |
| Latencia de milisegundos y coste por predicción mínimo | ML clásico | Un boosting sirve millones de predicciones por céntimos en CPU; un LLM cuesta órdenes de magnitud más por llamada | Si la predicción vale mucho por unidad (pocas decisiones de alto valor), el coste del LLM puede ser irrelevante |
| Series temporales de demanda/ventas | Métodos estadísticos + boosting con features temporales | Simples, robustos, ganadores en competiciones de forecasting retail (familia M5) | Foundation models de forecasting existen y mejoran; evalúa contra el baseline clásico, como siempre |
La regla mental que resume la tabla:
La forma de los datos elige la familia de modelos. Tabla → boosting. Píxeles/ondas → deep learning. Lenguaje → LLM. Pocos datos → clásico regularizado o few-shot. Y en todos los casos: baseline primero.
Caso empresarial: por qué un banco sigue usando XGBoost para riesgo crediticio y no un LLM.
Un banco europeo mediano concede 200 000 créditos al consumo al año. Su modelo de riesgo (probabilidad de impago) es un gradient boosting sobre ~60 variables tabulares (ingresos, historial, ratios de endeudamiento...). En 2025 un comité propuso "modernizarlo con IA generativa". El equipo de riesgo lo descartó con cuatro argumentos que ilustran perfectamente el criterio profesional:
- Regulación e interpretabilidad. La normativa bancaria (y en la UE, el AI Act, que clasifica el credit scoring como sistema de alto riesgo) exige explicar cada denegación: qué factores pesaron y cuánto, de forma consistente y auditable. Con boosting + SHAP el banco genera esa explicación por solicitud y la defiende ante el regulador. Un LLM no puede ofrecer hoy esa trazabilidad: sus "explicaciones" son texto plausible, no una descomposición fiel del cálculo.
- Los datos son tabulares. No hay texto ni imagen en la decisión: hay 60 números y categorías. En ese terreno el boosting es el estado del arte práctico; un LLM no aporta capacidad de representación extra, y los experimentos internos del banco confirmaron AUC igual o peor con costes muy superiores.
- Coste y latencia. El scoring corre en cada solicitud (online) y en re-evaluaciones masivas de cartera (batch de millones de filas). El boosting resuelve cada predicción en microsegundos en CPU propia, coste marginal ≈ 0. Un LLM por API habría costado cientos de miles de euros al año y añadido latencia y dependencia de un proveedor externo — con datos personales financieros de por medio.
- Estabilidad y validación. El modelo de riesgo se valida, se estresa y se congela bajo gobernanza de modelos (model risk management). Un modelo determinista y versionado encaja en ese marco; un servicio de LLM que el proveedor actualiza sin previo aviso, no.
Donde SÍ usa LLMs ese mismo banco: resumen automático de documentación aportada por el cliente, extracción de campos de nóminas escaneadas (que luego alimentan como features tabulares al XGBoost), asistente interno para analistas y borradores de informes regulatorios. La lección: LLMs alrededor del proceso, ML clásico en el corazón de la decisión regulada. No es una guerra de tecnologías: es asignar cada herramienta a su territorio.
Nota
esta división del trabajo —LLM como preprocesador/interfaz, ML clásico como decisor— se ha convertido en uno de los patrones arquitectónicos más comunes de 2025-2026. Saber ML clásico no es saber tecnología antigua: es saber construir la mitad del sistema que el LLM no puede construir.
Ejercicio rápido 5¶
Para cada escenario, elige familia (ML clásico / deep learning / LLM) y justifica en una frase: (a) startup con 300 ejemplos etiquetados quiere clasificar emails de soporte en 5 categorías; (b) aseguradora con 2M de pólizas históricas quiere predecir la probabilidad de siniestro con 80 variables tabulares; (c) fábrica quiere detectar defectos visuales en piezas a partir de fotos de la línea.
Ver solución
- **(a) LLM few-shot.** Es texto y hay poquísimos datos: un LLM con instrucciones y un puñado de ejemplos por categoría rinde bien sin entrenar nada. Con el tiempo, si el volumen se dispara y el coste por email duele, se puede destilar a un modelo pequeño ajustado con los datos que se vayan etiquetando. - **(b) ML clásico (gradient boosting).** Tabular puro, muchos datos, sector regulado que exigirá explicar la prima/decisión: territorio natural de XGBoost/LightGBM con SHAP. Un LLM no aporta nada aquí y compromete coste, latencia e interpretabilidad. - **(c) Deep learning (visión).** Datos perceptuales (imágenes): una CNN o un transformer de visión preentrenado y ajustado con las fotos de la fábrica. Probablemente desplegado en edge (embebido junto a la cámara) por latencia. El ML clásico no compite con píxeles crudos.9. Caso empresarial completo: predicción de demanda en un e-commerce¶
Caso empresarial
Cerramos el capítulo recorriendo el workflow entero con un caso narrado y realista. Los nombres son ficticios; los conflictos, las cifras aproximadas y las decisiones son fieles a cómo ocurren estos proyectos.
La empresa. NovaHome, e-commerce de menaje y pequeño electrodoméstico. 12 000 referencias (SKUs), un almacén central, ~40 M€ de facturación anual. El equipo de compras (4 personas) decide cada lunes cuánto pedir a los proveedores, con plazos de entrega de 2 a 4 semanas.
El dolor. Doble y contradictorio, como casi siempre en demanda: (a) roturas de stock en productos de alta rotación — se estima un 4% de ventas perdidas; (b) exceso de stock en productos de baja rotación — 1.8 M€ inmovilizados, parte del cual acaba liquidado con margen negativo. La dirección pide "IA para predecir la demanda".
Fase 1-2: Problema y filtro ML¶
La científica de datos, Marta, dedica las dos primeras semanas a NO programar. Entrevista al equipo de compras y descubre el proceso real: cada comprador estima "a ojo" mirando las ventas de las últimas 4 semanas en una hoja de cálculo, con ajustes intuitivos por estacionalidad ("en septiembre sube el menaje de cocina").
Primer hallazgo incómodo: el problema de decisión no es "predecir la demanda" sino "cuánto pedir el lunes", que además involucra plazos de proveedor, lotes mínimos y descuentos por volumen. Marta negocia el alcance: el modelo predecirá la demanda por SKU para las próximas 4 semanas; la decisión de pedido seguirá siendo humana, informada por la predicción. Optimizar el pedido directamente (un problema de optimización, no solo de ML) queda para una fase 2 futura.
¿Es un problema de ML? Sí, con matices: hay patrón (estacionalidad, tendencia, promociones), hay datos (3 años de ventas diarias por SKU) y una predicción imperfecta aporta valor. Pero Marta detecta el matiz que salvará el proyecto: de los 12 000 SKUs, 7 000 venden menos de 1 unidad por semana. Para esos, ningún modelo hará magia con series casi vacías: se acuerda tratarlos con una regla simple de cobertura de stock, y aplicar ML solo a los ~5 000 SKUs con historial denso, que concentran el 92% de la facturación. Reducir el alcance del ML al territorio donde puede ganar es una decisión de senior.
Fase 3: Métricas¶
- Métrica de negocio: reducción de ventas perdidas por rotura (objetivo: del 4% al 2.5%) y del stock inmovilizado (objetivo: −15%), medidos por categoría a los 3 meses del despliegue.
- Métrica técnica: WMAPE (error porcentual absoluto ponderado por ventas) a 4 semanas vista, por SKU y agregado. Se elige ponderado para que los SKUs importantes pesen más, y se acuerda medir también el sesgo (¿el modelo se pasa o se queda corto sistemáticamente?) porque los costes son asimétricos: la rotura duele más que el exceso en top-ventas, y al revés en cola larga.
- Umbral de éxito pactado por escrito: batir al baseline naive estacional en ≥10% de WMAPE agregado. Si no se supera, no se despliega.
Fase 4: Datos¶
Seis semanas, las más duras. Lo que aparece:
- Las ventas históricas registran ventas, no demanda: cuando hubo rotura de stock, la venta fue 0 aunque la demanda existiera. Entrenar con eso enseña al modelo que "tras quedarnos sin stock, la demanda cae" — una profecía autocumplida. Solución: reconstruir la demanda censurada en los días de rotura (imputación con la media de días equivalentes) y marcar esos días con una feature.
- Las promociones históricas están en un Excel del equipo de marketing, con formatos cambiantes. Dos semanas de arqueología de datos para reconstruir el calendario promocional. Sin él, el modelo confundiría promociones con estacionalidad.
- Un cambio de plataforma en 2024 dejó 3 meses de datos corruptos. Se documenta y se excluyen.
- Split temporal, no aleatorio: entrenamiento hasta diciembre 2025, validación enero-marzo 2026, test abril-junio 2026. Backtesting: se simula estar en cada lunes histórico y predecir las 4 semanas siguientes solo con la información disponible ese lunes (regla de oro anti-fuga temporal).
Fase 5: Baseline¶
Dos baselines en una tarde:
- Naive estacional: "las próximas 4 semanas venderán lo mismo que las mismas 4 semanas del año pasado, ajustado por la tendencia de los últimos 2 meses". WMAPE agregado: 38%.
- La hoja de cálculo humana: se reconstruyen las estimaciones implícitas del equipo de compras a partir de sus pedidos históricos. WMAPE: 34%. Los humanos ganan al naive — llevan años haciéndolo y saben cosas que no están en los datos.
El número a batir queda fijado: 34%, con mejora mínima pactada del 10% → objetivo ≤ 30.6%.
Fase 6: Iteración¶
Ocho semanas de bucle experimental, con registro de cada experimento:
- Iteración 1 — Regresión lineal regularizada con features de calendario (semana del año, festivos) y lags de ventas: WMAPE 33%. Ya bate a ambos baselines. Primer aviso interesante: la mayor parte de la señal está en los lags y la estacionalidad.
- Iteración 2 — LightGBM con las mismas features: 29%. El boosting captura interacciones (p. ej., "la estacionalidad de ventiladores depende de la categoría") que la lineal no.
- Iteración 3 — + calendario promocional reconstruido: 25%. La mejor mejora del proyecto no vino de un modelo mejor sino de un dato mejor. Se confirma la asimetría datos > modelos.
- Iteración 4 — + features de precio relativo a la competencia (scraping ya disponible en la empresa): 24.5%. Mejora marginal; se conserva por su valor en promociones.
- Iteración 5 — Prueba con una red neuronal de forecasting: 24.8%, más lenta de entrenar y más difícil de explicar. Se descarta: no paga su complejidad. Decisión anotada en el registro para que nadie la repita en seis meses.
Modelo candidato: LightGBM, WMAPE 24.5% en validación.
Fase 7: Evaluación¶
Contra el test (abril-junio 2026, intocado): WMAPE 26% — algo peor que en validación, como es normal y honesto reportar. Supera de sobra el umbral (30.6%).
El análisis por segmentos revela dos problemas que el número global escondía:
- En productos nuevos (sin historial), el modelo es malo (WMAPE 55%): no tiene de dónde aprender. Se acuerda excluirlos del modelo y tratarlos con una regla de analogía ("como su categoría") el primer trimestre de vida.
- En semanas de promoción intensa, el modelo se queda corto sistemáticamente (sesgo −12%). Se documenta como limitación conocida y se añade al dashboard un aviso para que compras lo corrija manualmente en esas semanas, mientras se investiga para la v2.
Traducción a negocio presentada al comité: con el error reducido del 34% al 26% y una política de stock de seguridad recalculada, la simulación sobre el histórico estima ~400 k€/año entre ventas recuperadas y stock liberado. El comité aprueba el despliegue. Conflicto final: dirección quería "automatizar los pedidos ya"; Marta y el equipo de compras defienden (y ganan) el despliegue como herramienta de apoyo con humano al mando durante al menos 6 meses. Motivo: confianza, casos límite (lotes mínimos, negociaciones con proveedor) y la necesidad de validar la métrica de negocio en real antes de ceder el volante.
Fase 8: Despliegue¶
Patrón batch: cada domingo por la noche, un job reentrena con los datos más recientes, valida automáticamente contra el último mes (si el WMAPE del job supera un umbral, el despliegue se aborta y avisa), y escribe las predicciones en una tabla que alimenta la herramienta del equipo de compras — que sigue siendo su entorno familiar, con una columna nueva: "predicción modelo" junto a "tu estimación". Primer mes en shadow mode: el modelo predice pero no se muestra, y se comparan sus aciertos con las decisiones humanas. Los compradores participan en el diseño de la interfaz; dos de sus sugerencias (mostrar el intervalo, no solo el punto; y marcar las semanas de promoción) resultan claves para la adopción.
Fase 9: Monitorización¶
Dashboard con tres capas: WMAPE semanal real vs. esperado (el real llega con 4 semanas de retraso, naturaleza del problema), distribución de las features de entrada (alerta de drift si el calendario promocional deja de actualizarse — ocurrió en el mes 2 y la alerta lo cazó), y tasa de adopción: ¿en qué % de pedidos el comprador siguió la predicción? Empezó en el 30% y subió al 70% en tres meses conforme creció la confianza. A los 3 meses: ventas perdidas del 4% al 2.9% (objetivo 2.5%: cerca, no del todo), stock inmovilizado −11%. Resultado honesto: éxito parcial claro, proyecto renovado, v2 en marcha (mejor manejo de promociones y arranque del optimizador de pedidos).
Moralejas del caso, mapeadas al capítulo:
| Momento del caso | Principio del capítulo |
|---|---|
| Marta pasa 2 semanas sin programar | Fase 1: la decisión primero, la tecnología después |
| 7 000 SKUs se resuelven con una regla | Fase 2: no todo es ML; reducir alcance es de senior |
| Umbral de éxito pactado por escrito antes de entrenar | Fase 3: métricas antes que modelos |
| Ventas ≠ demanda; el Excel de promociones | Fase 4: los datos son el 80% del trabajo y del riesgo |
| El humano (34%) bate al naive (38%) | Fase 5: la heurística existente es EL baseline a batir |
| La mayor mejora vino del calendario promocional | Datos > modelos |
| La red neuronal se descarta pese a ser "moderna" | La mejora debe pagar su complejidad |
| WMAPE 26% en test > 24.5% en validación | La evaluación honesta reporta el test, aunque sea peor |
| Shadow mode + humano al mando | Fase 8: despliegue gradual y confianza |
| La alerta de drift cazó el calendario desactualizado | Fase 9: monitorizar datos y adopción, no solo servidores |
10. Buenas prácticas¶
- Empieza todo proyecto con la pregunta de la decisión: qué decisión cambia, quién la toma, cómo se toma hoy. Documéntalo en un one-pager antes de abrir un notebook.
- Pacta métricas y umbral de éxito por escrito antes de entrenar. Métrica técnica, métrica de negocio y el puente entre ambas.
- Baseline siempre, y siempre primero.
DummyClassifier/DummyRegressoro la heurística de negocio existente. Primera fila de todas tus tablas de resultados. - El test se toca una vez. Itera contra validación (o validación cruzada); reserva el test para el veredicto final.
- Todo preprocesado dentro de un
Pipeline.fiten train,transformen el resto, garantizado por construcción; y un único artefacto desplegable. stratify=yen clasificación, split temporal en series temporales. Yrandom_statefijado y anotado en todo lo aleatorio.- Registra cada experimento (aunque sea en un CSV al principio): datos usados, features, hiperparámetros, métrica. Tu yo de dentro de dos meses te lo agradecerá.
- Desconfía de los resultados demasiado buenos. Un salto enorme sobre el baseline es, hasta demostrar lo contrario, una fuga de datos.
- Evalúa por segmentos y analiza los errores grandes uno a uno. El promedio esconde desastres locales.
- Sube la escalera de complejidad peldaño a peldaño (lineal → árbol → ensemble → boosting) y exige que cada peldaño pague su coste extra en mantenimiento, latencia e interpretabilidad.
- Habla con los expertos de dominio antes, durante y después. Saben qué columnas mienten y qué features tienen sentido.
- Planifica la monitorización como parte del proyecto, no como epílogo. Drift de datos, métrica real y adopción por los usuarios.
11. Malas prácticas¶
- Empezar por el modelo ("quiero usar XGBoost/deep learning/LLMs") y buscarle después un problema.
- Optimizar accuracy en problemas desbalanceados sin mirar precision, recall o F1.
- Preprocesar con estadísticas del dataset completo (imputar/escalar antes del split): data leakage silencioso.
- Evaluar contra test repetidamente y quedarse con la mejor pasada: el test degenera en un segundo conjunto de validación y tu estimación final es optimista.
- Split aleatorio en datos temporales: entrenar con el futuro es la fantasía más cara del ML aplicado.
- Cambiar varias cosas por experimento: imposible atribuir la mejora.
- No versionar datos, código ni modelo: irreproducibilidad garantizada.
- Presentar métricas técnicas a negocio sin traducción ("logramos 0.87 de AUC") y sin baseline de referencia.
- Reimplementar el preprocesado a mano en producción: divergencia entrenamiento-servicio esperando su momento.
- Desplegar al 100% sin shadow mode ni rollback, y declarar el proyecto terminado el día del despliegue.
- Meter features "por si acaso" sin razón de dominio ni garantía de disponibilidad en producción: invitación al overfitting y a la maldición de la dimensionalidad.
- Ignorar la heurística existente del equipo de negocio: suele ser el baseline más difícil de batir y la fuente de features más valiosa.
12. Errores comunes¶
| # | Error | Síntoma | Causa raíz | Prevención / solución |
|---|---|---|---|---|
| 1 | Data leakage por columna | Métricas casi perfectas de repente | Una feature contiene información posterior al momento de predicción | Auditar cada feature: "¿existía este valor en el momento de predecir?"; sospechar de resultados demasiado buenos |
| 2 | Leakage de preprocesado | Métrica offline algo inflada; producción rinde peor | fit_transform antes del split |
Pipeline siempre; fit solo con train |
| 3 | Fuga temporal | Backtest brillante, producción mediocre | Split aleatorio en datos con orden temporal | Split por fecha; simular "estar en el pasado" |
| 4 | Accuracy en desbalanceados | 95%+ de accuracy que no sirve de nada | Métrica que premia predecir la clase mayoritaria | F1/AUC/recall según el coste; comparar contra DummyClassifier |
| 5 | Sobreajuste al test | Test excelente, producción decepciona | Iterar mirando el test muchas veces | Test intocable; iterar con validación cruzada |
| 6 | ValueError: Expected 2D array |
Excepción al hacer fit/predict | Pasar (n,) donde sklearn espera (n, 1) |
X.reshape(-1, 1) para una sola feature |
| 7 | Categoría nueva en producción | El servicio revienta con datos válidos | OneHotEncoder sin política para categorías no vistas |
handle_unknown="ignore" y validación de entradas |
| 8 | Olvidar random_state |
Resultados distintos en cada ejecución | Aleatoriedad sin semilla en split y modelos | Fijar y documentar semillas en todo |
| 9 | Diagnóstico invertido de bias/varianza | "Más datos" no mejora nada (o "modelo más grande" empeora) | No mirar el error de train antes de decidir | Regla de 10 segundos: train malo → bias; hueco train-val grande → varianza |
| 10 | Modelo que nadie usa | Métricas técnicas buenas, cero impacto | No se definió la decisión ni se involucró al usuario final | Fase 1 bien hecha; medir adopción en la monitorización |
13. FAQ — Preguntas frecuentes¶
1. ¿Necesito saber implementar los algoritmos desde cero, como hice con la regresión lineal en el módulo 01?
Implementar uno o dos (ya lo hiciste) es un ejercicio formativo insustituible para entender qué pasa dentro. Pero profesionalmente usarás implementaciones de biblioteca: están optimizadas, testeadas por miles de usuarios y mantenidas. Tu valor no está en reimplementar fit(), sino en todo lo que rodea al fit(): problema, datos, métricas, evaluación, despliegue. Eso es justo lo que las bibliotecas no hacen por ti.
2. ¿Cuántos datos necesito para hacer machine learning? La respuesta honesta: depende de la complejidad del patrón y del ruido. Órdenes de magnitud orientativos: con cientos de filas, modelos muy simples y regularizados (y validación cruzada, no un único split); con miles, ML clásico funciona con comodidad; con cientos de miles o millones, se abren boosting a plena potencia y deep learning. Pero la calidad manda sobre la cantidad: 5 000 filas limpias con buenas features baten a 500 000 filas con el target mal definido.
3. ¿Por qué mi modelo funciona peor en producción que en mi notebook? Las cuatro causas por orden de probabilidad: (1) alguna forma de leakage en el entrenamiento (temporal, de preprocesado o de columna); (2) training-serving skew: el preprocesado de producción no es idéntico al del entrenamiento (solución: Pipeline serializado); (3) los datos de producción tienen otra distribución que los de entrenamiento (drift, o un split de evaluación poco representativo); (4) evaluaste demasiadas veces contra el test y tu estimación era optimista. Revisa en ese orden.
4. ¿Sigue mereciendo la pena aprender scikit-learn en 2026, con los LLMs? Sí, y con más motivo. La mayoría de los datos de empresa son tabulares, y ahí el ML clásico sigue siendo el estado del arte práctico por rendimiento, coste, latencia e interpretabilidad (revisa el caso del banco en la sección 8). Además, los patrones que sklearn te enseña —fit/transform, pipelines, validación honesta, baselines— son los mismos que necesitas para evaluar y desplegar sistemas con LLMs. El workflow de la sección 2 aplica igual cuando el "modelo" es un prompt.
5. ¿Qué diferencia hay entre científico de datos, ML engineer y data engineer? Fronteras borrosas y dependientes de la empresa, pero como brújula: el data engineer construye y mantiene las tuberías y almacenes de datos (fase 4 en su parte de infraestructura); el científico de datos define el problema con negocio, explora, modela y evalúa (fases 1-7); el ML engineer industrializa: despliegue, escalado, monitorización (fases 8-9). En empresas pequeñas, una sola persona hace las tres cosas — razón de más para conocer el workflow completo.
6. ¿Cuándo uso predict_proba en lugar de predict?
Casi siempre que la decisión tenga costes asimétricos o requiera priorizar. predict aplica un umbral fijo (0.5 por defecto) que rara vez es el óptimo para tu negocio. Con predict_proba obtienes la probabilidad y decides tú el umbral (o el ranking: "los 500 clientes más probables"). En el capítulo de clasificación ajustaremos umbrales con curvas precision-recall.
7. ¿Debo escalar las features siempre? No siempre: depende del modelo. Los basados en distancias o en gradiente sobre combinaciones lineales (k-NN, SVM, regresión logística/lineal regularizada, redes) sí lo necesitan o lo agradecen mucho. Los basados en árboles (decision tree, random forest, gradient boosting) son invariantes a transformaciones monótonas de cada feature: escalar no les afecta. Como con el Pipeline el escalado es un paso más que no puede filtrar información, el coste de incluirlo es casi nulo.
8. ¿Qué hago si mi dataset está muy desbalanceado (por ejemplo, 1% de positivos)?
Primero: no entres en pánico ni resamplees por reflejo. Pasos en orden: (1) elige la métrica correcta (AUC-PR, F1, recall@precision-mínima) y un baseline; (2) usa stratify en los splits; (3) prueba class_weight="balanced" en los modelos que lo soportan; (4) ajusta el umbral de decisión con predict_proba según los costes; (5) solo si lo anterior no basta, considera técnicas de remuestreo (oversampling/SMOTE) — con cuidado de aplicarlas solo al train y dentro del pipeline. Lo veremos a fondo en el capítulo de clasificación.
9. ¿Cada cuánto hay que reentrenar un modelo en producción? No hay número universal: depende de la velocidad a la que cambia el fenómeno. Demanda en retail: semanal suele funcionar; scoring de crédito: mensual o trimestral con re-validación formal; fraude: casi continuo porque el adversario se adapta. La respuesta profesional es monitorizar el drift y la métrica real, y reentrenar cuando los datos lo pidan — con validación automática antes de promover cada modelo nuevo (como el job dominical de NovaHome en la sección 9).
10. ¿Puedo fiarme de los datasets de ejemplo como Iris para aprender? Para aprender la mecánica (API, splits, métricas), sí: son perfectos porque eliminan el ruido de la limpieza. Para aprender el oficio, no bastan: son pequeños, limpios y sin las trampas reales (NaN, categorías, desbalanceo, fugas, temporalidad). Por eso este capítulo empareja Iris con un dataset sintético sucio, y por eso los ejercicios del módulo usarán datos progresivamente más incómodos.
14. Resumen del capítulo¶
- El ML profesional es un oficio donde el modelado es ~10% del trabajo; el resto es problema, datos, evaluación, despliegue y monitorización. La decisión de negocio es el producto; el modelo, un medio.
- El workflow de 9 fases (problema → ¿es ML? → métricas → datos → baseline → iteración → evaluación → despliegue → monitorización) es el mapa de todo el módulo. Es cíclico, con retrocesos constantes, y cada fase tiene responsable, errores típicos y entregable.
- Regla del baseline: nunca entrenes nada sin haber medido el modelo trivial (
DummyRegressor/DummyClassifiero la heurística existente). Da contexto, detecta problemas sin señal y desenmascara fugas de datos. - scikit-learn gana por su API uniforme: estimators con
fit, predictors conpredict, transformers contransform. Hiperparámetros en el constructor; lo aprendido, en atributos con_final. Esa uniformidad habilita intercambiar modelos en una línea y componer Pipelines. - El flujo end-to-end canónico: cargar →
train_test_split(constratify=yen clasificación, temporal en series) → baseline → entrenar → predecir → evaluar con la métrica correcta. Y todo preprocesado dentro de un Pipeline: elimina el leakage por construcción y produce un único artefacto desplegable. - Bias-variance: error de train alto → bias (underfitting): modelo más expresivo o mejores features. Hueco grande train-validación → varianza (overfitting): más datos, regularización o simplificar. Las curvas de validación hacen visible el punto dulce. La maldición de la dimensionalidad recuerda que cada feature debe ganarse su plaza.
- La taxonomía (regresión, binaria, multiclase, multilabel, clustering, anomalías, ranking, series temporales) determina modelo y métrica. Clasificar bien el problema es la mitad de resolverlo.
- En 2026: tabular → boosting; percepción → deep learning; lenguaje → LLMs; pocos datos → clásico regularizado o few-shot. En dominios regulados, la interpretabilidad, el coste y la gobernanza mantienen al ML clásico en el corazón de la decisión, con los LLMs trabajando alrededor.
- El caso NovaHome demostró el workflow en acción: la mayor mejora vino de un dato (el calendario promocional), no de un modelo; el baseline más duro era humano; y el éxito se midió en euros y adopción, no solo en WMAPE.
En el próximo capítulo bajamos al primer territorio de la taxonomía con la artillería nueva: regresión con scikit-learn — métricas a fondo (MAE vs. RMSE y cuándo cada una), regularización Ridge y Lasso como respuesta directa al tradeoff bias-variance que acabas de aprender, y features polinómicas para que un modelo lineal capture curvas.
15. Bibliografía y recursos¶
Libros de referencia:
- Aurélien Géron — Hands-On Machine Learning with Scikit-Learn, Keras & TensorFlow (3ª ed., O'Reilly, 2022). El complemento práctico ideal de este módulo. https://www.oreilly.com/library/view/hands-on-machine-learning/9781098125967/
- Chip Huyen — Designing Machine Learning Systems (O'Reilly, 2022). El workflow profesional (fases 1-9) en profundidad de libro entero. https://www.oreilly.com/library/view/designing-machine-learning/9781098107956/
- Andriy Burkov — The Hundred-Page Machine Learning Book (2019). Panorámica compacta y rigurosa; ideal para consolidar. https://themlbook.com/
- Trevor Hastie, Robert Tibshirani, Jerome Friedman — The Elements of Statistical Learning (2ª ed., Springer). La referencia teórica (bias-variance en el cap. 7). Gratuito: https://hastie.su.domains/ElemStatLearn/
- Gareth James et al. — An Introduction to Statistical Learning (2ª ed., Springer). La versión accesible del anterior, con labs en Python. Gratuito: https://www.statlearning.com/
Documentación y artículos:
- Documentación oficial de scikit-learn — guía de usuario, API y ejemplos: https://scikit-learn.org/stable/
- scikit-learn — Common pitfalls and recommended practices (leakage, pipelines, reproducibilidad): https://scikit-learn.org/stable/common_pitfalls.html
- Pedro Domingos — A Few Useful Things to Know About Machine Learning (CACM, 2012). Doce lecciones del oficio en 9 páginas; lectura obligatoria: https://homes.cs.washington.edu/~pedrod/papers/cacm12.pdf
- Sculley et al. — Hidden Technical Debt in Machine Learning Systems (NeurIPS, 2015). Por qué el modelo es la parte pequeña del sistema: https://papers.nips.cc/paper/2015/hash/86df7dcfd896fcaf2674f757a2463eba-Abstract.html
- Grinsztajn, Oyallon, Varoquaux — Why do tree-based models still outperform deep learning on tabular data? (NeurIPS, 2022). La evidencia detrás de "tabular → boosting": https://arxiv.org/abs/2207.08815
- Google — Rules of Machine Learning (Martin Zinkevich). 43 reglas de ingeniería ML; la regla #1 es, literalmente, "no tengas miedo de lanzar sin ML": https://developers.google.com/machine-learning/guides/rules-of-ml
- Reglamento de IA de la UE (AI Act) — texto y recursos oficiales: https://artificialintelligenceact.eu/
Cursos y práctica:
- Andrew Ng — Machine Learning Specialization (Coursera/DeepLearning.AI): https://www.deeplearning.ai/courses/machine-learning-specialization/
- Kaggle Learn — micro-cursos gratuitos y datasets para practicar el workflow completo: https://www.kaggle.com/learn
- MLflow (experiment tracking de código abierto): https://mlflow.org/
| Anterior | Módulo | Siguiente |
|---|---|---|
| README del módulo | 02-MACHINE-LEARNING | Capítulo 2: Regresión |