"Un modelo sin evaluación es como un examen sin calificación: no sabes si aprobaste… o si solo tuviste suerte."
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 accuracy"… 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 "modelos buenos". Pero eso es bueno. La humildad es la madre de la mejora.
Al finalizar, serás capaz de:
✅ Calcular e interpretar la accuracy de tu modelo.
✅ Construir y entender una matriz de confusión.
✅ Calcular e interpretar precisión, recall y F1-score.
✅ Saber cuándo usar cada métrica según el problema.
✅ Evaluar no solo las predicciones, sino las probabilidades.
✅ Detectar si tu modelo es "tonto" o verdaderamente inteligente.
✅ Sentirte cómodo/a 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 de la Lección 4. Si no, aquí está 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)
Vamos a empezar 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"Accuracy: {acc:.4f} → {acc*100:.2f}%")
📌 Salida típica:
Accuracy: 0.9821 → 98.21%
→ ¡Guau! 98% de aciertos. ¿Significa que el modelo es excelente?
¡NO! Y aquí está el porqué.
Recuerda: en nuestro dataset, solo el 13.4% son spam. El 86.6% son ham.
Imagina un modelo tonto que siempre predice "ham".
¿Cuál sería su accuracy?
Accuracy = (Verdaderos Ham) / (Total) = 955 / 1115 ≈ 85.65%
→ ¡Un modelo que nunca detecta spam tendría 85.65% de accuracy!
Tu modelo tiene 98.21% → es mejor que el tonto… pero ¿cuánto mejor en lo que realmente importa: detectar spam?
📌 Conclusión: La accuracy 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, vamos a cuantificar 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?"
Precision (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 email importante como spam → el usuario podría perder información crítica.
"De todos los spam que existían, ¿cuántos detecté?"
Recall (Spam) = TP / (TP + FN) = 145 / (145 + 15) = 145/160 ≈ 0.91
→ 91% de recall en spam: detectó el 91% de todos los spam. ¡Muy bueno!
📌 ¿Cuándo importa el recall?
Cuando el costo de un falso negativo es alto.
Ejemplo: Dejar pasar un spam fraudulento → el usuario podría hacer clic y perder dinero.
"Promedio armónico entre precisión y recall. Ideal cuando quieres equilibrio."
F1 = 2 * (Precision * Recall) / (Precision + Recall)
= 2 * (0.97 * 0.91) / (0.97 + 0.91) ≈ 0.94
→ 94% de F1-score: un buen equilibrio entre no molestar al usuario (precisión) y protegerlo (recall).
📌 ¿Cuándo usar F1?
Cuando no sabes qué es más importante, o cuando quieres una sola métrica 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 qué tan 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", sino que te dice "estoy 99.98% seguro".
¿Qué pasa si cambias el umbral de decisión?
Por defecto, si probabilidad > 0.5 → spam.
Pero ¿y 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('Recall')
plt.ylabel('Precision')
plt.title('Curva Precision-Recall')
plt.legend()
plt.grid(True)
plt.show()
📌 ¿Qué ves?
Una curva que muestra el trade-off entre precisión y recall para diferentes umbrales.
Ideal para elegir un umbral que se adapte a tus necesidades (más precisión o más recall).
☐ Calcular e interpretar la accuracy.
☐ Construir y leer una matriz de confusión.
☐ Calcular e interpretar precisión, recall y F1-score.
☐ Saber cuándo priorizar precisión vs recall según el problema.
☐ Obtener y analizar probabilidades de predicción.
☐ Entender que un modelo "bueno" depende del contexto, no de un número.
☐ Sentirte cómodo/a evaluando modelos con rigor profesional.
"No midas tu modelo por cuánto acierta… mídelo por cuánto importa lo que acierta."
Course: AI-course0
Language: ES
Lesson: 4 train model