70,000 dígitos escritos a mano. El objetivo del capítulo no es clasificarlos — es aprender a medir un clasificador, que resulta bastante más resbaladizo que medir una regresión.
El primer ejercicio es un detector de una sola cosa: ¿esta imagen es un 5?
Solo el 9% de las imágenes son cincos. Así que un modelo que responda “no es un 5” a todo acierta el 91% de las veces sin haber mirado un solo píxel. Ese modelo tonto se entrena a propósito, para ponerlo al lado del real:
Seis puntos separan un modelo que aprendió de uno que no hizo nada. Y en la práctica las clases raras son la norma, no la excepción: el fraude, la enfermedad, la falla del equipo. Ahí la exactitud regala el 90% de entrada.
| Métrica | Qué responde | Valor |
|---|---|---|
| Precisión | De lo que llamó 5, ¿cuánto era 5? | 0.809 |
| Exhaustividad | De todos los 5 que había, ¿cuántos encontró? | 0.868 |
| F1 | Media armónica — penaliza el desequilibrio entre ambas | 0.838 |
Un clasificador no responde sí o no: calcula una puntuación y la compara contra un umbral. Subirlo lo vuelve más exigente — se equivoca menos cuando dice “sí”, pero se le escapan más.

Dónde pararse en esa curva no es una decisión técnica. Un filtro de spam quiere precisión — mandar un correo importante a la basura es peor que dejar pasar publicidad. Un detector de tumores quiere exhaustividad — revisar de más es un susto, pasar uno por alto es otra cosa. Mismo modelo, distinto punto de operación, según lo que cueste equivocarse.

⚠️ Pero esta métrica engaña cuando la clase positiva es rara. La curva ROC usa la proporción de falsos positivos, y con 54,000 negativos contra 5,400 positivos el denominador es tan grande que la curva se ve optimista. Para clases desbalanceadas, la curva de precisión contra exhaustividad cuenta la verdad más incómoda.
Saber que el modelo acierta el 91% no dice qué hacer después. La matriz de confusión sí: muestra qué dígito se confunde con cuál.


El modelo no está siendo tonto — esos dígitos son genuinamente ambiguos incluso para una persona. Y eso cambia qué se hace después.
No es “entrena más”: un modelo lineal ve píxeles sueltos y no puede representar «tiene un lazo cerrado arriba», que es justamente la diferencia entre un 4 y un 9. La solución no está en este capítulo, está en el 14 — redes convolucionales, que sí capturan forma.

Este ejercicio deja clara una frontera borrosa: quitar ruido de una imagen suena a regresión, no a clasificación. La distinción no siempre es nítida — las categorías del libro son herramientas, no compartimentos.
La primera corrida daba 0.70 de exactitud, muy por debajo de lo normal en este dataset. La causa no era el modelo: el archivo viene ordenado por clase de pasaje, así que sin barajar, el primer pliegue de la validación cruzada era 100% primera clase y los dos últimos 100% tercera. El modelo se entrenaba con un perfil de pasajero y se evaluaba con otro.
Lo insidioso es que la partición estratificada no lo detecta: equilibra la supervivencia, no la clase. Basta barajar para pasar de 0.70 a 0.81. Es la lección del capítulo anterior —la partición importa— reapareciendo con otra cara.
3,000 correos reales, con cabeceras, HTML y codificaciones rotas. Aquí la precisión pesa más que la exhaustividad: mandar un correo legítimo a la basura es mucho peor que dejar pasar publicidad.
Pero optimizar solo precisión lleva a otro sitio. La regresión logística alcanza precisión perfecta —cero legítimos perdidos— a cambio de dejar pasar84 spam. El SVM lineal pierde 2 legítimos y solo deja pasar 28. Cuál conviene depende de cuánto duele cada error, y ninguna métrica sola lo responde.
El ejercicio pide superar el 97% de exactitud. Se logra con k-vecinos afinado (0.9714) y sube a 0.9763 replicando cada imagen desplazada un píxel en las cuatro direcciones — el conjunto pasa de 60,000 a 300,000.
Pero la primera versión del script agotó la memoria de una máquina de 16 GB. Pedía paralelismo en dos niveles a la vez: la búsqueda lanzaba diez procesos, cada uno con su copia completa de los datos, y cada uno pedía otros diez hilos.
El culpable oculto fue un valor por defecto: scikit-learn reserva hasta1 GB por hilo para los bloques de distancias. Con diez núcleos, diez gigas. Bajarlo a 128 MB y usar precisión simple llevó el pico de 3.00 GB a 0.49 GB — y encima quedó más rápido.
El error de método fue peor que el bug: la estimación de costo se midió con una configuración y después se escribió el script con otra, sin volver a medir.