📘 Lección 5: ¿Funcionó bien? Evalúa tu modelo con métricas importantes

"No midas tu modelo por cuánto acierta... mídelo por cuánto importa lo que acierta."


⏱️ Duración estimada de esta lección: 75-90 minutos


🧭 ¿Por qué es tan importante esta lección?

Porque aquí es donde dejas de confiar y empiezas a verificar.

Entrenaste un modelo.
Hizo predicciones.
¡Pero eso no significa que sea bueno!

Muchos principiantes se emocionan con un "98% de precisión"... y luego descubren que su modelo falla en los casos más importantes.

En esta lección aprenderás:

  • Por qué la precisión no lo es todo (¡especialmente con datos desbalanceados!).
  • Qué es una matriz de confusión y cómo leerla como un profesional.
  • Qué significan precisión, exhaustividad (recall) y F1-score... y cuándo usar cada uno.
  • Cómo interpretar probabilidades, no solo etiquetas.
  • Cómo evitar engañarte a ti mismo con métricas superficiales.

⚠️ Advertencia amistosa: Esta lección te hará cuestionar todo lo que creías saber sobre "buenos modelos". Pero eso es bueno. La humildad es madre de la mejora.


🎯 Objetivos de esta lección

Al finalizar, podrás:

✅ Calcular e interpretar la precisión de tu modelo.
✅ Construir y comprender una matriz de confusión.
✅ Calcular e interpretar precisión, exhaustividad y F1-score.
✅ Saber cuándo usar cada métrica según el problema.
✅ Evaluar no solo predicciones, sino también probabilidades.
✅ Detectar si tu modelo es "tonto" o verdaderamente inteligente.
✅ Sentirte cómodo tomando decisiones basadas en métricas, no en intuición.


🛠️ Herramientas que usarás

  • Scikit-learnaccuracy_score, confusion_matrix, classification_report, precision_recall_curve.
  • Matplotlib / Seaborn → Para visualizar métricas.
  • Pandas → Para manipular resultados.

💡 Asegúrate de tener tu modelo entrenado y tus predicciones (y_test, y_pred) listas desde la Lección 4. Si no, aquí tienes el código rápido para ponerte al día:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB

# Cargar y preparar datos
url = "https://raw.githubusercontent.com/justmarkham/DAT8/master/data/sms.tsv  "
data = pd.read_csv(url, sep='\t', names=['label', 'message'])
data['label_encoded'] = data['label'].map({'ham': 0, 'spam': 1})

X = data['message']
y = data['label_encoded']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

vectorizer = CountVectorizer()
X_train_vec = vectorizer.fit_transform(X_train)
X_test_vec = vectorizer.transform(X_test)

# Entrenar modelo
model = MultinomialNB()
model.fit(X_train_vec, y_train)

# Predecir
y_pred = model.predict(X_test_vec)

📊 Parte 1: La ilusión de la precisión — ¿Es realmente del 98%?

Empecemos con la métrica más popular... y más peligrosa.


🔹 Paso 1: Calcular la precisión

from sklearn.metrics import accuracy_score

acc = accuracy_score(y_test, y_pred)
print(f"Precisión: {acc:.4f} → {acc*100:.2f}%")

📌 Salida típica:

Precisión: 0.9821 → 98.21%

→ ¡Guau! 98% correcto. ¿Significa esto que el modelo es excelente?

¡NO! Y aquí está por qué.


🔍 El problema con datos desbalanceados

Recuerda: en nuestro conjunto de datos, solo el 13.4% son spam. El 86.6% son ham.

Imagina un modelo tonto que siempre predice "ham".
¿Cuál sería su precisión?

Precisión = (Hams verdaderos) / (Total) = 955 / 1115 ≈ 85.65%

→ ¡Un modelo que nunca detecta spam tendría 85.65% de precisión!

Tu modelo tiene 98.21% → es mejor que el tonto... pero ¿cuánto mejor en lo que realmente importa: detectar spam?

📌 Conclusión: La precisión engaña cuando hay desbalance. Necesitas métricas más inteligentes.


🧩 Parte 2: La matriz de confusión — Tu microscopio de errores

Aquí es donde ves exactamente qué aciertos y qué errores está cometiendo tu modelo.


🔹 Paso 2: Construir la matriz de confusión

from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt

cm = confusion_matrix(y_test, y_pred)

# Visualizar
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', 
            xticklabels=['Ham (Pred)', 'Spam (Pred)'], 
            yticklabels=['Ham (Real)', 'Spam (Real)'])
plt.title("Matriz de Confusión - Clasificador de Spam", fontsize=16)
plt.ylabel("Etiqueta Real", fontsize=12)
plt.xlabel("Etiqueta Predicha", fontsize=12)
plt.show()

📌 Salida típica (valores aproximados):

          Predicho
          Ham  Spam
Real Ham   950    5
Real Spam   15  145

🔍 ¿Cómo leer esta matriz?

  • Verdaderos Negativos (TN): 950
    → Mensajes ham que el modelo dijo que eran ham. ✅ ¡Perfecto!

  • Falsos Positivos (FP): 5
    → Mensajes ham que el modelo dijo que eran spam. ❌ ¡Error grave! (Marcar un mensaje importante como spam).

  • Falsos Negativos (FN): 15
    → Mensajes spam que el modelo dijo que eran ham. ❌ ¡Error grave! (Dejar pasar spam).

  • Verdaderos Positivos (TP): 145
    → Mensajes spam que el modelo dijo que eran spam. ✅ ¡Perfecto!

📌 ¡Esto es oro! Ahora sabes dónde falla tu modelo. No es un número abstracto... son errores concretos que puedes mejorar.


🎯 Parte 3: Precisión, Exhaustividad y F1-Score — Las 3 métricas que importan

Ahora, cuantifiquemos esos errores con métricas profesionales.


🔹 Paso 3: Calcular el informe de clasificación

from sklearn.metrics import classification_report

report = classification_report(y_test, y_pred, 
                               target_names=['Ham', 'Spam'], 
                               output_dict=False)
print(report)

📌 Salida típica:

              precision    recall  f1-score   support

         Ham       0.98      0.99      0.99       955
        Spam       0.97      0.91      0.94       160

    accuracy                           0.98      1115
   macro avg       0.98      0.95      0.96      1115
weighted avg       0.98      0.98      0.98      1115

🔍 ¿Qué significan estas métricas?

1. Precisión

"De todos los que dije que eran spam, ¿cuántos realmente lo eran?"

Precisión (Spam) = TP / (TP + FP) = 145 / (145 + 5) = 145/150 ≈ 0.97

97% de precisión en spam: cuando el modelo dice "spam", tiene razón el 97% de las veces. ¡Excelente!

📌 ¿Cuándo importa la precisión?
Cuando el costo de un falso positivo es alto.
Ejemplo: Marcar un correo importante como spam → el usuario podría perder información crítica.


2. Exhaustividad (Recall, Tasa de Verdaderos Positivos)

"De todo el spam que existía, ¿cuánto detecté?"

Recall (Spam) = TP / (TP + FN) = 145 / (145 + 15) = 145/160 ≈ 0.91

91% de exhaustividad en spam: detectó el 91% de todo el spam. ¡Muy bueno!

📌 ¿Cuándo importa la exhaustividad?
Cuando el costo de un falso negativo es alto.
Ejemplo: Dejar pasar spam fraudulento → el usuario podría hacer clic y perder dinero.


3. F1-Score

"Promedio armónico entre precisión y exhaustividad. Ideal cuando quieres equilibrio."

F1 = 2 * (Precisión * Exhaustividad) / (Precisión + Exhaustividad)
   = 2 * (0.97 * 0.91) / (0.97 + 0.91) ≈ 0.94

94% de F1-score: buen equilibrio entre no molestar al usuario (precisión) y protegerlo (exhaustividad).

📌 ¿Cuándo usar F1?
Cuando no sabes qué es más importante, o cuando quieres una métrica única que resuma el rendimiento en clases desbalanceadas.


📈 Parte 4: Más allá de las etiquetas — Evaluar probabilidades

Tu modelo no solo predice "spam" o "ham". También te da probabilidades.

Esto es poderoso. Porque a veces, no quieres una decisión binaria... quieres saber cuán seguro está el modelo.


🔹 Paso 4: Obtener probabilidades

# Obtener probabilidades para cada clase
y_proba = model.predict_proba(X_test_vec)

# Para spam (clase 1), es la segunda columna
y_proba_spam = y_proba[:, 1]

# Ver las primeras 10 probabilidades
for i in range(10):
    print(f"Mensaje {i+1}: Probabilidad de spam = {y_proba_spam[i]:.4f} → Predicción: {'Spam' if y_pred[i] == 1 else 'Ham'}")

📌 Salida típica:

Mensaje 1: Probabilidad de spam = 0.0002 → Predicción: Ham
Mensaje 2: Probabilidad de spam = 0.9998 → Predicción: Spam
Mensaje 3: Probabilidad de spam = 0.0015 → Predicción: Ham
...

→ ¡Increíble! El modelo no solo dice "spam", te dice "Estoy 99.98% seguro".


🔹 Paso 5: Curva Precisión-Exhaustividad (opcional, pero reveladora)

¿Qué pasa si cambias el umbral de decisión?

Por defecto, si probabilidad > 0.5 → spam.
¿Pero qué pasa si usas 0.7? ¿O 0.3?

from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt

precision, recall, thresholds = precision_recall_curve(y_test, y_proba_spam)

plt.figure(figsize=(10, 6))
plt.plot(recall, precision, marker='.', label='Naive Bayes')
plt.xlabel('Exhaustividad')
plt.ylabel('Precisión')
plt.title('Curva Precisión-Exhaustividad')
plt.legend()
plt.grid(True)
plt.show()

📌 ¿Qué ves?
Una curva que muestra el equilibrio entre precisión y exhaustividad para distintos umbrales.
Ideal para elegir un umbral que se ajuste a tus necesidades (más precisión o más exhaustividad).


❌ Errores comunes en esta lección (¡Evítalos!)

  1. Confiar solo en la precisión → Te pierdes errores críticos.
  2. Ignorar la matriz de confusión → No ves dónde falla el modelo.
  3. No entender la diferencia entre precisión y exhaustividad → Tomas decisiones equivocadas.
  4. Olvidar que las métricas dependen del problema → En medicina, la exhaustividad es vital; en publicidad, la precisión.
  5. No usar probabilidades → Pierdes información valiosa sobre la confianza del modelo.

✅ Lista de verificación para esta lección — ¿Qué deberías saber hacer ahora?

☐ Calcular e interpretar la precisión.
☐ Construir y leer una matriz de confusión.
☐ Calcular e interpretar precisión, exhaustividad y F1-score.
☐ Saber cuándo priorizar precisión vs exhaustividad según el problema.
☐ Obtener y analizar probabilidades de predicción.
☐ Entender que un modelo "bueno" depende del contexto, no solo de un número.
☐ Sentirte cómodo evaluando modelos con rigor profesional.


🎯 Frase para recordar:

"No midas tu modelo por cuánto acierta... mídelo por cuánto importa lo que acierta."


← Anterior: Lección 4: Entrena tu primer modelo | Siguiente: Proyecto Final →

Course Info

Course: AI-course0

Language: ES

Lesson: 5 evaluate model