Cómo construir un modelo Deep Learning en 10 líneas

(tiempo de lectura: 20 minutos)

[versión en inglés]

[código en GitHub]

Guía básica para sumergirse rápidamente en Deep Learning

Este post presentará al lector los conceptos básicos de las redes neuronales a través de un estudio de caso que al usar solo 10 líneas de código Python crea y entrena una red neuronal que reconoce dígitos escritos a mano en 3 pasos básicos:

  1. Cargar y preprocesado los datos
  2. Definir el modelo
  3. Entrena al modelo

Para hacer esto, utilizaremos la API TensorFlow Keras, la biblioteca más popular actualmente en la comunidad de Deep Learning. ¡Vamos a por ello!

Dígitos escritos a mano

Como caso de estudio, crearemos un modelo que nos permita identificar dígitos escritos a mano, como los siguientes:

El objetivo es crear un modelo matemático que, dada una imagen, el modelo identifique el número que representa. Por ejemplo, si alimentamos al modelo con la primera imagen, esperaríamos que responda que es un 5. El siguiente un 0, el siguiente un 4, y así sucesivamente.

Problema de clasificación

En realidad, estamos lidiando con un problema de clasificación, que dada una imagen, el modelo la clasifica entre 0 y 9. Pero a veces, incluso podemos encontrarnos con ciertas dudas, por ejemplo, ¿la primera imagen representa un 5 o un 3?

Para este propósito, la red neuronal que crearemos devuelve un vector con 10 posiciones que indica la probabilidad de cada uno de los diez dígitos posibles:

Sólo 10 líneas de código

Sí, en sólo 10 líneas de código de Python puede crear y entrenar un modelo de red neuronal que clasifique dígitos escritos a mano:

1: import tensorflow as tf
2: from tensorflow.keras.utils import to_categorical
3:(x_train, y_train), _ = tf.keras.datasets.mnist.load_data()
4: x_train = x_train.reshape(60000, 784).astype('float32')/255
5: y_train = to_categorical(y_train, num_classes=10)
6: model = tf.keras.Sequential()
7: model.add(tf.keras.layers.Dense(10, activation='sigmoid', 
            input_shape=(784,)))
8: model.add(tf.keras.layers.Dense(10, activation='softmax'))
9: model.compile(loss="categorical_crossentropy", optimizer="sgd", 
                 metrics = ['accuracy'])
10: model.fit(x_train, y_train, epochs=10, verbose=0)

Ya hemos avanzado que utilizamos las API Keras de TensorFlow. Es la librería Python recomendada para principiantes, ya que su curva de aprendizaje es más suave en comparación con otras, y en este momento es sin lugar a dudas uno de los middleware más populares para implementar redes neuronales. La librería de Keras fue desarrollado y mantenido por François Chollet, ingeniero de Google, y actualmente está incluido en la librería Tensorflow.

Entorno de pruebas

Sugiero usar el Colaboratory (Colab) de Google si se desea (y recomiendo encarecidamente) ejecutar el código descrito en este post.

Colab es un proyecto de investigación de Google creado para ayudar a difundir la educación e investigación de Machine Learning. Es un entorno de notebook Jupyter que no requiere configuración y se ejecuta completamente en el Cloud de Google, lo que permite el uso de diferentes librerías de Deep Learning como TensorFlow y PyTorch. La característica más importante que distingue a Colab de otros servicios gratuitos en la nube es que Colab proporciona acceso a una GPU (o TPU)  y es totalmente gratuito. Puede encontrar información detallada sobre el servicio en la página de preguntas frecuentes.

Por defecto, los notebooks de Colab se ejecutan en la CPU pero se puede cambiar para que funcione con GPU (o TPU). Para obtener acceso a una GPU (o TPU), debemos elegir la pestaña Tiempo de ejecución y luego seleccionar «Change runtime type» como se muestra en la siguiente figura:

Cuando aparezca una ventana emergente, seleccione «GPU» (o «TPU»). Asegúrese de que «Hardware accelerator» esté configurado en GPU (el valor predeterminado es CPU).
Luego, asegúrese de estar conectado en tiempo de ejecución (hay una marca de verificación verde junto a «CONNECTED» en la barra de menú):

Ahora puedes ejecutar el código presentado en este post. Sugiero copiar y pegar el código de este post en Colab para ver la ejecución mientras se está  leyendo este post. ¿Listo?

1. Cargar y preprocesar datos

En primer lugar, necesitamos importar algunas librerías de Python que necesitamos para programar nuestra red neuronal en TensorFlow:

import tensorflow as tf
from tensorflow.keras.utils import to_categorical

El siguiente paso es cargar los datos que se utilizarán para entrenar nuestra red neuronal. Usaremos el conjunto de datos MNIST, que se puede descargar desde la página  MNIST database. Este conjunto de datos contiene 60,000 imágenes de dígitos hechos a mano para entrenar el modelo y es ideal para utilizar técnicas de reconocimiento de patrones por primera vez sin tener que pasar mucho tiempo preprocesando y formateando datos, pasos muy importantes y costosos en el análisis de datos y de especial complejidad al trabajar con imágenes.

Con TensorFlow esto se puede hacer usando esta línea de código (línea 3):

(x_train, y_train), _ = tf.keras.datasets.mnist.load_data()

Paso opcional: si lo desea, puede verificar los datos cargados utilizando el siguiente código:

import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(25, 4))
for idx in np.arange(20):
    ax = fig.add_subplot(2, 20/2, idx+1, xticks=[], yticks=[])
    ax.imshow(x_train[idx], cmap=plt.cm.binary)
    ax.set_title(str(y_train[idx]))


Este conjunto de datos de imágenes en blanco y negro (las imágenes contienen niveles de gris) se ha normalizado a 20 × 20 píxeles, manteniendo su relación de aspecto. Posteriormente, las imágenes se centraron, calculando el centro de masa de estas y moviendo la imagen para colocar este punto en el centro del campo de 28 × 28.

Estas imágenes MNIST de 28 × 28 píxeles se representan como una matriz de números cuyos valores van desde [0, 255] de tipo uint8. Pero es habitual escalar los valores de entrada de las redes neuronales a ciertos rangos. En el ejemplo de esta publicación, los valores de entrada deben escalarse a valores de tipo float32 dentro del intervalo [0, 1].

Por otro lado, para facilitar la entrada de datos en nuestra red neuronal, debemos hacer una transformación de los datos de entrada (imagen) de 2 dimensiones (2D) a un vector de 1 dimensión (1D). Es decir, la matriz de 28 × 28 números puede representarse mediante un vector de 784 números (concatenando fila a fila), que es el formato que acepta como entrada una red neuronal densamente conectada como la que proponemos en este post.

Podemos lograr estas transformaciones con la siguiente línea de código (línea 4):

x_train = x_train.reshape(60000, 784).astype('float32')/255

Además, el conjunto de datos tiene una etiqueta para cada una de las imágenes que indica qué dígito representa (descargado en y_train). En nuestro caso, hay números entre 0 y 9 que indican qué dígito representa la imagen, es decir, a qué clase está asociada.

Necesitamos representar cada etiqueta con un vector de 10 posiciones como presentamos anteriormente, donde la posición correspondiente al dígito que representa la imagen contiene un 1 y el resto contiene 0s. Este proceso de transformar las etiquetas en un vector de tantos ceros como el número de etiquetas diferentes, y poner un 1 en el índice correspondiente a la etiqueta, se conoce como codificación one-hot. Por ejemplo, el número 7 se codificará como:

Podemos lograr estas transformaciones con la siguiente línea de código (línea 5):

y_train = to_categorical(y_train, num_classes=10)

2. Definir el modelo

Para definir el modelo con la API de Keras solo requerimos las siguientes líneas de código (líneas 6–8):

model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(10,activation='sigmoid', input_shape=(784,)))
model.add(tf.keras.layers.Dense(10,activation='softmax'))

Sin embargo, antes de explicar estas líneas de código, permítanme presentarles algunos conceptos básicos de redes neuronales.

Una neurona artificial simple

Para mostrar cómo es una neuronal básica, supongamos un ejemplo simple donde tenemos un conjunto de puntos en un plano bidimensional y cada punto ya está etiquetado como «cuadrado» o «círculo»:

Dado un nuevo punto «X», queremos saber qué etiqueta le corresponde:

Un enfoque común es dibujar una línea que separe los dos grupos y usar esta línea como clasificador:

En este caso, los datos de entrada estarán representados por vectores de la forma (x1, x2) que indican sus coordenadas en este espacio bidimensional, y nuestra función devolverá ‘0’ o ‘1’ (arriba o debajo de la línea) para saber si debe clasificarse como «cuadrado» o «círculo». Este clasificador se puede definir por:

De manera más general, podemos expresar la línea como:

Para clasificar los elementos de entrada X, que en nuestro caso son bidimensionales, debemos aprender un vector de peso W de la misma dimensión que los vectores de entrada, es decir, el vector (w1, w2) y un sesgo b.

Con estos valores calculados, ahora podemos construir una neurona artificial para clasificar un nuevo elemento X. Básicamente, la neurona aplica este vector W de pesos calculados en los valores en cada dimensión del elemento de entrada X, y al final agrega el sesgo b . El resultado de esto se pasará a través de una función de «activación» no lineal para producir un resultado de ‘0’ o ‘1’. La función de esta neurona artificial que acabamos de definir se puede expresar de una manera más formal como:

Ahora, necesitaremos una función que aplique una transformación a la variable z para que se convierta en ‘0’ o ‘1’. Aunque hay varias funciones («funciones de activación»), para este ejemplo usaremos una conocida como función sigmoid que devuelve un valor de salida entre 0 y 1 para cualquier valor de entrada:

Si analizamos la fórmula anterior, podemos ver que siempre tiende a dar valores cercanos a 0 o 1. Si la entrada z es razonablemente grande y positiva, «e» elevado a menos z es cero y, por lo tanto, la variable y toma el valor de 1. Si por el contrario la variable z tiene un valor grande y negativo, resulta que para «e» elevado a un número positivo grande, el denominador de la fórmula se convertirá en un número grande y, por lo tanto, el valor de la variable de salida y estará cerca de 0. Gráficamente, la función sigmoid presenta esta forma:

Hasta ahora hemos presentado cómo definir una neurona artificial, la arquitectura más simple que puede tener una red neuronal. En particular, esta arquitectura se conoce en la literatura del tema como Perceptron (también llamada linear threshold unit (LTU)), inventada en 1957 por Frank Rosenblatt, y resumida visualmente de manera general con el siguiente esquema:

Perceptrón multicapa

Antes de seguir adelante con el ejemplo, presentemos  brevemente la forma que las redes neuronales suelen tomar cuando se construyen a partir de perceptrones como el que acabamos de presentar.
En la literatura del área nos referimos a un Perceptrón Multicapa ( Multi-Layer Percentron, MLP) cuando encontramos redes neuronales que tienen una capa de entrada, una o más capas compuestas de perceptrones, llamadas capas ocultas y una capa final con varios perceptrones llamada capa de salida. En general, nos referimos a Deep Learning cuando el modelo basado en redes neuronales se compone de múltiples capas ocultas. Visualmente se puede presentar sintéticamente con el siguiente esquema:

Los MLP a menudo se usan para la clasificación, y específicamente cuando las clases son exclusivas, como en el caso de la clasificación de imágenes de dígitos (en clases del 0 al 9). En este caso, la capa de salida devuelve la probabilidad de pertenecer a cada una de las clases, gracias a una función llamada softmax. Visualmente podríamos representarlo de la siguiente manera:

Como ya mencionamos, hay varias funciones de activación además del sigmoid, cada una con diferentes propiedades. Una de ellas es la que acabamos de mencionar, la función de activación softmax, que será útil para presentar un ejemplo de red neuronal simple para clasificar en más de dos clases. Por el momento podemos considerar la función softmax como una generalización de la función sigmoid que nos permite clasificar más de dos clases.

Función de activación Softmax

Resolveremos el problema de manera que, dada una imagen de entrada, obtengamos las probabilidades de que sea cada uno de los 10 dígitos posibles. De esta manera, tendremos un modelo que, por ejemplo, podría predecir un cinco en una imagen, pero solo con la seguridad en un 70% de que es un cinco. Debido al trazo de la parte superior del número en esta imagen, parece que podría convertirse en un tres en una probabilidad del 25% e incluso podría dar una cierta probabilidad a cualquier otro número. Aunque en este caso particular consideraremos que la predicción de nuestro modelo es un cinco, ya que es el que tiene la mayor probabilidad, este enfoque de usar una distribución de probabilidad puede darnos una mejor idea de cuán seguros estamos de nuestra predicción. Esto es bueno en este caso, donde los números se hacen a mano, y seguramente en muchos de ellos, no podemos reconocer los dígitos con 100% de certeza.

Por lo tanto, para este ejemplo de clasificación obtendremos, para cada ejemplo de entrada, un vector de salida con la distribución de probabilidad sobre un conjunto de etiquetas mutuamente excluyentes. Es decir, un vector de 10 probabilidades cada una correspondiente a un dígito y también la suma de todas estas 10 probabilidades da como resultado el valor de 1 (las probabilidades se expresarán entre 0 y 1).

Como ya hemos avanzado, esto se logra mediante el uso de una capa de salida en nuestra red neuronal con la función de activación softmax, en la que cada neurona en esta capa softmax depende de las salidas de todas las otras neuronas en la capa, ya que la suma de la salida de todos ellos debe ser 1.

Pero, ¿cómo funciona la función de activación softmax? La función softmax se basa en calcular «la evidencia» de que cierta imagen pertenece a una clase particular y luego estas evidencias se convierten en probabilidades de que pertenezca a cada una de las clases posibles.

Un enfoque para medir la evidencia de que cierta imagen pertenece a una clase particular es hacer una suma ponderada de la evidencia de pertenencia a cada uno de sus píxeles a esa clase. Para explicar la idea, usaré un ejemplo visual.

Supongamos que ya tenemos el modelo aprendido para el número cero. Por el momento, podemos considerar un modelo como «algo» que contiene información para saber si un número es de cierta clase. En este caso, para el número cero, supongamos que tenemos un modelo como el que se presenta a continuación:

En este caso, con una matriz de 28 × 28 píxeles, donde los píxeles en rojo representan pesos negativos (es decir, reducen la evidencia de que pertenece), mientras que los píxeles en azul representan pesos positivos (cuya evidencia es mayor aumenta) . El color blanco representa el valor neutral.

Imaginemos que trazamos un cero sobre él. En general, el rastro de nuestro cero caería en la zona azul (recuerde que estamos hablando de imágenes que se han normalizado a 20 × 20 píxeles y luego se centraron en una imagen de 28 × 28). Es bastante evidente que si nuestro trazo sobrepasa la zona roja, lo más probable es que no estemos escribiendo un cero; por lo tanto, usar una métrica basada en sumar si pasamos por la zona azul y restar si pasamos por la zona roja parece razonable.

Para confirmar que es una buena métrica, imaginemos ahora que trazamos un tres; Está claro que la zona roja del centro del modelo anterior que utilizamos para el cero penalizará la métrica antes mencionada ya que, como podemos ver en la parte izquierda de la siguiente figura, al escribir un tres pasamos por encima:

Pero, por otro lado, si el modelo de referencia es el correspondiente al número 3 como se muestra en la parte derecha de la figura anterior, podemos ver que, en general, las diferentes trazas posibles que representan un tres se mantienen principalmente en zona azul.

Espero que el lector, al ver este ejemplo visual, ya intuya cómo la aproximación de los pesos indicados anteriormente nos permite estimar de que número se trata.

Una vez que se ha calculado la evidencia de pertenencia a cada una de las 10 clases, estas deben convertirse en probabilidades cuya suma de todos sus componentes suma 1. Para esto, softmax usa el valor exponencial de la evidencia calculada y luego los normaliza para que la suma equivale a uno, formando una distribución de probabilidad. La probabilidad de pertenecer a la clase i es:

Intuitivamente, el efecto obtenido con el uso de exponenciales es que una unidad más de evidencia tiene un efecto multiplicador y una unidad menos tiene el efecto inverso. Lo interesante de esta función es que una buena predicción tendrá una sola entrada en el vector con un valor cercano a 1, mientras que las entradas restantes estarán cerca de 0. En una predicción débil, habrá varias etiquetas posibles, que tener más o menos la misma probabilidad.

Clase Sequential en Keras

La estructura de datos principal en Keras es la clase sequential, que permite la creación de una red neuronal básica. Keras también ofrece una API que permite implementar modelos más complejos en forma de un grafo que puede tener múltiples entradas, múltiples salidas, con conexiones arbitrarias en el medio, pero está más allá del propósito introductorio de este post.
La Sequential class de la librería de Keras es un contenedor para el modelo de red neuronal secuencial que ofrece Keras y se puede crear de la siguiente manera:

model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(10,activation='sigmoid',input_shape=(784,)))
model.add(tf.keras.layers.Dense(10,activation='softmax'))

En este caso, el modelo en Keras se considera como una secuencia de capas y cada una de ellas «destila» gradualmente los datos de entrada para obtener la salida deseada. En Keras podemos encontrar todos los tipos de capas necesarios que se pueden agregar fácilmente a modelar a través del método add().

Aquí, la red neuronal se ha definido como una secuencia de dos capas que están densamente conectadas o completamente conectadas (densely connected o fully connected), lo que significa que todas las neuronas de cada capa están conectadas a todas las neuronas de la siguiente capa. Visualmente podríamos representarlo de la siguiente manera:

En el código anterior hemos expresado explícitamente en el argumento input_shape de la primera capa cómo son los datos de entrada: un tensor de 784 características del modelo.

Una característica muy interesante de la biblioteca Keras es que deducirá automáticamente la forma de los tensores entre capas después de la primera. Esto significa que el programador solo tiene que establecer esta información para el primero de ellos. Además, para cada capa indicamos el número de nodos que tiene y la función de activación que aplicaremos en ella (en este ejemplo, sigmoid).

La segunda capa en este ejemplo es una capa softmax de 10 neuronas, lo que significa que devolverá una matriz de 10 valores de probabilidad que representan los 10 dígitos posibles (en general, la capa de salida de una red de clasificación tendrá tantas neuronas como clases, excepto en una clasificación binaria, donde solo se necesita una neurona). Cada valor será la probabilidad de que la imagen del actual pertenezca a cada uno de ellos.

Optional step: A very useful method that Keras provides to check the architecture of our model is summary():

model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 10) 7850
_________________________________________________________________
dense_2 (Dense) (None, 10) 110
=================================================================
Total params: 7,960
Trainable params: 7,960
Non-trainable params: 0

Para nuestro ejemplo simple, vemos que indica que se requieren 7,960 parámetros (columna Param #), que corresponden a 7,850 parámetros a la primera capa y 110 a la segunda.

En la primera capa, para cada neurona i (entre 0 y 9) requerimos 784 parámetros para los pesos wij y, por lo tanto, 10 × 784 parámetros para almacenar los pesos de las 10 neuronas. Además de los 10 parámetros adicionales para los 10 sesgos bj correspondientes a cada uno de ellos. En la segunda capa, que es una función softmax, se requiere conectar las 10 neuronas con las 10 neuronas de la capa anterior. Por lo tanto, se requieren 10 × 10 parámetros wi y además 10 bj sesgos correspondientes a cada nodo.

Los detalles de los argumentos que podemos indicar para el esta capa se pueden encontrar en el manual de Keras. En nuestro ejemplo, aparecen los más relevantes. El primer argumento indica el número de neuronas en la capa; La siguiente es la función de activación que usaremos en ella. En este otro post discutimos con más detalle otras posibles funciones de activación más allá de las dos presentadas aquí: sigmoid y softmax.

3. Entrenar el modelo

Ya casi hemos terminado, solo nos queda por explicar las dos últimas líneas de código:

model.compile(loss="categorical_crossentropy", optimizer="sgd",metrics = ['accuracy'])
model.fit(x_train, y_train, epochs=10, verbose=0)

Proceso de aprendizaje

La forma en que la red neuronal puede aprender los pesos W y los sesgos b de las neuronas es un proceso iterativo para todos los ejemplos de entrada (que están etiquetados con el valor a que corresponde), comparando el valor de su etiqueta estimado a través del modelo, con el valor esperado de la etiqueta de cada elemento. Después de cada iteración, los valores de los parámetros se ajustan de tal manera que la discordancia (error) entre el valor estimado para la imagen y el valor real, se hace más pequeña. El siguiente esquema quiere resumir visualmente el proceso de aprendizaje de un perceptrón de manera general:

Configuración del proceso de aprendizaje

Podemos configurar cómo será este proceso de aprendizaje con el método compile(), con el que podemos especificar algunas propiedades a través de los argumentos del método.

El primero de estos argumentos es la función de pérdida (loss function) que usaremos para evaluar el grado de error entre las salidas calculadas y las salidas deseadas de los datos de entrenamiento. Por otro lado, especificamos un optimizador que es la forma en que tenemos que especificar el algoritmo de optimización que permite a la red neuronal calcular los pesos de los parámetros a partir de los datos de entrada y la función de pérdida definida.

Y finalmente debemos indicar la métrica que usaremos para monitorear el proceso de aprendizaje de nuestra red neuronal. En este primer ejemplo, solo consideraremos la precisión (Accuracy), fracción de imágenes que se clasifican correctamente. Por ejemplo, en nuestro caso podemos especificar los siguientes argumentos en el método compile() para probarlo:

model.compile(loss="categorical_crossentropy",
optimizer="sgd",
metrics = ['accuracy'])

En este ejemplo, especificamos que la función de pérdida es categorical_crossentropy, el optimizador utilizado es el descenso de gradiente estocástico (sgd) y la métrica es la precisión, con la que evaluaremos el porcentaje de conjeturas correctas.

En este otro post, el lector podría ingresar con más detalle sobre el proceso de aprendizaje.

Entrenamiento del modelo

Una vez que nuestro modelo ha sido definido y el método de aprendizaje configurado, está listo para ser entrenado. Para esto podemos entrenar o «ajustar» el modelo a los datos de entrenamiento disponibles invocando el método fit() del modelo:

model.fit(x_train, y_train, epochs=10, verbose=0)

En los primeros dos argumentos hemos indicado los datos con los que entrenaremos el modelo en forma de matrices de Numpy. El argumento batch_size indica el número de datos que usaremos para cada actualización de los parámetros del modelo y con las epochs indicaremos el número de veces que usaremos todos los datos en el proceso de aprendizaje.

Este método encuentra el valor de los parámetros de la red a través del algoritmo de entrenamiento iterativo que mencionamos anteriormente. Aproximadamente, en cada iteración de este algoritmo, este toma datos de entrenamiento de x_train, los pasa a través de la red neuronal (con los valores que tienen sus parámetros en ese momento), compara el resultado obtenido con el esperado (indicado en y_train) y calcula el error para guiar el proceso de ajuste de los parámetros del modelo, que consiste intuitivamente en aplicar el optimizador especificado anteriormente para calcular un nuevo valor de cada uno de los parámetros del modelo (pesos y sesgos) en cada iteración en tal manera que se reduce el error.

Este es el método que, como veremos, puede llevar más tiempo y Keras nos permite ver su progreso utilizando el argumento detallado (por defecto, igual a 1), además de indicar una estimación de cuánto tiempo toma cada época:

Epoch 1/5
60000/60000 [========] - 1s 15us/step - loss: 2.1822 - acc: 0.2916
Epoch 2/5
60000/60000 [========] - 1s 12us/step - loss: 1.9180 - acc: 0.5283
Epoch 3/5
60000/60000 [========] - 1s 13us/step - loss: 1.6978 - acc: 0.5937
Epoch 4/5
60000/60000 [========] - 1s 14us/step - loss: 1.5102 - acc: 0.6537
Epoch 5/5
60000/60000 [========] - 1s 13us/step - loss: 1.3526 - acc: 0.7034
10000/10000 [========] - 0s 22us/step

Modelo entrenado

Para usar el modelo, podemos descargar otro conjunto de imágenes (diferentes de las imágenes de entrenamiento) con el siguiente código:

_, (x_test_, y_test_)= tf.keras.datasets.mnist.load_data()
x_test = x_test_.reshape(10000, 784).astype('float32')/255
y_test = to_categorical(y_test_, num_classes=10)

Paso opcional: evaluación del modelo

En este punto, la red neuronal ha sido entrenada y su comportamiento con nuevos datos de prueba ahora se puede evaluar utilizando el método de evaluation(). Este método devuelve dos valores:

test_loss, test_acc = model.evaluate(x_test, y_test)

Estos valores indican qué tan bien o mal se comporta nuestro modelo con datos nuevos que nunca ha visto. Estos datos se almacenaron en x_test e y_test cuando realizamos mnist.load_data () y los pasamos al método como argumentos. En el alcance de este post introductorio, solo veremos uno de ellos, la precisión:

print(‘Test accuracy:’, test_acc)
Test accuracy: 0.9018

La precisión nos dice que el modelo que hemos creado, aplicado a datos que el modelo nunca había visto antes, clasifica el 90% de ellos correctamente.

Generar predicciones con el modelo

Finalmente, el lector o lectora necesitará saber cómo puede usar el modelo entrenado para hacer predicciones. En nuestro ejemplo, consiste en predecir qué dígito representa una imagen. Para hacer esto, Keras proporciona el método predict().

Elijamos una imagen (y visualizemosla) para predecir el número:

image = 5
_ = plt.imshow(x_test_[image], cmap=plt.cm.binary)

y para predecir el número de esta imagen mediante el modelo podemos usar el siguiente código:

import numpy as np
prediction = model.predict(x_test_)
print("Model prediction: ", np.argmax(prediction[image]) )

 

Model prediction: 1

¡Y eso es todo!

¿Próximo paso?

Si le ha gustado la experiencia, ahora el lector o lectora puede interesarle el libro Python Deep Learning  de la editorial Marcombo que acabamos de publicar.