W3docs

Machine Learning con Regresión Logística en Python

Aprende cómo funciona la regresión logística, cómo entrenar clasificadores binarios y multiclase en Python con scikit-learn, evaluarlos y ajustar la regularización.

La regresión logística es un algoritmo de clasificación supervisada que estima la probabilidad de que una muestra pertenezca a una clase determinada. A pesar de la palabra "regresión" en su nombre, se utiliza para tareas de clasificación — predecir una etiqueta categórica como spam/no-spam, enfermedad/sano o clic/sin-clic.

Este capítulo cubre:

  • Cómo funciona la regresión logística (la función sigmoide y el log-odds)
  • Construcción de un clasificador binario en Python con scikit-learn
  • Evaluación de un clasificador más allá de la precisión bruta
  • Manejo de problemas multiclase
  • Regularización y cuándo ajustarla
  • Escalado de características y por qué importa
  • Cuándo usar regresión logística frente a otros clasificadores

Cómo Funciona la Regresión Logística

De la Regresión Lineal a las Probabilidades

La regresión lineal predice un valor continuo. Si intentaras usarla para clasificación, las predicciones podrían caer fuera del rango [0, 1], lo que las haría imposibles de interpretar como probabilidades. La regresión logística resuelve esto pasando la combinación lineal por la función sigmoide:

σ(z) = 1 / (1 + e^(-z))

Donde z = w₀ + w₁x₁ + w₂x₂ + … + wₙxₙ es la suma ponderada de las características de entrada. La sigmoide comprime cualquier número real al rango (0, 1), proporcionando una estimación de probabilidad válida.

Frontera de Decisión

El modelo predice la clase 1 cuando la probabilidad supera un umbral (0.5 por defecto), y la clase 0 en caso contrario:

ŷ = 1  if σ(z) ≥ 0.5
ŷ = 0  if σ(z) < 0.5

El umbral σ(z) = 0.5 corresponde a z = 0, lo que define la frontera de decisión — un hiperplano en el espacio de características que separa las dos clases.

Log-Odds (Logit)

Tomar el logaritmo del cociente de probabilidades muestra por qué el modelo es lineal en los parámetros:

log(p / (1 - p)) = w₀ + w₁x₁ + … + wₙxₙ

Cada coeficiente wᵢ representa el cambio en el log-odds por un aumento de una unidad en la característica xᵢ, manteniendo constantes las demás características. Esto hace que la regresión logística sea interpretable.

Cómo Se Aprenden los Parámetros

A diferencia de la regresión lineal, no existe una solución de forma cerrada. El modelo minimiza la función de coste log-loss (entropía cruzada) mediante un optimizador iterativo (por defecto lbfgs en scikit-learn):

Loss = -1/m Σ [ yᵢ log(p̂ᵢ) + (1 - yᵢ) log(1 - p̂ᵢ) ]

Un log-loss menor significa que las probabilidades predichas están mejor calibradas con las etiquetas verdaderas.

Clasificación Binaria en Python

El siguiente ejemplo utiliza el conjunto de datos Breast Cancer Wisconsin — 569 muestras, 30 características numéricas, objetivo binario (maligno = 1, benigno = 0). Se incluye con scikit-learn, por lo que no se necesitan archivos externos.

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report

# 1. Load data
data = load_breast_cancer()
X, y = data.data, data.target           # X: (569, 30)  y: 0=malignant, 1=benign

# 2. Split into train (80 %) and test (20 %)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 3. Scale features — logistic regression converges faster and more reliably
#    when features are on a similar scale
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc  = scaler.transform(X_test)

# 4. Train
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)

# 5. Evaluate
y_pred = clf.predict(X_test_sc)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(classification_report(y_test, y_pred, target_names=data.target_names))

Salida esperada:

Accuracy: 0.974
              precision    recall  f1-score   support

   malignant       0.98      0.95      0.96        43
      benign       0.97      0.99      0.98        71

    accuracy                           0.97       114
   macro avg       0.97      0.97      0.97       114
weighted avg       0.97      0.97      0.97       114

El modelo alcanza ~97 % de precisión en datos no vistos. Ten en cuenta que LogisticRegression de scikit-learn añade regularización L2 por defecto (C=1.0), lo que favorece la generalización.

Por Qué Importa el Escalado de Características

La regresión logística utiliza optimización basada en gradiente. Sin escalado, una característica con valores grandes (p. ej., radio medio ~14) domina las actualizaciones del gradiente, ralentizando la convergencia o provocando fallos en el solver. StandardScaler transforma cada característica a media cero y varianza unitaria.

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Without scaling — needs more iterations, may warn about convergence
clf_raw = LogisticRegression(max_iter=200, random_state=42)
clf_raw.fit(X_train, y_train)
print(f"Unscaled accuracy : {clf_raw.score(X_test, y_test):.3f}")

# With scaling
scaler = StandardScaler()
clf_sc = LogisticRegression(max_iter=200, random_state=42)
clf_sc.fit(scaler.fit_transform(X_train), y_train)
print(f"Scaled accuracy   : {clf_sc.score(scaler.transform(X_test), y_test):.3f}")

Ajusta siempre el escalador únicamente en el conjunto de entrenamiento y luego utiliza ese mismo escalador ajustado para transformar tanto el conjunto de entrenamiento como el de prueba — esto previene la fuga de datos.

Evaluación de un Clasificador

La precisión por sí sola puede ser engañosa cuando las clases están desbalanceadas. Usa una matriz de confusión y las métricas derivadas de ella.

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc  = scaler.transform(X_test)

clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)
y_pred = clf.predict(X_test_sc)

cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(cm, display_labels=data.target_names)
disp.plot(cmap="Blues")
plt.title("Logistic Regression — Breast Cancer")
plt.tight_layout()
plt.savefig("confusion_matrix.png", dpi=150)
plt.show()

Métricas clave derivadas de la matriz de confusión:

MétricaFórmulaSignificado
PrecisiónTP / (TP + FP)De todos los positivos predichos, cuántos son verdaderamente positivos
Recall (Sensibilidad)TP / (TP + FN)De todos los positivos reales, cuántos detectó el modelo
F1-score2 × (P × R) / (P + R)Media armónica de precisión y recall
EspecificidadTN / (TN + FP)De todos los negativos reales, cuántos rechazó correctamente el modelo

En diagnóstico médico, el recall (sensibilidad) suele ser más importante que la precisión — un caso maligno no detectado es peor que una falsa alarma.

Puntuaciones de Probabilidad y la Curva AUC-ROC

En lugar de una predicción discreta, puedes obtener la probabilidad de la clase positiva con predict_proba():

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score

data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(scaler.fit_transform(X_train), y_train)

# Probability of the positive class (benign = 1)
y_proba = clf.predict_proba(scaler.transform(X_test))[:, 1]
print(f"AUC-ROC: {roc_auc_score(y_test, y_proba):.3f}")

Salida esperada:

AUC-ROC: 0.997

Un AUC cercano a 1.0 significa que el modelo clasifica las muestras positivas por encima de las negativas casi a la perfección. Consulta el capítulo Curva AUC-ROC para ver cómo trazar e interpretar la curva completa.

Clasificación Multiclase

Cuando el objetivo tiene más de dos clases, scikit-learn extiende la regresión logística automáticamente. A partir de scikit-learn 1.5, el solver lbfgs siempre utiliza el enfoque multinomial (softmax), que entrena un único modelo con una capa de salida softmax y minimiza la entropía cruzada sobre todas las clases de forma conjunta. Esto generalmente es más preciso que la antigua estrategia One-vs-Rest (OvR), que entrenaba un clasificador binario separado por clase.

El conjunto de datos Iris tiene tres especies de flores — un ejemplo natural de problema multiclase:

from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score

data = load_iris()
X, y = data.data, data.target

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc  = scaler.transform(X_test)

# From scikit-learn 1.5+, multinomial softmax is the default for lbfgs
clf = LogisticRegression(solver="lbfgs", max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)

y_pred = clf.predict(X_test_sc)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")

# Class probabilities for the first three test samples
print("\nClass probabilities (first 3 samples):")
for proba in clf.predict_proba(X_test_sc)[:3]:
    print([f"{p:.3f}" for p in proba])

Salida esperada:

Accuracy: 1.000

Class probabilities (first 3 samples):
['0.011', '0.876', '0.113']
['0.964', '0.036', '0.000']
['0.000', '0.003', '0.997']

Regularización

La regularización penaliza los coeficientes grandes para evitar el sobreajuste. scikit-learn ofrece dos tipos mediante el parámetro penalty:

ParámetroTipoEfecto
penalty='l2' (por defecto)RidgeReduce todos los coeficientes hacia cero; conserva todas las características
penalty='l1'LassoLleva algunos coeficientes exactamente a cero; selección implícita de características
penalty='elasticnet'MezclaCombina L1 y L2; requiere solver='saga'
penalty=NoneNingunaSin regularización; usar solo si los datos son grandes y limpios

La intensidad de la regularización se controla con C (el inverso de la fuerza de regularización — un C más pequeño significa una regularización más fuerte):

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

data = load_breast_cancer()
X, y = data.data, data.target

results = {}
for C in [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]:
    pipe = Pipeline([
        ("scaler", StandardScaler()),
        ("clf", LogisticRegression(C=C, max_iter=1000, random_state=42)),
    ])
    scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
    results[C] = scores.mean()
    print(f"C={C:7.3f}  CV accuracy: {scores.mean():.4f} ± {scores.std():.4f}")

Esto utiliza validación cruzada para encontrar el valor de C que mejor generaliza. Para una búsqueda sistemática sobre múltiples hiperparámetros, consulta Grid Search.

Uso de un Pipeline

Un Pipeline encadena el preprocesamiento y el modelo en un único objeto. Esto previene fugas de datos accidentales y simplifica la validación cruzada y el despliegue:

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score

data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", LogisticRegression(C=1.0, max_iter=1000, random_state=42)),
])

pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")

# Predict probabilities on a new sample (raw, unscaled)
new_sample = X_test[:1]  # first test sample
print(f"Predicted class   : {pipe.predict(new_sample)[0]}")
print(f"Class probability : {pipe.predict_proba(new_sample)[0]}")

Salida esperada:

Accuracy: 0.974
Predicted class   : 1
Class probability : [0.11359025 0.88640975]

El pipeline gestiona el escalado internamente — llamas a predict() con los valores de características sin procesar.

Inspección de Coeficientes

Los coeficientes entrenados revelan qué características empujan la predicción hacia cada clase. Los valores absolutos mayores indican una influencia más fuerte:

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import numpy as np

data = load_breast_cancer()
X, y = data.data, data.target

scaler = StandardScaler()
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(scaler.fit_transform(X), y)

# Sort by absolute coefficient value
coefs = clf.coef_[0]          # shape (n_features,) for binary classification
sorted_idx = np.argsort(np.abs(coefs))[::-1]

print(f"{'Feature':<35} {'Coefficient':>12}")
print("-" * 48)
for i in sorted_idx[:5]:
    print(f"{data.feature_names[i]:<35} {coefs[i]:>12.4f}")

Salida esperada (top 5 características por peso absoluto):

Feature                              Coefficient
------------------------------------------------
worst texture                            -1.3206
radius error                             -1.2893
worst radius                             -1.0266
area error                               -0.9989
worst area                               -0.9947

Los coeficientes negativos (después del escalado) empujan hacia la clase 0 (maligno); los coeficientes positivos empujan hacia la clase 1 (benigno).

Ventajas y Limitaciones

Cuándo usar la regresión logística

  • Necesitas estimaciones de probabilidad, no solo etiquetas de clase.
  • La relación entre las características y el log-odds es aproximadamente lineal.
  • Necesitas un modelo interpretable — los coeficientes tienen significado.
  • Como línea base rápida antes de probar modelos más complejos como Árboles de Decisión o métodos de conjunto.
  • Los conjuntos de datos son grandes (la regresión logística escala bien con muchas muestras).

Limitaciones

LimitaciónMitigación
Asume una frontera de decisión linealUsa características polinómicas o cambia a Árbol de Decisión / K-Vecinos Más Cercanos
Sensible al escalado de característicasAplica siempre StandardScaler o MinMaxScaler
Problemas con características altamente correlacionadasElimínalas o regulariza con L1 (penalty='l1')
No es adecuada para interacciones de características muy complejasUsa métodos de conjunto o redes neuronales

Regresión Logística vs. Clasificadores Relacionados

AlgoritmoFrontera de decisiónEscalado necesarioSalida probabilística
Regresión LogísticaLinealSí (calibrada)
Árbol de DecisiónNo lineal (alineada con ejes)NoSí (menos calibrada)
K-Vecinos Más CercanosNo lineal (basada en instancias)
Regresión LinealLineal (salida continua)No

Conclusiones Clave

  • La regresión logística estima una probabilidad mediante la función sigmoide; la clase se asigna aplicando un umbral a esa probabilidad.
  • Escala siempre las características con StandardScaler antes de entrenar — acelera la convergencia y mejora la precisión.
  • Usa un Pipeline para agrupar el escalado y el modelo; evita la fuga de datos y simplifica el despliegue.
  • Evalúa con precisión, recall, F1 y AUC-ROC en lugar de solo la exactitud, especialmente con datos desbalanceados. Consulta los capítulos Matriz de Confusión y Curva AUC-ROC.
  • Controla el sobreajuste con el parámetro C (menor = regularización más fuerte); usa validación cruzada o grid search para ajustarlo.
  • Para problemas multiclase, usa solver="lbfgs" (el valor por defecto); scikit-learn 1.5+ siempre usa softmax (multinomial), que maneja bien las clases superpuestas.
Was this page helpful?