sábado, 29 de mayo de 2021

Crear un archivo de log para una aplicación de VB.net

En este post vamos a ver como crear un archivo de Log para una aplicación vb.net que vaya escribiendo cualquier cosa que deseemos en un archivo de texto en la ubicación que queramos.

Primero definimos una variable del tipo streamwriter

Dim swLog As System.IO.StreamWriter

 

Crear un archivo de log para una aplicación de VB.net


En nuestro programa, cada vez que queramos escribir algo en el log lo llamamos así: 

 EscribirLog("-------------ERROR----------------------------")

            EscribirLog("Se ha producido un error: " & ex.Message & Now())

            EscribirLog(strVariable_con_la_información_a_mostrar & " Texto fijo - " & Now())

            EscribirLog("-----------------------------------------")

 

Finalmente las definiciones de para crear el log son estas. Al comienzo del programa llamaremos a AbrirLog, cuando queramos escribir llamamos a EscribirLog y cuando finalice el programa lo cerramos con CerrarLog.

Sub AbrirLog()

Dim strMisDocumentos As String

strMisDocumentos = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

     If Not System.IO.Directory.Exists(strMisDocumentos & "\" & "Logs") Then

            System.IO.Directory.CreateDirectory(strMisDocumentos & "\" & "Logs")

        End If

 

        swLog = New System.IO.StreamWriter(strMisDocumentos & "\Logs\" & Now.ToString("yyyyMMddHHmmss") & ".txt")

        EscribirLog("Usuario: " & Environment.UserName & "  Maquina: " & Environment.MachineName)

        EscribirLog("")

        EscribirLog("-----------------------")

        EscribirLog("  COMIENZO PROCESO")

        EscribirLog("-----------------------")

        EscribirLog("")

        EscribirLog(Now.ToString("dd/MM/yyyy HH:mm:ss") & " - Inicio del proceso")

End Sub

Sub CerrarLog()

        EscribirLog(Now.ToString("dd/MM/yyyy HH:mm:ss") & " - Fin del proceso")

        EscribirLog("")

        EscribirLog("------------------------------------")

        EscribirLog("FIN PROCESO")

        EscribirLog("-------------------------------------")

        EscribirLog("")

        If Not swLog Is Nothing Then

            swLog.Close()

        End If

        swLog = Nothing

End Sub

 

Sub EscribirLog(ByVal texto As String)

        If Not swLog Is Nothing Then

            swLog.WriteLine(texto)

            swLog.Flush()

            Console.WriteLine(texto)

        End If

 End Sub

 

sábado, 22 de mayo de 2021

GANs Convolucionales profundas DCGAN

Las características principales de las GANs convolucionales profundas son las siguientes:

Reemplazan cualquier capa de agrupación con convoluciones de paso (en el discriminador) y convoluciones traspuestas (en el generador)

Utilizan Normalización Batch en el generador y en el discriminador, excepto en la capa de salida del generador y en la capa de entrada del discriminador.

Elimina todas las capas ocultas completamente conectadas para arquitecturas más profundas.

Utiliza activación ReLU en el generador para todas las capas excepto la capa de salida la cual debe utilizar tanh.

Utiliza activación leaky ReLU en todas las capas del discriminador.

Estas características se utilizan en muchos casos, pero no siempre. Debemos poder experimentar con diferentes hiperparámetros, cambindo la semilla aleatoria y probando el modelo una y otra vez.

El siguiente ejemplo de DCGAN funciona razonablemente bien con eldataset de moda MNIST.

tf.random.set_seed(42)

np.random.seed(42)

codings_size = 100

generator = keras.models.Sequential([

    keras.layers.Dense(7 * 7 * 128, input_shape=[codings_size]),

    keras.layers.Reshape([7, 7, 128]),

    keras.layers.BatchNormalization(),

    keras.layers.Conv2DTranspose(64, kernel_size=5, strides=2, padding="SAME",

                                 activation="selu"),

    keras.layers.BatchNormalization(),

    keras.layers.Conv2DTranspose(1, kernel_size=5, strides=2, padding="SAME",

                                 activation="tanh"),

])

discriminator = keras.models.Sequential([

    keras.layers.Conv2D(64, kernel_size=5, strides=2, padding="SAME",

                        activation=keras.layers.LeakyReLU(0.2),

                        input_shape=[28, 28, 1]),

    keras.layers.Dropout(0.4),

    keras.layers.Conv2D(128, kernel_size=5, strides=2, padding="SAME",

                        activation=keras.layers.LeakyReLU(0.2)),

    keras.layers.Dropout(0.4),

    keras.layers.Flatten(),

    keras.layers.Dense(1, activation="sigmoid")

])

gan = keras.models.Sequential([generator, discriminator])

El generador toma codificaciones (codings) de tamaño 100 y las proyecta a 6272 dimensiones 7*7*28 y cambia la forma del resultado a un tensor de 7x7x128. Este tensor utiliza normalización por lotes y alimenta a una capa convolucional traspuesta con un paso de 2, la cual ensancha las muestras de 7x7  a 14x14 y reduce su profundidad de 128 a 64. El resultado se normaliza por lotes de nuevo y alimenta otr capa convolucional traspuesta con un paso de 2 la cual se ensancha de nuevo de 14x14 a 28x28 y reduce su profundidad de 64 a 1 esta capa utiliza la función de activación tanh con salidas en el rango -1 a 1 por esta razón antes de entrenar la GAN necesitamos reescalar el set de entrenamiento y ponerlo en el mismo rango. También necesitamos cambiarle la forma para añadirle un canal de dimensión.

discriminator.compile(loss="binary_crossentropy", optimizer="rmsprop")

discriminator.trainable = False

gan.compile(loss="binary_crossentropy", optimizer="rmsprop")

X_train_dcgan = X_train.reshape(-1, 28, 28, 1) * 2. - 1. # cambia la forma y la escala

El discriminador se parece mucho a una CNN regular para clasificación binaria pero en vez de utilizar capas de agrupación max para reducir la muestra de la imagen usa convoluciones de paso 2. Hay que notar también que utiliza la función de activación Leaky ReLU.

Respecto a las líneas marcadas al principio para las DCGAN hemos sustituido las capas de normalización por lotas por capas de Dropout en el discriminador y se ha reemplazado ReLU por SELU en el generador. Hay que sentirse libre para cambiar la arquitectura especialmente los relativos a las tasas de aprendizaje de ambas redes neuronales.

Finalmente construimos el dataset, entrenamos y compilamos el modelo.

batch_size = 32

dataset = tf.data.Dataset.from_tensor_slices(X_train_dcgan)

dataset = dataset.shuffle(1000)

dataset = dataset.batch(batch_size, drop_remainder=True).prefetch(1)

train_gan(gan, dataset, batch_size, codings_size)

tf.random.set_seed(42)

np.random.seed(42)

noise = tf.random.normal(shape=[batch_size, codings_size])

generated_images = generator(noise)

plot_multiple_images(generated_images, 8)

save_fig("dcgan_generated_images_plot", tight_layout=False)

Después de 50 épocas el modelo será capaz de genera imágenes como las de abajo.

GANs Convolucionales profundas DCGAN


 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”


sábado, 15 de mayo de 2021

AWS in a nutshell 16. Soluciones de Resolución de Nombres. Route 53

Utilizaremos los servicios de AWS adecuados para diseñar e implementar aplicaciones que cumplan con los requisitos dados, migrar aplicaciones complejas en AWS, diseñar e implementar operaciones escalables en toda la empresa en AWS e implementar estrategias de control de costos. Exploraremos cómo configurar las opciones de resolución de nombres de AWS utilizando Amazon Route 53, incluidas las zonas alojadas y las políticas de enrutamiento de registros DNS. 

16.1 Route 53 

Amazon Route 53 es un servicio DNS administrado. Esto significa que no tenemos que aprovisionar manualmente un servidor y configurar el software DNS en él. Solo necesitamos configurar los detalles para la resolución de nombres DNS.

 

AWS Ruta 53

Menú de Route 53

Tecleamos Route 53 en el buscador de la página principal de AWS y nos muestra este menú


Menú de Route 53

16.2 Route 53 Politicas de Enrutamiento

Route 53 admite una serie de políticas de enrutamiento cuando creamos un conjunto de registros DNS. El propósito general es reducir la cantidad de tiempo que le lleva a un cliente conectarse a un recurso. Eso sucede cuando proporcionamos acceso a una dirección IP con la menor cantidad de latencia de red y mediante el uso de  comprobaciones de estado de DNS para evitar el enrutamiento de las solicitudes de los clientes a los servidores backend que no funcionan correctamente o no responden.

16.3 Políticas de ponderadas de route 53

Este es un tipo de configuración para un registro DNS, donde tenemos múltiples registros DNS que usan el mismo FQDN. Sin embargo, cada uno de ellos está configurado con un valor de peso numérico diferente. Y si tenemos uno con un valor de 100 y otro registro con un valor de 200, el mismo nombre, diferentes IP. El que tiene el valor de 200 se usa el doble de veces para devolver esa dirección IP al cliente, que el mismo FDQN con el valor de peso de solo 100.


sábado, 8 de mayo de 2021

Redes Generativas Adversarias (GAN)

Las Redes Generativas Adversarias se componen de un generador y un discriminador.

Generador

Toma a su entrada una distribución aleatoria, suele ser Gaussiana y devuelve a la salida los datos que suelen ser imágenes. Podemos pensar en las entradas aleatorias como en las imágenes latentes que serán generadas. Como se puede ver, el generador ofrece la misma funcionalidad que en un autoencoder variacional y se puede utilizar para generar nuevas imágenes, alimentado por ruido Gaussiano de donde saca las nuevas imágenes.

Discriminador

Toma como entrada una imagen falsa generada por el Generador y otra del dataset de entrenamiento, entonces trata de averiguar cual es la generada y la del dataset. 

Durante el entrenamiento, el generador y el discriminador tiene objetivos opuestos, el discriminador intenta distinguir la imágenes falsas de las auténticas, mientras el generador intenta producir imágenes tan realistas que el discriminador no la distinga de las imágenes del dataset. Como una GAN está compuesta por dos redes con diferentes objetivos, no se puede entrenar como una red neuronal convencional, cada iteracción en el entrenamiento está dividida en dos fases:

En la primera fase, entrenamos el dicriminador. Introducimos un lote de imágenes generadas por el generador y otrode imágenes del dataset y etiquetamos con 0 las del generador y con 1 las del dataset y entrenamos al discriminador con este nuevo dataset utilizando entropía cruzada binaria. Durante esta fase sólo se actualizan los pesos del discriminador.

La segunda fase consiste en entrenar al generador. Para ello, producimos otro lote de imágenes falsas generadas por el generador y utilizamos de nuevo al discriminador para etiquetar las falsas de las reales. Pero esta vez no añadimos imágenes reales en el lote y todas las imágenes son etiquetadas como reales (ponemos 1). Lo que queremos es que el discriminador etiquete erróneamente imágenes falsas como verdaderas. En este paso congelamos los pesos del discriminador, pues lo que nos interesa es entranar al generador.

Vamos a construir una GAN con nuestro dataset de moda MNIST. Para ello construimos el generador y el discriminador. El generador se´ra similar a un decodificador de un autoencoder, y el discriminador es un clasificador binario regular. Toma una imagen de entrada y termina con una capa densa conteniendo una sola neurona con una función de activación sigmoide. En la segunda fase para cada iteracción de entrenamiento necesitamos el modelo GAN completo conteniendo el generador seguido por el discriminador.

np.random.seed(42)

tf.random.set_seed(42)

codings_size = 30

generator = keras.models.Sequential([

    keras.layers.Dense(100, activation="selu", input_shape=[codings_size]),

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

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

    keras.layers.Reshape([28, 28])

])

discriminator = keras.models.Sequential([

    keras.layers.Flatten(input_shape=[28, 28]),

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

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

    keras.layers.Dense(1, activation="sigmoid")

])

gan = keras.models.Sequential([generator, discriminator])

Ahora necesitamos compilar estos modelos. Como el discriminador es un clasificador binario podemos utilizar la pérdida binaria de entropía cruzada. El generador sólo será entrenado a través del modelo gan así que no necesitamos compilarlo. El modelo gan también es un clasificador binario, luego también podemos utilizar la pérdida binaria de entropía cruzada. Es importante no entrenar el discriminador durante la segunda fase, así que lo hacemos no entrenable antes de compilar el modelo gan.

discriminator.compile(loss="binary_crossentropy", optimizer="rmsprop")

discriminator.trainable = False

gan.compile(loss="binary_crossentropy", optimizer="rmsprop")

Durante el entrenamiento no debemos utilizar el método fit() en su lugar podemos escribir un bucle de entrenamiento personalizado, para esto necesitamos crear un Dataset para iterar a través de las imágenes.

batch_size = 32

dataset = tf.data.Dataset.from_tensor_slices(X_train).shuffle(1000)

dataset = dataset.batch(batch_size, drop_remainder=True).prefetch(1)

Con esto ya estamos preparados para el entrenamiento. Lo vamos a envolver en una función que llamaremos train_gan().

def train_gan(gan, dataset, batch_size, codings_size, n_epochs=50):

    generator, discriminator = gan.layers

    for epoch in range(n_epochs):

        print("Epoch {}/{}".format(epoch + 1, n_epochs))                      for X_batch in dataset:

            # fase 1 entrenamos el discriminador

            noise = tf.random.normal(shape=[batch_size, codings_size])

            generated_images = generator(noise)

            X_fake_and_real = tf.concat([generated_images, X_batch], axis=0)

            y1 = tf.constant([[0.]] * batch_size + [[1.]] * batch_size)

            discriminator.trainable = True

            discriminator.train_on_batch(X_fake_and_real, y1)

            # fase 2 – entrenamos el generador

            noise = tf.random.normal(shape=[batch_size, codings_size])

            y2 = tf.constant([[1.]] * batch_size)

            discriminator.trainable = False

            gan.train_on_batch(noise, y2)

        plot_multiple_images(generated_images, 8)                    

        plt.show()                                                

Como se ha indicado antes, podemos ver dos fases en cada iteracción.

En la fase uno alimentamos con ruido Gaussiano el generador, para producir imágenes falsas y completamos el lote concatenando un número igual de imágenes reales. A las imágenes reales y1 las ponemos la etiqueta 1 y a las falsas la etiqueta 0. También ponemos el atributo trainable del discriminador como true, esto es solo para evitar que salga una advertencia de Keras cuando el atributo es ahora false y anteriormente era true o viceversa.

En la fase 2 alimentamos la GAN con algo de ruido gausiano. Su generador debe producir imágenes falsas y el discriminador debe averiguar si las imágenes son generadas o del dataset. Nosotros queremos que el discriminador crea que las imágenes falsas sean reales. Así que los objetivos y2 son puestos a 1. Ponemos el atributo trainable del discriminador a False una vez más para evitar el Warning.

Desafortunadamente las imágenes nunca serán mucho mejores que estas, incluso si entrenamos la red más épocas la GAN parece olvidar lo aprendido.

tf.random.set_seed(42)

np.random.seed(42)

noise = tf.random.normal(shape=[batch_size, codings_size])

generated_images = generator(noise)

plot_multiple_images(generated_images, 8)

save_fig("gan_generated_images_plot", tight_layout=False)

train_gan(gan, dataset, batch_size, codings_size)

Redes Generativas Adversarias (GAN)

Dificultades de entrenar GANs

Cuando el generador produce imágenes perfectamente realistas  y el discriminador es forzado a adivinar el 50% falsas y el 50 % verdaderas se debería alcanzar el equilibrio de Nash 

Consideramos que si entrenamos una GAN un tiempo suficiente, esta alcanzará dicho equilibrio, teniendo de este modo un generador perfecto, pero no hay nada que nos garantice que se alcanzará dicho equilibrio.

La principal dificultad se llama modo colapso. Consiste en que el generador cada vez saca imágenes menos diversas. Pues si consigue engañar al discriminador mejor con zapatos que con otras prendas, tratará de generar zapatos más amenudo. Gradualmente olvidará genera otras prendas y generará solo zapatos. Del mismo modo el discriminador olvidará cómo discriminar otras prendas que no sean zapatos con lo que forzará al generador a moverse hacia otras prendas pero ambos olvidarán los zapatos, de modo que irán rotando por diferentes prendas y olvidando las anteriores con lo entra en un bucle cerrado del que no saldrá ninguna mejora de la red.

Además como el generador y el discriminador están constantemente luchando uno contra otro, sus parámetros pueden acabar oscilando y haciéndose inestables. Con lo que de repente pueden comenzar a diverger sin razón aparente. Po resto las GAN son muy sensibles a los hiperparámetros y es necesario un gran esfuerzo en ajustarlas convenientemente.

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”