En este tutorial vamos a contruir y entrenar una red neuronal en Python paso a paso y desde cero.
Las redes neuronales tienen mucho que ver con la regresión logística así que te recomiendo que si no lo has hecho todavía le eches un vistazo a este tutorial.
De hecho una regresión logística no es más que una red neuronal super sencilla con una única capa y una función de activación sigmoide.
El entrenamiento es el mismo.
Como digo una red neuronal es como resolver el problema de regresión logística a lo grande puesto que la regresión logística es básicamente una neurona individual con una operación lineal seguida de una función de activación sigmoide.
En una red neuronal, múltiples de estas "neuronas" se combinan y se organizan en capas para crear un modelo más complejo y poderoso.
Cada neurona en estas capas realiza dos operaciones principales:
Para construir nuestra red neuronal desde cero y que no se nos haga bola vamos a hacerlo por partes.
Como funciones de activación tendremos una sigmoide y la función ReLU, un clásico de las redes neuronales.
Seguimos...
La salida de nuestra red con los coeficientes que hemos inicializado en el primer paso será un churro.
Nuestro objetivo es actualizar los coeficientes de manera iterativa hasta que el error entre la predicción de nuestra red y el resultado real sea lo menor posible.
Esto suena a optimización con el algoritmo de descenso de gradiente.
Cada paso de este proceso de entrenamiento será una función.
Primero escribiremos el código de todas estas funciones en Numpy y después podremos entrenar la red para que sea capaz de clasificar gatitos en una imagen (como no 😽)
Lo primero es importar las librerías que vamos a utilizar...
import numpy as np import copy import matplotlib.pyplot as plt
¡Y al lío!
Ya tenemos todos los pasos para poder construir y entrenar una red neuronal desde cero:
En este ejemplo vamos a clasificar imágenes de gatitos 🐱
Vamos a utilizar un dataset con imágenes de baja resolución y un Notebook de Google Colab. No es necesario que te instales nada, solo te hace falta una cuenta en Google 🙂
Así que te aconsejo que vayas siguiendo el tutorial en tu propio Notebook de Colab, será bastante más divertido que sólo leer 😉
Para poder utilizar el dataset directamente en Google Colab sólo tienes que seguir este tutorial y sustituir el dataset que se descarga en ese tutorial por el que realmente queremos utilizar aquí.
!kaggle datasets download -d martaarroyo/pixelpurr-small-size-catnot-cat-image-dataset
Esto es lo que encontrarás:
1
(😻)0
(😿)Vamos a ver qué nos encontramos...
train_data_flattened = np.loadtxt('train_images.csv', delimiter=',') train_labels = np.loadtxt('train_labels.csv', delimiter=',') test_data_flattened = np.loadtxt('test_images.csv', delimiter=',') test_labels = np.loadtxt('test_labels.csv', delimiter=',')
Lo primero que haremos es escalar la intensidad de los píxeles para poder visualizar alguna imagen.
train_data_flattened = train_data_flattened/255 test_data_flattened = test_data_flattened/255
Después tenemos que darle forma de imagen porque ahora mismo cada imagen es un vector con todos los píxeles seguidos unos detrás de otro.
Es por ello que la matriz de Numpy con las imágenes de entrenamiento tiene unas dimensiones de (209, 12288)
:
Para visualizar la imagen tenemos que devolverla a su forma original:
train_data = train_data_flattened.reshape(209, 64, 64, 3) test_data = test_data_flattened.reshape(50, 64, 64, 3) print(f"Las dimensiones del dataset de entrenamiento son: {train_data.shape}") print(f"Las dimensiones de las etiquetas de entrenamiento son: {train_labels.shape}") print(f"Las dimensiones del dataset de test son: {test_data.shape}") print(f"Las dimensiones de las etiquetas de test son: {test_labels.shape}")
Las dimensiones del dataset de entrenamiento son: (209, 64, 64, 3) Las dimensiones de las etiquetas de entrenamiento son: (209,) Las dimensiones del dataset de test son: (50, 64, 64, 3) Las dimensiones de las etiquetas de test son: (50,)
¡Vamos a visualizar una! 🔍
index = 25 plt.imshow(train_data[index]) print(f"La imagen {index} tiene la etiqueta = {train_labels[index]}")
Además, para no tener problemas con las dimensiones de los vectores numpy de etiquetas vamos a redimensionarlos:
train_labels = train_labels.reshape(1, -1) test_labels = test_labels.reshape(1, -1)
La arquitectura de la red que vamos a usar va a tener 4 capas:
Usaremos una tasa de aprendizaje de 0.0075 y ejecutaremos la optimización del descenso de gradiente 2500 veces.
layers_dims = [12288, 20, 7, 5, 1] alpha = 0.0075 num_iters = 2500
Puedes probar a cambiar la arquitectura de la red y los hiperparámetros y ver si mejoras los resultados 🙂
Para la fase de entrenamiento únicamente tenemos que unir todos los bloques que hemos hecho hasta ahora en una única función que ejecutará la optimización con el algoritmo del descenso de gradiente.
Recuerda que la idea es actualizar los coeficientes del modelo de manera iterativa según el gradiente de la función de coste.
def entrenamiento(X, Y, layers_dims, learning_rate = 0.0075, num_iters = 3000, imprime_coste=False): """ Implementa una red neuronal de L capas: [LINEAR->RELU]*(L-1)->LINEAR->SIGMOID. Argumentos: X -- datos de entrada. Array de numpy con la imagen vectorizada y dimensiones (num_px * num_px * 3, número de ejemplos) Y -- vector de "etiquetas" verdaderas (contiene 1 si es gato, 0 si no es gato). Dimensiones: (1, número de ejemplos) layers_dims -- lista que contiene el tamaño de entrada y el tamaño de cada capa, de longitud (número de capas + 1). learning_rate -- tasa de aprendizaje del descenso del gradiente num_iters -- número de iteraciones del bucle de optimización imprime_coste -- si es True, imprime el coste cada 100 pasos Devuelve: parameteros -- parámetros aprendidos por el modelo. """ np.random.seed(1) # Para conseguir que nuestros resultados sean reproducibles costes = [] # Guardamos un histórico del coste # Inicializacion de coeficientes del modelo parametros = inicializa_coef_deep(layers_dims) # Descenso de gradiente for i in range(0, num_iters): # Propagación (forward pass): [LINEAR -> RELU]*(L-1) -> LINEAR -> SIGMOID. AL, caches = modelo_forward(X, parametros) # Cálculo del coste coste = funcion_coste(AL, Y) # Retropropagación (backpropagation grads = modelo_backward(AL, Y, caches) # Actualización de parámetros parametros = actualiza_coefs(parametros, grads, learning_rate) # Imprimimos el coste cada 100 iteraciones if imprime_coste and i % 100 == 0 or i == num_iters - 1: print(f"Coste después de la iteración {i}: {np.squeeze(coste)}") if i % 100 == 0 or i == num_iters: costes.append(coste) return parametros, costes
Seleccionamos los datos de entrenamiento (imágenes y etiquetas), la artquitectura de la red (layers_dims
), la tasa de aprendizaje y el número de iteraciones.
¡Y a entrenar!
parametros, costes = entrenamiento(X_train, train_labels, layers_dims, learning_rate = 0.0075, num_iters = 2500, imprime_coste=True)
Después de un número suficiente de iteraciones (num_iters
) la función de coste debería haber disminuido mucho.
Coste después de la iteración 0: 0.7717493284237686 Coste después de la iteración 100: 0.6720534400822913 Coste después de la iteración 200: 0.6482632048575212 Coste después de la iteración 300: 0.6115068816101354 Coste después de la iteración 400: 0.567047326836611 Coste después de la iteración 500: 0.5401376634547801 Coste después de la iteración 600: 0.5279299569455267 Coste después de la iteración 700: 0.4654773771766852 Coste después de la iteración 800: 0.369125852495928 Coste después de la iteración 900: 0.3917469743480535 Coste después de la iteración 1000: 0.31518698886006163 Coste después de la iteración 1100: 0.27269984417893856 Coste después de la iteración 1200: 0.23741853400268134 Coste después de la iteración 1300: 0.19960120532208644 Coste después de la iteración 1400: 0.18926300388463305 Coste después de la iteración 1500: 0.1611885466582775 Coste después de la iteración 1600: 0.14821389662363316 Coste después de la iteración 1700: 0.13777487812972944 Coste después de la iteración 1800: 0.1297401754919012 Coste después de la iteración 1900: 0.12122535068005211 Coste después de la iteración 2000: 0.11382060668633714 Coste después de la iteración 2100: 0.10783928526254133 Coste después de la iteración 2200: 0.10285466069352679 Coste después de la iteración 2300: 0.10089745445261787 Coste después de la iteración 2400: 0.09287821526472397 Coste después de la iteración 2499: 0.088439943441702
Una vez que hemos entrenado la red neuronal y ya tenemos los parámetros de la red podemos clasificar nuevas imágenes.
AL, _ = modelo_forward(X_test, parametros) pred = AL > 0.5
index = 10 plt.imshow(test_data[index]) print(f"La imagen {index} tiene la etiqueta = {test_labels[0,index]}")
¡Voilà! Un gatito 😽
Para calcular la precisión de la red sobre el conjunto de imágenes de test sólo tenemos que comprobar cuantas veces nos hemos equivocado de etiqueta.
print(f"La precisión de la red neuronal es: {np.sum(pred == test_labels)/X_test.shape[1]}")
En este caso hemos obtenido una precisión de 0.8 🙂