Machine Learning: Comprender la Matriz de Confusión
Aprende qué es una matriz de confusión, cómo construirla en Python con scikit-learn, visualizarla con seaborn y derivar precisión, exhaustividad y F1-score.
Una matriz de confusión es una tabla que resume los resultados de predicción de un modelo de clasificación comparando las etiquetas reales con las etiquetas predichas. Es el punto de partida para calcular la precisión, la exhaustividad, el F1-score y la mayoría de las demás métricas de clasificación.
Este capítulo cubre:
- Qué significan las cuatro celdas (TP, FP, TN, FN) y por qué cada una importa
- Cómo calcular métricas comunes a partir de esas celdas
- Cómo construir y visualizar una matriz de confusión en Python con scikit-learn y seaborn
- Matrices de confusión multiclase
- Errores comunes, especialmente en conjuntos de datos desequilibrados
¿Qué es una Matriz de Confusión?
Para un clasificador binario (dos clases posibles: positiva y negativa), la matriz de confusión es una tabla de 2×2:
| Predicción Positiva | Predicción Negativa | |
|---|---|---|
| Real Positivo | Verdadero Positivo (TP) | Falso Negativo (FN) |
| Real Negativo | Falso Positivo (FP) | Verdadero Negativo (TN) |
Cada celda cuenta un tipo específico de resultado:
| Término | Abreviatura | Significado |
|---|---|---|
| Verdadero Positivo | TP | El modelo predice positivo; la realidad es positiva ✓ |
| Verdadero Negativo | TN | El modelo predice negativo; la realidad es negativa ✓ |
| Falso Positivo | FP | El modelo predice positivo; la realidad es negativa ✗ (Error de Tipo I) |
| Falso Negativo | FN | El modelo predice negativo; la realidad es positiva ✗ (Error de Tipo II) |
Un truco mnemotécnico: la primera palabra (Verdadero / Falso) indica si la predicción fue correcta; la segunda palabra (Positivo / Negativo) indica qué predijo el modelo.
Métricas Derivadas de la Matriz
Los cuatro conteos alimentan todas las métricas estándar de clasificación:
| Métrica | Fórmula | Qué mide |
|---|---|---|
| Exactitud | (TP + TN) / (TP + TN + FP + FN) | Fracción total de predicciones correctas |
| Precisión | TP / (TP + FP) | De todas las predicciones positivas, cuántas fueron correctas |
| Exhaustividad (Sensibilidad) | TP / (TP + FN) | De todos los positivos reales, cuántos fueron encontrados |
| Especificidad | TN / (TN + FP) | De todos los negativos reales, cuántos se descartaron correctamente |
| F1-Score | 2 × Precisión × Exhaustividad / (Precisión + Exhaustividad) | Media armónica de precisión y exhaustividad |
Cuándo priorizar precisión vs. exhaustividad
- Prioriza la exhaustividad cuando omitir un caso positivo es costoso — cribado médico, detección de fraude, filtros de spam que deben detectar cada mensaje de spam.
- Prioriza la precisión cuando las falsas alarmas son costosas — recomendaciones quirúrgicas, marcado de documentos legales, sistemas de notificaciones push.
- El F1-score equilibra ambas y es la métrica predeterminada cuando el conjunto de datos está desequilibrado.
Ejemplo Numérico Trabajado
Supón que un modelo examina a 100 pacientes para detectar una enfermedad:
- TP = 50 (pacientes enfermos correctamente identificados)
- FP = 5 (pacientes sanos marcados incorrectamente como enfermos)
- FN = 10 (pacientes enfermos no detectados)
- TN = 35 (pacientes sanos correctamente descartados)
Cálculos paso a paso:
Accuracy = (50 + 35) / 100 = 0.85 (85 %)
Precision = 50 / (50 + 5) ≈ 0.909 (90.9 %)
Recall = 50 / (50 + 10) ≈ 0.833 (83.3 %)
F1-Score = 2 × 0.909 × 0.833 / (0.909 + 0.833) ≈ 0.869 (86.9 %)Observa que la exactitud (85 %) parece aceptable, pero la exhaustividad es solo del 83 % — lo que significa que 10 de cada 60 pacientes enfermos no fueron detectados. En un contexto médico, esa diferencia importa mucho más que el dato de exactitud.
Construir una Matriz de Confusión en Python
Usando scikit-learn
sklearn.metrics proporciona confusion_matrix() y un informe de texto listo para usar mediante classification_report().
from sklearn.metrics import confusion_matrix, classification_report
# Ground-truth labels and model predictions
y_true = [1, 1, 0, 0, 1, 0, 1, 0, 1, 0]
y_pred = [1, 0, 0, 1, 1, 0, 1, 0, 1, 1]
cm = confusion_matrix(y_true, y_pred)
print("Confusion Matrix:")
print(cm)
print()
print(classification_report(y_true, y_pred, target_names=["Negative", "Positive"]))confusion_matrix() devuelve un array de NumPy. De forma predeterminada, las filas son las clases reales y las columnas son las clases predichas (coincidiendo con el diseño de la tabla mostrado anteriormente). La disposición del array es:
[[TN FP]
[FN TP]]Calcular métricas manualmente
Puedes extraer las cuatro celdas y calcular las métricas tú mismo para verificar:
from sklearn.metrics import confusion_matrix
y_true = [1, 1, 0, 0, 1, 0, 1, 0, 1, 0]
y_pred = [1, 0, 0, 1, 1, 0, 1, 0, 1, 1]
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
accuracy = (tp + tn) / (tp + tn + fp + fn)
precision = tp / (tp + fp)
recall = tp / (tp + fn)
f1 = 2 * precision * recall / (precision + recall)
print(f"TP={tp}, FP={fp}, FN={fn}, TN={tn}")
print(f"Accuracy : {accuracy:.3f}")
print(f"Precision: {precision:.3f}")
print(f"Recall : {recall:.3f}")
print(f"F1-Score : {f1:.3f}")Salida esperada:
TP=4, FP=2, FN=1, TN=3
Accuracy : 0.700
Precision: 0.667
Recall : 0.800
F1-Score : 0.727Visualizar con seaborn
Un mapa de calor hace que la matriz de confusión sea más fácil de leer, especialmente para problemas multiclase:
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import confusion_matrix
y_true = [1, 1, 0, 0, 1, 0, 1, 0, 1, 0]
y_pred = [1, 0, 0, 1, 1, 0, 1, 0, 1, 1]
cm = confusion_matrix(y_true, y_pred)
sns.heatmap(
cm,
annot=True,
fmt="d",
cmap="Blues",
xticklabels=["Negative", "Positive"],
yticklabels=["Negative", "Positive"],
)
plt.xlabel("Predicted label")
plt.ylabel("True label")
plt.title("Confusion Matrix")
plt.tight_layout()
plt.savefig("confusion_matrix.png", dpi=150)
plt.show()annot=True imprime el conteo dentro de cada celda; fmt="d" los formatea como enteros.
Matrices de Confusión Multiclase
Cuando hay más de dos clases, la matriz de confusión se expande a una cuadrícula N×N. Cada fila sigue representando las clases reales; cada columna representa las clases predichas. Las celdas de la diagonal son predicciones correctas; las celdas fuera de la diagonal son errores.
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt
# Three classes: cat, dog, rabbit
y_true = ["cat", "dog", "rabbit", "cat", "dog", "rabbit",
"cat", "dog", "cat", "rabbit"]
y_pred = ["cat", "dog", "rabbit", "dog", "dog", "cat",
"cat", "rabbit", "cat", "rabbit"]
labels = ["cat", "dog", "rabbit"]
cm = confusion_matrix(y_true, y_pred, labels=labels)
print(cm)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)
disp.plot(cmap="Blues")
plt.title("Multi-Class Confusion Matrix")
plt.tight_layout()
plt.savefig("cm_multiclass.png", dpi=150)
plt.show()ConfusionMatrixDisplay (añadido en scikit-learn 0.24) es una alternativa conveniente de una sola línea al mapa de calor de seaborn y no requiere la dependencia de seaborn.
Para problemas multiclase, la precisión y la exhaustividad se calculan por clase y luego se promedian. classification_report() ofrece tres estrategias de promediado:
macro— promedio no ponderado entre clases (trata todas las clases por igual).weighted— promedio ponderado por soporte (número de instancias reales por clase).micro— agrega TP/FP/FN en todas las clases antes de dividir (da la exactitud general para conjuntos de datos equilibrados).
Errores Comunes
La exactitud es engañosa en conjuntos de datos desequilibrados
Considera un conjunto de datos donde el 95 % de las muestras son negativas. Un modelo que siempre predice negativo alcanza un 95 % de exactitud pero tiene una exhaustividad de cero — nunca detecta un caso positivo. La matriz de confusión revela esto de inmediato: toda la primera fila (Real Positivo) estará compuesta por FN.
Combina siempre la exactitud con la precisión, la exhaustividad o el F1-score en datos desequilibrados. Consulta el capítulo Train/Test Split para saber cómo crear una división representativa, y el capítulo Curva AUC-ROC para obtener una métrica de evaluación independiente del umbral.
Elegir la estrategia de promediado incorrecta
Usar el promediado macro cuando las clases están muy desequilibradas infla la puntuación de las clases poco frecuentes. Usa weighted para obtener una imagen realista de la calidad general del modelo sobre el conjunto de datos completo.
Olvidar la normalización
Los conteos brutos dependen del tamaño del conjunto de datos. Al comparar modelos entrenados en conjuntos de datos de diferentes tamaños, normaliza la matriz dividiendo cada fila por su suma (pasa normalize='true' a confusion_matrix()):
cm_normalized = confusion_matrix(y_true, y_pred, normalize="true")
print(cm_normalized.round(2))Ahora cada fila suma 1.0, mostrando la fracción de cada clase real predicha correctamente.
Matriz de Confusión vs. Otras Herramientas de Evaluación
| Herramienta | Ideal para |
|---|---|
| Matriz de confusión | Comprender los tipos específicos de errores que comete un modelo |
| Curva AUC-ROC | Comparar clasificadores en todos los umbrales de decisión |
| Validación Cruzada | Estimar qué tan bien generaliza la matriz a datos no vistos |
| Grid Search | Ajustar hiperparámetros usando una métrica elegida (p. ej., F1-score) |
Conclusiones Clave
- Una matriz de confusión desglosa las predicciones en TP, FP, TN y FN — cuatro conteos que revelan qué errores comete un modelo, no solo cuántos.
- La exactitud por sí sola es insuficiente; comprueba siempre la precisión, la exhaustividad y el F1-score, especialmente en datos desequilibrados.
- Usa
sklearn.metrics.confusion_matrix()para el cálculo y seaborn oConfusionMatrixDisplaypara la visualización. - Las matrices multiclase siguen la misma convención de fila = real, columna = predicho y escalan a N×N.
- Adapta la estrategia de promediado (
macro,weighted,micro) a la distribución de clases de tu conjunto de datos.