Regresión Logística con Scikit-learn: Tu Primer Clasificador Explicado Paso a Paso
Contexto del Problema: ¿Qué es la Clasificación y Por Qué Importa?
En el mundo del Machine Learning, a menudo nos enfrentamos a problemas donde el objetivo no es predecir un valor numérico continuo (como el precio de una casa), sino asignar una categoría o 'clase' a una observación. Esto es lo que llamamos clasificación. Imagina que quieres construir un sistema que determine si un correo electrónico es spam o no spam, si una transacción financiera es fraudulenta o legítima, o si un paciente tiene una enfermedad específica basándose en sus síntomas. Todos estos son ejemplos de problemas de clasificación.
La clasificación es una de las tareas más fundamentales y extendidas en el aprendizaje automático. Es la base de muchas aplicaciones que usamos a diario, desde filtros de spam hasta sistemas de recomendación y diagnósticos médicos. Para los desarrolladores que se inician en IA/ML, entender los algoritmos de clasificación es un paso crucial.
Dentro de los algoritmos de clasificación, la Regresión Logística es un excelente punto de partida. A pesar de su nombre, que puede sonar a 'regresión' (predicción de valores continuos), es un modelo potente y ampliamente utilizado para problemas de clasificación, especialmente la clasificación binaria (cuando solo hay dos clases posibles). Su simplicidad y la interpretabilidad de sus resultados la hacen ideal para empezar.
Conceptos Clave: Desglosando la Regresión Logística
Antes de sumergirnos en el código, es vital entender los pilares de la Regresión Logística:
- Regresión Logística: Más que una Regresión: Aunque comparte el término 'regresión' con la regresión lineal, su propósito es la clasificación. No predice un valor directamente, sino la probabilidad de que una observación pertenezca a una clase específica. [3, 4]
- Función Sigmoide (o Logística): El corazón de la Regresión Logística. Esta función matemática toma cualquier valor real y lo 'aplasta' en un rango entre 0 y 1. Esto es perfecto para representar probabilidades. Si la salida de la función sigmoide es 0.8, significa que hay un 80% de probabilidad de que la observación pertenezca a la clase positiva. [2, 3, 4]
- Clasificación Binaria: Es el tipo de problema más común que aborda la Regresión Logística, donde la variable objetivo solo puede tomar dos valores (ej., 0 o 1, 'Sí' o 'No', 'Spam' o 'No Spam'). [4, 9]
- Umbral de Decisión: Una vez que el modelo calcula una probabilidad (entre 0 y 1), necesitamos un umbral para decidir a qué clase asignar la observación. Comúnmente, este umbral es 0.5. Si la probabilidad es mayor o igual a 0.5, se asigna a la clase positiva (1); de lo contrario, a la clase negativa (0).
Glosario Rápido
- Clasificación: Tarea de Machine Learning que asigna una categoría o etiqueta a una observación.
- Regresión Logística: Algoritmo de clasificación que estima la probabilidad de que una observación pertenezca a una clase.
- Función Sigmoide: Función matemática que transforma cualquier número real en un valor entre 0 y 1, ideal para probabilidades.
- Umbral de Decisión: Valor límite (comúnmente 0.5) usado para convertir una probabilidad en una decisión de clase.
- Scikit-learn: Librería de Python fundamental para Machine Learning, que proporciona implementaciones eficientes de algoritmos.
Implementación Paso a Paso con Scikit-learn
Vamos a construir nuestro primer clasificador de Regresión Logística usando Python y la librería Scikit-learn. Usaremos un dataset sintético para mantener el enfoque en el algoritmo.
1. Preparación del Entorno
Primero, asegúrate de tener las librerías necesarias instaladas. Si no las tienes, puedes instalarlas con pip:
pip install scikit-learn pandas numpy
2. Carga y Preparación de Datos
Para este tutorial, usaremos el dataset load_breast_cancer de Scikit-learn, que es un conjunto de datos real para clasificación binaria (diagnóstico de cáncer: maligno o benigno). [5]
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# Cargar el dataset
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target)
print("Primeras 5 filas de las características (X):")
print(X.head())
print("\nPrimeras 5 etiquetas (y):")
print(y.head())
print(f"\nForma de X: {X.shape}")
print(f"Forma de y: {y.shape}")
print(f"Nombres de las clases: {data.target_names}")
Explicación del código:
- Importamos
pandaspara manejar los datos en un DataFrame,load_breast_cancerpara el dataset,train_test_splitpara dividir los datos yStandardScalerpara escalar. - Cargamos el dataset y lo separamos en
X(características) yy(etiquetas objetivo). - Imprimimos las primeras filas y las formas para verificar que los datos se cargaron correctamente. Las clases son 'malignant' (0) y 'benign' (1).
3. División de Datos y Escalado
Es crucial dividir nuestros datos en conjuntos de entrenamiento y prueba para evaluar el rendimiento del modelo en datos no vistos. Además, la Regresión Logística (especialmente con regularización) se beneficia del escalado de características.
# Dividir los datos en conjuntos de entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"\nForma de X_train: {X_train.shape}")
print(f"Forma de X_test: {X_test.shape}")
# Escalar las características
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print("\nPrimeras 5 filas de X_train_scaled (escaladas):")
print(pd.DataFrame(X_train_scaled, columns=X.columns).head())
Explicación del código:
train_test_splitdivide los datos.test_size=0.2significa que el 20% de los datos se usarán para prueba.random_state=42asegura que la división sea la misma cada vez que ejecutes el código, lo que es vital para la reproducibilidad.StandardScalertransforma los datos para que tengan una media de 0 y una desviación estándar de 1. Esto ayuda a que el algoritmo converja más rápido y mejore su rendimiento. [1]- Es importante usar
fit_transformen el conjunto de entrenamiento y solotransformen el conjunto de prueba para evitar la fuga de datos (data leakage).
4. Entrenamiento del Modelo de Regresión Logística
Ahora, inicializamos y entrenamos nuestro modelo de Regresión Logística.
from sklearn.linear_model import LogisticRegression
# Inicializar el modelo de Regresión Logística
# max_iter aumenta el número de iteraciones para asegurar la convergencia
model = LogisticRegression(random_state=42, max_iter=1000)
# Entrenar el modelo con los datos escalados de entrenamiento
model.fit(X_train_scaled, y_train)
print("\nModelo de Regresión Logística entrenado exitosamente.")
print(f"Coeficientes del modelo: {model.coef_}")
print(f"Intercepto del modelo: {model.intercept_}")
Explicación del código:
- Importamos
LogisticRegression. - Creamos una instancia del modelo.
random_statepara reproducibilidad ymax_iterpara asegurar que el algoritmo tenga suficientes iteraciones para encontrar la mejor solución. - Usamos el método
.fit()para entrenar el modelo con nuestras características escaladas (X_train_scaled) y las etiquetas correspondientes (y_train). - Los coeficientes y el intercepto son los parámetros que el modelo ha aprendido para hacer sus predicciones.
5. Realizar Predicciones y Evaluar el Modelo
Una vez entrenado, podemos usar el modelo para hacer predicciones en el conjunto de prueba y evaluar su rendimiento.
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# Realizar predicciones en el conjunto de prueba escalado
y_pred = model.predict(X_test_scaled)
y_pred_proba = model.predict_proba(X_test_scaled)
print("\nPrimeras 10 predicciones (clase):")
print(y_pred[:10])
print("\nPrimeras 10 probabilidades de predicción (clase 0, clase 1):")
print(y_pred_proba[:10])
# Evaluar el rendimiento del modelo
accuracy = accuracy_score(y_test, y_pred)
conf_matrix = confusion_matrix(y_test, y_pred)
class_report = classification_report(y_test, y_pred)
print(f"\nPrecisión (Accuracy): {accuracy:.4f}")
print("\nMatriz de Confusión:\n", conf_matrix)
print("\nReporte de Clasificación:\n", class_report)
Explicación del código:
.predict()devuelve la clase predicha (0 o 1) para cada observación..predict_proba()devuelve las probabilidades de que cada observación pertenezca a cada clase. La suma de las probabilidades para cada fila es 1.- Métricas de Evaluación:
accuracy_score: La proporción de predicciones correctas. Es una métrica sencilla pero puede ser engañosa en datasets desbalanceados. [4]confusion_matrix: Una tabla que muestra el número de predicciones correctas e incorrectas para cada clase. Es fundamental para entender dónde falla el modelo.classification_report: Proporciona métricas más detalladas como Precisión (Precision), Exhaustividad (Recall) y Puntuación F1 (F1-Score) para cada clase, además del soporte (número de instancias de cada clase). Estas métricas son cruciales para una evaluación completa, especialmente en problemas con clases desbalanceadas.
Mini Proyecto / Aplicación Sencilla: Predicción de Diagnóstico
Vamos a simular una pequeña aplicación donde, dados nuevos datos de un paciente, nuestro modelo predice si el diagnóstico es maligno o benigno.
import numpy as np
# Simular nuevos datos de un paciente (deben tener la misma cantidad de características que los datos de entrenamiento)
# Estos valores son de ejemplo y deben ser realistas para el dataset de cáncer de mama
# (30 características)
new_patient_data = np.array([
[17.99, 10.38, 122.8, 1001.0, 0.1184, 0.2776, 0.3001, 0.1471, 0.2419, 0.07871,
1.095, 0.3568, 7.673, 103.4, 0.006399, 0.04904, 0.05373, 0.01587, 0.03003, 0.006193,
25.38, 17.33, 184.6, 2019.0, 0.1622, 0.6656, 0.7119, 0.2654, 0.4601, 0.1189]
])
# Es crucial escalar los nuevos datos usando el mismo scaler que se usó para entrenar el modelo
new_patient_data_scaled = scaler.transform(new_patient_data)
# Realizar la predicción
predicted_class = model.predict(new_patient_data_scaled)
predicted_proba = model.predict_proba(new_patient_data_scaled)
# Interpretar el resultado
class_names = data.target_names # 'malignant', 'benign'
print(f"\nDatos del nuevo paciente (escalados): {new_patient_data_scaled[0][:5]}...")
print(f"Clase predicha: {class_names[predicted_class[0]]}")
print(f"Probabilidad de ser maligno (clase 0): {predicted_proba[0][0]:.4f}")
print(f"Probabilidad de ser benigno (clase 1): {predicted_proba[0][1]:.4f}")
# Ejemplo de un caso benigno (valores de ejemplo)
new_patient_data_benign = np.array([
[13.54, 14.36, 87.46, 566.3, 0.09779, 0.08129, 0.06664, 0.04781, 0.1885, 0.05766,
0.2699, 0.7886, 2.058, 23.56, 0.008462, 0.0146, 0.02387, 0.01315, 0.0198, 0.0023,
15.11, 19.54, 96.87, 688.2, 0.1313, 0.181, 0.2012, 0.1065, 0.2597, 0.06599]
])
new_patient_data_benign_scaled = scaler.transform(new_patient_data_benign)
predicted_class_benign = model.predict(new_patient_data_benign_scaled)
predicted_proba_benign = model.predict_proba(new_patient_data_benign_scaled)
print(f"\nClase predicha para caso benigno: {class_names[predicted_class_benign[0]]}")
print(f"Probabilidad de ser maligno (clase 0): {predicted_proba_benign[0][0]:.4f}")
print(f"Probabilidad de ser benigno (clase 1): {predicted_proba_benign[0][1]:.4f}")
Explicación del código:
- Creamos un array NumPy con los datos de un 'nuevo paciente'. Es crucial que este array tenga la misma cantidad de características (columnas) que los datos con los que se entrenó el modelo.
- ¡Importante! Escalamos los nuevos datos usando la misma instancia de
StandardScalerque usamos para entrenar. Esto asegura que la transformación sea consistente. - Realizamos la predicción de la clase y las probabilidades.
- Interpretamos el resultado usando los nombres de las clases originales del dataset.
Errores Comunes y Depuración para Desarrolladores Junior
Al trabajar con Regresión Logística y Scikit-learn, es común encontrarse con algunos problemas. Aquí te presento los más frecuentes y cómo abordarlos:
-
No Escalar los Datos Numéricos: La Regresión Logística, especialmente cuando se usa con regularización (que es por defecto en Scikit-learn), es sensible a la escala de las características. Si una característica tiene valores mucho mayores que otra, puede dominar el proceso de optimización. [1]
Solución: Siempre escala tus características numéricas usando
StandardScaleroMinMaxScalerantes de entrenar el modelo. Recuerda aplicar el mismo escalador a tus datos de entrenamiento y prueba, y a cualquier dato nuevo que quieras predecir. -
Ignorar el Desbalance de Clases: Si una clase tiene muchas más instancias que la otra (ej., 95% 'No Spam' y 5% 'Spam'), un modelo que siempre predice 'No Spam' tendrá una precisión del 95%, pero será inútil. La precisión (accuracy) por sí sola puede ser engañosa. [4]
Solución: Utiliza métricas como Precisión, Exhaustividad (Recall) y F1-Score (disponibles en
classification_report) para evaluar el rendimiento. Para problemas muy desbalanceados, considera técnicas como el sobremuestreo (oversampling) de la clase minoritaria (ej., con SMOTE) o el submuestreo (undersampling) de la clase mayoritaria. -
Overfitting (Sobreajuste) o Underfitting (Subajuste): Si tu modelo funciona muy bien en los datos de entrenamiento pero mal en los de prueba, está sobreajustado. Si funciona mal en ambos, está subajustado.
Solución: Para el sobreajuste, puedes probar con menos características, más datos de entrenamiento, o ajustar los parámetros de regularización del modelo (el parámetro
CenLogisticRegression: valores más pequeños aumentan la regularización). Para el subajuste, asegúrate de que tus características sean relevantes y considera modelos más complejos si la Regresión Logística es demasiado simple para el problema. -
Problemas de Convergencia: A veces, el algoritmo de optimización no logra encontrar una solución óptima en el número predeterminado de iteraciones, lo que resulta en una advertencia (warning).
Solución: Aumenta el parámetro
max_iteren la inicialización deLogisticRegression(como hicimos en el ejemplo) o prueba con un optimizador diferente (el parámetrosolver).
Aprendizaje Futuro: ¿Qué Estudiar Después?
¡Felicidades! Has construido y evaluado tu primer clasificador de Regresión Logística. Este es un paso fundamental en tu camino como desarrollador de IA/ML. Para seguir avanzando, te recomiendo explorar los siguientes temas:
- Otros Algoritmos de Clasificación: Sumérgete en modelos como Máquinas de Soporte Vectorial (SVM), Árboles de Decisión, Random Forests y Gradient Boosting. Cada uno tiene sus fortalezas y debilidades.
- Validación Cruzada (Cross-Validation): Una técnica más robusta para evaluar el rendimiento del modelo y obtener una estimación más fiable de su capacidad de generalización.
- Ajuste de Hiperparámetros: Aprende a optimizar los parámetros de tu modelo (como
Cen Regresión Logística) usando técnicas comoGridSearchCVoRandomizedSearchCVde Scikit-learn. - Manejo de Datos Categóricos: En muchos datasets, las características no son solo numéricas, sino también categóricas (ej., 'color', 'ciudad'). Aprende técnicas como One-Hot Encoding para prepararlas para los modelos.
- Pipelines de Scikit-learn: Organiza tus pasos de preprocesamiento y modelado en un flujo de trabajo coherente y reproducible.
La documentación oficial de Scikit-learn es un recurso invaluable para profundizar en cada uno de estos temas. ¡Sigue experimentando y construyendo!