sábado, 1 de mayo de 2021

AWS in a nutshell 15. VPCs

En este post, exploraremos la creación y configuración de VPC, direcciones IP elásticas y la configuración de emparejamiento de VPC para alinearlas con las necesidades del negocio. 

15.1 VPC  

En Amazon Web Services, una Virtual Private Cloud o VPC es una definición de red que creamos en el entorno de la nube para acomodar los recursos que se ejecutarán en esa red.  Tendremos automáticamente VPC predeterminados, uno específicamente para cada región de AWS.

El propósito de esto es que facilita el lanzamiento de recursos como instancias EC2 de inmediato, sin tener que crear una VPC. Pero si utilizamos a gran escala AWS, querremos configurar nuestros propios VPC para satisfacer nuestros propios requisitos de red. Una VPC, puede ser configurarla con un bloque CIDR IPv4 o un bloque CIDR IPv6. 

AWS VPC


15.2 Creación de una VPC por GUI

Desde la consola de AWS tecleamos  → VPC →  y pulsamos en el botón Launch VPC Wizard

Creación de una VPC por GUI


podemos ver las VPCs creadas desde Consola → VPC → your VPCs

También se pueden crear por comandos de texto desde el CLI y desde PowerShell

15.3 Opciones de DHCP de VPC

Un conjunto de opciones de DHCP es una configuración que se asocia con una VPC.

En la consola de administración de VPC, tenemos que seleccionar una VPC existente.  debajo de la pestaña Descripción, podemos ver que hay un enlace, un hipervínculo, para el conjunto de opciones de DHCP asociado con esta VPC en particular. Y si hacemos clic en él, cambia a la vista Conjuntos de opciones de DHCP, donde se filtra en la parte superior para la ID del conjunto de opciones de DHCP que seleccionamos.

AWS Opciones de DHCP de VPC


15.4 Configuración de DNS de VPC

La resolución de nombres DNS es importante porque permite una forma fácil de comunicación entre  dispositivos a través de una red utilizando un nombre DNS, en lugar de la dirección IP más difícil de recordar.

Consola → EC2 → instances  dentro de una instancia pestaña Description y abajo aparece VPC ID con un enlace  pulsamos sobre el enlace y Pestaña Actions donde elegiremos Edit DNS resolution donde elegiremos Edit DNS resolution

AWS Configuración de DNS de VPC


15.5 Direcciones IP elásticas

Las direcciones IP elásticas o EIP son recursos independientes de AWS que nos permiten tener un elemento separado que tiene una dirección IP que luego podemos asociar a una instancia EC2.

Servicios de AWS donde se enumeran varios servicios, como EC2, Amazon Forecast, etc.

Consola –> VPC  → elastic Ips   y pulsamos el botón allocate new adress

15.6 Direccionamiento de IP pública de subred

Una de las muchas opciones que podemos especificar al iniciar una nueva instancia EC2 es si tiene o no una dirección IP pública.

Consola →  EC2 → clic en la opción Instancias para abrir la página Instancias.   Y Launch instance y  elegimos una AMI

17.7 VPC Peering

La interconexión de VPC vincula los VPC directamente entre sí. Podemos hacer esto entre diferentes cuentas de AWS o dentro de una cuenta de AWS, incluso entre regiones de AWS donde cada VPC podría estar en una región diferente. VPC Peering está diseñado para que tengamos instancias EC2, por ejemplo, en cualquiera de las VPC que se comuniquen entre sí, como si estuvieran en la misma red individual aunque no lo estén.

AWS VPC Peering


15.8 Configurar el emparejamiento VPC

Para configurar una subred determinamos si una dirección IP pública se habilita automáticamente para las instancias de EC2 iniciadas en esa subred.

Consola → VPC → sunets


sábado, 24 de abril de 2021

Autoencoders IV: Autoencoders variacionales

 Los autoencoders variacionales son bastante diferentes a los autoencoders vistos previamente, en particular en estas dos características:

-       Son autoencoders probabilísticos, es decir sus salidas son parcialmente determinadas aleatoriamente, incluso después de su entrenamiento

-       Son autoencoders generativos. Es decir pueden generar nuevas instancias similares a sus datos de entrenamiento.

Ejecutan una inferencia Bayesana. Son similares al resto de los autoencoders, solo que para cada entrada dada producen una codificación media y una desviación estándar. La codificación real es entonces modificada aleatoriamente con una distribución Gaussiana con la codificación media m y la desviación estándar s después el decodificador decodifica la muestra normalmente   .

El codificador genera m y s y lo codifica de forma aleatoria, luego lo decodifica y el resultado es una instancia aleatoria pero similar a las de la entrada.   

La estructura del autoencoder se puede ver en la siguiente figura.

Autoencoders variacionales


Aunque las entradas tengan una distribución muy compleja, un autoencoder variacional tiende a producir codificaciones (codings) similares a la distribución Gaussiana simple de las muestras de entrada. Durante el entrenamiento la función de costo empuja a los codings a migrar gradualmente dentro del espacio del coding (también llamado espacio latente) hasta acabar pareciéndose a una nube de puntos Gaussianos. Una gran consecuencia es que después de entrenar un autoencoder variacional podemos fácilmente generar nuevas instancias simplemente eligiendo un punto aleatorio dentro de nuestro espacio Gaussiano.

Es cuanto a la función de costo, está compuesta por dos partes. La primera es la típica reconstrucción de pérdida que empuja al autoencoder a reproducir sus entradas. Podemos utilizar entropía cruzada para esta parte. La segunda parte es la perdida latente que empuja al autoencoder a tener codings similares a las muestras de su distribución Gaussiana: esto es la divergencia KL entre la distribución objetivo (la distribución Gaussiana) y la distribución real del coding.

Vamos a comenzar construyendo un autoencoder variacional para el dataset de moda MNIST. Lo primero que necesitamos es una capa personalizada para los codings.

class Sampling(keras.layers.Layer):

    def call(self, inputs):

        mean, log_var = inputs

        return K.random_normal(tf.shape(log_var)) * K.exp(log_var / 2) + mean

Esta capa de muestreo toma dos entradas, la means (m) y la log_var(g). Utiliza la función K.random_normal() para muestrear u vector aleatorio en el mismo espacio de g con una media de 0 y una desviación estándar de 1donde luego lo multiplica por exp(g \2) lo que lo convierte en s, y finalmente le añade m y retorna el resultado.

Lo siguiente es crear el codificador utilizando una API funcional ya que el modelo no es completamente secuencial.

tf.random.set_seed(42)

np.random.seed(42)

codings_size = 10

inputs = keras.layers.Input(shape=[28, 28])

z = keras.layers.Flatten()(inputs)

z = keras.layers.Dense(150, activation="selu")(z)

z = keras.layers.Dense(100, activation="selu")(z)

codings_mean = keras.layers.Dense(codings_size)(z)

codings_log_var = keras.layers.Dense(codings_size)(z)

codings = Sampling()([codings_mean, codings_log_var])

variational_encoder = keras.models.Model(

    inputs=[inputs], outputs=[codings_mean, codings_log_var, codings])

Nótese  que las capas densas que sacan codings_mean(m) y codings_log_var(g) tienen las mismas entradas (las salidas de la segunda capa densa) entonces se pasan ambas a la capa Sampling. Finalmente el modelo variational_encoder tiene tres salidas.

A continuación el decodificador.

decoder_inputs = keras.layers.Input(shape=[codings_size])

x = keras.layers.Dense(100, activation="selu")(decoder_inputs)

x = keras.layers.Dense(150, activation="selu")(x)

x = keras.layers.Dense(28 * 28, activation="sigmoid")(x)

outputs = keras.layers.Reshape([28, 28])(x)

variational_decoder = keras.models.Model(inputs=[decoder_inputs], outputs=[outputs])

Para este decodificador hemos utilizado la API Secuencial en vez de la API Funcional, pues es más sencilla, una simple pila de capas.

Finalmente construimos el modelo de autoencoder variacional.

_, _, codings = variational_encoder(inputs)

reconstructions = variational_decoder(codings)

variational_ae = keras.models.Model(inputs=[inputs], outputs=[reconstructions])

 

Nótese que hemos ignorado las dos primeras salidas del codificador (pues sólo queremos alimentar los codings del decodificador). Finalmente debemos añadir la pérdida latente y la reconstrucción de pérdida.

 

latent_loss = -0.5 * K.sum(

    1 + codings_log_var - K.exp(codings_log_var) - K.square(codings_mean),

    axis=-1)

variational_ae.add_loss(K.mean(latent_loss) / 784.)

variational_ae.compile(loss="binary_crossentropy", optimizer="rmsprop", metrics=[rounded_accuracy])

 

Primero calcula la pérdida latent para cada instancia, luego calcula la pérdida media sobre todas las instancias y divide el resultado entre 784 para asegurarse que tiene una escala apropiada comparada con la recosntrucción de pérdida. Verdaderamente la reconstrucción de pérdida del autodencoder variacional se supone como la suma de los errores de reconstrucción de los pixels, pero cuando Keras calcula la pérdida binary_crossentropy, lo hace sobre la media de los 784 pixels más bien que con su suma.De modo  que la reconstrucción de la pérdida es 784 veces más pequeña de lo que necesitamos. Para ello, definimos una pérdida personalizada que calcula la suma en vez de la media. Para ello utilizamos el optimizador RMSprop que funciona bien para este caso, y finalmente entrenamos el modelo.

history = variational_ae.fit(X_train, X_train, epochs=25, batch_size=128,

                       validation_data=(X_valid, X_valid))

Generando imágenes de moda MNIST

Vamos a utilizar este codificador variacional para generar imágenes similares a las que contiene el dataset MNIST. Lo único que necesitamos son codings aleatorios procedentes de una distribución Gaussiana y decodificarlos.

def plot_multiple_images(images, n_cols=None):

    n_cols = n_cols or len(images)

    n_rows = (len(images) - 1) // n_cols + 1

    if images.shape[-1] == 1:

        images = np.squeeze(images, axis=-1)

    plt.figure(figsize=(n_cols, n_rows))

    for index, image in enumerate(images):

        plt.subplot(n_rows, n_cols, index + 1)

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

        plt.axis("off")

Vamos a generar algunas codificaciones aleatorias, decodificarlas y mostrar las imágenes resultantes:

tf.random.set_seed(42)

codings = tf.random.normal(shape=[12, codings_size])

images = variational_decoder(codings).numpy()

plot_multiple_images(images, 4)

save_fig("vae_generated_images_plot", tight_layout=False)

Autoencoders variacionales

La mayor parte de estas imágenes parecen bastante convincentes, aunque son un poco borrosas. Vamos a ajustar un poco mejor el autoencoder para hacerlas mejor.

Los autoencoders variacionales, permiten ejecutar la interpolación semántica , en vez de interpolar dos imágenes a nivel de pixel (lo qu emostrarís dos imágenes solapadas) puede hacer la interpolación a nivel de coding. Tomamos dos imágenes interpolamos sus codings y los decodificamos obteniendo una imagen final similar a cualquier otra del dataset MNIST.

A continuación tomaremos 12 codings y los organizaremos en una matriz de 3X4, utilizando la función  tf.image.resize() de TensorFlow para redimensionar esta matriz a una de 5X7 . Por defecto la función resize() realiza interpolación linear de modo que cada imagen adicional contendrá codings interpolados, finalmente decodificamos los codings para obtener las imágenes.

tf.random.set_seed(42)

np.random.seed(42)

 

codings_grid = tf.reshape(codings, [1, 3, 4, codings_size])

larger_grid = tf.image.resize(codings_grid, size=[5, 7])

interpolated_codings = tf.reshape(larger_grid, [-1, codings_size])

images = variational_decoder(interpolated_codings).numpy()

 

plt.figure(figsize=(7, 5))

for index, image in enumerate(images):

    plt.subplot(5, 7, index + 1)

    if index%7%2==0 and index//7%2==0:

        plt.gca().get_xaxis().set_visible(False)

        plt.gca().get_yaxis().set_visible(False)

    else:

        plt.axis("off")

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

save_fig("semantic_interpolation_plot", tight_layout=False)

La imagen inferior muestra las imágenes resultantes. Las originales están enmarcadas y el resto son el resultado de la interpolación semántica entre sus imágenes cercanas. Nótese por ejemplo como el zapato de la cuarta fila es una interpolación entre los que tiene encima y debajo.

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”