{"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":"## Import the libraries","metadata":{}},{"cell_type":"code","source":"from kaggle_datasets import KaggleDatasets\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport PIL\nimport shutil\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport tensorflow_addons as tfa","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:27:58.765581Z","iopub.execute_input":"2021-12-01T15:27:58.765928Z","iopub.status.idle":"2021-12-01T15:27:58.772617Z","shell.execute_reply.started":"2021-12-01T15:27:58.765896Z","shell.execute_reply":"2021-12-01T15:27:58.771620Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Enable the TPU cluster","metadata":{}},{"cell_type":"code","source":"# Enable the TPU cluster resolver.\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":{"execution":{"iopub.status.busy":"2021-12-01T15:28:03.329671Z","iopub.execute_input":"2021-12-01T15:28:03.330156Z","iopub.status.idle":"2021-12-01T15:28:09.571330Z","shell.execute_reply.started":"2021-12-01T15:28:03.330122Z","shell.execute_reply":"2021-12-01T15:28:09.570137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Import the Dataset\n#### Requires turning on the Internet on the Settings tab to work.","metadata":{}},{"cell_type":"code","source":"KAGGLE_GCS_PATH = KaggleDatasets().get_gcs_path()","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:13.493602Z","iopub.execute_input":"2021-12-01T15:28:13.494685Z","iopub.status.idle":"2021-12-01T15:28:14.220845Z","shell.execute_reply.started":"2021-12-01T15:28:13.494621Z","shell.execute_reply":"2021-12-01T15:28:14.219503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"MONET_TFREC_IMAGE_FILES = tf.io.gfile.glob(str(KAGGLE_GCS_PATH + '/monet_tfrec/*.tfrec'))\nPHOTOGRAPH_TFREC_IMAGE_FILES = tf.io.gfile.glob(str(KAGGLE_GCS_PATH + '/photo_tfrec/*.tfrec'))\n\nprint(\"TFREC Records for Monet Images: %s\" % len(MONET_TFREC_IMAGE_FILES))\nprint(\"TFREC Records for Photograph Images: %s\" % len(PHOTOGRAPH_TFREC_IMAGE_FILES))","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:16.013567Z","iopub.execute_input":"2021-12-01T15:28:16.013889Z","iopub.status.idle":"2021-12-01T15:28:16.156609Z","shell.execute_reply.started":"2021-12-01T15:28:16.013858Z","shell.execute_reply":"2021-12-01T15:28:16.155679Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Image Helper functions","metadata":{}},{"cell_type":"code","source":"DATASET_IMAGE_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, [*DATASET_IMAGE_SIZE, 3])\n    return image\n\ndef tfrecord_read(sample):\n    tfrecord_format = {\"image_name\": tf.io.FixedLenFeature([], tf.string),\n                       \"image\": tf.io.FixedLenFeature([], tf.string),\n                       \"target\": tf.io.FixedLenFeature([], tf.string)}\n    sample = tf.io.parse_single_example(sample, tfrecord_format)\n    image = decode_image(sample[\"image\"])\n    return image\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(tfrecord_read, num_parallel_calls=AUTOTUNE)\n    return dataset","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:21.161770Z","iopub.execute_input":"2021-12-01T15:28:21.162229Z","iopub.status.idle":"2021-12-01T15:28:21.170710Z","shell.execute_reply.started":"2021-12-01T15:28:21.162176Z","shell.execute_reply":"2021-12-01T15:28:21.169945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load the Dataset for the Model\n#### The format are tfrecords which can be cycled through an iterator. Unlike other methods, you cannot determine the length of the records without iterating through all the records.","metadata":{}},{"cell_type":"code","source":"monet_images_dataset = load_dataset(MONET_TFREC_IMAGE_FILES, labeled = True).batch(1)\nphotographs_images_dataset = load_dataset(PHOTOGRAPH_TFREC_IMAGE_FILES, labeled = True).batch(1)","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:25.228548Z","iopub.execute_input":"2021-12-01T15:28:25.228835Z","iopub.status.idle":"2021-12-01T15:28:25.342429Z","shell.execute_reply.started":"2021-12-01T15:28:25.228807Z","shell.execute_reply":"2021-12-01T15:28:25.341262Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_records_monet = sum(1 for record in monet_images_dataset)\nnum_records_photographs = sum(1 for record in photographs_images_dataset)\nprint(\"# of monet images to train with: %s\" % num_records_monet)\nprint(\"# of photographs to predict: %s\" % num_records_photographs)","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:27.190996Z","iopub.execute_input":"2021-12-01T15:28:27.191853Z","iopub.status.idle":"2021-12-01T15:28:36.649880Z","shell.execute_reply.started":"2021-12-01T15:28:27.191798Z","shell.execute_reply":"2021-12-01T15:28:36.648044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There are more photographs for the prediction than there are monet images. This is good, as the photographs transformations will be very useful in training and validating. The monet images are mostly for the model to learn patterns from and apply the transformation to the photos","metadata":{}},{"cell_type":"markdown","source":"## View a sample of the Dataset\n#### Iterate through some of the records and show a pyplot","metadata":{}},{"cell_type":"code","source":"sample_monet_image = next(iter(monet_images_dataset))\nsample_photograph_image = next(iter(photographs_images_dataset))\n\nplt.subplot(1,2,1)\nplt.title(\"Photograph\")\nplt.imshow((sample_photograph_image[0] * 0.5) + 0.5)\nplt.subplot(1,2,2)\nplt.title(\"Monet Artwork\")\nplt.imshow((sample_monet_image[0] * 0.5) + 0.5)","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:40.529810Z","iopub.execute_input":"2021-12-01T15:28:40.530146Z","iopub.status.idle":"2021-12-01T15:28:41.011625Z","shell.execute_reply.started":"2021-12-01T15:28:40.530112Z","shell.execute_reply":"2021-12-01T15:28:41.010486Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Custom layers for the GAN Model (downsampling, upsampling, Reflection Padding)","metadata":{}},{"cell_type":"markdown","source":"#### Define the initializers used to feed the functions below ","metadata":{}},{"cell_type":"code","source":"# Weights initializer for the layers.\nkernel_weights_initializer = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n# Gamma initializer for instance normalization.\ngamma_normalization_initializer = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:44.461150Z","iopub.execute_input":"2021-12-01T15:28:44.461467Z","iopub.status.idle":"2021-12-01T15:28:44.468024Z","shell.execute_reply.started":"2021-12-01T15:28:44.461437Z","shell.execute_reply":"2021-12-01T15:28:44.466867Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Define the Downsampling, Upsampling and ConstantPadding2D layers","metadata":{}},{"cell_type":"code","source":"class PaddingConstant2D(layers.Layer):\n    '''Implements a Reflection Padding as a layer.\n\n    Args:\n        padding(tuple): Amount of padding for the\n        spatial dimensions.\n\n    Returns:\n        A padded tensor with the same type as the input tensor.\n    '''\n    \n    def __init__(self, padding = (1,1), **kwargs):\n        self.padding = padding\n        super(PaddingConstant2D, self).__init__(**kwargs)\n        \n    def call(self, input_tensor, mask = None):\n        padding_width, padding_height = self.padding\n        padding_tensor = [[0,0], [padding_height, padding_height], [padding_width, padding_width], [0,0]]\n        return tf.pad(input_tensor, padding_tensor, mode=\"CONSTANT\")\n\ndef downsampling_layers(model_layers, filters, activation, kernel_initializer=kernel_weights_initializer, kernel_size=(3,3),\n                        strides=(2,2), padding=\"same\", gamma_initializer=gamma_normalization_initializer, use_bias=False):\n    # Conv2D -> Activation (optional)\n    model_layers = layers.Conv2D(filters, kernel_size, strides=strides, kernel_initializer=kernel_initializer,\n                                 padding=padding, use_bias=use_bias)(model_layers)\n    \n    if activation:\n        model_layers = activation(model_layers)\n    return model_layers\n\ndef upsampling_layers(model_layers, filters, activation, kernel_initializer=kernel_weights_initializer, kernel_size=(3,3),\n                      strides=(2,2), padding=\"same\", gamma_initializer=gamma_normalization_initializer, use_bias=False):\n    # Conv2DTranspose -> Normalization -> Activation (optional)\n    model_layers = layers.Conv2DTranspose(filters, kernel_size, strides=strides, padding=padding, \n                                          kernel_initializer=kernel_initializer, use_bias=use_bias)(model_layers)\n    model_layers = tfa.layers.InstanceNormalization(gamma_initializer = gamma_initializer)(model_layers)\n    \n    if activation:\n        model_layers = activation(model_layers)\n    return model_layers\n\ndef residual_block_layers(model_layers, activation, kernel_initializer=kernel_weights_initializer,\n                          kernel_size=(3,3), strides=(1,1), padding=\"valid\",\n                          gamma_initializer=gamma_normalization_initializer, use_bias=False):\n    input_tensor = model_layers\n    num_output_filters = model_layers.shape[-1]\n\n    # Append all the layers here.\n    model_layers = PaddingConstant2D()(input_tensor)\n    model_layers = layers.Conv2D(num_output_filters, kernel_size, strides=strides, kernel_initializer=kernel_initializer,\n                                 padding=padding, use_bias=use_bias)(model_layers)\n    model_layers = tfa.layers.InstanceNormalization(gamma_initializer=gamma_initializer)(model_layers)\n    model_layers = activation(model_layers)\n    model_layers = PaddingConstant2D()(model_layers)\n    model_layers = layers.Conv2D(num_output_filters, kernel_size, strides=strides, kernel_initializer=kernel_initializer,\n                                 padding=padding, use_bias=use_bias)(model_layers)\n    model_layers = tfa.layers.InstanceNormalization(gamma_initializer=gamma_initializer)(model_layers)\n    model_layers = layers.add([input_tensor, model_layers])\n    return model_layers","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:47.239830Z","iopub.execute_input":"2021-12-01T15:28:47.240705Z","iopub.status.idle":"2021-12-01T15:28:47.259572Z","shell.execute_reply.started":"2021-12-01T15:28:47.240646Z","shell.execute_reply":"2021-12-01T15:28:47.258253Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Build the Generator and Discriminator Models","metadata":{}},{"cell_type":"code","source":"#-------------- Generator Model\ndef generator_model(filters=64, num_downsampling_blocks=2, num_residual_blocks=9, num_upsampling_blocks=2,\n                    kernel_initializer=kernel_weights_initializer, gamma_initializer=gamma_normalization_initializer,\n                    name=None):\n    image_layer_name = name + \"_image_input\"\n    image_input = layers.Input(shape=(256,256,3), name=image_layer_name)\n\n    # Define the Relu activation layer.\n    relu_activation_layer = layers.Activation(\"relu\")\n    \n    model_layers = PaddingConstant2D(padding=(3,3))(image_input)\n    model_layers = layers.Conv2D(filters, (7,7), kernel_initializer=kernel_initializer, use_bias=False)(model_layers)\n    model_layers = tfa.layers.InstanceNormalization(gamma_initializer=gamma_initializer)(model_layers)\n    model_layers = relu_activation_layer(model_layers)\n    \n    # Add Downsampling layers\n    for _ in range(num_downsampling_blocks):\n        filters *= 2\n        model_layers = downsampling_layers(model_layers, filters=filters, activation=relu_activation_layer)\n        \n    # Add Residual block layers\n    for _ in range(num_residual_blocks):\n        model_layers = residual_block_layers(model_layers, activation=relu_activation_layer)\n        \n    # Add Upsampling layers\n    for _ in range(num_upsampling_blocks):\n        filters //= 2\n        model_layers = upsampling_layers(model_layers, filters=filters, activation=relu_activation_layer)\n        \n    # Final layers with Tanh activation.\n    model_layers = PaddingConstant2D(padding=(3,3))(model_layers)\n    model_layers = layers.Conv2D(3, (7,7), padding=\"valid\")(model_layers)\n    model_layers = layers.Activation(\"tanh\")(model_layers)\n    \n    model = keras.models.Model(image_input, model_layers, name=name)\n    return model\n\n\n#-------------- Discriminator Model\ndef discriminator_model(filters=64, kernel_initializer=kernel_weights_initializer, num_downsampling=3, name=None):\n    image_layer_name = name + \"_image_input\"\n    image_input = layers.Input(shape=(256,256,3), name=image_layer_name)\n    \n    # Define the leaky relu activation layer.\n    leaky_relu_activation_layer = layers.LeakyReLU(0.2)\n    \n    # Add a convolution layer with 2x2 strides followed by a leaky relu activation layer.\n    model_layers = layers.Conv2D(filters, (4,4), strides=(2,2), padding=\"same\", \n                                 kernel_initializer=kernel_initializer)(image_input)\n    model_layers = leaky_relu_activation_layer(model_layers)\n    \n    # Add Downsampling layers.\n    num_filters = filters\n    for num_downsample_block in range(3):\n        num_filters *= 2\n        if num_downsample_block < 2:\n            model_layers = downsampling_layers(model_layers, filters=num_filters, activation=leaky_relu_activation_layer,\n                                               kernel_size=(4,4), strides=(2,2))\n        else:\n            model_layers = downsampling_layers(model_layers, filters=num_filters, activation=leaky_relu_activation_layer,\n                                               kernel_size=(4,4),strides=(1,1))\n\n    # Finally add the convolution layer with 1x2 stride at the end of the model.\n    model_layers = layers.Conv2D(1, (4,4), strides=(1,1), padding=\"same\",\n                                 kernel_initializer=kernel_initializer)(model_layers)\n    model = keras.models.Model(image_input, model_layers, name = name)\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:51.260568Z","iopub.execute_input":"2021-12-01T15:28:51.260854Z","iopub.status.idle":"2021-12-01T15:28:51.281958Z","shell.execute_reply.started":"2021-12-01T15:28:51.260827Z","shell.execute_reply":"2021-12-01T15:28:51.280811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Enable the TPU on the Generator and Discriminator Models and Model Optimizers","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    # Define the generator and discriminator models\n    monet_generator = generator_model(name=\"generator_monet\")  # Transforms photos to monet\n    photo_generator = generator_model(name=\"generator_photo\")  # Transforms monet to photos\n    monet_discriminator = discriminator_model(name=\"discriminator_monet\") # Differentiates real monets and generated monets\n    photo_discriminator = discriminator_model(name=\"discriminator_photo\") # Differentiates real photos and generated photos\n    \n    # Define the optimizers for the models.\n    monet_generator_optimizer = keras.optimizers.Adam(learning_rate=2e-4, beta_1=0.5)\n    photo_generator_optimizer = keras.optimizers.Adam(learning_rate=2e-4, beta_1=0.5)\n    monet_discriminator_optimizer = keras.optimizers.Adam(learning_rate=2e-4, beta_1=0.5)\n    photo_discriminator_optimizer = keras.optimizers.Adam(learning_rate=2e-4, beta_1=0.5)","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:28:55.471874Z","iopub.execute_input":"2021-12-01T15:28:55.472166Z","iopub.status.idle":"2021-12-01T15:28:59.053351Z","shell.execute_reply.started":"2021-12-01T15:28:55.472136Z","shell.execute_reply":"2021-12-01T15:28:59.052284Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"converted_monet_image = monet_generator(sample_photograph_image)\n\nplt.subplot(1,2,1)\nplt.title(\"Original Photograph\")\nplt.imshow((sample_photograph_image[0] * 0.5) + 0.5)\nplt.subplot(1,2,2)\nplt.title(\"Generate Monet Artwork\")\nplt.imshow((converted_monet_image[0] * 0.5) + 0.5)","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:29:02.102020Z","iopub.execute_input":"2021-12-01T15:29:02.102349Z","iopub.status.idle":"2021-12-01T15:29:02.859162Z","shell.execute_reply.started":"2021-12-01T15:29:02.102319Z","shell.execute_reply":"2021-12-01T15:29:02.858178Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see that the model is initialized and ready to be trained.","metadata":{}},{"cell_type":"markdown","source":"## Build the Loss Functions to be ran on the TPU","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def discriminator_loss(real, generated):\n        loss_real = keras.losses.BinaryCrossentropy(from_logits=True,\n                                                    reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(real), real)\n        loss_fake = keras.losses.BinaryCrossentropy(from_logits=True,\n                                                    reduction=tf.keras.losses.Reduction.NONE)(tf.zeros_like(generated), generated)\n        total_discriminator_loss = (loss_real + loss_fake) * 0.5\n        return total_discriminator_loss    \n\n    def generator_loss(generated):\n        gen_loss = keras.losses.BinaryCrossentropy(from_logits=True,\n                                                   reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(generated), generated)\n        return gen_loss\n\n    def cycle_loss(real_image, cycled_image, lamda):\n        loss = tf.reduce_mean(tf.abs(real_image - cycled_image))\n        cyc_loss = loss * lamda\n        return cyc_loss\n\n    def identity_loss(real_image, same_image, lamda1, lamda2):\n        loss = tf.reduce_mean(tf.abs(real_image - same_image))\n        id_loss = loss * lamda1 * lamda2\n        return id_loss","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:29:06.317981Z","iopub.execute_input":"2021-12-01T15:29:06.319508Z","iopub.status.idle":"2021-12-01T15:29:06.331022Z","shell.execute_reply.started":"2021-12-01T15:29:06.319450Z","shell.execute_reply":"2021-12-01T15:29:06.329808Z"},"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__(self, monet_generator, photo_generator,\n                 monet_discriminator, photo_discriminator,\n                 lambda_cycle=10.0, lambda_identity=0.5):\n        super(CycleGAN, self).__init__()\n        self.monet_generator = monet_generator\n        self.photo_generator = photo_generator\n        self.monet_discriminator = monet_discriminator\n        self.photo_discriminator = photo_discriminator\n        self.lambda_cycle = lambda_cycle\n        self.lambda_identity = lambda_identity\n        \n    def compile(self, monet_generator_optimizer, photo_generator_optimizer,\n                monet_discriminator_optimizer, photo_discriminator_optimizer,\n                generator_loss_function, discriminator_loss_function,\n                cycle_loss_function, identity_loss_function):\n        super(CycleGAN, self).compile()\n        self.monet_generator_optimizer = monet_generator_optimizer\n        self.photo_generator_optimizer = photo_generator_optimizer\n        self.monet_discriminator_optimizer = monet_discriminator_optimizer\n        self.photo_discriminator_optimizer = photo_discriminator_optimizer\n        self.generator_loss = generator_loss_function\n        self.discriminator_loss = discriminator_loss_function\n        self.cycle_loss = cycle_loss_function\n        self.identity_loss = identity_loss_function\n    \n    # The naming convention below is shared by keras.Model per epoch.\n    def train_step(self, batch):\n        monet_real, photo_real = batch\n        \n        with tf.GradientTape(persistent=True) as tape:\n            # photo --> monet --> photo\n            monet_fake = self.monet_generator(photo_real, training=True)\n            photo_cycled = self.photo_generator(monet_fake, training=True)\n            \n            # monet --> photo --> monet\n            photo_fake = self.photo_generator(monet_real, training=True)\n            monet_cycled = self.monet_generator(photo_fake, training=True)\n            \n            # Identity mapping. Attempt to reproduce the same source image for model accuracy comparison.\n            monet_approximate = self.monet_generator(monet_real, training=True)\n            photo_approximate = self.photo_generator(photo_real, training=True)\n            \n            # Discriminator outputs used for training.\n            discriminator_monet_real = self.monet_discriminator(monet_real, training=True)\n            discriminator_monet_fake = self.monet_discriminator(monet_fake, training=True)\n            discriminator_photo_real = self.photo_discriminator(photo_real, training=True)\n            discriminator_photo_fake = self.photo_discriminator(photo_fake, training=True)\n            \n            # Generator cycle losses\n            monet_cycled_loss = self.cycle_loss(monet_real, monet_cycled, self.lambda_cycle)\n            photo_cycled_loss = self.cycle_loss(photo_real, photo_cycled, self.lambda_cycle)\n            total_cycle_loss = monet_cycled_loss + photo_cycled_loss\n            \n            # Generator losses for the discriminator fake outputs.\n            monet_generator_loss = self.generator_loss(discriminator_monet_fake)\n            photo_generator_loss = self.generator_loss(discriminator_photo_fake)\n            \n            # Identity losses\n            monet_identity_loss = self.identity_loss(monet_real, monet_approximate,\n                                                     self.lambda_cycle, self.lambda_identity)\n            photo_identity_loss = self.identity_loss(photo_real, photo_approximate,\n                                                     self.lambda_cycle, self.lambda_identity)\n            total_identity_loss = monet_identity_loss + photo_identity_loss\n            \n            # Total generator losses\n            total_monet_generator_loss = monet_generator_loss + total_cycle_loss + total_identity_loss\n            total_photo_generator_loss = photo_generator_loss + total_cycle_loss + total_identity_loss\n            \n            # Discriminator losses\n            monet_discriminator_loss = self.discriminator_loss(discriminator_monet_real, discriminator_monet_fake)\n            photo_discriminator_loss = self.discriminator_loss(discriminator_photo_real, discriminator_photo_fake)\n        \n        # Calculate Gradients for Generator and Discriminators.\n        monet_generator_gradients = tape.gradient(total_monet_generator_loss,\n                                                  self.monet_generator.trainable_variables)\n        photo_generator_gradients = tape.gradient(total_photo_generator_loss,\n                                                  self.photo_generator.trainable_variables)\n        monet_discriminator_gradients = tape.gradient(monet_discriminator_loss,\n                                                      self.monet_discriminator.trainable_variables)\n        photo_discriminator_gradients = tape.gradient(photo_discriminator_loss,\n                                                      self.photo_discriminator.trainable_variables)\n        \n        self.monet_generator_optimizer.apply_gradients(zip(monet_generator_gradients,\n                                                           self.monet_generator.trainable_variables))\n        self.photo_generator_optimizer.apply_gradients(zip(photo_generator_gradients,\n                                                           self.photo_generator.trainable_variables))\n        self.monet_discriminator_optimizer.apply_gradients(zip(monet_discriminator_gradients,\n                                                               self.monet_discriminator.trainable_variables))\n        self.photo_discriminator_optimizer.apply_gradients(zip(photo_discriminator_gradients,\n                                                               self.photo_discriminator.trainable_variables))\n        \n        return {'monet_gen_loss': total_monet_generator_loss,\n                'photo_gen_loss': total_photo_generator_loss,\n                'monet_disc_loss': monet_discriminator_loss,\n                'photo_disc_loss': photo_discriminator_loss}","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:29:10.041875Z","iopub.execute_input":"2021-12-01T15:29:10.042240Z","iopub.status.idle":"2021-12-01T15:29:10.066092Z","shell.execute_reply.started":"2021-12-01T15:29:10.042181Z","shell.execute_reply":"2021-12-01T15:29:10.065347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Compile the CycleGAN Model to run on the TPU","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    cycle_gan_model = CycleGAN(monet_generator, photo_generator, monet_discriminator, photo_discriminator)\n    cycle_gan_model.compile(monet_generator_optimizer=monet_generator_optimizer,\n                            photo_generator_optimizer=photo_generator_optimizer,\n                            monet_discriminator_optimizer=monet_discriminator_optimizer,\n                            photo_discriminator_optimizer=photo_discriminator_optimizer,\n                            generator_loss_function=generator_loss,\n                            discriminator_loss_function=discriminator_loss,\n                            cycle_loss_function=cycle_loss,\n                            identity_loss_function=identity_loss)","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:29:15.366978Z","iopub.execute_input":"2021-12-01T15:29:15.367959Z","iopub.status.idle":"2021-12-01T15:29:15.420742Z","shell.execute_reply.started":"2021-12-01T15:29:15.367920Z","shell.execute_reply":"2021-12-01T15:29:15.419769Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train the CycleGAN Model with 50 epochs\nThe model fitting will be stuck at Epoch 1/50 for sometime while loading to the TPU, after sometime, the TPU will kickstart and the training will begin.\n\nWith the TPU, each epoch takes approximately 2 minutes. This means 50 epochs takes about 120 minutes or about 2 hours.","metadata":{}},{"cell_type":"code","source":"history = cycle_gan_model.fit(tf.data.Dataset.zip((monet_images_dataset, photographs_images_dataset)),\n                              epochs=50)","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:30:13.434565Z","iopub.execute_input":"2021-12-01T15:30:13.435746Z","iopub.status.idle":"2021-12-01T15:35:57.559404Z","shell.execute_reply.started":"2021-12-01T15:30:13.435678Z","shell.execute_reply":"2021-12-01T15:35:57.558345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualize the Predictions from the monet_generator","metadata":{}},{"cell_type":"code","source":"# Show 10 photographs transformed to Monet artworks.\n_, axes = plt.subplots(10, 2, figsize=(20,20))\nfor idx, photo_image in enumerate(photographs_images_dataset.take(10)):\n    # Make sure training=False so that we don't affect the trained generator.\n    monet_of_photo = monet_generator(photo_image, training=False)[0].numpy()\n    \n    # Scale the Monet generated output and input photograph.\n    monet_of_photo = (monet_of_photo * 0.5) + 0.5\n    photo_image = ((photo_image[0] * 0.5) + 0.5).numpy()\n    \n    # Show the comparison images.\n    axes[idx, 0].imshow(photo_image)\n    axes[idx, 1].imshow(monet_of_photo)\n    axes[idx, 0].set_title(\"Photograph Input\")\n    axes[idx, 1].set_title(\"Generated Monet Output\")\n    axes[idx, 0].axis(\"off\")\n    axes[idx, 1].axis(\"off\")\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:36:13.552753Z","iopub.execute_input":"2021-12-01T15:36:13.553107Z","iopub.status.idle":"2021-12-01T15:36:18.885543Z","shell.execute_reply.started":"2021-12-01T15:36:13.553071Z","shell.execute_reply":"2021-12-01T15:36:18.884370Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"## Generate the Kaggle Submission\n#### First make a directory to store the images from the model.","metadata":{}},{"cell_type":"code","source":"# Make an images directory.\n! mkdir ../images","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:36:25.007722Z","iopub.execute_input":"2021-12-01T15:36:25.008415Z","iopub.status.idle":"2021-12-01T15:36:25.852307Z","shell.execute_reply.started":"2021-12-01T15:36:25.008348Z","shell.execute_reply":"2021-12-01T15:36:25.851031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Obtain the monet generated outputs from monet_generator and save them into the images folder. Finally, zip it.\n#### Use the TPU to speed up the process.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    count = 1\n    for photo_image in photographs_images_dataset:\n        monet_of_photo = monet_generator(photo_image, training=False)[0].numpy()\n        monet_of_photo = ((monet_of_photo * 127.5) + 127.5).astype(np.uint8)\n        monet_image = PIL.Image.fromarray(monet_of_photo)\n        monet_image.save(\"../images/\" + str(count) + \".jpg\")\n        count += 1\n        if (count % 101 == 0):\n            print(\"Generated Monet of photograph for photo = %s/%s\" % (count, num_records_photographs))\n    \n# Make an archive in /kaggle/working/images as images.zip\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{"execution":{"iopub.status.busy":"2021-12-01T15:36:28.349449Z","iopub.execute_input":"2021-12-01T15:36:28.350370Z","iopub.status.idle":"2021-12-01T15:37:21.076267Z","shell.execute_reply.started":"2021-12-01T15:36:28.350322Z","shell.execute_reply":"2021-12-01T15:37:21.074909Z"},"trusted":true},"execution_count":null,"outputs":[]}]}