{"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 Tensorflow Packages","metadata":{}},{"cell_type":"code","source":"from kaggle_datasets import KaggleDatasets\nimport matplotlib.pyplot as plt\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport PIL\nimport io\nimport os\nimport random\nimport warnings\nimport zipfile\n\n\nimport tensorflow as tf # tensorflow\nfrom tensorflow import keras \nfrom tensorflow.keras import (\n    layers, # tensorflow keras layers\n    Model, \n    layers, \n    optimizers, \n    losses, \n    utils\n)\nimport tensorflow_addons as tfa\n\nwarnings.filterwarnings(\"ignore\")\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:43:09.414949Z","iopub.execute_input":"2023-06-06T22:43:09.415971Z","iopub.status.idle":"2023-06-06T22:43:09.426866Z","shell.execute_reply.started":"2023-06-06T22:43:09.415933Z","shell.execute_reply":"2023-06-06T22:43:09.425564Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Detect TPU","metadata":{}},{"cell_type":"code","source":"# detect TPU\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\n    print(\"Using TPU:\")\nexcept: \n    print(\"No TPU found. Falling back to GPU/CPU:\")\n    strategy = tf.distribute.MirroredStrategy()\n    \nprint('Number of accelerators:', strategy.num_replicas_in_sync)\nprint(\"Tensorflow version:\", tf.__version__)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:18:56.151538Z","iopub.execute_input":"2023-06-06T22:18:56.152074Z","iopub.status.idle":"2023-06-06T22:19:05.086485Z","shell.execute_reply.started":"2023-06-06T22:18:56.152042Z","shell.execute_reply":"2023-06-06T22:19:05.084784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataset Configuration","metadata":{}},{"cell_type":"code","source":"# URL on google cloud storage where dataset is stored\nGCS_PATH = KaggleDatasets().get_gcs_path(\"gan-getting-started\")\nprint(GCS_PATH)\n\nIMAGE_SIZE = [256, 256] # image dimensions\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE ","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:05.087754Z","iopub.execute_input":"2023-06-06T22:19:05.088072Z","iopub.status.idle":"2023-06-06T22:19:05.094291Z","shell.execute_reply.started":"2023-06-06T22:19:05.088044Z","shell.execute_reply":"2023-06-06T22:19:05.093363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define dataset functions\n\nImages Data:  \n* Size: $256 \\text{ x } 256$   \n* RGB: $3$ channels    \n* Scale: $[-1, 1]$\n\nFunctions to extract and process images from files","metadata":{}},{"cell_type":"code","source":"def decode_image(image, channels=3):\n    # Decode a JPEG-encoded image to a uint8 tensor, \n    # attr channels indicates the desired number of color channels for the decoded imag3\n    img = tf.image.decode_jpeg(image, channels=channels)\n    img = (tf.cast(img, tf.float32) / 127.5) - 1\n    img = tf.reshape(img, [*IMAGE_SIZE, channels])\n    return img\n\ndef read_tfrecord(input):\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    input = tf.io.parse_single_example(input, tfrecord_format)\n    image = decode_image(input['image'])\n    return image\n\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#     dataset = dataset.cache() # helps speed up data acess \n#     dataset = dataset.shuffle(2048)\n    return dataset","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:05.095433Z","iopub.execute_input":"2023-06-06T22:19:05.095706Z","iopub.status.idle":"2023-06-06T22:19:05.108514Z","shell.execute_reply.started":"2023-06-06T22:19:05.095682Z","shell.execute_reply":"2023-06-06T22:19:05.107676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load monet dataset\nMONET_FILES = tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nmonet_ds = load_dataset(MONET_FILES, labeled=True).batch(1)\nprint('Monet TFRecord Files:', len(MONET_FILES))","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:05.111166Z","iopub.execute_input":"2023-06-06T22:19:05.111481Z","iopub.status.idle":"2023-06-06T22:19:05.315079Z","shell.execute_reply.started":"2023-06-06T22:19:05.111455Z","shell.execute_reply":"2023-06-06T22:19:05.314125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load photos dataset\nPHOTO_FILES = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\nphoto_ds = load_dataset(PHOTO_FILES, labeled=True).batch(1)\nprint('Photo TFRecord Files:', len(PHOTO_FILES))","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:05.316197Z","iopub.execute_input":"2023-06-06T22:19:05.316508Z","iopub.status.idle":"2023-06-06T22:19:05.354978Z","shell.execute_reply.started":"2023-06-06T22:19:05.316481Z","shell.execute_reply":"2023-06-06T22:19:05.354009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Obtain an example image (Monet & Photo)","metadata":{}},{"cell_type":"code","source":"# next() function returns the next item from the iterator \nexample_monet = next(iter(monet_ds))\nexample_photo = next(iter(photo_ds))\n\n# shape\nexample_monet.shape, example_photo.shape","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:05.356161Z","iopub.execute_input":"2023-06-06T22:19:05.356506Z","iopub.status.idle":"2023-06-06T22:19:05.450942Z","shell.execute_reply.started":"2023-06-06T22:19:05.356476Z","shell.execute_reply":"2023-06-06T22:19:05.449630Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualize an example image (Monet vs Photo)","metadata":{}},{"cell_type":"code","source":"plt.subplot(1, 2, 1)\nplt.title('Monet')\nplt.imshow(example_monet[0]* 0.6 + 0.6)\n\nphoto = example_photo[0]\nplt.subplot(1, 2, 2)\nplt.title('Photo')\nplt.imshow(example_photo[0] * 0.6 + 0.6)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:05.452467Z","iopub.execute_input":"2023-06-06T22:19:05.452803Z","iopub.status.idle":"2023-06-06T22:19:05.866782Z","shell.execute_reply.started":"2023-06-06T22:19:05.452774Z","shell.execute_reply":"2023-06-06T22:19:05.865866Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build the Generator in a CycleGan\n\n### Define methods for UNet architecture for CycleGan\n\n``` downsample ``` [method](https://www.freecodecamp.org/news/how-to-format-code-in-markdown/) reduces the 2D dimensions, the height and the width of the image by the stride (the length of the step the filter takes). We apply an instance normalization from Tensorflow Add-ons.","metadata":{}},{"cell_type":"code","source":"def downsample(filters, size, apply_instancenorm=True):\n    # allow you to pre-specify an initialization strategy, encoded in the Initializer object, without knowing the shape and dtype of the variable being initialized.\n    initializer = tf.random_normal_initializer(0., 0.02)\n    # initializer that generates tensors with a normal distribution\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n    \n    # appropriate for a plain stack of layers where each layer has exactly one input tensor and one output tensor\n    result = keras.Sequential()\n    # 2D convolution layer (e.g. spatial convolution over images)\n    result.add(layers.Conv2D(filters, \n                             size, \n                             strides=2, \n                             padding='same', \n                             kernel_initializer=initializer, \n                             use_bias=False\n                            )\n              )\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":"2023-06-06T22:19:05.867898Z","iopub.execute_input":"2023-06-06T22:19:05.868216Z","iopub.status.idle":"2023-06-06T22:19:05.875620Z","shell.execute_reply.started":"2023-06-06T22:19:05.868192Z","shell.execute_reply":"2023-06-06T22:19:05.874821Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"``` upsample ``` method increases the dimensions of the image.\n```Conv2DTranspose``` does the opposite of a ```Conv2D``` layer.","metadata":{}},{"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, \n                                      size, \n                                      strides=2, \n                                      padding='same', \n                                      kernel_initializer=initializer, \n                                      use_bias=False\n                                     )\n              )\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":"2023-06-06T22:19:05.876628Z","iopub.execute_input":"2023-06-06T22:19:05.876970Z","iopub.status.idle":"2023-06-06T22:19:05.889232Z","shell.execute_reply.started":"2023-06-06T22:19:05.876946Z","shell.execute_reply":"2023-06-06T22:19:05.888329Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Define the generator \n\n- Number of input channels in the image\n- Number of filters for the convolutional blocks with ```Conv2D```.","metadata":{}},{"cell_type":"code","source":"def Generator(channels = 3, res_filters=256):\n    inputs = layers.Input(shape=[res_filters,res_filters, channels])\n    \n    # bs = batch size \n    down_stack = [\n        downsample(64, 4, apply_instancenorm=False), \n        downsample(128, 4),\n        downsample(res_filters, 4), \n        downsample(512, 4), \n        downsample(512, 4), \n        downsample(512, 4), \n        downsample(512, 4),\n        downsample(512, 4), \n    ]\n    \n    up_stack = [\n        upsample(512, 4, apply_dropout=True), \n        upsample(512, 4, apply_dropout=True),\n        upsample(512, 4, apply_dropout=True),\n        upsample(512, 4),\n        upsample(res_filters, 4),\n        upsample(128, 4),\n        upsample(64, 4),\n    ]\n    \n    initializer = tf.random_normal_initializer(0., 0.02)\n    last = layers.Conv2DTranspose(filters=channels, \n                                  kernel_size=4, \n                                  strides=2, \n                                  padding='same', \n                                  kernel_initializer=initializer, \n                                  activation='tanh'\n                                 )\n    \n    x = inputs \n    \n    # downsample 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    # upsample and stablish 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":"2023-06-06T22:19:05.890269Z","iopub.execute_input":"2023-06-06T22:19:05.890539Z","iopub.status.idle":"2023-06-06T22:19:05.905273Z","shell.execute_reply.started":"2023-06-06T22:19:05.890515Z","shell.execute_reply":"2023-06-06T22:19:05.904443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Initialize a Generator model","metadata":{}},{"cell_type":"code","source":"g = Generator() # Initialize a Generator model\ng.summary() # get a Generator summary\ndel g # free up RAM ","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:05.906221Z","iopub.execute_input":"2023-06-06T22:19:05.906505Z","iopub.status.idle":"2023-06-06T22:19:07.855707Z","shell.execute_reply.started":"2023-06-06T22:19:05.906482Z","shell.execute_reply":"2023-06-06T22:19:07.854623Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Build the discriminator \n\nDiscriminator takes input image and classifies the image as real or generated. Outputs a smaller 2D image with higher pixel values indicating a real classification and lower values indicating a generated classification\n","metadata":{}},{"cell_type":"code","source":"def Discriminator(channels=3, res_filters=256, conv_filters=None):\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=[res_filters, res_filters, channels], name='input_image')\n    \n    x = inp\n    \n    down1 = downsample(64, 4, False)(x)\n    down2 = downsample(128, 4)(down1)\n    down3 = downsample(256, 4)(down2)\n    \n    zero_pad1 = layers.ZeroPadding2D()(down3)\n    conv = layers.Conv2D(512, 4, strides=1, kernel_initializer=initializer, use_bias=False)(zero_pad1)\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)\n    \n    last = layers.Conv2D(filters=1, \n                         kernel_size=4, \n                         strides=1, \n                         kernel_initializer=initializer\n                        )(zero_pad2)\n    \n    return tf.keras.Model(inputs=inp, outputs=last)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:07.857007Z","iopub.execute_input":"2023-06-06T22:19:07.857359Z","iopub.status.idle":"2023-06-06T22:19:07.868179Z","shell.execute_reply.started":"2023-06-06T22:19:07.857331Z","shell.execute_reply":"2023-06-06T22:19:07.867148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Initialize a Discriminator model","metadata":{}},{"cell_type":"code","source":"d = Discriminator() # Initialize a Discriminator model \nd.summary() # get a Discriminator summary\ndel d # free up RAM ","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:07.872583Z","iopub.execute_input":"2023-06-06T22:19:07.872906Z","iopub.status.idle":"2023-06-06T22:19:08.163854Z","shell.execute_reply.started":"2023-06-06T22:19:07.872878Z","shell.execute_reply":"2023-06-06T22:19:08.162778Z"},"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 photo-like \n    \n    monet_discriminator = Discriminator() # differentiates real Monet paintings\n    photo_discriminator = Discriminator() # differentiates real photos","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:08.165157Z","iopub.execute_input":"2023-06-06T22:19:08.165612Z","iopub.status.idle":"2023-06-06T22:19:21.082277Z","shell.execute_reply.started":"2023-06-06T22:19:08.165583Z","shell.execute_reply":"2023-06-06T22:19:21.081157Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build the CycleGAN model \n\nDefine a CycleGan model\n- Generator and Discriminator for the Monet paintings\n- Generator and Discriminator for the real-life photos\n\nThe forward pass takes a real-life photo and a Monet painting:\n\nStep 1: \n- converts the real-life photo to Monet-esque painting using the generator for Monet paintings in```fake_monet```\n- converts the Monet painting to real-life photo using the generator for real-life photos in```fake_photo```\n\nStep 2: \n- recreates the real-life photo from ```fake_monet``` in ```cycled_photo```\n- recreates the Monet painting from ```fake_photo``` in ```cycled_monet```\n\nStep 3: \n- passes real Monet painting through Monet generator to test how good the generator is at recognizing its own paintings in  ``identity_monet```\n- passes real photo through real-life photo generator to test how good the generator is at recognizing its own photos in ``identity_photo```\n\nStep 4: \n- Classifies the real Monet painting using the Monet paintings discriminator. Expected output should be close to 1 in ```discriminated_real_monet``` since Monet painting is not generated.\n- Classifies the real real-life photo using the real-life photo discriminator. Expected output should be close to 1 in ```discriminated_real_photo``` since real-life photo is not generated.\n\nStep 5:\n- Classifies the fake Monet painting ```fake_monet``` using the Monet paintings discriminator. Expected output should be close to 0 in ```discriminated_fake_monet``` since ```fake_monet``` is generated.\n- Classifies the fake real-life photo ```fake_photo``` using the real-life photo discriminator. Expected output should be close to 0 in ```discriminated_fake_photo``` since ```fake_photo``` is generated.\n\nOutputs are returned in a dictionary.","metadata":{}},{"cell_type":"code","source":"class CycleGan(keras.Model):\n    \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.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        \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 -> monet -> photo\n            fake_monet = self.monet_generator(real_photo, training=True)\n            cycled_photo = self.photo_generator(fake_monet, training=True)\n            \n            # monet -> photo -> monet \n            fake_photo = self.photo_generator(real_monet, training=True)\n            cycled_monet = self.monet_generator(fake_photo, training=True)\n            \n            # painting & photo generating themselves\n            identity_monet = self.monet_generator(real_monet, training=True)\n            identity_photo = self.photo_generator(real_photo, training=True)\n            \n            # discriminator used to check, inputing real images\n            discriminated_real_monet = self.monet_discriminator(real_monet, training=True)\n            discriminated_real_photo = self.photo_discriminator(real_photo, training=True)\n            \n            # discriminator used to check, inputing fake images\n            discriminated_fake_monet = self.monet_discriminator(fake_monet, training=True)\n            discriminated_fake_photo = self.photo_discriminator(fake_photo, training=True)\n            \n            # evaluate generator loss\n            monet_gen_loss = self.gen_loss_fn(discriminated_fake_monet)\n            photo_gen_loss = self.gen_loss_fn(discriminated_fake_photo)\n            \n            # evaluate 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, identity_monet, self.lambda_cycle)\n            total_photo_gen_loss = photo_gen_loss + total_cycle_loss + self.identity_loss_fn(real_photo, identity_photo, self.lambda_cycle)\n            \n            # evaluate discriminator loss \n            monet_disc_loss = self.disc_loss_fn(discriminated_real_monet, discriminated_fake_monet)\n            photo_disc_loss = self.disc_loss_fn(discriminated_real_photo, discriminated_fake_photo)\n            \n            # calculate gradients for generator and discriminator\n            monet_generator_gradients = tape.gradient(total_monet_gen_loss, self.monet_generator.trainable_variables)\n            photo_generator_gradients = tape.gradient(total_photo_gen_loss, self.monet_generator.trainable_variables)\n            monet_discriminator_gradients = tape.gradient(monet_disc_loss, self.monet_discriminator.trainable_variables)\n            photo_discriminator_gradients = tape.gradient(photo_disc_loss, self.photo_discriminator.trainable_variables)\n           \n            # apply gradients to optimizer\n            self.m_gen_optimizer.apply_gradients(zip(monet_generator_gradients, self.monet_generator.trainable_variables))\n            self.p_gen_optimizer.apply_gradients(zip(photo_generator_gradients, self.photo_generator.trainable_variables))\n            self.m_disc_optimizer.apply_gradients(zip(monet_discriminator_gradients, self.monet_discriminator.trainable_variables))\n            self.p_disc_optimizer.apply_gradients(zip(photo_discriminator_gradients, self.photo_discriminator.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":"2023-06-06T22:19:21.083584Z","iopub.execute_input":"2023-06-06T22:19:21.083942Z","iopub.status.idle":"2023-06-06T22:19:21.105484Z","shell.execute_reply.started":"2023-06-06T22:19:21.083914Z","shell.execute_reply":"2023-06-06T22:19:21.104443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Initialize a ```CycleGAN``` model","metadata":{}},{"cell_type":"code","source":"gan = CycleGan(monet_generator = monet_generator, \n               photo_generator = photo_generator, \n               monet_discriminator = monet_discriminator,\n               photo_discriminator = photo_discriminator)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:21.106553Z","iopub.execute_input":"2023-06-06T22:19:21.106906Z","iopub.status.idle":"2023-06-06T22:19:21.127129Z","shell.execute_reply.started":"2023-06-06T22:19:21.106882Z","shell.execute_reply":"2023-06-06T22:19:21.126146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define loss functions","metadata":{}},{"cell_type":"markdown","source":"### Define the discriminator loss\n- measures the sum of the binary cross entropy loss for the real image and the fake image since the discriminator is a binary classifier","metadata":{}},{"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":"2023-06-06T22:19:21.128270Z","iopub.execute_input":"2023-06-06T22:19:21.128585Z","iopub.status.idle":"2023-06-06T22:19:21.138010Z","shell.execute_reply.started":"2023-06-06T22:19:21.128558Z","shell.execute_reply":"2023-06-06T22:19:21.137107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Define the generator loss\n- measures how good the generator is at fooling the discriminator","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def generator_loss(generated):\n        \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":"2023-06-06T22:19:21.139071Z","iopub.execute_input":"2023-06-06T22:19:21.139392Z","iopub.status.idle":"2023-06-06T22:19:21.149974Z","shell.execute_reply.started":"2023-06-06T22:19:21.139365Z","shell.execute_reply":"2023-06-06T22:19:21.149100Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Define the cycle consistency loss\n- calculates the loss with the L1 norm\n- measures how far away cycled image is from the original image\n- LAMBDA is the scaling factor for the loss, the contribution of this loss to the final generator loss","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def calc_cycle_loss(real_image, cycled_image, LAMBDA):\n        loss = tf.reduce_mean(tf.abs(real_image - cycled_image))\n        return LAMBDA * loss","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:21.151019Z","iopub.execute_input":"2023-06-06T22:19:21.151330Z","iopub.status.idle":"2023-06-06T22:19:21.161973Z","shell.execute_reply.started":"2023-06-06T22:19:21.151303Z","shell.execute_reply":"2023-06-06T22:19:21.160892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Define the identity loss\n- measures how far away the identity image is from the original image","metadata":{}},{"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":"2023-06-06T22:19:21.163072Z","iopub.execute_input":"2023-06-06T22:19:21.163376Z","iopub.status.idle":"2023-06-06T22:19:21.173136Z","shell.execute_reply.started":"2023-06-06T22:19:21.163350Z","shell.execute_reply":"2023-06-06T22:19:21.172285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Initialize tensorflow optimizers and Compile the Model","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":"2023-06-06T22:19:21.174130Z","iopub.execute_input":"2023-06-06T22:19:21.174448Z","iopub.status.idle":"2023-06-06T22:19:21.362029Z","shell.execute_reply.started":"2023-06-06T22:19:21.174421Z","shell.execute_reply":"2023-06-06T22:19:21.360916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Initialize CycleGAN model","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    cycle_gan_model = CycleGan(monet_generator, photo_generator, monet_discriminator, photo_discriminator)\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":"2023-06-06T22:19:21.363196Z","iopub.execute_input":"2023-06-06T22:19:21.363486Z","iopub.status.idle":"2023-06-06T22:19:21.505840Z","shell.execute_reply.started":"2023-06-06T22:19:21.363461Z","shell.execute_reply":"2023-06-06T22:19:21.504719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Run CycleGAN model on the datasets!","metadata":{}},{"cell_type":"code","source":"history = cycle_gan_model.fit(\n    tf.data.Dataset.zip((monet_ds, photo_ds)), \n    epochs=25\n)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:19:21.507018Z","iopub.execute_input":"2023-06-06T22:19:21.507333Z","iopub.status.idle":"2023-06-06T22:37:00.619310Z","shell.execute_reply.started":"2023-06-06T22:19:21.507307Z","shell.execute_reply":"2023-06-06T22:37:00.618012Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Loss Curve\n- Visualize the loss curves for the generators and discriminators","metadata":{}},{"cell_type":"code","source":"# Calculate the mean loss per epoch\nkeys = [\"photo_gen_loss\", \"photo_disc_loss\", \"monet_gen_loss\", \"monet_disc_loss\"]\n\nepoch_history = {\"photo\": {}, \"monet\": {}}\n\nfor key in keys:\n    img_type, model, _ = key.split(\"_\")\n    epoch_history[img_type][model] = np.array(\n        [tf.reduce_mean(loss).numpy() for loss in history.history[key]]\n    )","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:37:18.535891Z","iopub.execute_input":"2023-06-06T22:37:18.536908Z","iopub.status.idle":"2023-06-06T22:37:18.572682Z","shell.execute_reply.started":"2023-06-06T22:37:18.536848Z","shell.execute_reply":"2023-06-06T22:37:18.571425Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"titles = {\"photo\": \"Image\", \"monet\": \"Monet\"}\n\nf, axs = plt.subplots(1, len(epoch_history), figsize=(10, 5))\n\nfor (key, losses), ax in zip(epoch_history.items(), axs.flatten()):\n    ax.plot(epoch_history[key][\"gen\"], label=\"Generator\")\n    ax.plot(epoch_history[key][\"disc\"], label=\"Discriminator\")\n    ax.set_xlabel(\"Epochs\")\n    ax.set_ylabel(\"Loss\")\n    ax.set_title(titles[key])\n    ax.legend()\n\nplt.tight_layout()","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:37:41.519737Z","iopub.execute_input":"2023-06-06T22:37:41.520690Z","iopub.status.idle":"2023-06-06T22:37:42.180698Z","shell.execute_reply.started":"2023-06-06T22:37:41.520646Z","shell.execute_reply":"2023-06-06T22:37:42.179462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualize Monet-esque photos","metadata":{}},{"cell_type":"code","source":"f, 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(f\"Photo-{i + 1}\")\n    ax[i, 1].set_title(f\"Monet-esque-{i + 1}\")\n    ax[i, 0].axis(\"off\")\n    ax[i, 1].axis(\"off\")\n    \nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-06-06T22:38:13.262259Z","iopub.execute_input":"2023-06-06T22:38:13.263412Z","iopub.status.idle":"2023-06-06T22:38:15.549688Z","shell.execute_reply.started":"2023-06-06T22:38:13.263373Z","shell.execute_reply":"2023-06-06T22:38:15.548292Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create Submission File","metadata":{}},{"cell_type":"code","source":"def 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        print(i)\n        i += 1","metadata":{"execution":{"iopub.status.busy":"2023-06-06T23:14:52.771875Z","iopub.execute_input":"2023-06-06T23:14:52.772344Z","iopub.status.idle":"2023-06-06T23:14:52.779889Z","shell.execute_reply.started":"2023-06-06T23:14:52.772305Z","shell.execute_reply":"2023-06-06T23:14:52.778729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n# os.makedirs('../images/') # Create folder to save generated images\n\nGCS_PATH = KaggleDatasets().get_gcs_path('gan-getting-started')\nphoto_ds = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\npredict_and_save(load_dataset(photo_ds).batch(1), monet_generator, '../images/')","metadata":{"execution":{"iopub.status.busy":"2023-06-06T23:14:59.766212Z","iopub.execute_input":"2023-06-06T23:14:59.767397Z","iopub.status.idle":"2023-06-06T23:47:47.016534Z","shell.execute_reply.started":"2023-06-06T23:14:59.767358Z","shell.execute_reply":"2023-06-06T23:47:47.015305Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{"execution":{"iopub.status.busy":"2023-06-06T23:48:45.972559Z","iopub.execute_input":"2023-06-06T23:48:45.973525Z","iopub.status.idle":"2023-06-06T23:48:50.030470Z","shell.execute_reply.started":"2023-06-06T23:48:45.973490Z","shell.execute_reply":"2023-06-06T23:48:50.029369Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"Number of generated samples: {len([name for name in os.listdir('../images/') if os.path.isfile(os.path.join('../images/', name))])}\")","metadata":{"execution":{"iopub.status.busy":"2023-06-06T23:48:54.237271Z","iopub.execute_input":"2023-06-06T23:48:54.238069Z","iopub.status.idle":"2023-06-06T23:48:54.305383Z","shell.execute_reply.started":"2023-06-06T23:48:54.238031Z","shell.execute_reply":"2023-06-06T23:48:54.304312Z"},"trusted":true},"execution_count":null,"outputs":[]}]}