Analizando mi escritura con Machine Learning en Python

0
43

Este post lo estoy publicando como una continuación de mi post anterior sobre Machine Learning en Python:

En dicho post expliqué como se construye un algoritmo que permite clasificar imágenes en Python, utilizando el paradigma de Machine Learning. El algoritmo que construí funciona sobre el MNIST dataset, sobre el cual escribí un post:

El algoritmo que construí utiliza el dataset de entrenamiento, unas 50,000 imágenes de números manuscritos, para entrenar un Support Vector Machine. Luego, con el modelo entrenado, se intenta predecir los números representados en las imágenes del dataset de pruebas (10,000 imágenes). El modelo entrenado logra predecir cerca del 98% de las imágenes, con algunos errores en imágenes que podrían considerarse ambiguas.

Estas 3 imágenes no pudieron ser clasificadas por el algoritmo, aunque creo que es entendible el por qué del error. Aún para un ser humano, las imágenes mostradas resultan un poco confusas.

Luego de hacer distintas pruebas con el dataset MNIST me dio curiosidad y decidí probar el modelo entrenado con números escritos por mi mismo. Es decir, se supone que si entreno un modelo de Machine Learning con números manuscritos, este debe ser funcional para números que no estén incluidos en el dataset de pruebas, ¿no?. El procedimiento que utilicé para estas pruebas y los resultados obtenidos es lo que publicaré en este post.

Construyendo mi propio dataset de pruebas

Para probar si el algoritmo es capaz de reconocer mis números necesito construir un dataset de pruebas. Para ello escribí los números del 0 al 9 en una hoja blanca y les tomé una foto con mi celular:

Luego, utilizando Photoshop, recorté los números, desaturé la imagen y eliminé el color de fondo con la herramienta de la varita mágica. Este mismo resultado se puede lograr con cualquier software que permita hacer edición de imágenes. Así me quedó la imagen:

Lo siguiente que hice fue recortar cada imagen y guardarlas en formato PNG, con un tamaño de 300 x 300 pixeles. Así se ven las imágenes recortadas:

Las imágenes originales y las recortadas las he subido al repositorio de Panama Hitek en Github.

El MNIST dataset está formado por imágenes en formato 28 x 28 pixeles en escala de grises. Además las imágenes tienen fondo negro y están representadas en color blanco. El redimensionamiento e inversión de colores lo he realizado con un script en Python, el cual presentaré a continuación:

Este código se encuentra disponible en Github, junto con los archivos necesarios para reproducir mis resultados. Luego de ejecutar este script, el resultado es el siguiente:

Ahora los números que escribí se han convertido en imágenes en formato 28 x 28 pixeles, con colores invertidos.

El siguiente paso es convertir estas imágenes en un conjunto de valores numéricos almacenados en un archivo CSV, de manera similar a las imágenes del dataset MNIST. Eso lo podemos lograr utilizando el siguiente script:

El resultado de ejecutar este script será un archivo CSV como este:

Ahi tenemos, un archivo CSV con 10 filas y 785 columnas. Cada una de las filas es un número y las columnas son los pixeles que forman cada imagen. La primera columna representa las etiquetas de cada número.

Si utilizamos el script que publicamos en nuestro post de primeras pruebas con el MNIST dataset en Python. Utilizaré dicho script, disponible en nuestro repositorio, para convertir los pixeles del archivo CSV nuevamente en imágenes:

Estas imágenes han sido formadas a partir de mi escritura, que posteriormente fue almacenada en forma de pixeles en un archivo CSV. Como se trata de imágenes sacadas de una foto, es normal que exista algo de ruido en cada imagen:

Le agregaré una condición al script que convierte las imágenes en pixeles y las almacena en un archivo CSV. Si el valor de un pixel es menor a 50, que se considere un cero. El script está disponible en este enlace y produce este resultado:

Vemos que ahora tenemos una imagen más limpia, si la comparamos a la anterior. Ha sido un filtrado sencillo que nos permite eliminar información inútil para nuestros propósitos.

Identificando los números en Python

Ahora que tenemos las imágenes ajustadas en un dataset, vamos a proceder a probar el algoritmo con mis números.

El código que utilizaré es el siguiente (disponible en Github):

Este código está basado en el que publiqué en mi post anterior, ¿Cómo construir un clasificador de imágenes con Machine Learning en Python?. Le he hecho algunas modificaciones para que utilice mi dataset en vez del dataset de pruebas de MNIST.

La primera prueba que haré será entrenar mi modelo con 1000 imágenes, para ver si logra reconocer mis números manuscritos:

Como vemos, al utilizar 1000 muestras de entrenamiento se logra detectar el 7 de los 10 números que escribí. El algoritmo falló en detectar el 6, 7 y 9, confundiéndolos con el número 4.

Trataré de mejorar el performance del algoritmo aumentando la cantidad de muestras utilizadas en el entrenamiento, esta vez con 5000:

Ahora el algoritmo logra reconocer 8 de las 10 imágenes. El 7 y el 9 siguen siendo un problema. Aumentaré el dataset de entrenamiento hasta el valor máximo de 60,000 para ver si logramos reconocer todos los números:

No mejoró. Parece que tenemos un problema con el 7 y el 9. También lo intenté utilizando un kernel polinomial de grado 2 y grado 3, los cuales mostraron un performance en mi post anterior sobre este tema. El resultado no mejoró.

¿Cuál será la razón por la cual no está funcionando el algoritmo? Para responder esto hace falta revisar las imágenes del dataset de pruebas. Escribí un script que me permite visualizar 100 imágenes del set de pruebas para ver si tienen alguna diferencia con respecto a los números que escribí. El primer número que visualizaré será el 7:

Las imágenes encerradas en rojo son números 7 con una línea horizontal, similar a la forma como yo escribo el 7:

Yo creo que el problema está en que solamente 9 de las primeras 100 imágenes con el número 7 tienen una línea horizontal. Eso es un 9%, un porcentaje que no sabemos si se mantiene en el resto del dataset.

Es probable que el algoritmo que entrenamos es más sensible a los números 7 sin la línea horizontal en el medio. Es lo único que se me ocurre, pues no creo que el 7 que escribí sea especialmente difícil de interpretar.

En el caso del 9, así se ven las primeros 100 imágenes con el número 9 en el dataset de entrenamiento:

Esta vez las diferencias entre las imágenes con un 9 en el dataset no lucen tan distintas al 9 que escribí:

No estoy seguro cual es la diferencia. Talvez la inclinación, que en el caso de mi número está inclinado hacia la derecha y en el dataset la mayoría están inclinados hacia la izquierda. O talvez sea el grosor de la línea o la intensidad de la misma.

Podemos hacer una prueba aumentando el grosor de las líneas, agregando una condición al script que convierte las imágenes en pixeles. Le podemos decir que si un pixel tiene un valor superior a 50 (en la escala de 0 a 255), que lo convierta en un pixel blanco (255). El resultado se vería así:

Vamos a ver como le va al algoritmo de clasificación con estas nuevas imágenes:

Esto es… más que interesante. Hemos resuelto el problema del número 7, el cual parecía más difícil de resolver. El 9 sigue siendo un problema y, además, la confusión ahora es con el número 3. Esto nos demuestra que a veces es necesario aplicar un preprocesamiento de datos antes de aplicar los algoritmos de Machine Learning para lograr mejores resultados.

Esta prueba fue con el kernel RBF. Y si probamos con el kernel polinomial?

Pues no, parece que el kernel polinomial cúbico no es la solución. Y tampoco lo fue el cuadrático. Parece que tendremos que hacer un análisis más profundo para ver cual de todos los algoritmos disponibles podemos utilizar para reconocer mi propia escritura.

Por lo pronto esto es todo lo que compartiré en este post. En publicaciones futuras espero retomar este tema con otros algoritmos sobre los que estaré escribiendo. Espero que la información aquí publicada sea de utilidad para ustedes.

 

 

 

0 0 votes
Article Rating
Suscríbete
Notify of
guest

0 Comments
Inline Feedbacks
View all comments