wp-plugin-bluehost
domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init
action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home1/spotsail/public_html/martra/wp-includes/functions.php on line 6114Este es el segundo art\u00edculo del Tutorial de Redes Generativas Adversarias. En el primero<\/a> se vio como crear una DCGAN para el Dataset MNIST. Es decir, im\u00e1genes de 28×28 en escala de Grises. En este subimos un peque\u00f1o pelda\u00f1o en la complejidad del Dataset y usaremos el Cifar-10<\/a>. Un dataset tambi\u00e9n muy conocido, de hecho es uno de los Datasets que vienen con Keras. Este dataset est\u00e1 compuesto por 6000 im\u00e1genes de 32×32 a color. <\/p>\n\n\n\n Son im\u00e1genes un poco m\u00e1s grandes que las utilizadas en el primer art\u00edculo, pero sobre todo se introduce el color. Si hab\u00e9is llegado a este art\u00edculo buscando un tutorial de c\u00f3mo crear una GAN sencilla, no sufr\u00e1is, no hace falta que os le\u00e1is el art\u00edculo anterior<\/a>. <\/p>\n\n\n\n Voy a empezar dando unas m\u00ednimas explicaciones comunes para entender c\u00f3mo funciona una GAN. Acto seguido pasaremos a ver el c\u00f3digo por partes y explicarlo. Finalmente, tendremos el c\u00f3digo completo de la GAN. <\/p>\n\n\n\n Los que hab\u00e9is pasado por el primer art\u00edculo<\/a> y no quer\u00e9is dar un breve repaso os pod\u00e9is ir directamente a la secci\u00f3n \u00bfQu\u00e9 veremos de m\u00e1s? <\/strong><\/p>\n\n\n\n El c\u00f3digo fuente completo se encuentra en Kaggle, GitHub y Colab: <\/p>\n\n\n\n https:\/\/www.kaggle.com\/code\/peremartramanonellas\/gan-tutorial-2-generating-color-images<\/a><\/p>\n\n\n\n https:\/\/github.com\/oopere\/GANs\/blob\/main\/C2_GAN_CIFAR.ipynb<\/a><\/p>\n\n\n\n https:\/\/colab.research.google.com\/drive\/1atITnzNEDKDIYhGArP2wN2FwFhCVNo2W<\/a><\/p>\n<\/blockquote>\n\n\n\n Una GAN es una red neuronal, normalmente usada para generar im\u00e1genes, compuesta de dos redes: un generador y un discriminador. <\/p>\n\n\n\n El Generador recibe una entrada de datos, que son sencillamente ruido. Datos aleatorios ordenados en una forma gaussiana. No sufr\u00e1is que es tan solo una llamada a una funci\u00f3n. Con esta entrada, genera una salida con el formato de la imagen que queremos crear. <\/p>\n\n\n\n Vamos a generar el ruido a usar como entrada para el modelo Generador: <\/p>\n\n\n\n Esta sencilla llamada nos devuelve el ruido que podemos usar para el generador. Vemos que le estamos indicando que nos devuelva un array de 16 filas con 100 valores cada una. Estas 16 filas las utilizar\u00e1 el generado para crear 16 im\u00e1genes diferentes a partir del dato con 100 valores de longitud. Es decir, se necesita una fila para cada imagen a generar. <\/p>\n\n\n\n El tama\u00f1o del ruido no tiene por qu\u00e9 ser 100. Es un valor arbitrario, algunos de los valores m\u00e1s usados son: 50, 100, 128, 1024\u2026 <\/p>\n\n\n\n Posiblemente, el mejor estudio que se ha realizado por el momento sobre el input noise de una GAN sea el realizado en 2020 por Manisha Pandala, Denoit Das, Sujit Gujar: Effect of input noise Dimension in GANs<\/a>. <\/p>\n<\/blockquote>\n\n\n\n En el estudio se indica que para im\u00e1genes como las del Dataset Cifar-10, no se aprecian diferencias entre un valor de 100 y uno de 900, pero que con valores peque\u00f1os como 2 o 10 se pierde calidad y variedad en las im\u00e1genes generadas. <\/p>\n\n\n\n El otro modelo que forma parte de la GAN, es el Discriminador. Este se encarga de decidir cuando una imagen pertenece al Dataset Original o es Falsa. El discriminador, como entrada, recibe una imagen, en este caso con el formato 32 x 32 x 3, y tiene una salida booleana que indica cuando la imagen es aut\u00e9ntica o falsa. <\/p>\n\n\n\n Ambos modelos se entrenan juntos y modifican sus pesos en funci\u00f3n de la capacidad del discriminador para adivinar qu\u00e9 im\u00e1genes son verdaderas o falsas.<\/p>\n\n\n\n La intenci\u00f3n del generador es crear im\u00e1genes que enga\u00f1en al discriminador. Mientras que el discriminador lo que busca es ser capaz de identificar siempre que im\u00e1genes son las generadas por el generador. <\/p>\n\n\n\n Para los que ya hab\u00e9is pasado por el primer art\u00edculo, os explico un poco que veremos de m\u00e1s: <\/p>\n\n\n\n El modelo Generador va transformando los datos de entrada a medida que estos van pasando por las capas del modelo. Con lo que el ruido que se ha usado como entrada, de una longitud de predeterminada, acaba siendo una imagen de 32 x 32 x 3 a la salida del modelo. <\/p>\n\n\n\n Para ello, primero tendremos una capa Densa, que recibir\u00e1 los datos de entrada y que debe tener nodos suficientes como para contener una versi\u00f3n reducida de la imagen a generar. Pero como queremos que pueda aprender r\u00e1pidamente en cada paso de cada \u00e9poca, le daremos tama\u00f1o para contener X versiones de la imagen reducida. <\/p>\n\n\n\n En el generador que vamos a ver, la primera capa densa va a tener nodos suficientes para contener 128 x 4 x 4 es decir 2048 datos. En una segunda capa le cambiamos el shape, para que se adapte a la forma que necesitamos para contener estas im\u00e1genes reducidas, o capas de im\u00e1genes. Pensemos que queremos convertir el resultado final a 32 x 32 x 3 y por ahora tenemos un shape de 4 x 4 x 128. <\/p>\n\n\n\n A partir de este punto se debe ir realizando un proceso de upsampling hasta llegar al formato de la imagen a generar<\/strong>. <\/p>\n\n\n\n Este proceso lo ejecuto con una capa Conv2DTranspose<\/em>. Que va duplicando el tama\u00f1o de la imagen.<\/strong> Al tener el par\u00e1metro strides informado con el valor 2 los datos se multiplican por 4, y en esta primera pasada pasar\u00edamos de tener un shape de 4 x 4 = 16, a un shape de 8 x 8 = 64. <\/p>\n\n\n\n Despu\u00e9s de la capa de Conv2DTranspose<\/em> uso una capa de BartchNormalization. <\/em>Esta capa le da estabilidad al sistema. Coge la salida de la capa anterior y los normaliza antes de pasarlos a la siguiente capa. <\/p>\n\n\n\n Como activador se est\u00e1 usando una capa LeakyReLU<\/em>, dentro de la capa Conv2DTranspose<\/em>. Realmente podr\u00edamos usarla como una capa externa y situarla detr\u00e1s de la capa BatchNormalization para que recibiera la entrada normalizada. <\/p>\n\n\n\n Una estructura alternativa podr\u00eda ser: <\/p>\n\n\n\n Pero para el dataset usado no he notado diferencia en la calidad de las im\u00e1genes generadas. Tampoco creo que podamos encontrar diferencias de rendimiento, as\u00ed que depender\u00e1 de las preferencias de cada uno usar LeakyReLU<\/em> como una capa aparte o como activador en Conv2DTranspose<\/em>. <\/p>\n\n\n\n En el generador final, este proceso de upsampling se va a tener que repetir tres veces, para que la imagen pase de un tama\u00f1o de 4 x 4 a uno de 32 x 32. <\/p>\n\n\n\n Para finalizar el modelo Generador tenemos que tener una capa cuya salida coincida con el shape de la imagen a generar. <\/p>\n\n\n\n He aprovechado la \u00faltima capa Conv2DTranspose<\/em> para dar el shape necesario a la salida. La soluci\u00f3n m\u00e1s com\u00fan es usar una capa Conv2D<\/em> para dar el formato a la salida, y mantener la capa Conv2DTranspose<\/em> para efectuar el Upsample con un n\u00famero de nodos m\u00e1s elevado. <\/p>\n\n\n\n En ambas alternativas el activador de la \u00faltima capa es tanh<\/em><\/strong>, no importa si usamos una Conv2DTranspose<\/em> como Conv2D<\/em>. Este activador nos asegura un rango de salida entre -1 y 1, el mismo que hemos usado al normalizar la entrada. Es una de las recomendaciones que se pueden encontrar en las GAN Hack. <\/p>\n\n\n\n Ha llegado el momento de juntarlo todo y ver el c\u00f3digo del generador que he usado para la GAN. <\/p>\n\n\n\n Este es el c\u00f3digo del generador, con su entrada, el cuerpo principal donde se realizan los upsamples, y su capa de salida. <\/p>\n\n\n\n Como se puede ver uso el mismo n\u00famero de nodos en las dos capas Conv2DTranpose<\/em>. Una pr\u00e1ctica com\u00fan es reducir el n\u00famero de nodos a medida que se ejecuta el upsampling. Pero en este caso en concreto, como aprovecho la \u00faltima capa Conv2DTranspose<\/em> como capa de salida y sus nodos ya se han reducido bastante, no hace falta irlos reduciendo en las capas previas. <\/p>\n\n\n\n Al final del art\u00edculo hay un generador que reduce el n\u00famero de nodos entre upsamplings. En el notebook de Colab <\/a>pod\u00e9is encontrar los dos, y comparar el resultado <\/p>\n\n\n\n En el generador se han utilizado varias de las recomendaciones conocidas como GAN Hacks. <\/p>\n\n\n\n Resumiendo se ha creado un generador muy sencillo, pero muy optimizado para el Dataset usado. <\/p>\n\n\n\n
Funcionamiento de una GAN.<\/h2>\n\n\n\n
test_noise = tf.random.normal([16, 100])<\/pre>\n\n\n\n
\n
\u00bfQu\u00e9 veremos de m\u00e1s?<\/h2>\n\n\n\n
\n
Crear el Generador para im\u00e1genes a color. <\/h2>\n\n\n\n
keras.layers.Dense(4 * 4 * 128, input_shape=[noise_input], \n activation=keras.layers.LeakyReLU(alpha=0.2)),\nkeras.layers.Reshape([4, 4, 128]),<\/pre>\n\n\n\n
keras.layers.Conv2DTranspose(128, kernel_size=4, strides=2, padding=\"SAME\", \n activation=keras.layers.LeakyReLU(alpha=0.2)),\nkeras.layers.BatchNormalization(),<\/pre>\n\n\n\n
#Otra opci\u00f3n posible para realizar el upsampling. \nkeras.layers.Conv2DTranspose(128, kernel_size=4, strides=2, padding=\"SAME\"),\nkeras.layers.BatchNormalization(),\nkeras.layers.LeakyReLU(alpha=0.2), <\/pre>\n\n\n\n
keras.layers.Conv2DTranspose(3, kernel_size=4, strides=2, padding=\"SAME\",\n activation='tanh'),\n ])<\/pre>\n\n\n\n
#Otra alternativa para finalizar el modelo Generador. \nkeras.layers.Conv2DTranspose(128, kernel_size=4, strides=2, padding=\"SAME\", \n activation=keras.layers.LeakyReLU(alpha=0.2)),\nkeras.layers.BatchNormalization(),\nkeras.layers.Conv2D(3, kernel_size=5, activation='tanh', padding='same')\n<\/pre>\n\n\n\n
noise_input = 100\ngenerator = keras.models.Sequential([\n keras.layers.Dense(4 * 4 * 128, input_shape=[noise_input], \n activation=keras.layers.LeakyReLU(alpha=0.2)),\n keras.layers.Reshape([4, 4, 128]),\n keras.layers.BatchNormalization(),\n\n #First UpSample doubling the size to 8x8\n keras.layers.Conv2DTranspose(128, kernel_size=4, strides=2, padding=\"SAME\", \n activation=keras.layers.LeakyReLU(alpha=0.2)),\n keras.layers.BatchNormalization(),\n\n #Second UpSample doubling the size to 16x16\n keras.layers.Conv2DTranspose(128, kernel_size=4, strides=2, padding=\"SAME\", \n activation=keras.layers.LeakyReLU(alpha=0.2)),\n keras.layers.BatchNormalization(),\n\n #Last UpSample doubling the size to 32x32, and shaping the output. \n keras.layers.Conv2DTranspose(3, kernel_size=4, strides=2, padding=\"SAME\",\n activation='tanh'),\n ])<\/pre>\n\n\n\n
\n