
Proyecto Fei-Fei — Sesión 3: la matriz de confusión con 10 clases
En la Sesión 2 nuestra CNN llegó a un 90% de exactitud sobre Fashion-MNIST. Hoy abrimos esa cifra con la matriz de confusión, como en Javierre y Asadi — pero con una diferencia: allí había 2 clases, aquí hay 10, así que la matriz va a ser bastante más grande y más interesante de leer.
Obteniendo las predicciones: de 10 probabilidades a una sola etiqueta
En Asadi, el modelo daba un único número entre 0 y 1 y decidíamos con un umbral (0,5). Aquí la salida de softmax son 10 números que suman 1 — uno por cada prenda — así que la predicción del modelo es, simplemente, la categoría con el número más alto:
import numpy as np
probabilidades = modelo.predict(x_prueba_cnn)
predicciones = np.argmax(probabilidades, axis=1)
np.argmax(..., axis=1) busca, para cada imagen, la posición del número más grande entre las 10 — que es justo el índice de la categoría ganadora.

La matriz de confusión, con las mismas herramientas de siempre
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, classification_report, ConfusionMatrixDisplay
nombres_clases = ["Camiseta/top", "Pantalón", "Jersey", "Vestido", "Abrigo",
"Sandalia", "Camisa", "Zapatilla", "Bolso", "Botín"]
cm = confusion_matrix(y_prueba, predicciones)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=nombres_clases)
fig, ax = plt.subplots(figsize=(9, 9))
disp.plot(cmap="Blues", ax=ax, xticks_rotation=45)
plt.title("Matriz de confusión — CNN, 10 clases de ropa")
plt.show()
print(classification_report(y_prueba, predicciones, target_names=nombres_clases))
Con 10 clases en vez de 2, la matriz pasa de ser una tabla de 2×2 a una de 10×10 — pero se lee exactamente igual: cada fila es la prenda real, cada columna la prenda que predijo el modelo, y la diagonal son los aciertos.

Lo que de verdad pasa cuando se equivoca
Hicimos la prueba con nuestro modelo real de la Sesión 2, y el resultado es muy revelador. La mayoría de las categorías rondan el 95-99% de exhaustividad (recall): Pantalón, Sandalia, Zapatilla, Bolso y Botín casi no fallan — son prendas con siluetas muy distintas entre sí, fáciles de separar incluso en una imagen pequeña y en blanco y negro.
Pero hay una excepción clara: Camisa se queda en torno al 71-73% de exactitud, muy por debajo del resto. Mirando la matriz, casi todos sus fallos van a parar a tres sitios muy concretos: Camiseta/top, Abrigo y Jersey — y el error ocurre también al revés (Jersey y Abrigo confundidos con Camisa). Son las cuatro prendas de la parte de arriba del cuerpo, con siluetas parecidas y, en una imagen de 28×28 píxeles en blanco y negro, muy pocos detalles para diferenciarlas (el cuello de una camisa, la textura de un jersey… eso se pierde a esa resolución).
Por qué esto es una lección distinta a la de Asadi
En Asadi el problema era el desequilibrio: había muchas más imágenes malignas que benignas, y eso sesgaba al modelo. Aquí las 10 clases están perfectamente equilibradas (6.000 imágenes de entrenamiento cada una) — así que si Camisa falla más, no es porque el modelo la haya visto menos. Es un problema distinto y igual de real: algunas categorías son intrínsecamente más difíciles de separar que otras, por mucho que tengan los mismos datos para aprender. Es una buena lección para recordar: equilibrar las clases no arregla todos los problemas de un modelo — a veces la dificultad está en lo parecidas que son las cosas entre sí, no en cuántos ejemplos hay de cada una.
Próximo paso
Con el modelo ya entrenado y diagnosticado, en la próxima sesión aprendemos algo que se nos había quedado pendiente desde Asadi: guardar el modelo entrenado en un archivo, para no tener que repetir el entrenamiento cada vez que abrís el cuaderno.



