{"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":"**Importing required libraries**","metadata":{"execution":{"iopub.status.busy":"2021-11-24T14:27:10.746068Z","iopub.execute_input":"2021-11-24T14:27:10.746809Z","iopub.status.idle":"2021-11-24T14:27:10.752056Z","shell.execute_reply.started":"2021-11-24T14:27:10.746761Z","shell.execute_reply":"2021-11-24T14:27:10.751064Z"}}},{"cell_type":"code","source":"BATCH_SIZE = 4\nimport re\nimport os\nimport math\nimport cv2\nimport random\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport tensorflow_addons as tfa\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nfrom tensorflow.keras import Model, losses, optimizers\nfrom tensorflow.keras.callbacks import Callback\nfrom kaggle_datasets import KaggleDatasets","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:29:54.455380Z","iopub.execute_input":"2021-12-12T20:29:54.455744Z","iopub.status.idle":"2021-12-12T20:29:59.704833Z","shell.execute_reply.started":"2021-12-12T20:29:54.455658Z","shell.execute_reply":"2021-12-12T20:29:59.703940Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Enabling TPU for implementation**","metadata":{}},{"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    tpu_strategy = tf.distribute.experimental.TPUStrategy(tpu)\nexcept:\n    tpu_strategy = tf.distribute.get_strategy()\nprint('Number of replicas:', tpu_strategy.num_replicas_in_sync)\n\nAUTO_TUNE = tf.data.experimental.AUTOTUNE\n    \nprint(\"version:\",tf.__version__)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:29:59.706668Z","iopub.execute_input":"2021-12-12T20:29:59.706886Z","iopub.status.idle":"2021-12-12T20:30:05.609817Z","shell.execute_reply.started":"2021-12-12T20:29:59.706861Z","shell.execute_reply":"2021-12-12T20:30:05.609075Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Importing the dataset.**","metadata":{}},{"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path('gan-getting-started')\n\nmonets_tfr = tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nphotos_tfr = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\n\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\nmonet_jpg = count_data_items(monets_tfr)\nphoto_jpg = count_data_items(photos_tfr)\n\nEPOCHS = 30\n\nprint(\"Monet TFRecord files:\", len(monets_tfr))\nprint(\"Monet image files:\", monet_jpg)\nprint(\"Photo TFRecord files:\", len(photos_tfr))\nprint(\"Photo image files:\", photo_jpg)\nprint(\"EPOCHS:\",EPOCHS)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:05.612966Z","iopub.execute_input":"2021-12-12T20:30:05.613188Z","iopub.status.idle":"2021-12-12T20:30:06.161802Z","shell.execute_reply.started":"2021-12-12T20:30:05.613162Z","shell.execute_reply":"2021-12-12T20:30:06.160992Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Adding a function to display sample images**","metadata":{}},{"cell_type":"code","source":"def view_data(dataset, nrows, ncols):\n    ds_iter = iter(dataset)\n    plt.figure(figsize=(15, int(15*nrows/ncols)))\n    for j in range(nrows*ncols):\n        monet_sample = next(ds_iter)\n        plt.subplot(nrows,ncols,j+1)\n        plt.axis('off')\n        plt.imshow(monet_sample[0] * 0.5 + 0.5)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:06.162913Z","iopub.execute_input":"2021-12-12T20:30:06.163135Z","iopub.status.idle":"2021-12-12T20:30:06.169494Z","shell.execute_reply.started":"2021-12-12T20:30:06.163108Z","shell.execute_reply":"2021-12-12T20:30:06.168629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Setting up Image size**","metadata":{}},{"cell_type":"code","source":"IMAGE_SIZE = [256, 256]","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:06.171286Z","iopub.execute_input":"2021-12-12T20:30:06.172189Z","iopub.status.idle":"2021-12-12T20:30:06.181862Z","shell.execute_reply.started":"2021-12-12T20:30:06.172156Z","shell.execute_reply":"2021-12-12T20:30:06.181141Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We perform decoding of image and also read the images\n\nAll the images are sized to 256x256. As these images are RGB images, set the channel to 3. Additionally, we need to scale the images to a [-1, 1] scale. Because we are building a generative model, we don't need the labels or the image id so we'll only return the image from the TFRecord.","metadata":{}},{"cell_type":"code","source":"def decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = (tf.cast(image, tf.float32) / 127.5) - 1\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n\ndef read_tfrecord(example):\n    tfrecord_format = {\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    return image","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:06.182863Z","iopub.execute_input":"2021-12-12T20:30:06.183600Z","iopub.status.idle":"2021-12-12T20:30:06.192878Z","shell.execute_reply.started":"2021-12-12T20:30:06.183565Z","shell.execute_reply":"2021-12-12T20:30:06.192164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**IMAGE PRE-PROCESSING**\n\n**Resizing image** (In this case it would not be necessary to do it, because the images are already in the necessary size. But with this step if we wanted to add new images it would not be necessary to scale them previously)\n\n**Normalizing** the images to [-1, 1]\n\n**Random jittering and mirroring** to the training dataset. These are some of the image augmentation techniques that avoids overfitting, Random jittering performs:\n\nResize an image to bigger height and width\nRandomly crop to the target size\nRandomly flip the image horizontally","metadata":{}},{"cell_type":"code","source":"def data_augment(image):\n    rotate = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    spatial = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    crop = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    \n    if crop > .5:\n        image = tf.image.resize(image, [286, 286]) #resizing to 286 x 286 x 3\n        image = tf.image.random_crop(image, size=[256, 256, 3]) # randomly cropping to 256 x 256 x 3\n        if crop > .9:\n            image = tf.image.resize(image, [300, 300])\n            image = tf.image.random_crop(image, size=[256, 256, 3])\n            \n    if rotate > .9:\n        image = tf.image.rot90(image, k=3) # rotate 270º\n    elif rotate > .7:\n        image = tf.image.rot90(image, k=2) # rotate 180º\n    elif rotate > .5:\n        image = tf.image.rot90(image, k=1) # rotate 90º\n        \n        ## random mirroring\n    if spatial > .6:\n        image = tf.image.random_flip_left_right(image)\n        image = tf.image.random_flip_up_down(image)\n        if spatial > .9:\n            image = tf.image.transpose(image)\n    \n    return image","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:06.193891Z","iopub.execute_input":"2021-12-12T20:30:06.194510Z","iopub.status.idle":"2021-12-12T20:30:06.208708Z","shell.execute_reply.started":"2021-12-12T20:30:06.194481Z","shell.execute_reply":"2021-12-12T20:30:06.207834Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_dataset(filenames):\n    data = tf.data.TFRecordDataset(filenames)\n    data = data.map(read_tfrecord, num_parallel_calls=AUTO_TUNE)\n    return data","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:06.209761Z","iopub.execute_input":"2021-12-12T20:30:06.210346Z","iopub.status.idle":"2021-12-12T20:30:06.219083Z","shell.execute_reply.started":"2021-12-12T20:30:06.210307Z","shell.execute_reply":"2021-12-12T20:30:06.218454Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_gan_dataset(monet_files, photo_files, augment=None, repeat=True, shuffle=True, batch_size=1):\n\n    monet_ds = load_dataset(monet_files)\n    photo_ds = load_dataset(photo_files)\n    \n    if augment:\n        monet_ds = monet_ds.map(augment, num_parallel_calls=AUTO_TUNE)\n        photo_ds = photo_ds.map(augment, num_parallel_calls=AUTO_TUNE)\n        \n    if repeat:\n        monet_ds = monet_ds.repeat()\n        photo_ds = photo_ds.repeat()\n    if shuffle:\n        monet_ds = monet_ds.shuffle(2048)\n        photo_ds = photo_ds.shuffle(2048)\n        \n    monet_ds = monet_ds.batch(batch_size, drop_remainder=True)\n    photo_ds = photo_ds.batch(batch_size, drop_remainder=True)\n    monet_ds = monet_ds.cache()\n    photo_ds = photo_ds.cache()\n    monet_ds = monet_ds.prefetch(AUTO_TUNE)\n    photo_ds = photo_ds.prefetch(AUTO_TUNE)\n    \n    gan_ds = tf.data.Dataset.zip((monet_ds, photo_ds))\n    \n    return gan_ds","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:06.220526Z","iopub.execute_input":"2021-12-12T20:30:06.221102Z","iopub.status.idle":"2021-12-12T20:30:06.230020Z","shell.execute_reply.started":"2021-12-12T20:30:06.221065Z","shell.execute_reply":"2021-12-12T20:30:06.229434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**load in our datasets.**","metadata":{}},{"cell_type":"code","source":"data = get_gan_dataset(monets_tfr, photos_tfr, augment=data_augment, repeat=True, shuffle=True, batch_size=BATCH_SIZE)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:06.231296Z","iopub.execute_input":"2021-12-12T20:30:06.232003Z","iopub.status.idle":"2021-12-12T20:30:07.206527Z","shell.execute_reply.started":"2021-12-12T20:30:06.231965Z","shell.execute_reply":"2021-12-12T20:30:07.205622Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Display sample monet images **","metadata":{}},{"cell_type":"code","source":"view_data(load_dataset(monets_tfr).batch(1), 2, 3)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:07.207742Z","iopub.execute_input":"2021-12-12T20:30:07.207980Z","iopub.status.idle":"2021-12-12T20:30:08.589219Z","shell.execute_reply.started":"2021-12-12T20:30:07.207955Z","shell.execute_reply":"2021-12-12T20:30:08.583251Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Display sample photo images**","metadata":{}},{"cell_type":"code","source":"view_data(load_dataset(photos_tfr).batch(1),2,3)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:08.590980Z","iopub.execute_input":"2021-12-12T20:30:08.591268Z","iopub.status.idle":"2021-12-12T20:30:09.722395Z","shell.execute_reply.started":"2021-12-12T20:30:08.591235Z","shell.execute_reply":"2021-12-12T20:30:09.721554Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"**Define the path for the monet and photo images**","metadata":{}},{"cell_type":"code","source":"BASE_PATH = '../input/gan-getting-started/'\nMONET_PATH = os.path.join(BASE_PATH, 'monet_jpg')\nPHOTO_PATH = os.path.join(BASE_PATH, 'photo_jpg')","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:09.723541Z","iopub.execute_input":"2021-12-12T20:30:09.723789Z","iopub.status.idle":"2021-12-12T20:30:09.728289Z","shell.execute_reply.started":"2021-12-12T20:30:09.723763Z","shell.execute_reply":"2021-12-12T20:30:09.727514Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Batch visualization of photo and monet images**","metadata":{}},{"cell_type":"code","source":"def batch_visualization(path, n_images, is_random=True, figsize=(16, 16)):\n    plt.figure(figsize=figsize)\n    \n    w = int(n_images ** .5)\n    h = math.ceil(n_images / w)\n    \n    all_names = os.listdir(path)\n    \n    image_names = all_names[:n_images]\n    if is_random:\n        image_names = random.sample(all_names, n_images)\n    \n    for ind, image_name in enumerate(image_names):\n        img = cv2.imread(os.path.join(path, image_name))\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) \n        plt.subplot(h, w, ind + 1)\n        plt.imshow(img)\n        plt.axis('off')\n    \n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:09.732568Z","iopub.execute_input":"2021-12-12T20:30:09.732898Z","iopub.status.idle":"2021-12-12T20:30:09.742467Z","shell.execute_reply.started":"2021-12-12T20:30:09.732844Z","shell.execute_reply":"2021-12-12T20:30:09.741630Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_visualization(MONET_PATH, 6, is_random=True, figsize=(16, 16))","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:09.743479Z","iopub.execute_input":"2021-12-12T20:30:09.743743Z","iopub.status.idle":"2021-12-12T20:30:10.642747Z","shell.execute_reply.started":"2021-12-12T20:30:09.743718Z","shell.execute_reply":"2021-12-12T20:30:10.641789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_visualization(PHOTO_PATH, 6, is_random=True, figsize=(16, 16))","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:10.643997Z","iopub.execute_input":"2021-12-12T20:30:10.644200Z","iopub.status.idle":"2021-12-12T20:30:11.681045Z","shell.execute_reply.started":"2021-12-12T20:30:10.644176Z","shell.execute_reply":"2021-12-12T20:30:11.680108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Colour historgrams for monet and photo data**","metadata":{}},{"cell_type":"code","source":"def color_hist_visualization(image_path, figsize=(16, 4)):\n    plt.figure(figsize=figsize)\n    colors = ['red', 'green', 'blue']\n    \n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) \n    plt.subplot(1, 4, 1)\n    plt.imshow(img)\n    plt.axis('off')\n    \n    for i in range(len(colors)):\n        plt.subplot(1, 4, i + 2)\n        plt.hist(\n            img[:, :, i].reshape(-1),\n            bins=25,\n            alpha=0.5,\n            color=colors[i],\n            density=True\n        )\n        plt.xlim(0, 255)\n        plt.xticks([])\n        plt.yticks([])\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:11.682427Z","iopub.execute_input":"2021-12-12T20:30:11.682677Z","iopub.status.idle":"2021-12-12T20:30:11.690658Z","shell.execute_reply.started":"2021-12-12T20:30:11.682649Z","shell.execute_reply":"2021-12-12T20:30:11.689878Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_1 = '../input/gan-getting-started/monet_jpg/0260d15306.jpg'\nimage_2 = '../input/gan-getting-started/photo_jpg/0033c5f971.jpg'\ncolor_hist_visualization(image_1)\ncolor_hist_visualization(image_2)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:11.691922Z","iopub.execute_input":"2021-12-12T20:30:11.692349Z","iopub.status.idle":"2021-12-12T20:30:12.528997Z","shell.execute_reply.started":"2021-12-12T20:30:11.692320Z","shell.execute_reply":"2021-12-12T20:30:12.528154Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Channel visualization of monet and photo data**","metadata":{}},{"cell_type":"code","source":"def channels_visualization(image_path, figsize=(16, 4)):\n    plt.figure(figsize=figsize)\n    \n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) \n    plt.subplot(1, 4, 1)\n    plt.imshow(img)\n    plt.axis('off')\n    \n    for i in range(3):\n        plt.subplot(1, 4, i + 2)\n        tmp_img = np.full_like(img, 0)\n        tmp_img[:, :, i] = img[:, :, i]\n        plt.imshow(tmp_img)\n        plt.xlim(0, 255)\n        plt.xticks([])\n        plt.yticks([])\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:12.530116Z","iopub.execute_input":"2021-12-12T20:30:12.530332Z","iopub.status.idle":"2021-12-12T20:30:12.538012Z","shell.execute_reply.started":"2021-12-12T20:30:12.530307Z","shell.execute_reply":"2021-12-12T20:30:12.537116Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_path = '../input/gan-getting-started/monet_jpg/0bd913dbc7.jpg'\nchannels_visualization(img_path)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:12.539430Z","iopub.execute_input":"2021-12-12T20:30:12.539684Z","iopub.status.idle":"2021-12-12T20:30:13.133147Z","shell.execute_reply.started":"2021-12-12T20:30:12.539658Z","shell.execute_reply":"2021-12-12T20:30:13.132350Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**\"Downsample\" function will be created that passing the number of filters to it and if normalization is applied, it will create a keras.Sequential object**","metadata":{}},{"cell_type":"code","source":"def downsample(filters, size, apply_instancenorm=True):\n    initializer = tf.random_normal_initializer(0., 0.04)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n     \n    result = keras.Sequential()\n    # Convolutional layer\n    result.add(layers.Conv2D(filters, size, strides=2, padding='same',\n                             kernel_initializer=initializer, use_bias=False))\n # Normalization layer\n    if apply_instancenorm:\n        result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n # Activation layer\n    result.add(layers.LeakyReLU())\n\n    return result","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:13.134319Z","iopub.execute_input":"2021-12-12T20:30:13.134530Z","iopub.status.idle":"2021-12-12T20:30:13.142072Z","shell.execute_reply.started":"2021-12-12T20:30:13.134505Z","shell.execute_reply":"2021-12-12T20:30:13.140927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**\"Upsample\" function will be created that passing the number of filters to it and if dropout is applied, it will create a keras.Sequential object**","metadata":{}},{"cell_type":"code","source":"def upsample(filters, size, apply_dropout=False):\n     # Normalization layer\n    initializer = tf.random_normal_initializer(0., 0.04)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n     # Transpose convolutional layer\n    result = keras.Sequential()\n    result.add(layers.Conv2DTranspose(filters, size, strides=2,\n                                      padding='same',\n                                      kernel_initializer=initializer,\n                                      use_bias=False))\n#Instance Normalization\n    result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n# Dropout layer\n    if apply_dropout:\n        result.add(layers.Dropout(0.5))\n# Activation layer\n    result.add(layers.ReLU())\n\n    return result","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:13.143276Z","iopub.execute_input":"2021-12-12T20:30:13.143888Z","iopub.status.idle":"2021-12-12T20:30:13.156568Z","shell.execute_reply.started":"2021-12-12T20:30:13.143840Z","shell.execute_reply":"2021-12-12T20:30:13.155928Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Buid the Generator**","metadata":{}},{"cell_type":"code","source":"OUTPUT_CHANNELS = 3\n\ndef Generator_PM():\n    data = layers.Input(shape=[256,256,3])\n\n    # bs = batch size\n    down_sample = [\n        downsample(64, 4, apply_instancenorm=False), # (bs, 128, 128, 64)\n        downsample(128, 4), # (bs, 64, 64, 128)\n        downsample(256, 4), # (bs, 32, 32, 256)\n        downsample(512, 4), # (bs, 16, 16, 512)\n        downsample(512, 4), # (bs, 8, 8, 512)\n        downsample(512, 4), # (bs, 4, 4, 512)\n        downsample(512, 4), # (bs, 2, 2, 512)\n        downsample(512, 4), # (bs, 1, 1, 512)\n    ]\n\n    up_sample = [\n        upsample(512, 4, apply_dropout=True), # (bs, 2, 2, 1024)\n        upsample(512, 4, apply_dropout=True), # (bs, 4, 4, 1024)\n        upsample(512, 4, apply_dropout=True), # (bs, 8, 8, 1024)\n        upsample(512, 4), # (bs, 16, 16, 1024)\n        upsample(256, 4), # (bs, 32, 32, 512)\n        upsample(128, 4), # (bs, 64, 64, 256)\n        upsample(64, 4), # (bs, 128, 128, 128)\n    ]\n    \n    initialize = tf.random_normal_initializer(0., 0.02)\n    final = layers.Conv2DTranspose(OUTPUT_CHANNELS, 7,\n                                  strides=2,\n                                  padding='same',\n                                  kernel_initializer=initialize,\n                                  activation='tanh') # (bs, 256, 256, 3)\n\n    inputs = data\n\n    # Downsampling through the model\n    skips = []\n    for down in down_sample:\n        inputs = down(inputs)\n        skips.append(inputs)\n\n    skip_connection = reversed(skips[:-1])\n\n    # Upsampling and establishing the skip connections\n    for up, skip in zip(up_sample, skip_connection):\n        inputs = up(inputs)\n        inputs = layers.Concatenate()([inputs, skip])\n\n    inputs = final(inputs)\n\n    return keras.Model(inputs=data, outputs=inputs)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:13.157845Z","iopub.execute_input":"2021-12-12T20:30:13.158209Z","iopub.status.idle":"2021-12-12T20:30:13.170566Z","shell.execute_reply.started":"2021-12-12T20:30:13.158169Z","shell.execute_reply":"2021-12-12T20:30:13.169997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"generator = Generator_PM()\ntf.keras.utils.plot_model(generator, show_shapes=True, dpi=64)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:13.172061Z","iopub.execute_input":"2021-12-12T20:30:13.172594Z","iopub.status.idle":"2021-12-12T20:30:15.948603Z","shell.execute_reply.started":"2021-12-12T20:30:13.172545Z","shell.execute_reply":"2021-12-12T20:30:15.947687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Build the discriminator**","metadata":{}},{"cell_type":"code","source":"def Discriminator_PM():\n    initialize = tf.random_normal_initializer(0., 0.02)\n    init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    data = layers.Input(shape=[256, 256, 3], name='input_image')\n\n    inputs = data\n\n    down1 = downsample(64, 4, False)(inputs) # (bs, 128, 128, 64)\n    down2 = downsample(128, 4)(down1) # (bs, 64, 64, 128)\n    down3 = downsample(256, 4)(down2) # (bs, 32, 32, 256)\n\n    zero_pad1 = layers.ZeroPadding2D()(down3) # (bs, 34, 34, 256)\n    conv = layers.Conv2D(512, 7, strides=2,\n                         kernel_initializer=initialize,\n                         use_bias=False)(zero_pad1) # (bs, 31, 31, 512)\n\n    norm1 = tfa.layers.InstanceNormalization(gamma_initializer=init)(conv)\n\n    leaky_relu = layers.LeakyReLU()(norm1)\n    \n    zero_pad2 = layers.ZeroPadding2D()(leaky_relu) # (bs, 33, 33, 512)\n\n    final = layers.Conv2D(1, 7, strides=2,\n                         kernel_initializer=initialize)(zero_pad2) # (bs, 30, 30, 1)\n    \n    return tf.keras.Model(inputs=inputs, outputs=final)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:15.950246Z","iopub.execute_input":"2021-12-12T20:30:15.950501Z","iopub.status.idle":"2021-12-12T20:30:15.961568Z","shell.execute_reply.started":"2021-12-12T20:30:15.950469Z","shell.execute_reply":"2021-12-12T20:30:15.960594Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"discriminator_y = Discriminator_PM()\ntf.keras.utils.plot_model(discriminator_y, show_shapes=True, dpi=64)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:15.962740Z","iopub.execute_input":"2021-12-12T20:30:15.963385Z","iopub.status.idle":"2021-12-12T20:30:16.339213Z","shell.execute_reply.started":"2021-12-12T20:30:15.963345Z","shell.execute_reply":"2021-12-12T20:30:16.338248Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"**Generator part:**\n\nStarting from the photo, a simulation of a Monet painting is generated and later from this simulation an attempt is made to generate the original photo\n\nStarting from the monet, a photo simulation is generated and later from this simulation an attempt is made to generate the original monet\n\n**Discriminator part:**\n\nDiscriminator so that the fake photo looks like a real photo\nDiscriminator so that the monet fake looks like a Monet painting","metadata":{}},{"cell_type":"code","source":"with tpu_strategy.scope():\n    monet_generator = Generator_PM() # transforms photos to Monet-esque paintings\n    photo_generator = Generator_PM() # transforms Monet paintings to be more like photos\n\n    monet_discriminator = Discriminator_PM() # differentiates real Monet paintings and generated Monet paintings\n    photo_discriminator = Discriminator_PM() ","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:16.340810Z","iopub.execute_input":"2021-12-12T20:30:16.341095Z","iopub.status.idle":"2021-12-12T20:30:25.727389Z","shell.execute_reply.started":"2021-12-12T20:30:16.341064Z","shell.execute_reply":"2021-12-12T20:30:25.726507Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Define the CycleGan class that inherits from Keras.model, this will allow overwriting the train_step function that is used in the fit method in such a way that performance can be maximized with the execution in TPU.**","metadata":{}},{"cell_type":"code","source":"class CycleGan(keras.Model):\n    def __init__(\n        self,\n        monet_generator,\n        photo_generator,\n        monet_discriminator,\n        photo_discriminator,\n        lambda_cycle=20,\n    ):\n        super(CycleGan, self).__init__()\n        self.m_gen = monet_generator\n        self.p_gen = photo_generator\n        self.m_disc = monet_discriminator\n        self.p_disc = photo_discriminator\n        self.lambda_cycle = lambda_cycle\n        \n    def compile(\n        self,\n        m_gen_optimizer,\n        p_gen_optimizer,\n        m_disc_optimizer,\n        p_disc_optimizer,\n        gen_loss_fn,\n        disc_loss_fn,\n        cycle_loss_fn,\n        identity_loss_fn\n    ):\n        super(CycleGan, self).compile()\n        self.m_gen_optimizer = m_gen_optimizer\n        self.p_gen_optimizer = p_gen_optimizer\n        self.m_disc_optimizer = m_disc_optimizer\n        self.p_disc_optimizer = p_disc_optimizer\n        self.gen_loss_fn = gen_loss_fn\n        self.disc_loss_fn = disc_loss_fn\n        self.cycle_loss_fn = cycle_loss_fn\n        self.identity_loss_fn = identity_loss_fn\n        \n    def train_step(self, batch_data):\n        real_monet, real_photo = batch_data\n        \n        with tf.GradientTape(persistent=True) as tape:\n            # photo to monet back to photo\n            fake_monet = self.m_gen(real_photo, training=True)\n            cycled_photo = self.p_gen(fake_monet, training=True)\n\n            # monet to photo back to monet\n            fake_photo = self.p_gen(real_monet, training=True)\n            cycled_monet = self.m_gen(fake_photo, training=True)\n\n            # generating itself\n            same_monet = self.m_gen(real_monet, training=True)\n            same_photo = self.p_gen(real_photo, training=True)\n\n            # discriminator used to check, inputing real images\n            disc_real_monet = self.m_disc(real_monet, training=True)\n            disc_real_photo = self.p_disc(real_photo, training=True)\n\n            # discriminator used to check, inputing fake images\n            disc_fake_monet = self.m_disc(fake_monet, training=True)\n            disc_fake_photo = self.p_disc(fake_photo, training=True)\n            \n             # evaluates generator loss\n            monet_gen_loss = self.gen_loss_fn(disc_fake_monet)\n            photo_gen_loss = self.gen_loss_fn(disc_fake_photo)\n\n            # evaluates total cycle consistency loss\n            total_cycle_loss = self.cycle_loss_fn(real_monet, cycled_monet, self.lambda_cycle) + self.cycle_loss_fn(real_photo, cycled_photo, self.lambda_cycle)\n\n            # evaluates total generator loss\n            total_monet_gen_loss = monet_gen_loss + total_cycle_loss + self.identity_loss_fn(real_monet, same_monet, self.lambda_cycle)\n            total_photo_gen_loss = photo_gen_loss + total_cycle_loss + self.identity_loss_fn(real_photo, same_photo, self.lambda_cycle)\n\n            # evaluates discriminator loss\n            monet_disc_loss = self.disc_loss_fn(disc_real_monet, disc_fake_monet)\n            photo_disc_loss = self.disc_loss_fn(disc_real_photo, disc_fake_photo)\n\n        # Calculate the gradients for generator and discriminator\n        monet_generator_gradients = tape.gradient(total_monet_gen_loss,\n                                                  self.m_gen.trainable_variables)\n        photo_generator_gradients = tape.gradient(total_photo_gen_loss,\n                                                  self.p_gen.trainable_variables)\n        \n        monet_discriminator_gradients = tape.gradient(monet_disc_loss,\n                                                      self.m_disc.trainable_variables)\n        photo_discriminator_gradients = tape.gradient(photo_disc_loss,\n                                                      self.p_disc.trainable_variables)\n\n        # Apply the gradients to the optimizer\n        self.m_gen_optimizer.apply_gradients(zip(monet_generator_gradients,\n                                                 self.m_gen.trainable_variables))\n\n        self.p_gen_optimizer.apply_gradients(zip(photo_generator_gradients,\n                                                 self.p_gen.trainable_variables))\n\n        self.m_disc_optimizer.apply_gradients(zip(monet_discriminator_gradients,\n                                                  self.m_disc.trainable_variables))\n\n        self.p_disc_optimizer.apply_gradients(zip(photo_discriminator_gradients,\n                                                  self.p_disc.trainable_variables))\n        \n        return {\n            \"monet_gen_loss\": total_monet_gen_loss,\n            \"photo_gen_loss\": total_photo_gen_loss,\n            \"monet_disc_loss\": monet_disc_loss,\n            \"photo_disc_loss\": photo_disc_loss\n        }","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:25.728934Z","iopub.execute_input":"2021-12-12T20:30:25.729178Z","iopub.status.idle":"2021-12-12T20:30:25.750451Z","shell.execute_reply.started":"2021-12-12T20:30:25.729150Z","shell.execute_reply":"2021-12-12T20:30:25.749255Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"with tpu_strategy.scope():\n    # Discriminator loss {0: fake, 1: real} (The discriminator loss outputs the average of the real and generated loss)\n    def discriminator_loss(real, generated):\n        real_loss = losses.BinaryCrossentropy(from_logits=True, reduction=losses.Reduction.NONE)(tf.ones_like(real), real)\n\n        generated_loss = losses.BinaryCrossentropy(from_logits=True, reduction=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\n    \n    # Generator loss\n    def generator_loss(generated):\n        return losses.BinaryCrossentropy(from_logits=True, reduction=losses.Reduction.NONE)(tf.ones_like(generated), generated)\n    \n    \n    # Cycle consistency loss (measures if original photo and the twice transformed photo to be similar to one another)\n    with tpu_strategy.scope():\n        def calc_cycle_loss(real_image, cycled_image, LAMBDA):\n            loss1 = tf.reduce_mean(tf.abs(real_image - cycled_image))\n\n            return LAMBDA * loss1\n\n    # Identity loss (compares the image with its generator (i.e. photo with photo generator))\n    with tpu_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":"2021-12-12T20:30:25.751679Z","iopub.execute_input":"2021-12-12T20:30:25.751910Z","iopub.status.idle":"2021-12-12T20:30:25.767899Z","shell.execute_reply.started":"2021-12-12T20:30:25.751885Z","shell.execute_reply":"2021-12-12T20:30:25.766878Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with tpu_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":"2021-12-12T20:30:25.769665Z","iopub.execute_input":"2021-12-12T20:30:25.769909Z","iopub.status.idle":"2021-12-12T20:30:25.778812Z","shell.execute_reply.started":"2021-12-12T20:30:25.769884Z","shell.execute_reply":"2021-12-12T20:30:25.777918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Compile the CycleGAN model**","metadata":{}},{"cell_type":"code","source":"with tpu_strategy.scope():\n    cycle_gan_model = CycleGan(\n        monet_generator, photo_generator, \n        monet_discriminator, photo_discriminator\n    )\n\n    cycle_gan_model.compile(\n        m_gen_optimizer = monet_generator_optimizer,\n        p_gen_optimizer = photo_generator_optimizer,\n        m_disc_optimizer = monet_discriminator_optimizer,\n        p_disc_optimizer = photo_discriminator_optimizer,\n        gen_loss_fn = generator_loss,\n        disc_loss_fn = discriminator_loss,\n        cycle_loss_fn = calc_cycle_loss,\n        identity_loss_fn = identity_loss\n    )","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:25.780649Z","iopub.execute_input":"2021-12-12T20:30:25.780952Z","iopub.status.idle":"2021-12-12T20:30:25.853400Z","shell.execute_reply.started":"2021-12-12T20:30:25.780915Z","shell.execute_reply":"2021-12-12T20:30:25.852681Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Fit the CycleGAN model**","metadata":{}},{"cell_type":"code","source":"cycle_gan_model.fit(\n    data,\n    epochs=30,\n    steps_per_epoch=(max(monet_jpg, photo_jpg)//BATCH_SIZE),\n#     steps_per_epoch=1500\n)","metadata":{"execution":{"iopub.status.busy":"2021-12-12T20:30:25.854849Z","iopub.execute_input":"2021-12-12T20:30:25.855158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Predict and save generated images**","metadata":{}},{"cell_type":"code","source":"import PIL\ndef 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        i += 1","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nos.makedirs('../images/') # Create folder to save generated images\n\npredict_and_save(load_dataset(photos_tfr).batch(1), monet_generator, '../images/')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Create a zip folder with the generated images**","metadata":{}},{"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":"print(f\"Number of generated samples: {len([name for name in os.listdir('../images/') if os.path.isfile(os.path.join('../images/', name))])}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Display the generated samples for the test data**","metadata":{}},{"cell_type":"code","source":"def display_generated_samples(ds, model, n_samples):\n    ds_iter = iter(ds)\n    for n_sample in range(n_samples):\n        example_sample = next(ds_iter)\n        generated_sample = model.predict(example_sample)\n    \n        plt.subplot(121)\n        plt.title(\"Input image\")\n        plt.imshow(example_sample[0] * 0.5 + 0.5)\n        plt.axis('off')\n\n        plt.subplot(122)\n        plt.title(\"Generated image\")\n        plt.imshow(generated_sample[0] * 0.5 + 0.5)\n        plt.axis('off')\n        plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display_generated_samples(load_dataset(photos_tfr).batch(1), monet_generator, 7)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}