Nuestra primera red neuronal

David Baños Abril – 26 de marzo de 2022

Perceptrón

Nuestra primera red neuronal será un perceptrón: una sencillísima red de tres capas densamente conectadas, es decir, todas las neuronas de una capa establecen conexión con todas las neuronas de la capa precedente.

El banco de imágenes

El primer paso que ha de realizarse será cargar las imágenes con las que entrenar nuestra red neuronal. El banco de imágenes MNIST recoge miles de imágenes de dígitos manuscritos, todas a una misma resolución. Nuestro algoritmo deberá cargar esas imágenes desde Keras.

import tensorflow as tf
from tensorflow import keras
from keras.datasets import mnist

(entren_imagenes, entren_etiquetas), (test_imagenes, test_etiquetas) = mnist.load_data()

Como se ve MNIST contiene imágenes de entrenamiento e imágenes de prueba una vez que la red ha sido entrenada. Así mismo, también contiene las etiquetas (labels) con la que se asocia cada imagen.

Estructura de los datos

Es muy importante conocer cuál es el formato de estos datos. El método shape (perteneciente a la librería NumPy) nos devuelve la estructura que forman estos datos.

entren_imagenes.shape

» (60000, 28, 28)

test_imagenes.shape

» (10000, 28, 28)

Las estructuras de datos o matrices n-dimensionales se conocen como tensores. Como vemos, se trata de tensores tridimensionales. Las imágenes de entrenamiento se cuentan por 60.000, con una resolución de 28×28. Las de testeo tienen un mismo tamaño, pero son tan solo 10.000.

entren_etiquetas.shape

» (60000, )

Cada una de esas imágenes está etiquetada como un dígito, que toma un valor de cero a nueve. Es por ello que la estructura de datos nos muestra que existen unas 60.000 etiquetas (una por imagen). El segundo valor vacío indica que estas etiquetas son valores escalares, es decir, adimensionales.

El método dtype indica el tipo de dato que componen estas matrices

entren_imagenes.dtype

» uint8

uint8 son valores enteros de 8 bits.

Visualizando los datos

Para ver cómo luciría una de esta imágenes (que son realmente matrices de datos), podemos recurrir a la función imshow() que convierte los valores de la matriz en una escala de grises. En la imagen de abajo mostramos la imagen número 7000 y la etiqueta que que la identifica (evidentemente ocho).
import matplotlib.pyplot as plt

plt.imshow(entrm_imagenes[7000], cmap=plt.cm.binary)
print(entrm_etiquetas[7000])

» 8

Preparación de los datos de entrada

Normalización

Los datos de la matriz toman valores entre 0 (blanco) y 255 (negro máximo). Se recomienda en lo posible evitar valores excesivamente altos para evitar que la red se desestabilice. Por lo tanto es una práctica muy generalizada normalizar los datos, de manera que tomen valores entre 0 y 1 (simplemente dividiendo cada valor por 255). Naturalmente, esto implica primero que el tipo de dato deje de ser entero para transformarse en un decimal float32 haciendo uso de la función astype.

entrm_imagenes = entrm_imagenes.astype('float32')
test_imagenes = test_imagenes.astype('float32')
entrm_imagenes /= 255
test_imagenes /= 255

Reducción de dimensiones

Otro método para preprocesar los datos consiste en reorganizarlos de modo que cada matriz o imagen se convierta en un vector de tan solo una dimensión. Para ello se hace uso del método reshape(). Este vector formado por 28×28 = 784 valores será el input de nuestra red neuronal.
entrm_imagenes = entrm_imagenes.reshape(60000,784)
test_imagenes = test_imagenes.reshape(10000,784)

Etiquetas como vectores

Recordemos que para que la red pueda ser entrenada debe comparar la verdad subyacente, las probabilidades dadas por el entrenador, con las probabilidades que sugiere la red. Debemos por tanto convertir las etiquetas en vectores en el que el valor 1 marque el dígito de la imagen, y el resto de dígitos tenga un valor 0. Por ejemplo, Si la etiqueta de una imagen es 4, debemos transformarla en un vector tal que [0,0,0,0,1,0,0,0,0,0] (comenzando con el dígito 0). Esta transformación es automáticamente realizada por la función to_cathegorical().
entrm_etiquetas = to_categorical(entrm_etiquetas)
test_etiquetas = to_categorical(test_etiquetas)

Modelo

Comencemos a dar forma a nuestra red neuronal. Haciendo uso de Keras, esto es cuestión de unas pocas líneas de código. Nuestra red tendrá una capa de entrada, una de salida, y una capa oculta entre ambas.

from keras import models
from keras import layers
perceptron = models.Sequential()
perceptron.add(layers.Dense(512, activation='relu', input_shape=(784,))) perceptron.add(layers.Dense(256, activation='relu')) perceptron.add(layers.Dense(10, activation='softmax'))
Con models.Sequential() creamos una red definida como secuencial, es decir, forma un único tronco (redes más complejas podrían ramificarse). A continuación añadimos un par de capas definiendo su tipo (en este caso Dense, capas densamente conectadas), el número de neuronas que las forman (512, 256 y 10) así como su función de activación (RELU y softmax). En la primera capa establecemos también el tipo de input que recibe, en este caso, un vector de 784 valores.

Parámetros de la red

Podréis ver como queda la red al completo con la función summary() que os mostrará las capas que forman la red y el número de parámetros entrenables.
perceptron.summary()

Estos parámetros incluyen tanto a los pesos sinápticos como los sesgos. Cada conexión conlleva un sesgo que determina la «sensibilidad» de la neurona independientemente de los pesos sinápticos. Contando con este sesgo, el número de parámetros totales es 

[(784×512) + 512] + [(512×256) + 256] + [(256×10) + 10] = 535.818

Entrenamiento de la red

Parámetros de aprendizaje

Antes del entrenamiento como tal, Keras nos exige especificar los parámetros de entrenamiento por medio de la función compile(). Los argumentos de esta función incluyen el tipo de función de pérdida (loss), el algoritmo de actualización de los pesos sinápticos (optimizer) y la métrica (metrics) que monitorice el proceso de aprendizaje.
perceptron.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])
Entraremos en más detalle más adelante con las posibilidades que nos ofrece esta función. Por el momento baste reconocer la función de entropía cruzada para valores categoriales (categorical_crossentropy) y el descenso del gradiente estocástico (sgd por sus siglas en inglés).

Entrenamiento

Una vez determinados estos parámetros pasemos a entrenar definitivamente la red. La función fit es la encargada de llevar a cabo este proceso. Como argumento será necesario especificar el número de épocas. Recordemos que el entrenamiento implica pasar todo el paquete de datos varias veces. Cada una de estas veces es una época.

perceptron.fit(entrm_imagenes, entrm_etiquetas, epochs=5)

Epoch 1/5
1875/1875 [==============================] – 7s 3ms/step – loss: 0.5607 – accuracy: 0.8573
Epoch 2/5
1875/1875 [==============================] – 6s 3ms/step – loss: 0.2704 – accuracy: 0.9232
Epoch 3/5
1875/1875 [==============================] – 6s 3ms/step – loss: 0.2185 – accuracy: 0.9384
Epoch 4/5
1875/1875 [==============================] – 6s 3ms/step – loss: 0.1842 – accuracy: 0.9478
Epoch 5/5
1875/1875 [==============================] – 6s 3ms/step – loss: 0.1593 – accuracy: 0.9550

Al final de cada época se produce la actualización de los parámetros entrenables. Podemos comprobar como la función de pérdida va progresivamente reduciéndose a en cada época, al mismo tiempo que aumenta la precisión. Tras terminar el entrenamiento podemos ver que la red acierta el 95.5% de las ocasiones.

Evaluación de la red

El paso final será testar la red neuronal ya entrenada. Aunque hemos visto que la precisión final de la red es muy alta, debemos de comprobar que esta se mantiene para datos nuevos que la red aún no ha procesado antes. La función evaluate() recibe los datos nuevos con los que hacer la evaluación y devuelve dos valores, la función de pérdida y la precisión.
test_loss, test_accur = perceptron.evaluate(test_imagenes, test_etiquetas)

print(test_loss)
print(test_accu)
313/313 [==============================] – 2s 6ms/step – loss: 0.1540 – accuracy: 0.9543

0.15403586626052856

0.9542999863624573

Como vemos, la red responde con igual precisión a datos diferentes a los vistos durante el entrenamiento. En otras palabras, hemos evitado el problema del sobreajuste.

Evaluación individual

Podemos tomar una imagen concreta de nuestra base de datos de test y comprobar sí realiza una precisión certera. Por ejemplo, la imagen número 2022.

plt.imshow(test_imagenes[2022], cmap=plt.cm.binary)

Estamos de acuerdo en que es un cuatro ¿Nos dará la razón nuestra red neuronal? Lo primero utilizar la función predict() para construir un array formado con los resultados para cada dato de los datos de prueba. Debemos saber que cada resultado es así mismo un array que almacena valores entre 0 y 1 y que representan las probabilidades que ofrece la red de que el dato en cuestión sea uno de los diez dígitos.
resultados = perceptron.predict(test_imagenes)
print(resultados[2022])

[1.9566587e-04 3.6882045e-04 2.6386415e-03 1.0740548e-02 9.2218322e-01 7.3609320e-03 1.3715045e-03 8.1605352e-03 1.3376622e-02 3.3603624e-02]

Los resultados nos aparecen en base diez, pero podemos comprobar que el quinto puesto (que corresponde al dígito 4) es el que más valor tiene: 0.9221. El resto de resultados tienen un valor extremadamente pequeño (recordemos que entre todos deben sumar 1).

Conclusión

Con  este tutorial hemos ofrecido la posibilidad de construir una red neuronal desde cero con Keras. Recomendamos encarecidamente al lector que con estos conocimientos en mano realice sus propios experimentos, probando a añadir capas, aumentar o reducir el número de neuronas en cada una de ellas, cambiando la función de activación, etc…

Lecturas recomendadas

– Torres, J. (2020). Python Deep Learning. Introducción práctica con Keras y TensorFlow 2.

– Chollet, F. (20178). Deep Learning with Python.

Artículo relacionado

Redes Neuronales Artificiales

Las redes neuronales artificiales son el elemento básico del funcionamiento de los sistemas de aprendizaje profundo.

Bancos de imágenes para IA

Bancos de imágenes para IA

Los bancos de imágenes se han convertido en una herramienta indispensable en Deep Learning, necesarios para el entrenamiento de redes de IA.

Aprendizaje de Redes Neuronales

El aprendizaje de redes neuronales es la propiedad más característica de esos modelos computacionales altamente flexibles.

Series sobre
Deep Learning