{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Utiliser les architecture CycleGAN pour générer des tableaux de Monet","metadata":{}},{"cell_type":"markdown","source":"Ce notebook est la version finale de notre travail.\nNous avons retiré ce qui ne nous semblait pas pertinent pour la version finale notamment les différents tests que nous avons effectués sur les fonctions de perte.","metadata":{}},{"cell_type":"markdown","source":"Ce notebook a été inspiré par plusieurs notebook notamment : \n\n* https://www.kaggle.com/amyjang/monet-cyclegan-tutorial\n* https://www.kaggle.com/dimitreoliveira/improving-cyclegan-monet-paintings","metadata":{}},{"cell_type":"markdown","source":"Les contributions que nous avons tenté d'implémenter :\n* Retirer la cycle-consistency loss -> Pas d'amélioration notable\n* Retirer la identity_loss sur les tableaux et photos générées par eux mêmes\n* Adapter correctement la fonction d'augmentation de données\n* Mise en place de Callbacks Keras afin d'arrêter l'entraînement -> Difficulté de restituer les poids du modèle avec le TPU\n* Tuning des paramètres à l'aide de Keras et de l'Hyperband -> Beaucoup trop long \n* Ajout de données provenant de wikiart : https://www.wikiart.org/fr/claude-monet -> Difficile de faire les manipulation pour les avoir au bon format et en .TFRecord","metadata":{}},{"cell_type":"code","source":"!pip install tensorflow_addons","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np \nimport pandas as pd \nimport os\nimport math\nimport random\nimport matplotlib.pyplot as plt\nimport cv2\nimport tensorflow as tf\nfrom   tensorflow import keras\nfrom   tensorflow.keras import layers\nimport tensorflow_addons as tfa\nimport tensorflow_datasets as tfds\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport tensorflow.keras.backend as K\nimport os, random, json, PIL, shutil, re, imageio, glob\nfrom tensorflow.keras import Model, losses, optimizers\nfrom tensorflow.keras.callbacks import Callback\nfrom kaggle_datasets import KaggleDatasets","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Configuration TPU (Tensor Processing Unit)\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print(f'Running on TPU {tpu.master()}')\nexcept ValueError:\n    tpu = None\n\nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nelse:\n    strategy = tf.distribute.get_strategy()\n\n\nREPLICAS = strategy.num_replicas_in_sync\nAUTO = AUTOTUNE = tf.data.experimental.AUTOTUNE\nprint(f'REPLICAS: {REPLICAS}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load the datasets","metadata":{}},{"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Load the TFRecord files","metadata":{}},{"cell_type":"code","source":"# Get the Google Cloud Storage path URI (GCS path) for Kaggle Datasets\nGCS_PATH = KaggleDatasets().get_gcs_path('gan-getting-started')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Obtain two lists of files that match the given patterns specified in str()\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\nMONET_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nPHOTO_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\n\nn_monet_samples = count_data_items(MONET_FILENAMES)\nn_photo_samples = count_data_items(PHOTO_FILENAMES)\n\nprint('Number of Monet TFRecord Files:', len(MONET_FILENAMES))\nprint('Number of Photo TFRecord Files:', len(PHOTO_FILENAMES))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BUFFER_SIZE = 1000\nBATCH_SIZE =  4\nEPOCHS_NUM = 30\nIMG_WIDTH = 256\nIMG_HEIGHT = 256\nHEIGHT = 256\nWIDTH = 256\nCHANNELS = 3","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Décodage et lecture du format TFRecord","metadata":{}},{"cell_type":"code","source":"def decode_image(image):\n    # Decode a JPEG-encoded image to a uint8 tensor.\n    image = tf.image.decode_jpeg(image, channels=3)\n    \n    # Normalize the image to the range of the tanh activation function [-1, 1] for \n    # inputs to the generator and discriminator in GAN model \n    # (i.e. the pixel values are divided by (255/2) to form a value of in a range of [0, 2] and then subtract by 1\n    # to result into a range of [-1, 1])\n    image = (tf.cast(image, tf.float32) / 127.5) - 1        \n    \n    # Reshape the tensor using (256, 256, 3) where 3 is number of channels: Red, Green, and Blue \n    image = tf.reshape(image, [IMG_HEIGHT, IMG_WIDTH, 3])             \n    return image\n\ndef read_tfrecord(example):\n    # Define TFRecord format \n    tfrecord_format = {\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n        \"image\":      tf.io.FixedLenFeature([], tf.string),\n        \"target\":     tf.io.FixedLenFeature([], tf.string)\n    }\n    # Parse a single example\n    example = tf.io.parse_single_example(example, tfrecord_format)  \n    # Decode a JPEG image to a uint8 tensor by calling decode_image()\n    image = decode_image(example['image'])    \n    \n    return image # Return an image tensor","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Fonctions d'augmentation de données \nNous avons testé plusieurs fonctions pour l'augmentation de données pour modifier spatialement les images du jeu de données","metadata":{}},{"cell_type":"code","source":"def data_augment(image):\n    p_rotate = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    p_spatial = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    p_crop = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    \n    # Apply jitter\n    if p_crop > .5:\n        image = tf.image.resize(image, [286, 286])\n        image = tf.image.random_crop(image, size=[256, 256, 3])\n        if p_crop > .9:\n            image = tf.image.resize(image, [300, 300])\n            image = tf.image.random_crop(image, size=[256, 256, 3])\n    \n    # Random rotation\n    if p_rotate > .9:\n        image = tf.image.rot90(image, k=3) # rotate 270º\n    elif p_rotate > .7:\n        image = tf.image.rot90(image, k=2) # rotate 180º\n    elif p_rotate > .5:\n        image = tf.image.rot90(image, k=1) # rotate 90º\n    \n    # Random mirroring\n    if p_spatial > .6:\n        image = tf.image.random_flip_left_right(image)\n        image = tf.image.random_flip_up_down(image)\n        if p_spatial > .9:\n            image = tf.image.transpose(image)\n    \n    return image","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Première fonction de test\ndef data_augment_test(image): # input data augmentation\n    x = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    y = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    \n    if y > .5: # random crop image\n        image = tf.image.resize(image, [286, 286])\n        image = tf.image.random_crop(image, size=[256, 256, 3])\n            \n    if x > .6: # random flip image\n        image = tf.image.random_flip_left_right(image)\n    \n    return image","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_dataset(filenames, labeled=True, ordered=False):\n    dataset = tf.data.TFRecordDataset(filenames)\n    # map a dataset with a mapping function read_tfrecord and \n    # Number of parallel calls is set to AUTOTUNE constant previously defined\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTOTUNE)\n    return dataset","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BATCHSIZE = 1\nmonet_ds = load_dataset(MONET_FILENAMES, labeled=True).batch(BATCHSIZE, drop_remainder=True)\nphoto_ds = load_dataset(PHOTO_FILENAMES, labeled=True).batch(BATCHSIZE, drop_remainder=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_gan_dataset(monet_files, photo_files, augment=None, repeat=True, shuffle=True, batch_size=1):\n\n    monet_ds = load_dataset(monet_files)\n    photo_ds = load_dataset(photo_files)\n    \n    if augment:\n        monet_ds = monet_ds.map(augment, num_parallel_calls=AUTO)\n        photo_ds = photo_ds.map(augment, num_parallel_calls=AUTO)\n\n    if repeat:\n        monet_ds = monet_ds.repeat()\n        photo_ds = photo_ds.repeat()\n        \n    if shuffle:\n        monet_ds = monet_ds.shuffle(2048)\n        photo_ds = photo_ds.shuffle(2048)\n        \n    monet_ds = monet_ds.batch(batch_size, drop_remainder=True)\n    photo_ds = photo_ds.batch(batch_size, drop_remainder=True)\n    \n    monet_ds = monet_ds.cache()\n    photo_ds = photo_ds.cache()\n    \n    monet_ds = monet_ds.prefetch(AUTO)\n    photo_ds = photo_ds.prefetch(AUTO)\n    \n    gan_ds = tf.data.Dataset.zip((monet_ds, photo_ds))\n    \n    return gan_ds","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"full_dataset = get_gan_dataset(MONET_FILENAMES, PHOTO_FILENAMES, augment=data_augment, repeat=True, shuffle=True, batch_size=BATCH_SIZE)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def view_image(ds, nrows=1, ncols=5):\n    ds_iter = iter(ds)\n\n    fig = plt.figure(figsize=(25, nrows * 5.05 )) \n    \n    for i in range(ncols * nrows):\n        image = next(ds_iter)\n        image = image.numpy()\n        ax = fig.add_subplot(nrows, ncols, i+1, xticks=[], yticks=[])\n        ax.imshow(image[0] * 0.5 + .5) ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_image(monet_ds,2, 5)","metadata":{"_kg_hide-output":false,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_image(photo_ds,2, 5)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Build the generator","metadata":{}},{"cell_type":"code","source":"OUTPUT_CHANNELS = 3\n\ndef downsample(filters, size, apply_instancenorm=True):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = keras.Sequential()\n    result.add(layers.Conv2D(filters, size, strides=2, padding='same',\n                             kernel_initializer=initializer, use_bias=False))\n    \n    # Pas sur la première couche\n    if apply_instancenorm:\n        result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n\n    result.add(layers.LeakyReLU())\n\n    return result","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def upsample(filters, size, apply_dropout=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = keras.Sequential()\n    result.add(layers.Conv2DTranspose(filters, size, strides=2,\n                                      padding='same',\n                                      kernel_initializer=initializer,\n                                      use_bias=False))\n\n    result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n\n    if apply_dropout:\n        result.add(layers.Dropout(0.5))\n\n    result.add(layers.ReLU())\n\n    return result","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def Generator():\n    inputs = layers.Input(shape=[256,256,3])\n\n    # bs = batch size\n    down_stack = [\n        downsample(64, 4, apply_instancenorm=False), # Pas d'instancenorm sur la première couche\n        downsample(128, 4), # (bs, 64, 64, 128)\n        downsample(256, 4), # (bs, 32, 32, 256)\n        downsample(512, 4), # (bs, 16, 16, 512)\n        downsample(512, 4), # (bs, 8, 8, 512)\n        downsample(512, 4), # (bs, 4, 4, 512)\n        downsample(512, 4), # (bs, 2, 2, 512)\n        downsample(512, 4), # (bs, 1, 1, 512)\n    ]\n\n    up_stack = [\n        upsample(512, 4, apply_dropout=True), # (bs, 2, 2, 1024)\n        upsample(512, 4, apply_dropout=True), # (bs, 4, 4, 1024)\n        upsample(512, 4, apply_dropout=True), # (bs, 8, 8, 1024)\n        upsample(512, 4), # (bs, 16, 16, 1024)\n        upsample(256, 4), # (bs, 32, 32, 512)\n        upsample(128, 4), # (bs, 64, 64, 256)\n        upsample(64, 4), # (bs, 128, 128, 128)\n    ]\n\n    initializer = tf.random_normal_initializer(0., 0.02)\n    last = layers.Conv2DTranspose(OUTPUT_CHANNELS, 4,\n                                  strides=2,\n                                  padding='same',\n                                  kernel_initializer=initializer,\n                                  activation='tanh') # (bs, 256, 256, 3)\n\n    x = inputs\n\n    # Downsampling through the model\n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips.append(x)\n\n    skips = reversed(skips[:-1])\n\n    # Upsampling and establishing the skip connections\n    for up, skip in zip(up_stack, skips):\n        x = up(x)\n        x = layers.Concatenate()([x, skip])\n\n    x = last(x)\n\n    return keras.Model(inputs=inputs, outputs=x)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"generator_g = Generator()\ntf.keras.utils.plot_model(generator_g, show_shapes=True, dpi=64)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Build the discriminator","metadata":{}},{"cell_type":"code","source":"def Discriminator():\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    inp = layers.Input(shape=[256, 256, 3], name='input_image')\n\n    x = inp\n\n    down1 = downsample(64, 4, False)(x) # (bs, 128, 128, 64)\n    down2 = downsample(128, 4)(down1) # (bs, 64, 64, 128)\n    down3 = downsample(256, 4)(down2) # (bs, 32, 32, 256)\n\n    zero_pad1 = layers.ZeroPadding2D()(down3) # (bs, 34, 34, 256)\n    conv = layers.Conv2D(512, 4, strides=1,\n                         kernel_initializer=initializer,\n                         use_bias=False)(zero_pad1) # (bs, 31, 31, 512)\n\n    norm1 = tfa.layers.InstanceNormalization(gamma_initializer=gamma_init)(conv)\n\n    leaky_relu = layers.LeakyReLU()(norm1)\n\n    zero_pad2 = layers.ZeroPadding2D()(leaky_relu) # (bs, 33, 33, 512)\n\n    last = layers.Conv2D(1, 4, strides=1,\n                         kernel_initializer=initializer)(zero_pad2) # (bs, 30, 30, 1)\n\n    return tf.keras.Model(inputs=inp, outputs=last)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"discri_g = Discriminator()\ntf.keras.utils.plot_model(discri_g, show_shapes=True, dpi=64)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    monet_generator = Generator() # transforms photos to Monet-esque paintings\n    photo_generator = Generator() # transforms Monet paintings to be more like photos\n\n    monet_discriminator = Discriminator() # differentiates real Monet paintings and generated Monet paintings\n    photo_discriminator = Discriminator() # differentiates real photos and generated photos","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Build the CycleGAN model","metadata":{}},{"cell_type":"code","source":"class CycleGan(keras.Model):\n    def __init__(\n        self,\n        monet_generator,\n        photo_generator,\n        monet_discriminator,\n        photo_discriminator,\n        lambda_cycle=10,\n    ):\n        super(CycleGan, self).__init__()\n        self.m_gen = monet_generator\n        self.p_gen = photo_generator\n        self.m_disc = monet_discriminator\n        self.p_disc = photo_discriminator\n        self.lambda_cycle = lambda_cycle\n        \n    def compile(\n        self,\n        m_gen_optimizer,\n        p_gen_optimizer,\n        m_disc_optimizer,\n        p_disc_optimizer,\n        gen_loss_fn,\n        disc_loss_fn,\n        cycle_loss_fn,\n        identity_loss_fn\n    ):\n        super(CycleGan, self).compile()\n        self.m_gen_optimizer = m_gen_optimizer\n        self.p_gen_optimizer = p_gen_optimizer\n        self.m_disc_optimizer = m_disc_optimizer\n        self.p_disc_optimizer = p_disc_optimizer\n        self.gen_loss_fn = gen_loss_fn\n        self.disc_loss_fn = disc_loss_fn\n        self.cycle_loss_fn = cycle_loss_fn\n        self.identity_loss_fn = identity_loss_fn\n        \n    def train_step(self, batch_data):\n        real_monet, real_photo = batch_data\n        \n        with tf.GradientTape(persistent=True) as tape:\n            # Entrainement Photo -> Monet et Monet généré -> Photo\n            fake_monet = self.m_gen(real_photo, training=True)\n            cycled_photo = self.p_gen(fake_monet, training=True)\n\n            # Entrainement Monet -> Photo et Photo généré -> Monet\n            fake_photo = self.p_gen(real_monet, training=True)\n            cycled_monet = self.m_gen(fake_photo, training=True)\n\n            # Entrainement Photo -> Photo et Monet -> Monet\n            same_monet = self.m_gen(real_monet, training=True)\n            same_photo = self.p_gen(real_photo, training=True)\n\n            # Discriminateur pour les vrais images et les vrais tableaux\n            disc_real_monet = self.m_disc(real_monet, training=True)\n            disc_real_photo = self.p_disc(real_photo, training=True)\n\n            # Discriminateur pour les fausses images et les faux tableaux\n            disc_fake_monet = self.m_disc(fake_monet, training=True)\n            disc_fake_photo = self.p_disc(fake_photo, training=True)\n\n            # Fonction de perte des générateurs\n            monet_gen_loss = self.gen_loss_fn(disc_fake_monet)\n            photo_gen_loss = self.gen_loss_fn(disc_fake_photo)\n\n            # Fonction de perte cyclique\n            total_cycle_loss = self.cycle_loss_fn(real_monet, cycled_monet, self.lambda_cycle) + self.cycle_loss_fn(real_photo, cycled_photo, self.lambda_cycle)\n\n            # Fonction de perte globale des générateurs (+ cyclique)\n            total_monet_gen_loss = monet_gen_loss + total_cycle_loss + self.identity_loss_fn(real_monet, same_monet, self.lambda_cycle)\n            total_photo_gen_loss = photo_gen_loss + total_cycle_loss + self.identity_loss_fn(real_photo, same_photo, self.lambda_cycle)\n\n            # Fonction de perte des discriminateurs \n            monet_disc_loss = self.disc_loss_fn(disc_real_monet, disc_fake_monet)\n            photo_disc_loss = self.disc_loss_fn(disc_real_photo, disc_fake_photo)\n\n        # Montées et descentes des gradients pour les fonctions de pertes\n        monet_generator_gradients = tape.gradient(total_monet_gen_loss,\n                                                  self.m_gen.trainable_variables)\n        photo_generator_gradients = tape.gradient(total_photo_gen_loss,\n                                                  self.p_gen.trainable_variables)\n\n        monet_discriminator_gradients = tape.gradient(monet_disc_loss,\n                                                      self.m_disc.trainable_variables)\n        photo_discriminator_gradients = tape.gradient(photo_disc_loss,\n                                                      self.p_disc.trainable_variables)\n\n        # Application des gradients aux différents optimiseurs des réseaux\n        self.m_gen_optimizer.apply_gradients(zip(monet_generator_gradients,\n                                                 self.m_gen.trainable_variables))\n\n        self.p_gen_optimizer.apply_gradients(zip(photo_generator_gradients,\n                                                 self.p_gen.trainable_variables))\n\n        self.m_disc_optimizer.apply_gradients(zip(monet_discriminator_gradients,\n                                                  self.m_disc.trainable_variables))\n\n        self.p_disc_optimizer.apply_gradients(zip(photo_discriminator_gradients,\n                                                  self.p_disc.trainable_variables))\n        \n        return {\n            \"monet_gen_loss\": total_monet_gen_loss,\n            \"photo_gen_loss\": total_photo_gen_loss,\n            \"monet_disc_loss\": monet_disc_loss,\n            \"photo_disc_loss\": photo_disc_loss\n        }","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loss functions","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    # Discriminateur : fonction de perte {0: fake, 1: real} (The discriminator loss outputs the average of the real and generated loss)\n    def discriminator_loss(real, generated):\n        real_loss = losses.BinaryCrossentropy(from_logits=True, reduction=losses.Reduction.NONE)(tf.ones_like(real), real)\n\n        generated_loss = losses.BinaryCrossentropy(from_logits=True, reduction=losses.Reduction.NONE)(tf.zeros_like(generated), generated)\n\n        total_disc_loss = real_loss + generated_loss\n\n        return total_disc_loss * 0.5\n    \n    # Generator loss\n    def generator_loss(generated):\n        return losses.BinaryCrossentropy(from_logits=True, reduction=losses.Reduction.NONE)(tf.ones_like(generated), generated)\n    \n    # Cycle consistency loss (measures if original photo and the twice transformed photo to be similar to one another)\n    def calc_cycle_loss(real_image, cycled_image, LAMBDA):\n        loss1 = tf.reduce_mean(tf.abs(real_image - cycled_image))\n        return LAMBDA * loss1\n\n    # Identity loss (compares the image with its generator (i.e. photo with photo generator))\n    def identity_loss(real_image, same_image, LAMBDA):\n        loss = tf.reduce_mean(tf.abs(real_image - same_image))\n        return LAMBDA * 0.5 * loss","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    monet_generator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n    photo_generator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n\n    monet_discriminator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n    photo_discriminator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    cycle_gan_model = CycleGan(\n        monet_generator, photo_generator, \n        monet_discriminator, photo_discriminator\n    )\n\n    cycle_gan_model.compile(\n        m_gen_optimizer = monet_generator_optimizer,\n        p_gen_optimizer = photo_generator_optimizer,\n        m_disc_optimizer = monet_discriminator_optimizer,\n        p_disc_optimizer = photo_discriminator_optimizer,\n        gen_loss_fn = generator_loss,\n        disc_loss_fn = discriminator_loss,\n        cycle_loss_fn = calc_cycle_loss,\n        identity_loss_fn = identity_loss\n    )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Entrainement du modèle\nPour simplifier et rendre l'entraînement plus rapide nous vous conseillons de directement rajouter batch_size=16\nA savoir qu'un plus large batch size fera baisser la qualité de la génération\nLe nombre optimal que nous avons trouvé est 4 pour 30 epochs.","metadata":{}},{"cell_type":"code","source":"cycle_gan_model.fit(\n    full_dataset,\n    epochs=EPOCHS_NUM,\n    steps_per_epoch=(max(n_monet_samples, n_photo_samples)//BATCH_SIZE),\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Display generated photos","metadata":{}},{"cell_type":"code","source":"def display_generated_samples(ds, model, n_samples):\n    ds_iter = iter(ds)\n    for n_sample in range(n_samples):\n        example_sample = next(ds_iter)\n        generated_sample = model.predict(example_sample)\n    \n        plt.subplot(121)\n        plt.title(\"Input image\")\n        plt.imshow(example_sample[0] * 0.5 + 0.5)\n        plt.axis('off')\n\n        plt.subplot(122)\n        plt.title(\"Generated image\")\n        plt.imshow(generated_sample[0] * 0.5 + 0.5)\n        plt.axis('off')\n        plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display_generated_samples(load_dataset(PHOTO_FILENAMES).batch(1), monet_generator, 10)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prédiction et sauvegarde des images\n\nPour participer à la compétition nous devons générér 7000 à 10000 images et zipper le document les contenants\nCe code récupéré au sein de la communauté Kaggle permet de réaliser cette dernière tâche :","metadata":{}},{"cell_type":"code","source":"import PIL\ndef predict_and_save(input_ds, generator_model, output_path):\n    i = 1\n    for img in input_ds:\n        prediction = generator_model(img, training=False)[0].numpy() # make predition\n        prediction = (prediction * 127.5 + 127.5).astype(np.uint8)   # re-scale\n        im = PIL.Image.fromarray(prediction)\n        im.save(f'{output_path}{str(i)}.jpg')\n        i += 1","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nos.makedirs('../images/') # Create folder to save generated images\n\npredict_and_save(load_dataset(PHOTO_FILENAMES).batch(1), monet_generator, '../images/')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.make_archive('/kaggle/working/images/', 'zip', '../images')\n\nprint(f\"Number of generated samples: {len([name for name in os.listdir('../images/') if os.path.isfile(os.path.join('../images/', name))])}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}