Entrenamiento de redes convolucionales y teoría de la información

En un post pasado de teoría de la información en deep learning vimos como Naftali Tishby y su equipo han aplicado con éxito el método del cuello de botella de la información al deep learning.

En sus simulaciones analizaron cuánta información retiene cada capa de una red neuronal de la entrada y de la salida (objetivo) y cómo varía durante el proceso de entrenamiento.

A continuación vamos a hacer un experimento con una red convolucional (CNN) observando cómo evolucionan las representaciones en las capas intermedias en el proceso de entrenamiento. De esta forma, podremos hacernos una idea intuitiva de qué información se retiene de la entrada en el proceso de entrenamiento.

Para una descripción de cómo funcionan las redes convolucionales se pueden ver buenos tutoriales como este.

La red convolucional que vamos a utilizar consta de las siguientes capas:

  • Una capa con 20 filtros de convolución con su correspondiente función de activación con rectificador.
  • Una capa de pooling 2:1.
  • Una capa con 50 filtros de convolución con su correspondiente función de activación con rectificador.
  • Una capa de pooling 2:1.
  • Se convierten los filtros resultantes de dos dimensiones en 2450 unidades de una dimensión.
  • Una capa densa de 500 unidades con su correspondiente función de activación con rectificador.
  • Una última capa de 10 unidades con softmax para clasificación.

Vemos la arquitectura del modelo y un resumen en Keras.

Arquitectura red convolucional

Modelo CNN Keras

Los datos de entrada son los dígitos MNIST, que se han puesto en formato 60K x [1 x 28 x 28], convertiendo a float y normalizado a 1.

Para visualizar el contenido de las capas, una vez creado y entrenado el modelo, lo pasamos como entrada a una clase tipo Modelo de Keras que devuelve los valores de las salidas de las capas. Al final del post incluimos el código fuente utilizado.

En el experimento, introducimos un dígito “7” en el modelo y vemos cómo cambian las salidas de los filtros de la primera capa (observamos ampliado el filtro 11) de pooling en función del número de epochs (un epoch es una iteración sobre todas las muestras de entrenamiento) realizados en el entrenamiento.

Dígito 7

Entrenamos el modelo con un solo epoch y esto es lo que observamos.

Salida capa intermedia CNN Keras

Salida un filtro capa intermedia CNN Keras

Vemos como al principio del entrenamiento las capas todavía retienen mucha información de la entrada poco relevante para la salida (predicción del dígito observado).

Ahora hacemos lo mismo con 20 epochs.

Salida capa intermedia CNN Keras Epoch 20 Salida capa intermedia CNN Keras Epoch 20

Vemos que ya ha empezado la fase de compresión y la red se desprende de parte de la información de la entrada, reteniendo sólo las características más relevantes para la salida (objetivo). Parece como si se eliminara el ruido comprimiendo la información. En esta fase es donde mejora la generalización de la red clasificando mejor las entradas no vistas en el entrenamiento.

Por lo tanto, parece que nuestros resultados corroboran de manera intuitiva y pedagógica la interpretación del proceso de entrenamiento del deep learning desde el punto de vista de teoría de la información.

Código en Pyhton (puede haber problemas con la sangría por el copiado del código).


# se importan los paquetes
from keras import backend as K
from keras import models
from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Dense
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.datasets import mnist
from keras.utils import np_utils
from keras.optimizers import SGD, RMSprop, Adam
import numpy as np

import matplotlib.pyplot as plt

np.random.seed(1671) # semilla para reproducibilidad

#Se define la clase con la red convolucional
class LeNet:
@staticmethod
def build(input_shape, classes):
model = Sequential()
# CONV => RELU => POOL
model.add(Conv2D(20, kernel_size=5, padding="same",
input_shape=input_shape))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
# CONV => RELU => POOL
model.add(Conv2D(50, kernel_size=5, padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
# Flatten => RELU layers
model.add(Flatten())
model.add(Dense(500))
model.add(Activation("relu"))

# softmax para clasificación
model.add(Dense(classes))
model.add(Activation("softmax"))

return model

# parámetros
NB_EPOCH = 20
BATCH_SIZE = 128
VERBOSE = 1
OPTIMIZER = Adam()
VALIDATION_SPLIT=0.2

IMG_ROWS, IMG_COLS = 28, 28 # Las dimensiones de cada imagen son 28x28
NB_CLASSES = 10 # La salida tendrá 10 clases o digitos diferentes
INPUT_SHAPE = (1, IMG_ROWS, IMG_COLS)

# carga de los datos de entrenamiento y prueba
(X_train, y_train), (X_test, y_test) = mnist.load_data()
K.set_image_dim_ordering("th")

# conversión de los datos en float y normalización
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255

# Se añade un eje para formato de los datos de entrada 60K x [1 x 28 x 28]
X_train = X_train[:, np.newaxis, :, :]
X_test = X_test[:, np.newaxis, :, :]

print(X_train.shape[0], 'train samples')
print(X_test.shape[0], 'test samples')

# convertir las clases o dígitos en matrices binarias
y_train = np_utils.to_categorical(y_train, NB_CLASSES)
y_test = np_utils.to_categorical(y_test, NB_CLASSES)

# inicializar el modelo y el optimizador
model = LeNet.build(input_shape=INPUT_SHAPE, classes=NB_CLASSES)
model.compile(loss="categorical_crossentropy", optimizer=OPTIMIZER,
metrics=["accuracy"])

# entrenar el modelo
history = model.fit(X_train, y_train,
batch_size=BATCH_SIZE, epochs=NB_EPOCH,
verbose=VERBOSE, validation_split=VALIDATION_SPLIT)

# evaluar el modelo con los datos de test
score = model.evaluate(X_test, y_test, verbose=VERBOSE)
print("\nTest score:", score[0])
print('Test accuracy:', score[1])

# resumen del modelo
model.summary()

# Tensor para extraer las salidas de las capas
layer_outputs = [layer.output for layer in model.layers[:6]]
# Modelo al que se le pasa el modelo creado y devuelve las salidas
activation_model = models.Model(inputs=model.input, outputs=layer_outputs)

#Convertimos la imagen de entrada en un tesor 4D
X1_test=X_test[0]
X1_test = X1_test[:, np.newaxis, :, :]
X1_test.shape

plt.imshow(X_test[0,0], cmap=plt.get_cmap('gray'))
# Mostramos la imagen de entrada
plt.show()

# Devuleve una lista de arrays para cada capa
# del resultado de predecir la imagen de entrada
activations = activation_model.predict(X1_test)

# Mostramos los valores de todos los filtros en una capa seleccionada
first_layer_activation = activations[2]
print(first_layer_activation.shape)
for i in range(0, 20):
plt.subplot(4,5,i+1)
plt.imshow(first_layer_activation[0, i, :, :], cmap=plt.get_cmap('gray'))

plt.show()

plt.imshow(first_layer_activation[0, 10, :, :], cmap=plt.get_cmap('gray'))
plt.show()

Un comentario sobre “Entrenamiento de redes convolucionales y teoría de la información

Agrega el tuyo

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

Orgullosamente ofrecido por WordPress | Tema: Baskerville 2 por Anders Noren.

Subir ↑

A %d blogueros les gusta esto: