Qué son y cómo crear una red neuronal convolucional con Keras

Tratando imágenes mediante redes neuronales convolucionales con Keras

Las redes neuronales convolucionales consiste en aplicar redes neuronales sobre imágenes. ¿Sobre imágenes? Pues sí. Clasificar imágenes, detectar qué contienen, generar nuevas imágenes… todo esto es posible mediante redes neuronales convolucionales. En este post te voy a explicar qué son y cómo puedes crear una red neuronal convolucional en Keras con Python. ¿Suena interesante verdad? ¡Pues vamos a ello!

De imágenes a números

Una de las primeras preguntas que solemos hacernos cuando nos enfrentamos a este problema es, ¿cómo podemos trabajar sobre una imagen? Pues convirtiéndolo en números. Te explico cómo.

Pensemos en una imagen en monocromática (que tiene colores blanco o negro, como esta). Si lo piensas una imagen no son más que muchos píxeles (cuadraditos) juntos. Cada uno de esos píxeles guarda información. En el caso de las imágenes monocromáticas, por ejemplo, el pixel guarda dos valores: 1 si es blanco y 0 si es negro (o al revés).

Por tanto, si lo piensas, una imagen monocromática no es más que un dataset grande de unos y ceros. Pero, ¿qué pasa con las imágenes a color?

Pues la lógica es la misma, solo que en este caso, no hay una única capa, sino tres: una roja (R), una verde (G) y una azul (B). RGB. ¿Te suena de algo? Supongo que sí, ya que es la composición cromática de la mayoría de imágenes para pantallas.

En esta imágen podéis ver claramente como una imagen a color consta de 3 capas o layers las cuales, cuando las juntas, generan la imagen final.

<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?config=TeX-AMS_HTML"></script>
<!-- MathJax configuration -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
    tex2jax: {
        inlineMath: [ ['$','$'], ["\\(","\\)"] ],
        displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
        processEscapes: true,
        processEnvironments: true
    },
    // Center justify equations in code and markdown cells. Elsewhere
    // we use CSS to left justify single line equations in code cells.
    displayAlign: 'center',
    "HTML-CSS": {
        styles: {'.MathJax_Display': {"margin": 0}},
        linebreaks: { automatic: true }
    }
});
</script>
<!-- End of mathjax configuration --></head>

Tratando imágenes mediante redes neuronales convolucionales con Keras

Las redes neuronales convolucionales consiste en aplicar redes neuronales sobre imágenes. ¿Sobre imágenes? Pues sí. Clasificar imágenes, detectar qué contienen, generar nuevas imágenes… todo esto es posible mediante redes neuronales convolucionales. En este post te voy a explicar qué son y cómo puedes crear una red neuronal convolucional en Keras con Python. ¿Suena interesante verdad? ¡Pues vamos a ello!

De imágenes a números

Una de las primeras preguntas que solemos hacernos cuando nos enfrentamos a este problema es, ¿cómo podemos trabajar sobre una imagen? Pues convirtiéndolo en números. Te explico cómo.

Pensemos en una imagen en monocromática (que tiene colores blanco o negro, como esta). Si lo piensas una imagen no son más que muchos píxeles (cuadraditos) juntos. Cada uno de esos píxeles guarda información. En el caso de las imágenes monocromáticas, por ejemplo, el pixel guarda dos valores: 1 si es blanco y 0 si es negro (o al revés).

Por tanto, si lo piensas, una imagen monocromática no es más que un dataset grande de unos y ceros. Pero, ¿qué pasa con las imágenes a color?

Pues la lógica es la misma, solo que en este caso, no hay una única capa, sino tres: una roja (R), una verde (G) y una azul (B). RGB. ¿Te suena de algo? Supongo que sí, ya que es la composición cromática de la mayoría de imágenes para pantallas.

En esta imágen podéis ver claramente como una imagen a color consta de 3 capas o layers las cuales, cuando las juntas, generan la imagen final.

Y aunque visto en la imagen parece muy simple, seguro te han surgido alguna de las siguientes dudas:

  • ¿Qué dimensiones tiene que tener el Kernel?
  • ¿Qué valores?
  • ¿Cómo se crea una capa convolucional?
  • Si con cada capa, el resultado es más pequeño… ¿es que hay un límite de capas convolucionales que podamos meter?

Pues bien, vayamos por partes.

Qué dimensiones tiene que tener el Kernel de una red neuronal convolucional

Pues bien, para la primera pregunta no hay una respuesta exacta. Sí que parece que una red con muchas capas y kernels más pequeños es más eficiente que una red neuronal convolucional que tenga menos capas y Kernels más grandes (link). De hecho, lo más normal es que el Kernel tenga una dimensión de 3×3 (enlace).

Conclusión: usamos un Kernel de 3×3.

Qué valores tiene que tener el Kernel de una red neuronal convolucional

Aunque haya ciertas estructuras de filtros que permiten detectar ciertos tipos de formas, estos se suelen dejar como parámetros que la red neuronal deberá optimizar. Es decir, al igual que los pesos en las redes neuronales fully connected, el Kernel suele inicializarse con valores aleatorios para después optimziarse.

¿Cómo se crea una capa convolucional?

Crear una capa convolucional es muy sencillo. Habría dos formas de hacer, con Tensorflow o Keras:

  • Tensorflow: tf.nn.conv2d()
  • Keras: model.add(layers.Conv2D())
¿Hay un límite de capas convolucionales que puedes meter?

Efectivamente, con cada convolución, se reduce el tamaño del resultado, por lo que si no se hace nada, sí que habría un número máximo de capas convolucionales que se pueden aplicar. Pero, esto mismo lo podemos evitar incluyendo lo que se conoce como padding o relleno a la imagen.

El padding básicamente consiste en añadir un borde de una cierta cantidad de pixeles a la imagen antes de aplicar la convolución, de tal manera que el resultado de la convolución tenga el mismo tamaño que la matriz de entrada sin el relleno.

Con todo esto ya tendríamos explicado qué son las capas convolucionales, cómo funcionan y cómo se programan en Tensorflow y Keras. Ahora, vayamos con las capas de pooling.

Capa de Pooling

Como habrás visto, las capas convolucionales permiten detectar patrones en la imagen pudiendo mantener el tamaño del resultado. ¿Qué es lo que pasa con eso? Pues básicamente que al mantener el tamaño de la imagen, el proceso es muy difícil. Al fin y al cabo una imagen tiene muchos píxeles. Para solucionar esto están las capas de pooling.

Las capas de pooling permiten reducir el peso de la representación para así agilizar el proceso de aprendizaje de la red neuronal. Además, suele hacer que el resultado de las capas convolucionales sea más robusto. Suena bien, pero, ¿en qué consisten?

El funcionamiento de una capa de pooling es bastante sencillo. Supongamos que tenemos una matriz de 4×4. Si aplicamos una capa de Max Pooling, por ejemlo, solo tendríamos que dividir la matriz en 4 cuadrantes y crear una nueva matriz con el valor más alto que haya dentro de cada uno de los resultados. Te pongo un ejemplo:

Ejemplo de Max Pooling

¿Qué conseguimos con esto? Pues básicamente, si el valor es alto significa que la capa convolucional habrá encontrado un patrón, de tal forma que las capa de max pooling nos aseguran que los patrones detectados en la capa convolucional se mantengan en la siguiente capa de la red.

Además de la capa de max pooling existe también la capa de average pooling, la cual calcula el promedio en vez de el valor más alto. Sin embargo esta capa no se suele usar tanto como la capa de max pooling, ya que no suele dar tan buenos resultados.

Además, las capas de max pooling no tienen ningún parámetro que la red tendrá que apreneder, lo cual facilita el proceso. Lo que sí existen es una serie de hiperparámetros que tendremos que elegir y que serán fijos.

Una vez más, la funciónd e maxpooling la podemos aplicar tanto en Tensorflow como en Keras:

  • Tensorflow: tf.nn.max_pool2d()
  • Keras: tf.keras.layers.MaxPool2D()

Sabiendo esto, ya podríamos comenzar con nuestra red neuronal convolucional.

Creando una red neuronal convolucional

Teniendo en cuenta todo lo anterior, vamos a crear una red neuronal convolucional. La estructura que seguiremos serán:

  1. Una capa convolucional 3×3 (sin paddings) seguida de una capa de MaxPooling de 2×2
  2. Una capa convolucional 3×3 (sin paddings) seguida de una capa de MaxPooling de 2×2
  3. Aplanar el resultado para poder aplicar una

In [5]:

import tensorflow as tf

model = tf.keras.models.Sequential()

# Añadimos la primera capa
model.add(tf.keras.layers.Conv2D(64,(3,3), activation = 'relu', input_shape = (128,128,3)))
model.add(tf.keras.layers.MaxPooling2D(pool_size = (2,2)))

# Añadimos la segunda capa
model.add(tf.keras.layers.Conv2D(64,(3,3), activation = 'relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size = (2,2)))

# Hacemos un flatten para poder usar una red fully connected
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(64, activation='relu'))

# Añadimos una capa softmax para que podamos clasificar las imágenes
model.add(tf.keras.layers.Dense(2, activation='softmax'))


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

Ahora que ya tenemos el modelo creado vamos a:

  • Reclasificar las etiquetas del df en perro y gato.
  • Crear df de train y test.
  • Generador de batches y aplicación de data agumentation (cortesía del notebook de Usimity)

Como veréis, si bien los dos primeros pasos son similares a lo que haríamos en una red neuronal normal, el tercero paso es algo nuevo. Y es que, tratar con imágenes es un proceso que requiere de mucha memoria, por lo que no podemos hacerlo todo de golpe, sino que tenemos que hacerlo por lotes o o batches.

Además, podemos aplicar ciertas distorsiones a la imagen (zoom, darle la vuelta, etc.) para crear una imagen nueva y así aumentar nuestro dataset de entrenamiento. Todo esto lo hacemos con la función ImageDataGenerator de Keras. In [7]:

from sklearn.model_selection import train_test_split

# Convertimos la variable category en Gato o Perro
df["category"] = df["category"].replace({0: 'gato', 1: 'perro'})

# Creamos los df de train y test con un split del 75-25
df_train, df_test = train_test_split(df, test_size=0.25, random_state=42)

# Creamos unas modificaciones sobre las imágenes para tener más cantidad de imágenes con las que entrenar.
datos_train = tf.keras.preprocessing.image.ImageDataGenerator(
    rotation_range=15,
    rescale=1./255,        # Normalizar la imagen
    shear_range=0.1,
    zoom_range=0.2,
    horizontal_flip=True,
    width_shift_range=0.1,
    height_shift_range=0.1
)

# Generador de imágenes 
tamaño_batch = 15
generador_train = datos_train.flow_from_dataframe(
    df_train, 
    "train/", 
    x_col='filename',
    y_col='category',
    target_size= (128,128),
    class_mode= 'categorical',
    batch_size= tamaño_batch
)


# Repetimos el proceso para test
datos_test =  tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
generador_test = datos_test.flow_from_dataframe(
    df_test, 
    "train/", 
    x_col='filename',
    y_col='category',
    target_size=(128,128),
    class_mode='categorical',
    batch_size=tamaño_batch
)
Found 18750 validated image filenames belonging to 2 classes.
Found 6250 validated image filenames belonging to 2 classes.

Ya con esto podemos entrenar el modelo. In [8]:

epochs=3 
historia = model.fit_generator(
    generador_train, 
    epochs=epochs,
    validation_data=generador_test,
    validation_steps=df_test.shape[0]//tamaño_batch,
    steps_per_epoch=df_train.shape[0]//tamaño_batch
)
WARNING:tensorflow:From <ipython-input-8-34fa295a25fe>:7: Model.fit_generator (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
Please use Model.fit, which supports generators.
WARNING:tensorflow:sample_weight modes were coerced from
  ...
    to  
  ['...']
WARNING:tensorflow:sample_weight modes were coerced from
  ...
    to  
  ['...']
Train for 1250 steps, validate for 416 steps
Epoch 1/3
1250/1250 [==============================] - 516s 413ms/step - loss: 0.6497 - accuracy: 0.6429 - val_loss: 0.5669 - val_accuracy: 0.7091
Epoch 2/3
1250/1250 [==============================] - 403s 322ms/step - loss: 0.5747 - accuracy: 0.7042 - val_loss: 0.5024 - val_accuracy: 0.7591
Epoch 3/3
1250/1250 [==============================] - 396s 317ms/step - loss: 0.5385 - accuracy: 0.7395 - val_loss: 0.5020 - val_accuracy: 0.7505

Con esto ya tendríamos creada nuestro clasficador de imágenes. ¿Fácil verdad? Ya solo queda ver cómo ha ido aprendiendo nuestra red neuronal convolucional hecha en Keras. ¡Veamos! In [17]:

import matplotlib.pyplot as plt

acc = historia.history['accuracy']
epochs = range(epochs)
plt.plot(epochs, acc)
plt.title('Training and validation loss')

Out[17]:

Text(0.5, 1.0, 'Training and validation loss')

Como ves, en solo 3 epochs, nuestra red neuronal convolucional ha conseguido aprender y tener una tasa de acierto de casi el 75%, lo cual para tan pocas vueltas, está muy bien.

Espero que este tutorial te haya servido para entender un poco mejor cómo funciona el procesamiento de imágenes, las técnicas que se usan y cómo se programa en Keras.

Blog patrocinado por: