Conjuntos de Datos y Divisiones
Las divisiones de entrenamiento, validación y prueba definen lo que tu modelo ha visto durante el entrenamiento frente a lo que debe generalizar. Una división correcta es la base de una evaluación honesta.
Receta
Tarjeta de receta de referencia rápida, lista para copiar y pegar.
from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X_train, y_train, cv=cv, scoring="f1_macro")Cuándo usar esto:
- Al iniciar cualquier proyecto de aprendizaje supervisado antes de ajustar preprocesadores o modelos.
- Para comparar modelos de forma justa con validación cruzada solo en datos de entrenamiento.
- Para manejar clases desequilibradas con divisiones estratificadas.
- Para prevenir fugas cuando varias filas pertenecen al mismo usuario, sesión o ventana de tiempo.
Ejemplo de Trabajo
"""datasets_splits.py - flujo de trabajo de división sin fugas para clasificación tabular."""
from __future__ import annotations
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
# Cargar e inspeccionar
data = load_breast_cancer(as_frame=True)
X: pd.DataFrame = data.data
y: pd.Series = data.target
# 1) Reservar el conjunto de prueba final (nunca se usa durante la selección del modelo)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 2) Construir el pipeline - ajustar solo en los pliegues de entrenamiento dentro de CV
pipe = Pipeline([
("scaler", StandardScaler()),
("clf", RandomForestClassifier(n_estimators=100, random_state=42)),
])
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = cross_val_score(pipe, X_train, y_train, cv=cv, scoring="f1")
print(f"CV F1: {cv_scores.mean():.3f} +/- {cv_scores.std():.3f}")
# 3) Ajuste final en todos los datos de entrenamiento, evaluar una vez en la prueba
pipe.fit(X_train, y_train)
print(classification_report(y_test, pipe.predict(X_test)))Lo que esto demuestra:
- Un conjunto de prueba bloqueado evaluado solo después de que se completa la selección del modelo.
- Validación cruzada en datos de entrenamiento para estimaciones de rendimiento estables.
- Preprocesamiento dentro de un pipeline para que cada pliegue de CV ajuste el escalador de forma independiente.
- División estratificada para mantener el equilibrio de clases en los pliegues.
Inmersión Profunda
Cómo Funciona
- Conjunto de entrenamiento ajusta los parámetros del modelo y las estadísticas de preprocesamiento.
- Pliegues de validación/CV ajustan hiperparámetros y comparan arquitecturas sin tocar el conjunto de prueba.
- Conjunto de prueba proporciona una única estimación imparcial de la generalización después de que se toman todas las decisiones.
- Estratificación muestrea cada clase proporcionalmente en cada división, crucial para datos desequilibrados.
- Divisiones por grupos mantienen filas relacionadas (mismo paciente, usuario o día) en una sola partición.
Estrategias de División
| Estrategia | Usar Cuándo | API de sklearn |
|---|---|---|
| División aleatoria | Filas IID, clases balanceadas | train_test_split |
| División estratificada | Clasificación con clases raras | stratify=y |
| División basada en tiempo | Predicción, deriva temporal | TimeSeriesSplit |
| División por grupos | Múltiples filas por entidad | GroupKFold, GroupShuffleSplit |
| CV anidada | Ajuste de hiperparámetros imparcial | GridSearchCV dentro de CV externa |
Notas de Python
from sklearn.model_selection import TimeSeriesSplit, GroupKFold
# Series temporales: siempre entrenar con el pasado, probar con el futuro
tscv = TimeSeriesSplit(n_splits=5)
# Grupos: el mismo group_id nunca está en entrenamiento ni en prueba
gkf = GroupKFold(n_splits=5)
for train_idx, val_idx in gkf.split(X, y, groups=group_ids):
...Trampas Comunes
- Preprocesamiento antes de la división - ajustar un escalador en todo el conjunto de datos filtra estadísticas de prueba en el entrenamiento. Solución: dividir primero, o envolver transformaciones en un
Pipeline. - Ajuste en el conjunto de prueba - ejecutar búsqueda en cuadrícula y elegir el mejor modelo usando puntuaciones de prueba infla las métricas. Solución: usar CV en datos de entrenamiento; evaluar al ganador una vez en la prueba.
- Ignorar el desequilibrio de clases - las divisiones aleatorias pueden dejar clases raras fuera de los pliegues de entrenamiento. Solución: usar
stratify=yo estrategias de remuestreo. - Barajar series temporales - las divisiones aleatorias mezclan datos futuros en el entrenamiento para problemas temporales. Solución: usar
TimeSeriesSplito divisiones basadas en corte. - Entidades duplicadas entre divisiones - múltiples filas por usuario tanto en entrenamiento como en prueba inflan las puntuaciones. Solución: dividir por
group_id, no por fila. - Conjuntos de prueba pequeños - alta varianza en métricas con pocas muestras de prueba. Solución: aumentar el tamaño de la prueba o usar CV repetida e informar intervalos de confianza.
Alternativas
| Alternativa | Usar Cuándo | No Usar Cuándo |
|---|---|---|
| División única de entrenamiento/prueba | Conjuntos de datos grandes, iteración rápida | Datos pequeños donde la varianza de la división es alta |
| Validación cruzada K-fold | Comparación de modelos, datos limitados | Datos muy grandes donde una división es suficiente |
| Retención + conjunto de validación | Aprendizaje profundo con ejecuciones de entrenamiento largas | Necesitas muchas pruebas de hiperparámetros (usa CV) |
| Bootstrap / CV repetida | Métricas inestables en datos pequeños | El costo de entrenamiento por pliegue es prohibitivo |
Preguntas Frecuentes
¿Qué tan grande debe ser mi conjunto de prueba?
- El 20% es un valor predeterminado común para conjuntos de datos con miles de filas.
- Con menos de 500 filas, prefiere la validación cruzada sobre una gran retención de prueba.
- Ajusta el tamaño de la prueba al recuento mínimo por clase que necesitas para métricas fiables.
¿Cuándo necesito un conjunto de validación separado si ya uso validación cruzada?
- La CV en datos de entrenamiento es suficiente para la mayoría de los ajustes de hiperparámetros de ML clásico.
- Un conjunto de validación dedicado ayuda cuando cada ejecución de entrenamiento es costosa (aprendizaje profundo).
- Nunca uses el conjunto de prueba como conjunto de validación.
¿Qué hace stratify=y?
train_test_split(X, y, stratify=y) # preserva las proporciones de clase en ambas particiones- Cada clase aparece en entrenamiento y prueba en aproximadamente la misma proporción que el conjunto de datos completo.
- Esencial cuando cualquier clase está por debajo del 5% de las filas.
¿Puedo barajar datos de series temporales antes de dividirlos?
- No - barajar filtra información futura en el entrenamiento.
- Usa divisiones cronológicas o
TimeSeriesSplit. - La validación de avance (walk-forward validation) es el análogo en producción.
¿Cómo funcionan las divisiones por grupos?
- Pasa un array
groupscon un ID por fila (user_id, patient_id). GroupKFoldasegura que todas las filas de un grupo caigan en el mismo pliegue.- Evita que el modelo memorice patrones específicos de la entidad que no generalizarán.
¿Qué es la validación cruzada anidada?
- La CV externa estima la generalización; la CV interna ajusta los hiperparámetros.
- Imparcial pero computacionalmente costosa.
- Úsala cuando se requiera una evaluación de calidad de publicación.
¿Debería establecer random_state?
- Sí -
random_state=42(cualquier entero fijo) hace que las divisiones sean reproducibles. - Documenta la semilla en los registros de experimentos.
- Diferentes semillas ayudan al análisis de sensibilidad en conjuntos de datos pequeños.
¿Cómo divido un DataFrame de pandas ya cargado?
train, test = train_test_split(df, test_size=0.2, stratify=df["label"])
X_train, y_train = train["features"], train["label"]- Divide el DataFrame primero, luego extrae X e y.
- Mantiene los índices de fila alineados entre características y etiquetas.
¿Qué es la fuga de datos en el contexto de las divisiones?
- Cualquier información de los datos de prueba que influya en las decisiones de entrenamiento.
- Fuentes comunes: escalado global, selección de características en datos completos, entidades duplicadas.
- Los pipelines y el orden de división adecuado previenen la mayoría de las fugas.
¿Cuándo no es suficiente cross_val_score?
- Cuando necesitas predicciones por pliegue para calibración o análisis de errores.
- Usa
cross_val_predicten su lugar, todavía solo en datos de entrenamiento. - Para métricas finales, ajusta en todo el conjunto de entrenamiento y predice la prueba una vez.
¿Cómo manejo múltiples columnas objetivo?
- Los problemas de salida múltiple necesitan
MultiOutputClassifiero modelos separados por objetivo. - La estratificación funciona en una única columna de etiquetas - agrupa etiquetas combinadas o estratifica el objetivo principal.
¿Puedo reutilizar el conjunto de prueba en diferentes proyectos?
- Sí, si el conjunto de prueba se mantiene verdaderamente reservado y nunca se usa para ningún ajuste.
- Documenta que el conjunto de prueba está bloqueado y compartido entre experimentos.
- Considera un nuevo conjunto de prueba para cambios importantes en la distribución de datos.
Relacionado
- Ingeniería de Características - codificación después de dividir
- Pipelines - preprocesamiento sin fugas dentro de CV
- Evaluación de Modelos - métricas en datos retenidos
- Datos Desequilibrados - divisiones estratificadas y remuestreadas
- Ajuste de Hiperparámetros - búsqueda dentro de pliegues de CV
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.