{"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":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport tensorflow_addons as tfa\n\nfrom kaggle_datasets import KaggleDatasets\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# change the accelerator to TPU\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print('Device:', tpu.master())\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nexcept:\n    strategy = tf.distribute.get_strategy()\nprint('Number of replicas:', strategy.num_replicas_in_sync)\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n    \nprint(tf.__version__)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-01-19T20:28:45.655103Z","iopub.execute_input":"2022-01-19T20:28:45.655402Z","iopub.status.idle":"2022-01-19T20:28:56.635958Z","shell.execute_reply.started":"2022-01-19T20:28:45.655325Z","shell.execute_reply":"2022-01-19T20:28:56.635134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Data Pipeline","metadata":{}},{"cell_type":"code","source":"# import files from TFRecords, keep photo dataset and Monet dataset separate\nGCS_PATH = KaggleDatasets().get_gcs_path()\n\nMONET_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nprint('Monet TFRecord Files:', len(MONET_FILENAMES))\n\nPHOTO_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\nprint('Photo TFRecord Files:', len(PHOTO_FILENAMES))","metadata":{"execution":{"iopub.status.busy":"2022-01-19T20:29:06.02568Z","iopub.execute_input":"2022-01-19T20:29:06.025959Z","iopub.status.idle":"2022-01-19T20:29:06.633115Z","shell.execute_reply.started":"2022-01-19T20:29:06.025931Z","shell.execute_reply":"2022-01-19T20:29:06.632258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# all the images for the competition are already sized to 256x256. As these images are RGB images, set the channel to 3. \n# Additionally, we need to scale the images to a [-1,1] scale. Because we are building a generative model, we don't need the lables or the image id so we'll only return the image from the TFRecord.\nIMAGE_SIZE = [256, 256]\n\ndef decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = (tf.cast(image, tf.float32) / 127.5) - 1\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n\ndef read_tfrecord(example):\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    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    return image","metadata":{"execution":{"iopub.status.busy":"2022-01-19T20:29:12.743462Z","iopub.execute_input":"2022-01-19T20:29:12.743904Z","iopub.status.idle":"2022-01-19T20:29:12.752403Z","shell.execute_reply.started":"2022-01-19T20:29:12.743872Z","shell.execute_reply":"2022-01-19T20:29:12.751675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the function to extract the image from the files.\ndef load_dataset(filenames, labeled=True, ordered=False):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTOTUNE)\n    return dataset","metadata":{"execution":{"iopub.status.busy":"2022-01-19T20:29:15.010732Z","iopub.execute_input":"2022-01-19T20:29:15.011026Z","iopub.status.idle":"2022-01-19T20:29:15.015918Z","shell.execute_reply.started":"2022-01-19T20:29:15.01099Z","shell.execute_reply":"2022-01-19T20:29:15.015147Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load datasets\nmonet_ds = load_dataset(MONET_FILENAMES, labeled=True).batch(1)\nphoto_ds = load_dataset(PHOTO_FILENAMES, labeled=True).batch(1)","metadata":{"execution":{"iopub.status.busy":"2022-01-19T20:29:17.264478Z","iopub.execute_input":"2022-01-19T20:29:17.26497Z","iopub.status.idle":"2022-01-19T20:29:17.44963Z","shell.execute_reply.started":"2022-01-19T20:29:17.264919Z","shell.execute_reply":"2022-01-19T20:29:17.448894Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# visualize a photo example and a Monet example\nexample_monet = next(iter(monet_ds))\nexample_photo = next(iter(photo_ds))\n\nplt.subplot(121)\nplt.title('Photo')\nplt.imshow(example_photo[0] * 0.5 + 0.5)\n\nplt.subplot(122)\nplt.title('Monet')\nplt.imshow(example_monet[0] * 0.5 + 0.5)","metadata":{"execution":{"iopub.status.busy":"2022-01-19T20:29:18.846212Z","iopub.execute_input":"2022-01-19T20:29:18.846508Z","iopub.status.idle":"2022-01-19T20:29:20.212217Z","shell.execute_reply.started":"2022-01-19T20:29:18.846475Z","shell.execute_reply":"2022-01-19T20:29:20.211615Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Generator","metadata":{}},{"cell_type":"code","source":"# We'll be using a UNET architecture for our CycleGAN. Here are some articles explaining UNET: https://towardsdatascience.com/unet-line-by-line-explanation-9b191c76baf5\n# To build our generator, let's first define our downsample and upsample methods.\n# The downsample, as the name suggests, reduces the 2D dimensions, the width and height, of the image by the stride. \n# The stride is the length of the step the filter takes. Since the stride is 2, the filter is applied to every other pixel, hence reducing the weight and height by 2.\n# We'll be using an instance normalization instead of batch normalization. \n# As the instance normalization is not standard in the TensorFlow API, we'll use the layer from TensorFlow Add-ons.\n\nOUTPUT_CHANNELS = 3\n\n# Reduces the 2D dimensions (width and height) by the stride (length of the step the filter takes). \ndef downsample(filters, size, apply_instancenorm=True):\n    initializer = tf.random_normal_initializer(0., 0.02) # Initialize random values in a normal ditribution centered at 0 with stdev of .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    if apply_instancenorm:\n        result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n\n    result.add(layers.LeakyReLU())\n\n    return result","metadata":{"execution":{"iopub.status.busy":"2022-01-19T18:39:33.150064Z","iopub.execute_input":"2022-01-19T18:39:33.150388Z","iopub.status.idle":"2022-01-19T18:39:33.158534Z","shell.execute_reply.started":"2022-01-19T18:39:33.150347Z","shell.execute_reply":"2022-01-19T18:39:33.157551Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Increases the dimensions of the image. Conv2DTranspose does opposite of Conv2D layer.\ndef 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":{"execution":{"iopub.status.busy":"2022-01-19T18:39:33.159895Z","iopub.execute_input":"2022-01-19T18:39:33.160227Z","iopub.status.idle":"2022-01-19T18:39:33.171141Z","shell.execute_reply.started":"2022-01-19T18:39:33.160187Z","shell.execute_reply":"2022-01-19T18:39:33.17031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def Generator():\n    inputs = layers.Input(shape=[256,256,3])\n\n    down_stack = [\n        downsample(64, 4, apply_instancenorm=False), # (bs, 128, 128, 64)\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":{"execution":{"iopub.status.busy":"2022-01-19T18:39:33.172539Z","iopub.execute_input":"2022-01-19T18:39:33.172846Z","iopub.status.idle":"2022-01-19T18:39:33.18611Z","shell.execute_reply.started":"2022-01-19T18:39:33.172808Z","shell.execute_reply":"2022-01-19T18:39:33.185153Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 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":{"execution":{"iopub.status.busy":"2022-01-19T18:39:33.188611Z","iopub.execute_input":"2022-01-19T18:39:33.188849Z","iopub.status.idle":"2022-01-19T18:39:33.202265Z","shell.execute_reply.started":"2022-01-19T18:39:33.188822Z","shell.execute_reply":"2022-01-19T18:39:33.201307Z"},"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":{"execution":{"iopub.status.busy":"2022-01-19T18:39:33.203331Z","iopub.execute_input":"2022-01-19T18:39:33.203632Z","iopub.status.idle":"2022-01-19T18:39:43.260936Z","shell.execute_reply.started":"2022-01-19T18:39:33.203593Z","shell.execute_reply":"2022-01-19T18:39:43.260202Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Generator not yet trained and therefore monet-esque photo does not show what would be expected\nto_monet = monet_generator(example_photo)\n\nplt.subplot(1, 2, 1)\nplt.title(\"Original Photo\")\nplt.imshow(example_photo[0] * 0.5 + 0.5)\n\nplt.subplot(1, 2, 2)\nplt.title(\"Monet-esque Photo\")\nplt.imshow(to_monet[0] * 0.5 + 0.5)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-01-19T18:39:43.262169Z","iopub.execute_input":"2022-01-19T18:39:43.262396Z","iopub.status.idle":"2022-01-19T18:39:43.900837Z","shell.execute_reply.started":"2022-01-19T18:39:43.262369Z","shell.execute_reply":"2022-01-19T18:39:43.899913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 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            # photo to monet back to photo\n            fake_monet = self.m_gen(real_photo, training=True)\n            cycled_photo = self.p_gen(fake_monet, training=True)\n\n            # monet to photo back to monet\n            fake_photo = self.p_gen(real_monet, training=True)\n            cycled_monet = self.m_gen(fake_photo, training=True)\n\n            # generating itself\n            same_monet = self.m_gen(real_monet, training=True)\n            same_photo = self.p_gen(real_photo, training=True)\n\n            # discriminator used to check, inputing real images\n            disc_real_monet = self.m_disc(real_monet, training=True)\n            disc_real_photo = self.p_disc(real_photo, training=True)\n\n            # discriminator used to check, inputing fake images\n            disc_fake_monet = self.m_disc(fake_monet, training=True)\n            disc_fake_photo = self.p_disc(fake_photo, training=True)\n\n            # evaluates generator loss\n            monet_gen_loss = self.gen_loss_fn(disc_fake_monet)\n            photo_gen_loss = self.gen_loss_fn(disc_fake_photo)\n\n            # evaluates total cycle consistency loss\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            # evaluates total generator loss\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            # evaluates discriminator loss\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        # Calculate the gradients for generator and discriminator\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        # Apply the gradients to the optimizer\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":{"execution":{"iopub.status.busy":"2022-01-19T18:39:43.902482Z","iopub.execute_input":"2022-01-19T18:39:43.902776Z","iopub.status.idle":"2022-01-19T18:39:43.923674Z","shell.execute_reply.started":"2022-01-19T18:39:43.902736Z","shell.execute_reply":"2022-01-19T18:39:43.922999Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Loss Function","metadata":{"execution":{"iopub.status.busy":"2022-01-19T16:46:37.729493Z","iopub.execute_input":"2022-01-19T16:46:37.730206Z","iopub.status.idle":"2022-01-19T16:46:37.734178Z","shell.execute_reply.started":"2022-01-19T16:46:37.730161Z","shell.execute_reply":"2022-01-19T16:46:37.733372Z"}}},{"cell_type":"code","source":"with strategy.scope():\n    def discriminator_loss(real, generated):\n        real_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(real), real)\n\n        generated_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.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","metadata":{"execution":{"iopub.status.busy":"2022-01-19T18:39:43.924665Z","iopub.execute_input":"2022-01-19T18:39:43.925306Z","iopub.status.idle":"2022-01-19T18:39:43.941899Z","shell.execute_reply.started":"2022-01-19T18:39:43.925265Z","shell.execute_reply":"2022-01-19T18:39:43.940787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    def generator_loss(generated):\n        return tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(generated), generated)","metadata":{"execution":{"iopub.status.busy":"2022-01-19T18:39:43.943021Z","iopub.execute_input":"2022-01-19T18:39:43.943706Z","iopub.status.idle":"2022-01-19T18:39:43.95616Z","shell.execute_reply.started":"2022-01-19T18:39:43.943669Z","shell.execute_reply":"2022-01-19T18:39:43.955321Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    def calc_cycle_loss(real_image, cycled_image, LAMBDA):\n        loss1 = tf.reduce_mean(tf.abs(real_image - cycled_image))\n\n        return LAMBDA * loss1","metadata":{"execution":{"iopub.status.busy":"2022-01-19T18:39:43.957281Z","iopub.execute_input":"2022-01-19T18:39:43.9578Z","iopub.status.idle":"2022-01-19T18:39:43.969696Z","shell.execute_reply.started":"2022-01-19T18:39:43.957754Z","shell.execute_reply":"2022-01-19T18:39:43.968841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\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":{"execution":{"iopub.status.busy":"2022-01-19T18:39:43.970854Z","iopub.execute_input":"2022-01-19T18:39:43.971154Z","iopub.status.idle":"2022-01-19T18:39:43.982546Z","shell.execute_reply.started":"2022-01-19T18:39:43.971119Z","shell.execute_reply":"2022-01-19T18:39:43.981674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Train GAN","metadata":{}},{"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":{"execution":{"iopub.status.busy":"2022-01-19T18:39:43.983842Z","iopub.execute_input":"2022-01-19T18:39:43.984749Z","iopub.status.idle":"2022-01-19T18:39:43.994556Z","shell.execute_reply.started":"2022-01-19T18:39:43.984703Z","shell.execute_reply":"2022-01-19T18:39:43.99374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    cycle_gan_model = CycleGan(\n        monet_generator, photo_generator, 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":{"execution":{"iopub.status.busy":"2022-01-19T18:39:43.995897Z","iopub.execute_input":"2022-01-19T18:39:43.996222Z","iopub.status.idle":"2022-01-19T18:39:44.057492Z","shell.execute_reply.started":"2022-01-19T18:39:43.996173Z","shell.execute_reply":"2022-01-19T18:39:44.056535Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cycle_gan_model.fit(\n    tf.data.Dataset.zip((monet_ds, photo_ds)),\n    epochs=50\n)","metadata":{"execution":{"iopub.status.busy":"2022-01-19T18:39:44.058662Z","iopub.execute_input":"2022-01-19T18:39:44.058899Z","iopub.status.idle":"2022-01-19T18:57:09.371634Z","shell.execute_reply.started":"2022-01-19T18:39:44.058871Z","shell.execute_reply":"2022-01-19T18:57:09.370921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualize Generated Files","metadata":{}},{"cell_type":"code","source":"_, ax = plt.subplots(5, 2, figsize=(12, 12))\nfor i, img in enumerate(photo_ds.take(5)):\n    prediction = monet_generator(img, training=False)[0].numpy()\n    prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n    img = (img[0] * 127.5 + 127.5).numpy().astype(np.uint8)\n\n    ax[i, 0].imshow(img)\n    ax[i, 1].imshow(prediction)\n    ax[i, 0].set_title(\"Input Photo\")\n    ax[i, 1].set_title(\"Monet-esque\")\n    ax[i, 0].axis(\"off\")\n    ax[i, 1].axis(\"off\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-01-19T18:57:09.373804Z","iopub.execute_input":"2022-01-19T18:57:09.374025Z","iopub.status.idle":"2022-01-19T18:57:11.556992Z","shell.execute_reply.started":"2022-01-19T18:57:09.374Z","shell.execute_reply":"2022-01-19T18:57:11.556215Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Create Submission File","metadata":{}},{"cell_type":"code","source":"import PIL\n! mkdir ../images\ni = 1\n\nfor img in photo_ds:\n    prediction = monet_generator(img, training=False)[0].numpy()\n    prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n    im = PIL.Image.fromarray(prediction)\n    im.save(\"../images/\" + str(i) + \".jpg\")\n    i += 1\n    \nimport shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{"execution":{"iopub.status.busy":"2022-01-19T18:57:11.558216Z","iopub.execute_input":"2022-01-19T18:57:11.558898Z","iopub.status.idle":"2022-01-19T19:30:27.979595Z","shell.execute_reply.started":"2022-01-19T18:57:11.558862Z","shell.execute_reply":"2022-01-19T19:30:27.978361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}