Tu primer modelo de ML siempre falla: cómo evitar los errores
Los primeros modelos de machine learning son un desastre. Overfitting, datos sucios, métricas confusas. Te muestro cómo entrenar uno que funcione.
NUCBA
Entrenar tu primer modelo de machine learning es como aprender a manejar: vas a chocar varias veces antes de salir a la ruta. La diferencia es que acá no hay instructor al lado diciéndote "frená".
La realidad es que el 80% de los primeros modelos terminan sobreajustados, con métricas engañosas o directamente prediciendo cualquier cosa. No es tu culpa - es que nadie te cuenta los errores típicos antes de que te los encuentres.
Por qué fallan los primeros modelos de ML
Antes de tocar código, entendé los tres problemas más comunes:
Overfitting desde el día uno: Tu modelo memoriza los datos de entrenamiento en lugar de aprender patrones generales. Es como estudiar de memoria las respuestas del examen del año pasado.
Datos sin limpiar: Valores faltantes, outliers extremos, features que no aportan nada. Tu modelo va a aprender ruido en lugar de señal.
Métricas que mienten: Una accuracy del 95% suena genial hasta que descubrís que tu dataset tiene 95% de una sola clase.
Dataset real: precios de casas en Boston
Vamos a usar el dataset de precios de viviendas de Boston. Es perfecto para empezar porque:
- Tiene problemas reales (outliers, correlaciones raras)
- Es pequeño (506 registros)
- La variable target es continua (precio en miles de dólares)
import pandas as pd import numpy as np from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, r2_score import matplotlib.pyplot as plt # Cargamos el dataset boston = load_boston() X = pd.DataFrame(boston.data, columns=boston.feature_names) y = boston.target # Primer vistazo a los datos print(f"Shape del dataset: {X.shape}") print(f"Precio promedio: ${y.mean():.2f}k") print(f"Features: {list(X.columns)}")
Explorá los datos antes que nada
Este es el error número uno: saltar directo al modelo. Necesitás entender qué estás modelando.
# Estadísticas básicas print(X.describe()) # Buscamos valores faltantes print("Valores nulos por columna:") print(X.isnull().sum()) # Distribución del target plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.hist(y, bins=30, alpha=0.7) plt.title('Distribución de precios') plt.xlabel('Precio (miles de USD)') plt.subplot(1, 2, 2) plt.boxplot(y) plt.title('Boxplot de precios') plt.show() # Correlación con el target correlations = X.corrwith(pd.Series(y, name='price')).sort_values(ascending=False) print("\nCorrelaciones más fuertes:") print(correlations.head()) print(correlations.tail())
Lo que vas a encontrar:
- Algunos outliers en precios (casas de +40k cuando el promedio es 22k)
- La feature
RM(promedio de habitaciones) correlaciona fuerte con el precio LSTAT(% población de bajos ingresos) correlaciona negativo
Preparación de datos sin drama
Acá es donde la mayoría se complica con transformaciones innecesarias. Mantené la lógica simple:
# Split básico X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # Escalado - importante para algunos algoritmos scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # Convertimos de vuelta a DataFrame para mantener los nombres X_train_scaled = pd.DataFrame(X_train_scaled, columns=X.columns) X_test_scaled = pd.DataFrame(X_test_scaled, columns=X.columns) print(f"Train set: {X_train_scaled.shape}") print(f"Test set: {X_test_scaled.shape}")
¿Por qué StandardScaler? Features como CHAS (cerca del río) son binarias (0 o 1), mientras que DIS (distancia a centros de empleo) va de 1 a 12. Sin escalado, el modelo va a darle más peso a las features con valores más grandes.
Tu primer modelo que no sea un desastre
Random Forest es tu amigo para empezar. Es robusto, maneja outliers bien y no necesita tanto tuning.
# Modelo básico rf = RandomForestRegressor( n_estimators=100, random_state=42, max_depth=10 # Evitamos overfitting ) # Entrenamiento rf.fit(X_train_scaled, y_train) # Predicciones y_train_pred = rf.predict(X_train_scaled) y_test_pred = rf.predict(X_test_scaled) # Métricas train_mse = mean_squared_error(y_train, y_train_pred) test_mse = mean_squared_error(y_test, y_test_pred) train_r2 = r2_score(y_train, y_train_pred) test_r2 = r2_score(y_test, y_test_pred) print(f"Train MSE: {train_mse:.2f}") print(f"Test MSE: {test_mse:.2f}") print(f"Train R²: {train_r2:.3f}") print(f"Test R²: {test_r2:.3f}")
Cómo saber si tu modelo funciona
Las métricas te van a confundir al principio. Acá está lo que realmente importa:
MSE (Mean Squared Error): Si el MSE de test es mucho mayor que el de train, tenés overfitting. Una diferencia del 20-30% es normal.
R² Score: Qué porcentaje de la varianza explica tu modelo. 0.7+ está bien para un primer modelo.
# Visualización clave: predicho vs real plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.scatter(y_train, y_train_pred, alpha=0.6) plt.plot([y_train.min(), y_train.max()], [y_train.min(), y_train.max()], 'r--') plt.xlabel('Precio real') plt.ylabel('Precio predicho') plt.title('Train Set') plt.subplot(1, 2, 2) plt.scatter(y_test, y_test_pred, alpha=0.6) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--') plt.xlabel('Precio real') plt.ylabel('Precio predicho') plt.title('Test Set') plt.tight_layout() plt.show() # Feature importance importances = pd.DataFrame({ 'feature': X.columns, 'importance': rf.feature_importances_ }).sort_values('importance', ascending=False) print("\nFeatures más importantes:") print(importances.head())
Errores que vas a cometer (y cómo evitarlos)
Error 1: No usar random_state
# MAL rf = RandomForestRegressor(n_estimators=100) # BIEN rf = RandomForestRegressor(n_estimators=100, random_state=42)
Sin random_state, cada run te va a dar resultados diferentes.
Error 2: Evaluar solo en train
# MAL - solo mirás train train_score = rf.score(X_train, y_train) # BIEN - comparás train vs test train_score = rf.score(X_train_scaled, y_train) test_score = rf.score(X_test_scaled, y_test) print(f"Diferencia: {abs(train_score - test_score):.3f}")
Error 3: Hiperparámetros extremos
# MAL - overfitting garantizado rf = RandomForestRegressor(n_estimators=1000, max_depth=None) # BIEN - conservador para empezar rf = RandomForestRegressor(n_estimators=100, max_depth=10)
Próximos pasos sin morir
Cuando tengas este modelo funcionando, podés:
- Probar otros algoritmos: LinearRegression, XGBoost
- Feature engineering: Crear nuevas variables combinando existentes
- Cross-validation: Para estar más seguro de tus métricas
- Hyperparameter tuning: Pero recién después de dominar lo básico
Tu primer modelo no va a ser perfecto. El objetivo es que funcione y que entiendas por qué. Una vez que controlés este flujo básico, podés empezar a optimizar.
Preguntas frecuentes
¿Por qué Random Forest y no regresión lineal? Random Forest maneja automáticamente interacciones entre features y es más resistente a outliers. Para empezar, es más fácil de usar.
¿Cuándo sé que tengo overfitting? Cuando tu R² en train es 0.95+ pero en test baja a 0.70 o menos. También si el MSE de test es más del doble que el de train.
¿Necesito más datos siempre? No siempre. Si tu modelo ya está sobreajustado, más datos pueden ayudar. Pero mejor entendé bien los datos que tenés antes de buscar más.