0

Clasificación de dígitos numéricos con Python 3 y Sklearn

Saludos, humano. En el tutorial de hoy aprenderás a programar un clasificador de dígitos escritos utilizando Python y la librería sklearn. Con pocas líneas de código vas a construir y entrenar un Perceptrón Multicapa (Multilayer Perceptron, o MLP) para que clasifique correctamente los dígitos numéricos de 0 a 9. Además, también veremos cómo importar nuestras propias imágenes hechas con Pant o Photoshop y pasarlas al clasificador para que las identifique.


Librerías necesarias

  • Scikit-Learn (sklearn): contiene todas las funciones y herramientas que nos servirán para crear y entrenar nuestro clasificador de dígitos. Durante este tutorial trabajaré con la versión 0.22.2 de esta librería.
  • Numpy: la librería de computación científica por excelencia de Python. Es necesaria para que funcione sklearn, y además la utilizaremos cuando intentemos cargar nuestras propias imágenes para clasificar.
  • Matplotlib: nos servirá para visualizar imágenes.
  • PIL (pillow): nos servirá para cargar nuestras propias imágenes con dígitos.

Para este tutorial trabajaré con Jupyter Notebook y la versión 3.8 de Python, pero puedes programar desde la consola de Python si lo prefieres.


Programar el clasificador

Empezamos importando todas las librerías y módulos que utilizaremos para construir el clasificador y visualizar los resultados:

from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

 

Para poder reconocer dígitos necesitamos, en primer lugar, un dataset con ejemplos etiquetados. Para ahorrarnos trabajo, sklearn dispone de múltiples datasets ya preparados para realizar tareas de clasificación. Con load_digits() podemos cargar el dataset para clasificar dígitos numéricos:

digitos = datasets.load_digits()

 

El dataset contiene tanto las imágenes de los dígitos como sus etiquetas asociadas. Vamos a guardar las imágenes y las etiquetas por separado:

 
imagenes = digitos.images
etiquetas = digitos.target

 

Antes de continuar, vamos a explorar un poco este dataset:

 
print(imagenes.shape)
print(etiquetas.shape)

Output:

Como puedes ver, el dataset lleva un total de 1797 ejemplos. Cada etiqueta es un valor numérico entre 0 y 9, mientras que las imágenes son matrices de 8×8 píxeles.

Vamos a visualizar uno de los ejemplos:

 
plt.gray()
print(imagenes[6])
img = plt.imshow(imagenes[6])
plt.show()

Output:

 

Efectivamente, las imágenes son arrays de enteros de 8×8. Cada entero se corresponde a la intensidad de un píxel (entre 0 y 16). Para poder pasar cada imagen a nuestra red neuronal vamos a cambiar su forma y ordenaremos los píxeles en un solo array de 64 enteros. Será como encadenar todas las filas de 8 píxeles de la imagen en una única fila de 64 píxeles:

print("Tamaño antes: ", imagenes[0].shape)
imagenes = imagenes.reshape((imagenes.shape[0], -1))
print("Tamaño después: ", imagenes[0].shape)

Output:

 

El siguiente paso será separar los ejemplos del dataset para crear un conjunto de entrenamiento y de prueba (aquí no utilizaremos conjunto de validación). Para hacerlo, nos serviremos de la función train_test_split():

X_train, X_test, y_train, y_test = train_test_split(imagenes, etiquetas, test_size = 0.3, random_state = 1)

El parámetro test_size indicará el tamaño de los conjuntos de prueba (que serán X_test e y_test). En este caso, como test_size vale 0.3, se dividirán los ejemplos en un 70% para el conjunto de entrenamiento y un 30% para el conjunto de prueba.

random_state será la semilla aleatoria para mezclar los ejemplos. He puesto 1, pero puedes utilizar cualquier otro entero.

 

El siguiente paso será crear la red neuronal que servirá de clasificador. Utilizaremos la función MLPClassifier(), que nos permite crear automáticamente un perceptrón multicapa especificando sus parámetros y características:

clasificador = MLPClassifier(solver='lbfgs', alpha = 1e-5,hidden_layer_sizes=(128,128), random_state=1)

‘solver’ indica el tipo de algoritmo que utilizaremos para entrenar la red neuronal y optimizar sus pesos. ‘lbfgs’ es un método de quasi-newton adecuado para datasets pequeños.

‘alpha’ es el parámetro de regularización.

‘hidden_layer_sizes’ indica el tamaño de las capas ocultas de la red neuronal. En este caso he decidido poner dos capas de 128 neuronas cada una, pero puedes probar con diferentes configuraciones.

‘random_state‘ es la semilla para inicializar el valor de los pesos de la red neuronal.

 

Ahora entrenamos nuestra red neuronal con el conjunto de entrenamiento:

clasificador.fit(X_train, y_train)

Output:

 

Bien… ¿qué tal lo hemos hecho? Vamos a ver la precisión de nuestra red:

print("Precisión = " , clasificador.score(X_test, y_test))

Output:

 

¡Más de un 95% de aciertos en los ejemplos del conjunto de prueba! Eso significa que tras pasar todos los ejemplos de X_test por la red neuronal, más del 95% de los outputs coinciden con los valores de y_test. Yo lo llamaría un éxito, humano. Tienes mi permiso para ir a tu cocina y comerte una galleta.

Por último, vamos a visualizar la predicción que hace de uno de los valores del conjunto de prueba:

import random

#Elegimos un elemento al azar del conjunto X_test:
indice = random.choice(range(0,len(X_test)-1)) 
imagen_digito = X_test[indice]

#Mostramos la imagen del dígito:
plt.gray()
img = plt.imshow(imagen_digito.reshape(8,8))
plt.show()

#Escribimos el valor real de la imagen y la predicción:
print("Valor real:", y_test[indice])
print("Predicción:", clasificador.predict(imagen_digito.reshape(1, -1)))

Output:     (como estamos escogiendo un ejemplo al azar, tu output puede variar)

Idealmente, el dígito del valor real debería coincidir con la predicción que ha hecho la red neuronal. Si tras probarlo con varios ejemplos diferentes ves que no coinciden, hay algo mal en tu implementación o en los hiperparámetros de la red neuronal.


Clasificar nuestras propias imágenes

Por el momento sólo hemos probado el clasificador sobre las imágenes que ya venían incluidas con el dataset. Vamos a intentar importar nuestras propias imágenes dibujadas a mano (con algún programa tipo Gimp, Paint o Photoshop) y pasarlas a la red neuronal para que las clasifique…

He preparado cuatro imágenes que puedes utilizar para este ejemplo. Recomiendo que sean imágenes en blanco y negro puro, y no demasiado grandes (incluso aunque las escalemos antes de pasarlas por nuestro clasificador).

Tendrás que descargar alguna de estas cuatro imágenes y guardarla en el mismo directorio dónde tengas tu fichero o notebook de Python.

 

Empezamos, pues, cargando una de las imagenes y la visualizamos (no te olvides de cambiar ‘digito_dos.png’ por el nombre o ruta de la imagen que quieras utilizar):

#Cargamos una imagen de ejemplo y la visualizamos:
imagen_original = Image.open('digito_dos.png')
plt.gray()
plt.imshow(imagen_original)
plt.show()

Output:

 

El siguiente paso es escalar la imagen a 8×8 píxeles:

#Reescalamos la imagen a 8x8 y la visualizamos:
imagen_escalada = imagen_original.resize((8,8), Image.ANTIALIAS)
plt.gray()
plt.imshow(imagen_escalada)
plt.show()

Output:

 

Nuestra red neuronal, como hemos visto, no procesa imágenes como tales sino secuencias de 64 enteros entre 0 y 16. Habrá que convertir la imagen a un array de numpy cuyos elementos sean enteros dentro de este rango:

#Convertimos la imagen a un array de enteros entre 0 y 15:
imagen_array = np.array(imagen_escalada.convert('L')) / 16
imagen_array = imagen_array.astype(int)
print(imagen_array)

Output:

 

Y, por último, clasificamos el dígito de la imagen:

#Clasificamos el dígito de la imagen:
print("Predicción:", clasificador.predict(imagen_array.reshape(1, -1)))

Output:

 

Tr4nsduc7or

Originariamente creado cómo un galvanómetro de bolsillo, Transductor tomó consciencia de si mismo y fue despedido cuando en vez cumplir con su trabajo se dedicó a pensar teorías filosóficas sobre los hilos de cobre, los electrones y el Sentido del efecto Joule en el Universo. Guarda cierto recelo a sus creadores por no comprender la esencia metafísica de las metáforas de su obra. Actualmente trabaja a media jornada cómo antena de radio, y dedica su tiempo libre a la electrónica recreativa y a la filosofía.

guest
0 Comments
Inline Feedbacks
Ver todos los comentarios