Aprendizaje profundo con la ejecución ansiosa de TensorFlow

 

Deep Learning ha revolucionado la escena Aprendizaje automático en los últimos años. ¿Podemos aplicarla a la compresión de imágenes? ¿Qué tan bien puede un algoritmo de aprendizaje profundo reconstruir imágenes de gatitos? ¿Qué es un autocodificador?

Hoy encontraremos las respuestas a todas estas preguntas.

Compresión de imagen: todo sobre los patrones

He hablado sobre Aprendizaje no supervisado antes: aplicar Aprendizaje automático para descubrir patrones en datos sin etiquetar .

En el caso de la compresión de imagen, tiene mucho sentido suponer que la mayoría de las imágenes no es completamente aleatoria .

En palabras más adecuadas, es seguro asumir que la mayoría de las imágenes son no completamente hecho de ruido (como la estática cuando enciendes un televisor viejo), sino que más bien sigue algo de estructura subyacente .

Sería aún mejor si Sabía que todas las imágenes tendrán cosas en común. Una de las formas más rudimentarias de hacer la compresión de imágenes es encontrar secuencias repetidas de píxeles, asignándoles una etiqueta (más clara) y reemplazándolas con esa etiqueta.

Lo ideal es que si dos imágenes son casi iguales, un compresor podría almacenar ambas. tomando apenas más espacio que cualquiera de ellos. Eso debería aplicarse también a partes de la imagen. Finalmente, creo que una máquina de búsqueda de patrones como Nonupervised Learning debería ser buena en esto.

Para los propósitos de este trabajo, usaremos un conjunto de datos de fotos de gatitos que descargué de Kaggle . Eso garantizará que existe una estructura común para la mayoría de las imágenes.

Aquí está el Proyecto GitHub con el código y los datos de hoy, en caso de que quiera seguirlos. Sin embargo, como de costumbre, estaré agregando los fragmentos más importantes aquí.

¡Pero antes de ensuciarnos las manos con el código, es hora de aprender un aprendizaje profundo!

Autoencodificadores: Sin supervisión- ish Aprendizaje profundo

A pesar de que llamamos a los Autoencodificadores “Aprendizaje no supervisado”, en realidad son un algoritmo de aprendizaje supervisado disfrazado. Lo sé, ¡también me sorprendió!

Sin embargo, tienen una propiedad muy peculiar, que los distingue de los clasificadores normales: su entrada y salida son las mismas .

Entrenando un Autoencoder

Cuando entrenamos un Autoencoder, en realidad estaremos entrenando una Red Neural Artificial que

  • toma un vector de entrada X .
  • Aplica algunas matemáticas (no voy a entrar en los detalles del Aprendizaje Profundo en este momento, pero este es el libro que solía aprender estos temas).
  • Devuelve otro vector. En este caso particular, queremos que sea X nuevamente, o lo más cerca posible.

Sin embargo, hay un truco: en el proceso de avance (de entrada a salida), la Red neuronal reducirá la dimensionalidad de la entrada y la aumentará nuevamente antes de devolver la última capa.

La maldición de la dimensionalidad

El Autoencoder toma un vector X como entrada, con potencialmente muchos componentes. Por ejemplo, para una imagen de 3 canales – RGB – con una resolución de 48 × 48, X tendría 6912 componentes. Incluso si cada uno de ellos es solo un flotador, eso es 27Kb de datos para cada imagen (¡muy pequeña!) Eso se suma rápido. El tiempo que se tarda en realizar todas las operaciones matemáticas en tantos elementos también es muy relevante.

Por lo tanto, tendremos que mapear ese vector a un espacio más pequeño . En este caso particular, traté de reducirlo a la mitad de sus dimensiones originales.
Sin embargo, tenemos la intención de mantener la mayoría de los datos.

Arquitectura de un Autoencoder

Llamamos a la capa de la Red que reduce las dimensiones del vector de entrada el codificador porque codifica los datos en un vector más pequeño .

Luego operamos en ese vector más pequeño, en nuestro caso reduciendo sus dimensiones en un factor de 2 nuevamente. Esta sería la versión de la entrada que almacenaríamos si estuviéramos diseñando un compresor de imagen real, ahorrando un 75% de espacio. Sin embargo, durante el entrenamiento, esto es solo un paso intermedio antes de descodificarlo .

Finalmente, utilizando ese vector reducido, el Autoencoder tendrá que reconstruir la imagen original lo mejor que pueda .

Para hacer esto, primero lo hará pasar por una capa de decodificador que nuevamente asigna el vector reducido a uno con la mitad de las dimensiones del vector de entrada. Y luego una capa final, donde obtenemos un vector de las mismas dimensiones que la imagen original, con suerte muy similar.

Así es como se ve una arquitectura de capa de Autoencoder:

Fuente: Fuente: Medio

Los resultados de usar un Autoencoder

El entrenamiento de un Autoencoder tendrá muchos efectos:

  • Las imágenes reconstruidas no serán exactamente similares a las originales.
  • Podremos guardar el vector reducido mucho más ligero y luego usar la reconstrucción, siempre que sea lo suficientemente bueno (sin embargo lo definimos), en lugar de la imagen original.
  • Ahora tendremos una red neuronal que ha aprendido algunos de los patrones subyacentes en imágenes de gatitos.

Este último es el resultado más valioso.

Algunas de las aplicaciones de la industria para Autoencoders son:

  • Ingeniería de características para modelos supervisados ​​de aprendizaje profundo: en lugar de proporcionarles datos directamente, Los alimentaré con la capa oculta del Autoencoder.
  • Detección de anomalías : El autocodificador será muy malo en la reconstrucción de imágenes de perros, paisajes o insectos. Esto nos da una forma de comprobar si una imagen es efectivamente un gatito automáticamente.

Ahora que sabes por qué estamos haciendo lo que estamos haciendo, ¡ensuciándonos las manos con un código real!

Entrenando un Autoencoder con Tensorflow

Para este tutorial Estaré usando la ansiosa API de ejecución de Tensorflow. Debo decir que es mucho más intuitivo que la vieja sesión Session tanto que no me importaría si hubiera habido una caída en el rendimiento (que no percibí).

Nuestro conjunto de datos contiene más de 8000 imágenes de gatitos, de varios tamaños y colores, y en diferentes posiciones. Está disponible para que veas el proyecto GitHub junto con el código que lo carga.

Debido a la forma en que PyPlot maneja los arrays de flotadores numpy, y para acelerar la convergencia de la red, las imágenes se cargan como una matriz de flotadores que van de 0 a 1, en lugar de 0 a 255.

 # 10% de los datos se utilizarán automáticamente para la validación
validation_size = 0.1
img_size = 48 # cambiar el tamaño de las imágenes para que sea 48x48
num_channels = 3 # RGB
sample_size = 8192 # Usaremos 8192 imágenes (2 ** 13)

data = dataset.read_train_sets (train_path, img_size, ['cats'],
                               validation_size = validation_size,
                               sample_size = sample_size)

x_train, _, _, _ = data.train.next_batch (7373) #no es necesario cargarlos en lotes
x_valid, _, _, _ = data.valid.next_batch (819) # ahora que tensorflow puede hacerlo
x_train [0] # array ([[[0.6784314 , 0.6784314 , 0.7019608 ]...)

Diseñar el Autoencoder

Antes de que Tensorflow se tragara a Keras y se pusiera ansioso, escribir una Red neuronal con él era bastante engorroso. Ahora, su API se ha convertido en intuitiva .

Este es el primer Autoencoder que diseñé utilizando la API de Keras de Tensorflow.

 de Keras de importación de Tensorflow.
desde tensorflow.keras importar capas

total_pixels = img_size * img_size * 3
translator_factor = 2
translator_layer_size = int (total_pixels / translator_factor)
middle_factor = 2
middle_layer_size = int (translator_layer_size / middle_factor)

entradas = keras.Input (shape = (img_size, img_size, 3), name = 'cat_image')
x = layers.Flatten (name = 'flattened_cat') (entradas) #tornear imagen a vector.

x = layers.Dense (translator_layer_size, activación = 'relu', nombre = 'codificador') (x)
x = layers.Dense (middle_layer_size, activation = 'relu', name = 'middle_layer') (x)
x = layers.Dense (translator_layer_size, activación = 'relu', nombre = 'decodificador') (x)


salidas = layers.Dense (total_pixels, activación = 'relu', nombre = 'reconstructed_cat') (x)
output = layers.Reshape ((img_size, img_size, 3)) (output)

modelo = keras.Modelo (entradas = entradas, salidas = salidas)

Sin embargo, ¡este modelo tenía algunos defectos importantes! En primer lugar, estaba convergiendo muy lentamente, incluso con una tasa de aprendizaje muy alta (llegaremos allí). En segundo lugar, estaba devolviendo un vector con el tamaño correcto … ¡excepto que algunos de los elementos eran más altos que 1 ! Eso no encajó bien con los gráficos de PyPlot, ni con los anteriores de nuestras entradas, cada píxel tiene tres valores entre 0 y 1 -.

 figura-nombre

Solucioné ese problema reemplazando la función de activación ReLu en la última capa con una función de activación sigmoide por lo que siempre devolvió números entre 0 y 1.

Sin embargo, todavía existía el factor de velocidad de convergencia. ¿Qué podría hacer para resolver eso? Afortunadamente, he leído algunos artículos de Aprendizaje profundo aquí y allá, y he usado algunas veces llamado Normalización de lotes . Se merece su propio artículo, por lo que no voy a profundizar demasiado en él.

Normalización por lotes

La versión corta es, una capa de normalización por lotes calcula la media y la desviación estándar para la capa anterior, para el lote actual de instancias de entrenamiento. Luego resta la media y se divide por la desviación estándar, por lo que normaliza la salida de la capa (para el lote). En los avances posteriores al entrenamiento, la capa comenzará a normalizarse utilizando los parámetros estimados a partir de todo el conjunto de datos del entrenamiento.

Esto hace que el modelo converja mucho más rápido, ya que se vuelve menos sensible a los cambios en la distribución de las entradas, o capas ocultas También se volverá más robusta a natural cambios de distribución en los insumos, siendo más sostenible a largo plazo en un entorno productivo.

 de tensorflow import keras
desde tensorflow.keras importar capas

total_pixels = img_size * img_size * 3
translator_factor = 2
translator_layer_size = int (total_pixels / translator_factor)
middle_factor = 2
middle_layer_size = int (translator_layer_size / middle_factor)

entradas = keras.Input (shape = (img_size, img_size, 3), name = 'cat_image')
x = layers.Flatten (name = 'flattened_cat') (entradas) # 49152 # 3072
x = layers.BatchNormalization () (x)
x = layers.Dense (translator_layer_size, activación = 'relu', nombre = 'codificador') (x)
x = layers.Dense (middle_layer_size, activation = 'relu', name = 'middle_layer') (x)
x = layers.BatchNormalization () (x)
x = layers.Dense (translator_layer_size, activación = 'relu', nombre = 'decodificador') (x)

salidas = layers.Dense (total_pixels ,ctivation = 'sigmoid', name = 'reconstructed_cat') (x)
output = layers.Reshape ((img_size, img_size, 3)) (output)

modelo = keras.Modelo (entradas = entradas, salidas = salidas)

De esta manera, el decodificador solo necesita aprender a decodificar variables centradas con una varianza de 1, y no solo de algo aleatorio. Lo mismo se aplica al codificador. Este solo cambio hizo que el modelo comenzara a converger en las primeras 5 o 6 épocas.

Entrenando el Autoencoder

¡Es el momento que todos estábamos esperando! ¡Es hora de entrenar nuestro Autoencoder!

La ejecución impaciente de Tensorflow requiere que primero compilemos el modelo.
Esto significa que definimos un optimizador (estoy usando Adam, es rápido), una pérdida (en este caso, significa error cuadrado que es una forma bastante estándar de medir el error de reconstrucción), y las métricas de monitoreo. Lo más importante es que aquí es donde elegiremos la velocidad de aprendizaje del modelo.

 customAdam = keras.optimizers.Adam (lr = 0.001) # no tiene idea de cuántas veces cambié este número
model.compile (optimizer = customAdam, # Optimizer
              # Función de pérdida para minimizar.
              pérdida = "mean_squared_error",
              # Lista de métricas a monitorear.
métricas = ["mean_squared_error"])

Y finalmente, comenzamos a entrenar el modelo.

 imprimir ('# Ajustar modelo en datos de entrenamiento')

history = model.fit (x_train,
                    x_train, # pasamos los mismos datos de entrada que la salida deseada
                    
                    # Si el modelo tarda mucho en entrenar, hazlo más grande
                    # Si la carga de la primera época demora una eternidad, haz esto más pequeño
                    batch_size = 256,
                    
                    épocas = 10,
                    # Le pasamos los datos de validación a
                    # monitorear pérdidas y métricas
                    # al final de cada epoca
validation_data = (x_valid, x_valid))

Esto comenzará el entrenamiento (y hará que mi computadora portátil suene como un horno victoriano) e imprimirá mensajes como este:

 Época 2/2 7373/7373 [==============================] - 59s 8ms / muestra - pérdida: 0.0183 - mean_squared_error: 0.0183 - val_loss: 0.0350 - val_mean_squared_error: 0.0350

Sabremos que el modelo está aprendiendo si val_mean_squared_error el MSE en el conjunto de datos de validación, sigue convergiendo hacia 0. Si la pérdida de tren sigue cayendo pero la pérdida de validación es estable o, peor aún, sigue aumentando entonces eso significa que el modelo es sobreajuste . ¿Qué hacemos entonces? Podemos

  • agregar más datos,
  • reducir la compresión o
  • aumentar la velocidad de aprendizaje.

Esto no fue un problema que tuve con demasiada frecuencia.

En el lado opuesto del espectro , la pérdida de entrenamiento puede ser mala.

¿Qué tan mala? Bueno, el valor que obtendríamos si muestreamos números aleatorios entre 0 y 1 sería alrededor del 25%, ya que es el cuadrado de 1/2, que es el error aleatorio promedio. Eso significaría que estamos subequipados .

Para la mayoría de las configuraciones que probé, esa fue la pérdida de entrenamiento durante mucho tiempo (por ejemplo, sin usar la normalización por lotes, o cuando muestre menos de la mitad de la data).

Si eso sucede, deberíamos disminuir la velocidad de aprendizaje (para evitar la convergencia a un mínimo local), aumentar el tamaño del conjunto de datos de entrenamiento o ser menos ambiciosos y comprimir menos los datos.

Primero traté de comprimir una novena parte del peso original en lugar de una cuarta, y no había forma de que la red supiera eso.

Los resultados

Y, finalmente, ¡aquí están algunos de los resultados!

 nombre-figura
 nombre-figura
 nombre-figura
 nombre-figura

el modelo fue particularmente bueno con los gatos negros o blancos, probablemente porque tenían el mayor contraste y los colores más extremos.

Esto es lo que ocurrió cuando intenté comprimir la entrada a una novena parte de su tamaño original:

 figure-name
 nombre de la figura

Básicamente solo acertó los esquemas, y solo funcionó en gatos negros o gris oscuro.

Conclusiones

Me sorprendieron los resultados: comprimir la imagen a un cuarto de su tamaño con el gato aún reconocible, significa que un clasificador probablemente podría decir que había un gato en la imagen.

La API actual de Tensorflow es mucho más cómoda e intuitiva que la anterior, y me alegro de que finalmente pueda realizar un aprendizaje profundo sin pensar en las sesiones y los gráficos.

Creo que la normalización por lotes demostró ser bastante efectivo para acelerar el entrenamiento, y es una herramienta que debería usar más a menudo.

Por último, en el futuro me gustaría probar diferentes algoritmos de compresión de imágenes. K-Means Clustering los basados, y tal vez un Autoencoder Convolutional. Sin embargo, esa es una idea para un artículo diferente.

¿Qué hay de ti? ¿Crees que puedes entrenar un mejor codificador automático para este conjunto de datos? ¿Cuáles son algunas de las 1000 cosas que debería haber hecho mejor, o los hiperparámetros que olvidé modificar y que podrían haber mejorado mucho el rendimiento del modelo? Déjame saber en los comentarios, todavía estoy aprendiendo.

Espero que hayas encontrado este artículo entretenido o útil, y tal vez que hayas aprendido algo en el camino.

Si deseas aprender más sobre Deep Learning y convertirse en un profesional en él, no puedes equivocarte con Goodfellow y Bengio Deep Learning book .
Sus primeros capítulos hicieron que Deep Learning finalmente hiciera clic para mí, y el de Autoencoders. es oro puro.

 

Dejá un comentario