{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","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":30648,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-02-11T15:08:24.578299Z","iopub.execute_input":"2024-02-11T15:08:24.578712Z","iopub.status.idle":"2024-02-11T15:08:30.744086Z","shell.execute_reply.started":"2024-02-11T15:08:24.578653Z","shell.execute_reply":"2024-02-11T15:08:30.743158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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-02-11T15:08:34.606381Z","iopub.execute_input":"2024-02-11T15:08:34.606909Z","iopub.status.idle":"2024-02-11T15:08:37.984839Z","shell.execute_reply.started":"2024-02-11T15:08:34.606875Z","shell.execute_reply":"2024-02-11T15:08:37.983941Z"},"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-02-11T15:08:38.955374Z","iopub.execute_input":"2024-02-11T15:08:38.956008Z","iopub.status.idle":"2024-02-11T15:08:38.982463Z","shell.execute_reply.started":"2024-02-11T15:08:38.955977Z","shell.execute_reply":"2024-02-11T15:08:38.981623Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(tf.__version__)","metadata":{"execution":{"iopub.status.busy":"2024-02-11T15:08:42.095873Z","iopub.execute_input":"2024-02-11T15:08:42.096719Z","iopub.status.idle":"2024-02-11T15:08:42.102157Z","shell.execute_reply.started":"2024-02-11T15:08:42.096661Z","shell.execute_reply":"2024-02-11T15:08:42.100941Z"},"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-02-11T15:08:43.203331Z","iopub.execute_input":"2024-02-11T15:08:43.203719Z","iopub.status.idle":"2024-02-11T15:08:43.208482Z","shell.execute_reply.started":"2024-02-11T15:08:43.203685Z","shell.execute_reply":"2024-02-11T15:08:43.207456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path()","metadata":{"execution":{"iopub.status.busy":"2024-02-11T15:08:45.298668Z","iopub.execute_input":"2024-02-11T15:08:45.299050Z","iopub.status.idle":"2024-02-11T15:08:45.618860Z","shell.execute_reply.started":"2024-02-11T15:08:45.299020Z","shell.execute_reply":"2024-02-11T15:08:45.617963Z"},"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-02-11T15:08:46.411247Z","iopub.execute_input":"2024-02-11T15:08:46.412175Z","iopub.status.idle":"2024-02-11T15:08:46.656011Z","shell.execute_reply.started":"2024-02-11T15:08:46.412140Z","shell.execute_reply":"2024-02-11T15:08:46.655021Z"},"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-02-11T15:08:48.209109Z","iopub.execute_input":"2024-02-11T15:08:48.209514Z","iopub.status.idle":"2024-02-11T15:08:48.215131Z","shell.execute_reply.started":"2024-02-11T15:08:48.209472Z","shell.execute_reply":"2024-02-11T15:08:48.214163Z"},"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-02-11T15:08:48.796251Z","iopub.execute_input":"2024-02-11T15:08:48.796635Z","iopub.status.idle":"2024-02-11T15:08:48.804460Z","shell.execute_reply.started":"2024-02-11T15:08:48.796602Z","shell.execute_reply":"2024-02-11T15:08:48.803547Z"},"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-02-11T15:08:49.324380Z","iopub.execute_input":"2024-02-11T15:08:49.325155Z","iopub.status.idle":"2024-02-11T15:08:49.329727Z","shell.execute_reply.started":"2024-02-11T15:08:49.325119Z","shell.execute_reply":"2024-02-11T15:08:49.328743Z"},"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-02-11T15:08:49.964702Z","iopub.execute_input":"2024-02-11T15:08:49.965072Z","iopub.status.idle":"2024-02-11T15:08:50.718627Z","shell.execute_reply.started":"2024-02-11T15:08:49.965043Z","shell.execute_reply":"2024-02-11T15:08:50.717815Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"monet_data","metadata":{"execution":{"iopub.status.busy":"2024-02-11T15:08:50.720431Z","iopub.execute_input":"2024-02-11T15:08:50.720829Z","iopub.status.idle":"2024-02-11T15:08:50.728110Z","shell.execute_reply.started":"2024-02-11T15:08:50.720798Z","shell.execute_reply":"2024-02-11T15:08:50.727103Z"},"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-02-11T15:08:50.927138Z","iopub.execute_input":"2024-02-11T15:08:50.927763Z","iopub.status.idle":"2024-02-11T15:08:51.844457Z","shell.execute_reply.started":"2024-02-11T15:08:50.927732Z","shell.execute_reply":"2024-02-11T15:08:51.843440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nplt.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-02-11T15:08:52.789585Z","iopub.execute_input":"2024-02-11T15:08:52.790008Z","iopub.status.idle":"2024-02-11T15:08:53.263979Z","shell.execute_reply.started":"2024-02-11T15:08:52.789977Z","shell.execute_reply":"2024-02-11T15:08:53.263019Z"},"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    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-02-11T15:08:53.492092Z","iopub.execute_input":"2024-02-11T15:08:53.493017Z","iopub.status.idle":"2024-02-11T15:08:53.499366Z","shell.execute_reply.started":"2024-02-11T15:08:53.492982Z","shell.execute_reply":"2024-02-11T15:08:53.498273Z"},"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":{"iopub.status.busy":"2024-02-11T15:08:54.192729Z","iopub.execute_input":"2024-02-11T15:08:54.193432Z","iopub.status.idle":"2024-02-11T15:08:54.198176Z","shell.execute_reply.started":"2024-02-11T15:08:54.193398Z","shell.execute_reply":"2024-02-11T15:08:54.197199Z"},"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    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-02-11T15:08:56.438971Z","iopub.execute_input":"2024-02-11T15:08:56.439332Z","iopub.status.idle":"2024-02-11T15:08:56.446341Z","shell.execute_reply.started":"2024-02-11T15:08:56.439301Z","shell.execute_reply":"2024-02-11T15:08:56.445217Z"},"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":{"iopub.status.busy":"2024-02-11T15:08:56.840236Z","iopub.execute_input":"2024-02-11T15:08:56.840647Z","iopub.status.idle":"2024-02-11T15:08:56.845344Z","shell.execute_reply.started":"2024-02-11T15:08:56.840613Z","shell.execute_reply":"2024-02-11T15:08:56.844359Z"},"trusted":true},"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-02-11T15:08:57.663517Z","iopub.execute_input":"2024-02-11T15:08:57.664373Z","iopub.status.idle":"2024-02-11T15:08:57.674941Z","shell.execute_reply.started":"2024-02-11T15:08:57.664338Z","shell.execute_reply":"2024-02-11T15:08:57.673909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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-02-11T15:08:58.201919Z","iopub.execute_input":"2024-02-11T15:08:58.202286Z","iopub.status.idle":"2024-02-11T15:08:58.210802Z","shell.execute_reply.started":"2024-02-11T15:08:58.202254Z","shell.execute_reply":"2024-02-11T15:08:58.209715Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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-02-11T15:08:58.608281Z","iopub.execute_input":"2024-02-11T15:08:58.608659Z","iopub.status.idle":"2024-02-11T15:09:03.709570Z","shell.execute_reply.started":"2024-02-11T15:08:58.608627Z","shell.execute_reply":"2024-02-11T15:09:03.708735Z"},"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-02-11T15:09:03.711264Z","iopub.execute_input":"2024-02-11T15:09:03.711623Z","iopub.status.idle":"2024-02-11T15:09:06.998303Z","shell.execute_reply.started":"2024-02-11T15:09:03.711592Z","shell.execute_reply":"2024-02-11T15:09:06.997374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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-02-11T15:09:06.999872Z","iopub.execute_input":"2024-02-11T15:09:07.000229Z","iopub.status.idle":"2024-02-11T15:09:07.019356Z","shell.execute_reply.started":"2024-02-11T15:09:07.000200Z","shell.execute_reply":"2024-02-11T15:09:07.018387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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-02-11T15:09:07.021872Z","iopub.execute_input":"2024-02-11T15:09:07.022202Z","iopub.status.idle":"2024-02-11T15:09:07.030740Z","shell.execute_reply.started":"2024-02-11T15:09:07.022164Z","shell.execute_reply":"2024-02-11T15:09:07.029877Z"},"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-02-11T15:09:07.032079Z","iopub.execute_input":"2024-02-11T15:09:07.032478Z","iopub.status.idle":"2024-02-11T15:09:07.041079Z","shell.execute_reply.started":"2024-02-11T15:09:07.032424Z","shell.execute_reply":"2024-02-11T15:09:07.040195Z"},"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-02-11T15:09:07.042302Z","iopub.execute_input":"2024-02-11T15:09:07.042581Z","iopub.status.idle":"2024-02-11T15:09:07.048513Z","shell.execute_reply.started":"2024-02-11T15:09:07.042558Z","shell.execute_reply":"2024-02-11T15:09:07.047716Z"},"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-02-11T15:09:07.049491Z","iopub.execute_input":"2024-02-11T15:09:07.049794Z","iopub.status.idle":"2024-02-11T15:09:07.056525Z","shell.execute_reply.started":"2024-02-11T15:09:07.049770Z","shell.execute_reply":"2024-02-11T15:09:07.055803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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-02-11T15:09:07.057494Z","iopub.execute_input":"2024-02-11T15:09:07.058291Z","iopub.status.idle":"2024-02-11T15:09:07.073382Z","shell.execute_reply.started":"2024-02-11T15:09:07.058267Z","shell.execute_reply":"2024-02-11T15:09:07.072707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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-02-11T15:09:07.074340Z","iopub.execute_input":"2024-02-11T15:09:07.074602Z","iopub.status.idle":"2024-02-11T15:09:07.097417Z","shell.execute_reply.started":"2024-02-11T15:09:07.074579Z","shell.execute_reply":"2024-02-11T15:09:07.096690Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cyclegan_model.fit(tf.data.Dataset.zip((monet_data, photo_data)), epochs= 50)","metadata":{"execution":{"iopub.status.busy":"2024-02-11T15:09:09.236280Z","iopub.execute_input":"2024-02-11T15:09:09.237029Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import PIL\n!mkdir ../images","metadata":{"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":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}