sábado, 27 de febrero de 2021

Autoencoders y GAN´s (Redes Generativas Antagónicas)

Los Autoencoders (codificadores automáticos) son redes neuronales artificiales capaces de aprender representaciones densas de los datos de entrada, llamados representaciones latentes o codificadores (codings), sin supervisión, es decir entrenamiento no etiquetado. Estos codificadores normalmente tienen mucha menor dimensión que los datos de entrada, haciendo los codificadores útiles para la reducción dimensional, especialmente para propósitos de visualización. También funcionan como detectores de características y se pueden utilizar para preentrenar redes neuronales profundas. Finalmente algunos codificadores sirven como modelos generativos, son capaces de generar aleatoriamente nuevos datos muy similares a los datos de entrenamiento. Por ejemplo, si lo entrenamos con caras humanas, son capaces de generar nuevas aunque normalmente serán borrosas o con características que las hacen irreales.

En contraste, las caras humanas creadas por las Redes Generativas Adversarias (GAN) son bastante convincentes y es difícil cree que la gente que representan no existe. Las GAN´s son utilizadas actualmente para incrementar la resolución de una imagen, colorear imágenes en blanco y negro, hacer potentes ediciones de imágenes y convertir bocetos en fotos realistas etc.

Los codificadores y las GAN son ambos no supervisados, aprenden representaciones densas y son utilizados ambos como modelos generativos, y tienen aplicaciones muy similares sin embargo trabajan de forma muy diferente.

Autoencoders (Codificadores)

Simplemente aprenden a copiar la entrada en la salida. Esto puede sonar bastante trivial, pero veremos cómo algunas limitaciones en la red, lo convertirán en una tarea más bien difícil. Por ejemplo, podemos añadir ruido a una imagen de entrada y entrenar la red para que nos devuelva la imagen original. ,Esta limitaciones impiden que el codificador copie directamente la entrada en la salida y los fuerza a aprender vías eficientes de representación de datos.

GANs 

Están compuestas por dos redes neuronales: un generador que intenta generar datos similares a los de entrenamiento de la red y un discriminador, que trata de averiguar si la imagen recibida pertenece a los datos de entrenamiento o ha sido generada por el generador. El generador y el discriminador compiten entre sí durante el entrenamiento. El generador intenta generar imágenes nuevas pero indistinguibles de las del swet de entrenamiento, mientras que el discriminador trata de averiguar si  la imagen que procesa pertenece al set de entrenamiento o la ha generado el generador.

Autoencoders

Un autoecoder está siempre compuesto de dos partes: un codificador (o red de reconocimiento) que convierte la entrada en una representación latente, seguido de un decodificador (o red generativa) que convierte la representación interna en salidas. Un autoencoder tiene la misma arquitectura que un perceptrón multicapa (una red neuronal) excepto que el número de neuronas en la capa de salida debe ser igual al número de entradas. 

A menudo las salidas, son llamadas reconstrucciones por que el autoencoder trata de reconstruir las entradas, y la función de coste contiene una reconstrucción de pérdida que penaliza el modelo que penaliza el modelo cuando la reconstrucción difiere de la entrada.

Como la representación interna tiene una dimensionalidad inferior que los datos de entrada (por ejemplo contiene una representación en dos dimensiones 2D de un objeto de tres dimensiones 3D) el autoencoder se dice que es incompleto. Un autoencoder incompleto no puede copiar trivialmente sus entradas a los codificadores, antes debe encontrar una vía para trasladar una copia desde las entradas a las salidas, por tanto es forzado a aprender las características más importantes de los datos de entrada y eliminar las que nos son importantes.

A continuación vamos a implementar un autoencoder incompleto muy simple para reducir la dimensionalidad.

Ejecutando PCA (Principal Component Analisis) con un Autoencoder incompleto lineal

Si el autoencoder utiliza solamente activaciones lineales y la función de coste es el error cuadrático medio (MSE). Entonces termina actuando como un PCA.

Un PCA es un algoritmo que identifica el plano óptimo sobre el que representar los datos y proyecta sobre dicho plano el dataset, de modo que se produce una reducción de dimensionalidad con una pérdida mínima de información.

El siguiente código sirve para construir un Autoencoder lineal simple para ejecutar PCA desde un dataset de 3D y proyectarlo sobre un plano 2D.

Lo primero de todo es activar nuestro entorno virtual aislado y abrir un notebook de Jupyter apuntando a nuestro entorno virtual aislado. 

Entorno virtual aislado

Entorno virtual aislado


Primero, importaremos algunos módulos comunes, y nos aseguramos de que MatplotLib traza las figuras online y prepararemos una función para guardar las imágenes. También verificamos que la versión de Python sea 3.5 o posterior y esté instalado, así como Scikit-Learn ≥0.20 y TensorFlow ≥2.0.


# Se necesita una versión de Python ≥3.5

import sys

assert sys.version_info >= (3, 5)

 

# Se require una version de Scikit-Learn ≥0.20

import sklearn

assert sklearn.__version__ >= "0.20"

 

try:

    # %tensorflow_version solo existe en Colab.

    %tensorflow_version 2.x

    IS_COLAB = True

except Exception:

    IS_COLAB = False

 

# Se requiere TensorFlow ≥2.0

import tensorflow as tf

from tensorflow import keras

assert tf.__version__ >= "2.0"

 

if not tf.config.list_physical_devices('GPU'):

    print("No se ha detectado una GPU. LSTMs y CNNs pueden ser muy lentas sin una GPU")

    if IS_COLAB:

        print("Ir a Runtime > Change runtime y seleccionar GPU hardware accelerator.")

 

# Impoeraciones comunes

import numpy as np

import os

 

# para hacer este cuaderno estable

np.random.seed(42)

tf.random.set_seed(42)

 

# Para mostrar en pantalla gráficos

%matplotlib inline

import matplotlib as mpl

import matplotlib.pyplot as plt

mpl.rc('axes', labelsize=14)

mpl.rc('xtick', labelsize=12)

mpl.rc('ytick', labelsize=12)

 

# Directorio donde se guardarán las imágenes

PROJECT_ROOT_DIR = "."

CHAPTER_ID = "autoencoders"

IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, "imagenes", CHAPTER_ID)

os.makedirs(IMAGES_PATH, exist_ok=True)

 

def save_fig(fig_id, tight_layout=True, fig_extension="png", resolution=300):

    path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension)

    print("Guardando imagen", fig_id)

    if tight_layout:

        plt.tight_layout()

    plt.savefig(path, format=fig_extension, dpi=resolution)

Implementamos un par de funciones de utilidad para trazar una imagen en escala de grises de 28x28:

def plot_image(image):

    plt.imshow(image, cmap="binary")

    plt.axis("off")

 Creamos un dataset de tres dimensiones

np.random.seed(4)

def generate_3d_data(m, w1=0.1, w2=0.3, noise=0.1):

    angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5

    data = np.empty((m, 3))

    data[:, 0] = np.cos(angles) + np.sin(angles)/2 + noise * np.random.randn(m) / 2

    data[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2

    data[:, 2] = data[:, 0] * w1 + data[:, 1] * w2 + noise * np.random.randn(m)

    return data

X_train = generate_3d_data(60)

X_train = X_train - X_train.mean(axis=0, keepdims=0)

Y creamos el autoencoder

np.random.seed(42)

tf.random.set_seed(42)

encoder = keras.models.Sequential([keras.layers.Dense(2, input_shape=[3])])

decoder = keras.models.Sequential([keras.layers.Dense(3, input_shape=[2])])

autoencoder = keras.models.Sequential([encoder, decoder])

autoencoder.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1.5))

Este código en realidad no es muy diferente de cualquier MLP (Perceptrón multicapa) pero tiene algunas diferencias:

Hemos organizado el autoencoder en dos subcomponentes: el codificador y el decodificador. Ambos son modelos de secuencia regular cada uno con una capa densa simple y el autoencoder es un modelo secuencial que contiene el codificador seguido por el decodificador (cada modelo puede ser utilizado como una capa en otro modelo).

El número de salidas del autoencoder es igual al número de entradas, en este caso tres.

Al ejecutar un PCA simple no podemos usar ninguna función de activación, es decir, todas las neuronas tienen activación lineal, y la función de coste es el error cuadrático medio.

Vamos a entrenar el modelo con un dataset 3D simple y lo vamos a utilizar para codificar el mismo dataset y proyectarlo sobre dos dimensiones 2D. 

history = autoencoder.fit(X_train, X_train, epochs=20)

codings = encoder.predict(X_train)

fig = plt.figure(figsize=(4,3))

plt.plot(codings[:,0], codings[:, 1], "b.")

plt.xlabel("$z_1$", fontsize=18)

plt.ylabel("$z_2$", fontsize=18, rotation=0)

plt.grid(True)

save_fig("autoencoder lineal_pca")

plt.show()

El mismo dataset X_train se utiliza en las entradas y en los objetivos (la proyección de dicho dataset en un plano de 2D) que es la salida de la capa oculta del autoencoder. El autoencoder encuentra el mejor plano para proyectrar el dataset en 2D preservando la máxima varianza de los datos sobre dicho plano, justo como haría un PCA.

Podemos pensar que los autoencoders son una forma de aprendizaje autosupervisado, utilizando una técnica de aprendizaje con etiquetas generadas automáticamente.

Si vamos a la carpeta imágenes de nuestro entorno virtual aislado podemos ver que nos ha guardado la imagen del dataset de 3D proyectado sobre un plano 2D.

 

Ejecutando PCA (Principal Component Analisis) con un Autoencoder incompleto lineal

Traducido del capítulo 17 de “Hands-On Machine Learning with Scikit-Learn, Keras and Tensorflow, 2nd Edition by Aurelien Géron (O’Reilly). Copyright 2019 Kiwisoft S.A.S., 978-1-492-03264-9”