{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":30637,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**Importing Libraries**","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom kaggle_datasets import KaggleDatasets\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:15:35.374998Z","iopub.execute_input":"2024-01-25T08:15:35.375392Z","iopub.status.idle":"2024-01-25T08:15:53.785156Z","shell.execute_reply.started":"2024-01-25T08:15:35.375353Z","shell.execute_reply":"2024-01-25T08:15:53.784341Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.layers import Input, Conv2D, Conv2DTranspose, Dropout, LeakyReLU, ReLU, ZeroPadding2D, GroupNormalization, Concatenate, ZeroPadding2D\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.losses import BinaryCrossentropy\nfrom tensorflow.keras.optimizers import Adam","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:15:53.786685Z","iopub.execute_input":"2024-01-25T08:15:53.787208Z","iopub.status.idle":"2024-01-25T08:15:54.018242Z","shell.execute_reply.started":"2024-01-25T08:15:53.787181Z","shell.execute_reply":"2024-01-25T08:15:54.017204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Connecting to TPU**","metadata":{}},{"cell_type":"code","source":"print(tf.__version__)","metadata":{"execution":{"iopub.status.busy":"2024-01-25T06:28:02.498249Z","iopub.execute_input":"2024-01-25T06:28:02.499061Z","iopub.status.idle":"2024-01-25T06:28:02.503888Z","shell.execute_reply.started":"2024-01-25T06:28:02.499028Z","shell.execute_reply":"2024-01-25T06:28:02.502870Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# try:\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)\n# except:\n#     strategy = tf.distribute.get_strategy()\n# print('Number of replicas:', strategy.num_replicas_in_sync)\n\n# AUTOTUNE = tf.data.experimental.AUTOTUNE","metadata":{"execution":{"iopub.status.busy":"2024-01-25T06:17:24.682912Z","iopub.execute_input":"2024-01-25T06:17:24.683256Z","iopub.status.idle":"2024-01-25T06:17:33.036766Z","shell.execute_reply.started":"2024-01-25T06:17:24.683227Z","shell.execute_reply":"2024-01-25T06:17:33.035627Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Loading the Dataset**","metadata":{}},{"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path()","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:16:53.817921Z","iopub.execute_input":"2024-01-25T08:16:53.818677Z","iopub.status.idle":"2024-01-25T08:16:54.195723Z","shell.execute_reply.started":"2024-01-25T08:16:53.818644Z","shell.execute_reply":"2024-01-25T08:16:54.194843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#monet_files= tf.io.gfile.glob('/kaggle/input/gan-getting-started/monet_tfrec/*.tfrec')\n#photo_files= tf.io.gfile.glob('/kaggle/input/gan-getting-started/photo_tfrec/*.tfrec')\nmonet_files= tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nphoto_files= tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:16:54.630199Z","iopub.execute_input":"2024-01-25T08:16:54.630539Z","iopub.status.idle":"2024-01-25T08:16:54.841393Z","shell.execute_reply.started":"2024-01-25T08:16:54.630513Z","shell.execute_reply":"2024-01-25T08:16:54.840430Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('No. of Monet TFRecord files: ',len(monet_files))\nprint('No. of Photo TFRecord files: ',len(photo_files))","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:16:56.347192Z","iopub.execute_input":"2024-01-25T08:16:56.348090Z","iopub.status.idle":"2024-01-25T08:16:56.353295Z","shell.execute_reply.started":"2024-01-25T08:16:56.348054Z","shell.execute_reply":"2024-01-25T08:16:56.352142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMAGE_SIZE= [256,256]                                            # desired/required size of image\n\ndef decode_img(image):                                           # function for decoding the image present in jpeg format\n    image= tf.image.decode_jpeg(image,channels= 3)               # 3 channels because of RGB\n    image= (tf.cast(image, tf.float32)/255)*2 -1                 # converting the pixel values in range [-1,1]\n    image= tf.reshape(image, shape= [*IMAGE_SIZE,3])             # reshaping the image to proper size\n    return image\n\ndef read_tfrec(example):                                         # function for extracting image from TFRecord format\n    tfrec_format= {\n        'image_name': tf.io.FixedLenFeature([], tf.string),      # [] denotes fixed length feature where length= 1\n        'image': tf.io.FixedLenFeature([], tf.string),\n        'target': tf.io.FixedLenFeature([], tf.string)\n    }\n    example= tf.io.parse_single_example(example, tfrec_format)\n    image= decode_img(example['image'])\n    return image","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:19:30.539077Z","iopub.execute_input":"2024-01-25T08:19:30.539815Z","iopub.status.idle":"2024-01-25T08:19:30.547640Z","shell.execute_reply.started":"2024-01-25T08:19:30.539776Z","shell.execute_reply":"2024-01-25T08:19:30.546663Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_data(files):\n    data= tf.data.TFRecordDataset(files)\n    data= data.map(read_tfrec)                                   # (num_parallel_calls= AUTOTUNE) in  case of TPU\n    return data","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:19:30.805946Z","iopub.execute_input":"2024-01-25T08:19:30.806257Z","iopub.status.idle":"2024-01-25T08:19:30.810627Z","shell.execute_reply.started":"2024-01-25T08:19:30.806231Z","shell.execute_reply":"2024-01-25T08:19:30.809702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"monet_data= load_data(monet_files).batch(1)                      # forming batches of size=1 (i.e. 1 image processed at a time)\nphoto_data= load_data(photo_files).batch(1)","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:19:31.113681Z","iopub.execute_input":"2024-01-25T08:19:31.113995Z","iopub.status.idle":"2024-01-25T08:19:32.859514Z","shell.execute_reply.started":"2024-01-25T08:19:31.113969Z","shell.execute_reply":"2024-01-25T08:19:32.858556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"monet_data","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:19:32.861341Z","iopub.execute_input":"2024-01-25T08:19:32.862135Z","iopub.status.idle":"2024-01-25T08:19:32.869676Z","shell.execute_reply.started":"2024-01-25T08:19:32.862100Z","shell.execute_reply":"2024-01-25T08:19:32.868735Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ex_monet= next(iter(monet_data))\nex_photo= next(iter(photo_data))","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:19:32.870600Z","iopub.execute_input":"2024-01-25T08:19:32.870838Z","iopub.status.idle":"2024-01-25T08:19:33.459442Z","shell.execute_reply.started":"2024-01-25T08:19:32.870817Z","shell.execute_reply":"2024-01-25T08:19:33.458626Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.subplot(1,2,1)                                       # creating a subplot with 1 row and 2 columns\nplt.title('Photo')\nplt.imshow(ex_photo[0]*0.5 +0.5)                         # rescaling the image to [0,1] for displaying\n\nplt.subplot(1,2,2)\nplt.title('Monet')\nplt.imshow(ex_monet[0]*0.5 +0.5)                   ","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:19:33.461434Z","iopub.execute_input":"2024-01-25T08:19:33.461769Z","iopub.status.idle":"2024-01-25T08:19:34.144648Z","shell.execute_reply.started":"2024-01-25T08:19:33.461739Z","shell.execute_reply":"2024-01-25T08:19:34.143670Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Building the Generator (UNET Architecture)**","metadata":{}},{"cell_type":"code","source":"def downsample(filters, size, instance_norm= True):                                   # for extracting important features (size is reduced)\n    initializer= tf.random_normal_initializer(0,0.02)                                 # mean=0 and standard deviation=0.02 for initializing kernel weights\n    gamma_init= keras.initializers.RandomNormal(mean= 0, stddev= 0.02)\n    \n    model= keras.Sequential()                                          \n    model.add(Conv2D(filters, size, strides=2, padding='same', kernel_initializer= initializer, use_bias= False))\n    \n    if instance_norm:\n         model.add(GroupNormalization(groups= -1, gamma_initializer= gamma_init))     # groups= -1 to make it work like Instance Normalization\n   \n    model.add(LeakyReLU())\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:19:34.145952Z","iopub.execute_input":"2024-01-25T08:19:34.146321Z","iopub.status.idle":"2024-01-25T08:19:34.154933Z","shell.execute_reply.started":"2024-01-25T08:19:34.146285Z","shell.execute_reply":"2024-01-25T08:19:34.153939Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def downsample(filters, size, instance_norm= True):                                   # for extracting important features (size is reduced)\n#     initializer= tf.random_normal_initializer(0,0.02)                                 # mean=0 and standard deviation=0.02 for initializing kernel weights\n#     gamma_init= keras.initializers.RandomNormal(mean= 0, stddev= 0.02)\n    \n#     i= Input(shape= (None,None,filters))                                    \n#     x= Conv2D(filters, size, strides=2, padding='same', kernel_initializer= initializer, use_bias= False) (i)\n    \n#     if instance_norm:\n#          x= GroupNormalization(groups= -1, gamma_initializer= gamma_init) (x)        # groups= -1 to make it work like Instance Normalization\n   \n#     x= LeakyReLU() (x)\n    \n#     model= Model(i,x)\n    \n#     return model","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def upsample(filters, size, dropout= False):                                         # for locating features accurately using skip connections \n    initializer= tf.random_normal_initializer(0,0.02)\n    gamma_init= keras.initializers.RandomNormal(mean= 0, stddev= 0.02)\n    \n    model= keras.Sequential()\n    model.add(Conv2DTranspose(filters, size, strides= 2, padding= 'same', kernel_initializer= initializer, use_bias= False))\n    model.add(GroupNormalization(groups= -1, gamma_initializer= gamma_init))\n    \n    if dropout:\n        model.add(Dropout(0.5))\n    \n    model.add(ReLU())\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:19:34.156604Z","iopub.execute_input":"2024-01-25T08:19:34.156931Z","iopub.status.idle":"2024-01-25T08:19:34.175020Z","shell.execute_reply.started":"2024-01-25T08:19:34.156905Z","shell.execute_reply":"2024-01-25T08:19:34.173982Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def upsample(filters, size, dropout= False):                                         # for locating features accurately using skip connections \n#     initializer= tf.random_normal_initializer(0,0.02)\n#     gamma_init= keras.initializers.RandomNormal(mean= 0, stddev= 0.02)\n    \n#     i= Input(shape= (None,None,filters))\n#     x= Conv2DTranspose(filters, size, strides= 2, padding='same', kernel_initializer= initializer, use_bias= False) (i)\n#     x= GroupNormalization(groups= -1, gamma_initializer= gamma_init) (x)\n    \n#     if dropout:\n#         x= Dropout(0.5) (x)\n        \n#     model= Model(i,x)\n    \n#     return model","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def generator():\n    down_stack= [\n        downsample(64,4,False),                 # size= (128,128,64)  (size denotes the dimensions of image after the corresponding layer/operation)\n        downsample(128,4),                      # size= (64,64,128)\n        downsample(256,4),                      # size= (32,32,256)\n        downsample(512,4),                      # size= (16,16,512)\n        downsample(512,4),                      # size= (8,8,512)\n        downsample(512,4),                      # size= (4,4,512)\n        downsample(512,4),                      # size= (2,2,512)\n        downsample(512,4),                      # size= (1,1,512)\n    ]\n    \n    up_stack= [\n        upsample(512,4,True),                   # size= (2,2,1024)  (no. of channels doubled because upsample block concats output of last downsample block)    \n        upsample(512,4,True),                   # size= (4,4,1024)\n        upsample(512,4,True),                   # size= (8,8,1024)\n        upsample(512,4),                        # size= (16,16,1024)  (dropout= false so that information is maintained for generating detailed outputs)\n        upsample(256,4),                        # size= (32,32,512)\n        upsample(128,4),                        # size= (64,64,256)\n        upsample(64,4)                          # size= (128,128,128)\n    ]\n    \n    initializer= tf.random_normal_initializer(0,0.02)\n    last_layer= Conv2DTranspose(3, 4, strides= 2, padding= 'same', kernel_initializer= initializer, activation= 'tanh')     # 3 output channels required\n    \n    i= Input(shape= [256,256,3])                # input layer\n    x= i\n    skips= []\n    for down in down_stack:                     # downsampling\n        x= down (x) \n        skips.append(x)                         # appending skip connections to the 'skips' list\n        \n    skips= reversed(skips[:-1])                 # last skip connection is not used because of alignment with upsampling path\n    \n    for up, skip in zip(up_stack,skips):        # upsampling and concatenating output with skip connection\n        x= up (x)\n        x= Concatenate() ([x,skip])\n        \n    x= last_layer(x)                            # last layer (Conv2DTranspose) for generating the final output \n    \n    model= Model(i,x)\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:09.101788Z","iopub.execute_input":"2024-01-25T08:21:09.102326Z","iopub.status.idle":"2024-01-25T08:21:09.113790Z","shell.execute_reply.started":"2024-01-25T08:21:09.102285Z","shell.execute_reply":"2024-01-25T08:21:09.112852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Building the Discriminator**","metadata":{}},{"cell_type":"code","source":"def discriminator():\n    i= Input(shape= [256,256,3])\n    x= downsample(64,4) (i)                                # size= (128,128,64) \n    x= downsample(128,4) (x)                               # size= (64,64,128) \n    x= downsample(256,4) (x)                               # size= (32,32,256)\n    \n    x= ZeroPadding2D() (x)                                 # size= (34,34,256)    (1 pixel padding is added at top,bottom,left,right) \n    \n    initializer= tf.random_normal_initializer(0,0.02)\n    gamma_init= keras.initializers.RandomNormal(mean= 0, stddev= 0.02)\n    x= Conv2D(512, 4, strides= 1, padding= 'same', kernel_initializer= initializer, use_bias= False) (x)      # size= (31,31,512) (size= orig - kernel + 1)\n    x= GroupNormalization(groups= -1, gamma_initializer= gamma_init) (x)\n    x= LeakyReLU() (x)\n    \n    x= ZeroPadding2D() (x)                                 # size= (33,33,512)    (zero padding applied to maintain spatial information)\n    \n    x= Conv2D(1, 4, strides= 1, padding= 'same', kernel_initializer= initializer) (x)       # size= (30,30,1) \n                                                                                            # sigmoid not used to output unbounded logits\n    model= Model(i,x)                                                                       # (more numerically stable during training)\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:10.972543Z","iopub.execute_input":"2024-01-25T08:21:10.972910Z","iopub.status.idle":"2024-01-25T08:21:10.981110Z","shell.execute_reply.started":"2024-01-25T08:21:10.972882Z","shell.execute_reply":"2024-01-25T08:21:10.980336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Initializing the generator & discriminator objects**","metadata":{}},{"cell_type":"code","source":"# with strategy.scope():\nmonet_generator= generator()                     # photo to monet-esque\nmonet_discriminator= discriminator()             # to differentiate between generated monet-esque images and real monet-esque images\nphoto_generator= generator()                     # monet-esque to photo\nphoto_discriminator= discriminator()             # to differentiate between generated 'normal' images and real 'normal' images","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:26.996557Z","iopub.execute_input":"2024-01-25T08:21:26.996936Z","iopub.status.idle":"2024-01-25T08:21:30.315608Z","shell.execute_reply.started":"2024-01-25T08:21:26.996905Z","shell.execute_reply":"2024-01-25T08:21:30.314747Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"photo_to_monet= monet_generator(ex_photo)                # won't generate monet-esque photos as we have not fit the data into generator yet\n\nplt.subplot(1,2,1)                                       # creating a subplot with 1 row and 2 columns\nplt.title('Original Photo')\nplt.imshow(ex_photo[0]*0.5 +0.5)                         # rescaling the image to [0,1] for displaying\n\nplt.subplot(1,2,2)\nplt.title('Generated Monet-esque photo')\nplt.imshow(photo_to_monet[0]*0.5 +0.5)                   ","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:30.317377Z","iopub.execute_input":"2024-01-25T08:21:30.317707Z","iopub.status.idle":"2024-01-25T08:21:30.939181Z","shell.execute_reply.started":"2024-01-25T08:21:30.317680Z","shell.execute_reply":"2024-01-25T08:21:30.938287Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Building the CycleGAN**","metadata":{}},{"cell_type":"code","source":"class CycleGAN(keras.Model):                       # CycleGAN class inheriting from keras.Model class so that it can use its methods to train, compile etc.\n    def __init__(                                  # arguments to be passed in a CycleGAN class object   \n        self,\n        monet_gen,\n        monet_disc,\n        photo_gen,\n        photo_disc,\n        lambda_cycle= 10                           # 'lambda_cycle' controls the importance of cycle consistency loss\n    ):\n        super(CycleGAN,self).__init__()            # calls the constructor of the parent class (keras.Model), initializing the base properties and methods\n        self.m_gen= monet_gen                      # assigning argument values to attributes of a CycleGAN class object/instance\n        self.m_disc= monet_disc\n        self.p_gen= photo_gen\n        self.p_disc= photo_disc\n        self.lambda_cycle= lambda_cycle\n        \n    def compile(                                   \n        self,\n        m_gen_optimizer,\n        m_disc_optimizer,\n        p_gen_optimizer,\n        p_disc_optimizer,\n        gen_loss_function,\n        disc_loss_function,\n        cycle_loss_function,\n        identity_loss_function\n    ):\n        super(CycleGAN,self).compile()             # calls the 'compile' fn of the parent class (keras.Model), initializing the base properties and methods\n        self.m_gen_optimizer = m_gen_optimizer\n        self.m_disc_optimizer = m_disc_optimizer\n        self.p_gen_optimizer = p_gen_optimizer\n        self.p_disc_optimizer = p_disc_optimizer\n        self.gen_loss_function = gen_loss_function\n        self.disc_loss_function = disc_loss_function\n        self.cycle_loss_function = cycle_loss_function\n        self.identity_loss_function = identity_loss_function\n        \n    def train_step(self,batch_data):                                # automatically invoked when fit() method is called \n        real_monet, real_photo= batch_data\n        \n        with tf.GradientTape(persistent= True) as tape:             # to keep a track of operations (persistent= True bcz of multiple calls to Gradient())\n            \n            fake_monet= self.m_gen(real_photo, training= True)      # photo to monet and then cycled back to photo\n            cycled_photo= self.p_gen(fake_monet, training= True)\n            \n            fake_photo= self.p_gen(real_monet, training= True)      # monet to photo and then cycled back to monet\n            cycled_monet= self.m_gen(fake_photo, training= True)\n            \n            same_photo= self.p_gen(real_photo, training= True)      # generating itself (useful in calculating identity loss)\n            same_monet= self.m_gen(real_monet, training= True)      \n            \n            disc_real_photo= self.p_disc(real_photo, training= True)   # discriminator used to check by inputing real images\n            disc_real_monet= self.m_disc(real_monet, training= True)   \n            \n            disc_fake_photo= self.p_disc(fake_photo, training= True)   # discriminator used to check by inputing fake images\n            disc_fake_monet= self.m_disc(fake_monet, training= True)\n            \n            gen_monet_loss= self.gen_loss_function(disc_fake_monet)    # generator loss\n            gen_photo_loss= self.gen_loss_function(disc_fake_photo)\n            \n            total_cycle_loss = (self.cycle_loss_function(real_monet, cycled_monet, self.lambda_cycle) +     # total cycle consistency loss\n            self.cycle_loss_function(real_photo, cycled_photo, self.lambda_cycle))\n            \n            total_gen_monet_loss= (gen_monet_loss + total_cycle_loss   +                                    # total generator monet loss\n            self.identity_loss_function(real_monet, same_monet, self.lambda_cycle) )  \n            \n            total_gen_photo_loss= (gen_photo_loss + total_cycle_loss   +                                    # total generator photo loss\n            self.identity_loss_function(real_photo, same_photo, self.lambda_cycle) )\n            \n            disc_monet_loss= self.disc_loss_function(disc_real_monet, disc_fake_monet)                      # discriminator monet loss \n            disc_photo_loss= self.disc_loss_function(disc_real_photo, disc_fake_photo)                      # discriminator photo loss\n            \n            \n        gen_monet_gradients= tape.gradient(total_gen_monet_loss, self.m_gen.trainable_variables)            # calculate gradients for generators\n        gen_photo_gradients= tape.gradient(total_gen_photo_loss, self.p_gen.trainable_variables)            # diff loss fn wrt trainable variables of model\n        \n        disc_monet_gradients= tape.gradient(disc_monet_loss, self.m_disc.trainable_variables)               # calculate gradients for discriminators\n        disc_photo_gradients= tape.gradient(disc_photo_loss, self.p_disc.trainable_variables)\n        \n        self.m_gen_optimizer.apply_gradients(zip(gen_monet_gradients, self.m_gen.trainable_variables))      # apply the gradients to optimizer\n        self.p_gen_optimizer.apply_gradients(zip(gen_photo_gradients, self.p_gen.trainable_variables))      # basically performing gradient descent\n        self.m_disc_optimizer.apply_gradients(zip(disc_monet_gradients, self.m_disc.trainable_variables))\n        self.p_disc_optimizer.apply_gradients(zip(disc_photo_gradients, self.p_disc.trainable_variables))\n        \n        return {\n            'gen_monet_loss': total_gen_monet_loss,\n            'gen_photo_loss': total_gen_photo_loss,\n            'disc_monet_loss': disc_monet_loss,\n            'disc_photo_loss': disc_photo_loss\n        }","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:30.940832Z","iopub.execute_input":"2024-01-25T08:21:30.941196Z","iopub.status.idle":"2024-01-25T08:21:30.962116Z","shell.execute_reply.started":"2024-01-25T08:21:30.941166Z","shell.execute_reply":"2024-01-25T08:21:30.961166Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Loss Functions**","metadata":{}},{"cell_type":"code","source":"# with strategy.scope():\ndef gen_loss_fn(generated):            # from_logits=True used bcz disc return unbounded values & NONE redn used to return tensor of indiv losses bcz those values are returned at each epoch\n    return BinaryCrossentropy(from_logits= True, reduction= tf.keras.losses.Reduction.NONE)(tf.ones_like(generated),generated)","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:33.040060Z","iopub.execute_input":"2024-01-25T08:21:33.040793Z","iopub.status.idle":"2024-01-25T08:21:33.045258Z","shell.execute_reply.started":"2024-01-25T08:21:33.040764Z","shell.execute_reply":"2024-01-25T08:21:33.044334Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# with strategy.scope():\ndef disc_loss_fn(real, generated):\n    loss_real= BinaryCrossentropy(from_logits= True, reduction= tf.keras.losses.Reduction.NONE)(tf.ones_like(real),real)\n    loss_fake= BinaryCrossentropy(from_logits= True, reduction= tf.keras.losses.Reduction.NONE)(tf.zeros_like(generated),generated)\n        \n    total_loss= (loss_real + loss_fake)/2\n        \n    return total_loss","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:33.381749Z","iopub.execute_input":"2024-01-25T08:21:33.382066Z","iopub.status.idle":"2024-01-25T08:21:33.388043Z","shell.execute_reply.started":"2024-01-25T08:21:33.382039Z","shell.execute_reply":"2024-01-25T08:21:33.386976Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# with strategy.scope():\ndef cycle_loss_fn(real, cycled, lambda_cycle):\n    loss= tf.reduce_mean(tf.abs(real - cycled))\n        \n    return lambda_cycle*loss                        # lambda controls the weight of cycle consistency loss in overall loss ","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:34.184927Z","iopub.execute_input":"2024-01-25T08:21:34.185288Z","iopub.status.idle":"2024-01-25T08:21:34.190378Z","shell.execute_reply.started":"2024-01-25T08:21:34.185259Z","shell.execute_reply":"2024-01-25T08:21:34.189372Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# with strategy.scope():\ndef identity_loss_fn(real, same, Lambda):           # LAMBDA has same use as in case of cycle consistency loss\n    loss= tf.reduce_mean(tf.abs(real - same))\n        \n    return Lambda*loss*0.5                          # factor of '0.5' used for normalization purposes","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:34.871630Z","iopub.execute_input":"2024-01-25T08:21:34.871953Z","iopub.status.idle":"2024-01-25T08:21:34.876752Z","shell.execute_reply.started":"2024-01-25T08:21:34.871930Z","shell.execute_reply":"2024-01-25T08:21:34.875853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Optimizers**","metadata":{}},{"cell_type":"code","source":"# with strategy.scope():\nm_gen_opt= Adam(learning_rate= 2e-4, beta_1= 0.5)\nm_disc_opt= Adam(learning_rate= 2e-4, beta_1= 0.5)\n\np_gen_opt= Adam(learning_rate= 2e-4, beta_1= 0.5)\np_disc_opt= Adam(learning_rate= 2e-4, beta_1= 0.5)","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:36.746538Z","iopub.execute_input":"2024-01-25T08:21:36.747371Z","iopub.status.idle":"2024-01-25T08:21:36.760405Z","shell.execute_reply.started":"2024-01-25T08:21:36.747338Z","shell.execute_reply":"2024-01-25T08:21:36.759455Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Compiling and Training/Fitting**","metadata":{}},{"cell_type":"code","source":"# with strategy.scope():\ncyclegan_model= CycleGAN(monet_generator, monet_discriminator, photo_generator, photo_discriminator, 10)\ncyclegan_model.compile(m_gen_opt, m_disc_opt, p_gen_opt, p_disc_opt, gen_loss_fn, disc_loss_fn, \n                        cycle_loss_fn, identity_loss_fn)","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:21:38.398034Z","iopub.execute_input":"2024-01-25T08:21:38.398389Z","iopub.status.idle":"2024-01-25T08:21:38.421369Z","shell.execute_reply.started":"2024-01-25T08:21:38.398362Z","shell.execute_reply":"2024-01-25T08:21:38.420660Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cyclegan_model.fit(tf.data.Dataset.zip((monet_data, photo_data)), epochs= 40)","metadata":{"execution":{"iopub.status.busy":"2024-01-25T06:30:08.498627Z","iopub.execute_input":"2024-01-25T06:30:08.499482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Observing the Monet-esque Photos**","metadata":{}},{"cell_type":"code","source":"fig,ax= plt.subplots(6,2, figsize=(7,20))\nfor i,img in enumerate(photo_data.take(6)):\n    pred= monet_generator(img, training= False)[0].numpy()   # training= False to make sure not to update model's weights\n    pred= (pred*127.5 + 127.5).astype(np.uint8)              # making pixel range to [0,255]\n    img= (img[0]*127.5 + 127.5).numpy().astype(np.uint8)\n    \n    ax[i,0].imshow(img)\n    ax[i,1].imshow(pred)\n    ax[i,0].set_title('Real Photo')\n    ax[i,1].set_title('Generated Monet-esque')\n    ax[i,0].axis('off')\n    ax[i,1].axis('off')","metadata":{"execution":{"iopub.status.busy":"2024-01-25T06:11:45.003466Z","iopub.execute_input":"2024-01-25T06:11:45.004121Z","iopub.status.idle":"2024-01-25T06:11:47.756299Z","shell.execute_reply.started":"2024-01-25T06:11:45.004078Z","shell.execute_reply":"2024-01-25T06:11:47.755195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Submission File Creation**","metadata":{}},{"cell_type":"code","source":"import PIL\n!mkdir ../images","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:24:22.600990Z","iopub.execute_input":"2024-01-25T08:24:22.601402Z","iopub.status.idle":"2024-01-25T08:24:22.606159Z","shell.execute_reply.started":"2024-01-25T08:24:22.601370Z","shell.execute_reply":"2024-01-25T08:24:22.604972Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"i = 1\nfor image in photo_data:\n    pred = monet_generator(image, training=False)[0].numpy()\n    pred = (pred*127.5 + 127.5).astype(np.uint8)\n    im = PIL.Image.fromarray(pred)\n    im.save(\"../images/\" + str(i) + \".jpg\")\n    i += 1","metadata":{"execution":{"iopub.status.busy":"2024-01-25T08:10:40.500947Z","iopub.execute_input":"2024-01-25T08:10:40.502081Z","iopub.status.idle":"2024-01-25T08:10:41.457414Z","shell.execute_reply.started":"2024-01-25T08:10:40.502033Z","shell.execute_reply":"2024-01-25T08:10:41.456458Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**This was my first time working on CycleGAN along with other topics such as UNET Architecture...so I followed AMY JANG's notebook for reference. I REALLY loved her work!! I spent a lot of time understanding the concepts she used and apply those concepts on my own along with adding comments to help people understand important steps better... I will provide the link to her notebook here...so please do check it out!! And...Thank You for checking my notebook out!! :)\nhttps://www.kaggle.com/code/amyjang/monet-cyclegan-tutorial/notebook**","metadata":{}}]}