Aprendizaje profundo aplicado-Parte 3: Autoencoders

Bienvenido a la Parte 3 de la serie de Aprendizaje Profundo aplicado. La Parte 1 fue una introducción práctica a las Redes Neuronales Artificiales, cubriendo tanto la teoría como la aplicación con muchos ejemplos de código y visualización. En la Parte 2 aplicamos el aprendizaje profundo a conjuntos de datos del mundo real, cubriendo los 3 problemas más comunes como estudios de caso: clasificación binaria, clasificación multiclase y regresión.

Ahora comenzaremos a bucear en arquitecturas de aprendizaje profundo específicas, comenzando con las más simples: los Autoencoders.

  1. Introducción
  2. Arquitectura
  3. Implementación
  4. Autoencoders de eliminación de ruido
  5. Autoencoders dispersos
  6. Casos de uso
  7. Conclusión

El código de este artículo está disponible aquí como cuaderno de Jupyter, no dude en descargarlo y probarlo usted mismo.

Introducción

Los autoencoders son un tipo específico de redes neuronales de alimentación directa donde la entrada es la misma que la salida. Comprimen la entrada en un código de dimensiones inferiores y luego reconstruyen la salida a partir de esta representación. El código es un «resumen» compacto o «compresión» de la entrada, también llamada representación de espacio latente.

Un autoencoder consta de 3 componentes: codificador, código y decodificador. El codificador comprime la entrada y produce el código, el decodificador reconstruye la entrada solo con este código.

Para construir un autoencoder necesitamos 3 cosas: un método de codificación, decodificación método, y una función de pérdida para comparar el resultado con el de destino. Los exploraremos en la siguiente sección.

Los autoencoders son principalmente un algoritmo de reducción de dimensionalidad (o compresión) con un par de propiedades importantes:

  • Datos específicos: Los Autoencoders solo pueden comprimir datos de forma significativa de forma similar a lo que se les ha enseñado. Dado que aprenden características específicas para los datos de entrenamiento dados, son diferentes de un algoritmo de compresión de datos estándar como gzip. Por lo tanto, no podemos esperar un autoencodificador entrenado en dígitos escritos a mano para comprimir fotos de paisajes.
  • Con pérdidas: La salida del autoencoder no será exactamente la misma que la entrada, será una representación cercana pero degradada. Si desea compresión sin pérdida, no son el camino a seguir.
  • Sin supervisión: Para entrenar a un autoencoder no necesitamos hacer nada sofisticado, simplemente arrojarle los datos de entrada sin procesar. Los autoencoders se consideran una técnica de aprendizaje no supervisada, ya que no necesitan etiquetas explícitas para entrenar. Pero para ser más precisos, son auto supervisados porque generan sus propias etiquetas a partir de los datos de entrenamiento.

Arquitectura

Exploremos los detalles del codificador, el código y el decodificador. Tanto el codificador como el decodificador son redes neuronales de alimentación directa totalmente conectadas, esencialmente las ANN que cubrimos en la Parte 1. El código es una sola capa de una AN con la dimensionalidad de nuestra elección. El número de nodos en la capa de código (tamaño de código) es un hiperparámetro que configuramos antes de entrenar el autoencoder.

Esta es una visualización detallada de un autoencoder. Primero, la entrada pasa a través del codificador, que es un AN completamente conectado, para producir el código. El decodificador, que tiene la estructura ANN similar, produce la salida solo usando el código. El objetivo es obtener una salida idéntica a la entrada. Tenga en cuenta que la arquitectura del decodificador es la imagen especular del codificador. Esto no es un requisito, pero normalmente es el caso. El único requisito es que la dimensionalidad de la entrada y la salida deben ser las mismas. Se puede jugar con cualquier cosa en el medio.

Hay 4 hiperparámetros que necesitamos configurar antes de entrenar un autoencoder:

  • Tamaño del código: número de nodos en la capa intermedia. El tamaño más pequeño da como resultado una mayor compresión.
  • Número de capas: el autoencoder puede ser tan profundo como queramos. En la figura anterior tenemos 2 capas tanto en el codificador como en el decodificador, sin tener en cuenta la entrada y la salida.
  • Número de nodos por capa: la arquitectura de autoencoder en la que estamos trabajando se llama autoencoder apilado, ya que las capas se apilan una tras otra. Por lo general, los autoencoders apilados parecen un «intercomunicador». El número de nodos por capa disminuye con cada capa posterior del codificador y aumenta de nuevo en el decodificador. También el decodificador es simétrico al codificador en términos de estructura de capas. Como se señaló anteriormente, esto no es necesario y tenemos un control total sobre estos parámetros.
  • Función de pérdida: usamos error cuadrado medio (mse) o crossentropy binario. Si los valores de entrada están en el rango, generalmente usamos crossentropy, de lo contrario, usamos el error cuadrado medio. Para más detalles, echa un vistazo a este video.

Los autoencoders se entrenan de la misma manera que los ANNs a través de backpropagation. Consulte la introducción de la Parte 1 para obtener más detalles sobre cómo se entrenan las redes neuronales, se aplica directamente a los autoencoders.

Implementación

Ahora vamos a implementar un autoencoder para la siguiente arquitectura, 1 capa oculta en el codificador y decodificador.

vamos a utilizar la extremadamente popular MNIST conjunto de datos de entrada. Contiene imágenes en blanco y negro de dígitos escritos a mano.

son de tamaño 28 x 28 y los usamos como un vector de 784 entre los números . Consulta el cuaderno de jupyter para ver los detalles.

Ahora implementaremos el autoencoder con Keras. Los hiperparámetros son: 128 nodos en la capa oculta, el tamaño del código es 32 y la función de pérdida es la entropía binaria.

Esto es muy similar a las ANNs en las que trabajamos, pero ahora estamos usando la API funcional de Keras. Consulta esta guía para obtener más detalles, pero aquí tienes una comparación rápida. Antes solíamos agregar capas usando la API secuencial de la siguiente manera:

model.add(Dense(16, activation='relu'))
model.add(Dense(8, activation='relu'))

Con la API funcional hacemos esto:

layer_1 = Dense(16, activation='relu')(input)
layer_2 = Dense(8, activation='relu')(layer_1)

Es más detallado pero una forma más flexible de definir modelos complejos. Podemos agarrar fácilmente partes de nuestro modelo, por ejemplo, solo el decodificador, y trabajar con eso. La salida del método Denso es una capa llamable, utilizando la API funcional le proporcionamos la entrada y almacenamos la salida. La salida de una capa se convierte en la entrada de la siguiente capa. Con la API secuencial, el método add lo manejó implícitamente por nosotros.

Tenga en cuenta que todas las capas utilizan la función de activación relu, ya que es el estándar con redes neuronales profundas. La última capa utiliza la activación sigmoide porque necesitamos que las salidas estén entre . La entrada también está en el mismo rango.

También tenga en cuenta la función llamada a ajuste, antes con ANNs solíamos hacer:

model.fit(x_train, y_train)

Pero ahora lo hacemos:

model.fit(x_train, x_train)

Recuerde que los objetivos del autoencoder son los mismos que la entrada. Es por eso que suministramos los datos de entrenamiento como objetivo.

Visualización

Ahora visualicemos qué tan bien nuestro autoencoder reconstruye su entrada.

Ejecutamos el autoencoder en el conjunto de pruebas simplemente usando la función de predicción de Keras. Para cada imagen en el conjunto de pruebas, obtenemos la salida del autoencoder. Esperamos que la salida sea muy similar a la entrada.

de hecho, son bastante similares, pero no exactamente la misma. Podemos notarlo más claramente en el último dígito «4». Dado que esta era una tarea simple, nuestro autoencoder se desempeñó bastante bien.

Asesoramiento

Tenemos un control total sobre la arquitectura del autoencoder. Podemos hacerlo muy poderoso aumentando el número de capas, nodos por capa y, lo más importante, el tamaño del código. El aumento de estos hiperparámetros permitirá que el autoencoder aprenda codificaciones más complejas. Pero debemos tener cuidado de no hacerlo demasiado poderoso. De lo contrario, el autoencoder simplemente aprenderá a copiar sus entradas a la salida, sin aprender ninguna representación significativa. Simplemente imitará la función de identidad. El autoencoder reconstruirá los datos de entrenamiento a la perfección, pero se sobreajustará sin poder generalizar a nuevas instancias, que no es lo que queremos.

Esta es la razón por la que preferimos una arquitectura «sandwitch», y deliberadamente mantenemos el tamaño del código pequeño. Dado que la capa de codificación tiene una dimensión menor que los datos de entrada, se dice que el autoencoder está incompleto. No podrá copiar directamente sus entradas a la salida y se verá obligado a aprender funciones inteligentes. Si los datos de entrada tienen un patrón, por ejemplo, el dígito «1» generalmente contiene una línea algo recta y el dígito «0» es circular, aprenderá este hecho y lo codificará en una forma más compacta. Si los datos de entrada fueron completamente aleatorios sin ninguna correlación o dependencia interna, un autoencodificador incompleto no podrá recuperarlos a la perfección. Pero afortunadamente en el mundo real hay mucha dependencia.

Autoencoders de eliminación de ruido

Mantener la capa de código pequeña obligó a nuestro autoencoder a aprender una representación inteligente de los datos. Hay otra forma de forzar al autoencoder a aprender funciones útiles, que es agregar ruido aleatorio a sus entradas y hacer que recupere los datos originales sin ruido. De esta manera, el autoencoder no puede simplemente copiar la entrada a su salida porque la entrada también contiene ruido aleatorio. Le pedimos que reste el ruido y produzca los datos significativos subyacentes. Esto se llama una eliminación de ruido autoencoder.

La fila superior contiene las imágenes originales. Añadimos ruido gaussiano aleatorio a ellos y los datos ruidosos se convierten en la entrada al autoencoder. El autoencoder no ve la imagen original en absoluto. Pero entonces esperamos que el autoencoder regenere la imagen original sin ruido.

sólo Hay una pequeña diferencia entre la aplicación de la eliminación de ruido autoencoder y el ordinario. La arquitectura no cambia en absoluto, solo la función de ajuste. Entrenamos el autoencoder regular de la siguiente manera:

autoencoder.fit(x_train, x_train)

El autoencoder de eliminación de ruido está entrenado como:

autoencoder.fit(x_train_noisy, x_train)

Así de simple, todo lo demás es exactamente igual. La entrada al autoencoder es la imagen ruidosa, y el objetivo esperado es el original sin ruido.

Visualización

Ahora visualicemos si somos capaces de recuperar las imágenes sin ruido.

se Ve bastante buena. La fila inferior es la salida del autoencoder. Podemos hacer mejor uso más complejo autoencoder de la arquitectura, tales como convolucional autoencoders. Cubriremos las circunvoluciones en el próximo artículo.

Autoencoders dispersos

Presentamos dos formas de forzar al autoencoder a aprender funciones útiles: mantener el tamaño del código pequeño y eliminar los autoencoders. El tercer método es la regularización. Podemos regularizar el autoencoder usando una restricción de dispersión tal que solo una fracción de los nodos tenga valores distintos de cero, llamados nodos activos.

En particular, agregamos un término de penalización a la función de pérdida de manera que solo una fracción de los nodos se active. Esto obliga al autoencoder a representar cada entrada como una combinación de un pequeño número de nodos, y le exige descubrir una estructura interesante en los datos. Este método funciona incluso si el tamaño del código es grande, ya que solo un pequeño subconjunto de nodos estará activo en cualquier momento.

Es bastante fácil hacer esto en Keras con un solo parámetro. Como recordatorio, anteriormente creamos la capa de código de la siguiente manera:

code = Dense(code_size, activation='relu')(input_img)

Ahora agregamos otro parámetro llamado activity_regularizer especificando la fuerza de regularización. Este es típicamente un valor en el rango . Aquí elegimos 10e-6.

code = Dense(code_size, activation='relu', activity_regularizer=l1(10e-6))(input_img)

La pérdida final del modelo disperso es 0.01 mayor que el estándar, debido al término de regularización agregado.

Vamos a demostrar que las codificaciones generadas por el modelo regularizado son de hecho escasas. Si miramos el histograma de valores de código para las imágenes en el conjunto de pruebas, la distribución es la siguiente:

La media para el modelo estándar es del 6,6 pero para la regularización de la modelo es de 0,8, una gran reducción. Podemos ver que una gran parte de los valores de código en el modelo regularizado son de hecho 0, que es lo que queríamos. La varianza del modelo regularizado también es bastante baja.

Casos de uso

Ahora podríamos hacer las siguientes preguntas. ¿Qué tan buenos son los autoencoders para comprimir la entrada? ¿Y son una técnica de aprendizaje profundo de uso común?

Desafortunadamente, los autoencoders no se usan ampliamente en aplicaciones del mundo real. Como método de compresión, no funcionan mejor que sus alternativas, por ejemplo, jpeg hace la compresión de fotos mejor que un autoencoder. Y el hecho de que los autoencoders sean específicos de los datos los hace poco prácticos como técnica general. Sin embargo, tienen 3 casos de uso comunes:

  • Eliminación de datos: hemos visto un ejemplo de esto en imágenes.
  • Reducción de dimensionalidad: visualizar datos de alta dimensión es un reto. t-SNE es el método más comúnmente utilizado, pero lucha con un gran número de dimensiones (generalmente por encima de 32). Por lo tanto, los autoencoders se utilizan como paso de preprocesamiento para reducir la dimensionalidad, y esta representación comprimida es utilizada por t-SNE para visualizar los datos en el espacio 2D. Para grandes artículos sobre t-SNE, consulte aquí y aquí.
  • Autoencoders Variacionales (VAE): este es un caso de uso más moderno y complejo de autoencoders y los cubriremos en otro artículo. Pero como resumen rápido, VAE aprende los parámetros de la distribución de probabilidad modelando los datos de entrada, en lugar de aprender una función arbitraria en el caso de los autoencoders vainilla. Por puntos de muestreo de esta distribución también podemos utilizar el VAE como modelo generativo. Aquí hay una buena referencia.

Conclusión

Los autoencoders son una técnica de reducción de dimensionalidad muy útil. Son muy populares como material de enseñanza en cursos introductorios de aprendizaje profundo, probablemente debido a su simplicidad. En este artículo los cubrimos en detalle y espero que lo hayan disfrutado.

El código completo de este artículo está disponible aquí si quieres trabajar en ti mismo. Si tienes algún comentario, no dudes en comunicarte conmigo en Twitter.

Deja una respuesta

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

Previous post Diario de Nueva Inglaterra
Next post Una guía global para dar propinas al salir a cenar