"No midas tu modelo por cuánto acierta... mídelo por cuánto importa lo que acierta."
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:
⚠️ 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.
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.
accuracy_score, confusion_matrix, classification_report, precision_recall_curve.💡 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)
Empecemos con la métrica más popular... y más peligrosa.
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é.
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.
Aquí es donde ves exactamente qué aciertos y qué errores está cometiendo tu modelo.
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
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.
Ahora, cuantifiquemos esos errores con métricas profesionales.
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
"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.
"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.
"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.
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.
# 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".
¿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).
☐ 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.
"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: AI-course0
Language: ES
Lesson: 5 evaluate model