{"metadata":{"accelerator":"GPU","colab":{"machine_shape":"hm","provenance":[],"gpuType":"A100","collapsed_sections":["zpm5faIIcGtM"]},"kernelspec":{"display_name":"Python 3","name":"python3"},"widgets":{"application/vnd.jupyter.widget-state+json":{"7f85f435a3014ab9bfb36786c92357ad":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_e8b8cab4081e4ff2bfc6156a9764d5da","IPY_MODEL_50131b976aaa4cfbb254990c3d8204bd","IPY_MODEL_7df95b9317544334bfc955fc7834afbc"],"layout":"IPY_MODEL_c94726c6db7a495d8cd2a91dee30d307"}},"e8b8cab4081e4ff2bfc6156a9764d5da":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_9e0b08690e6f46eea13d7c28f42cece9","placeholder":"​","style":"IPY_MODEL_7a11381830a2474da1ac4b2297c2276f","value":"100%"}},"50131b976aaa4cfbb254990c3d8204bd":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_f1f7e19da3044697a99fbeb33cf06e18","max":7038,"min":0,"orientation":"horizontal","style":"IPY_MODEL_7bcd8242cb8548b4bf0a6cd1f2e91d74","value":7038}},"7df95b9317544334bfc955fc7834afbc":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_6770271d14e54bd1957c3a9d5b337d06","placeholder":"​","style":"IPY_MODEL_9853d4b5173945d9bf78ac36ad1acdd0","value":" 7038/7038 [02:07&lt;00:00, 54.89imgs/s]"}},"c94726c6db7a495d8cd2a91dee30d307":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9e0b08690e6f46eea13d7c28f42cece9":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7a11381830a2474da1ac4b2297c2276f":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"f1f7e19da3044697a99fbeb33cf06e18":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7bcd8242cb8548b4bf0a6cd1f2e91d74":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"6770271d14e54bd1957c3a9d5b337d06":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9853d4b5173945d9bf78ac36ad1acdd0":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"e4b2b0c52523422ca06ec1041742e83d":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_52ef4e03cb13418b9d9a77495c4ecb30","IPY_MODEL_6793804a07864a419aa8a75c407ab0e7","IPY_MODEL_e089e5b5b37f48c8903082188781081d"],"layout":"IPY_MODEL_a57dd27ffbc3426a97884e4d8e645b79"}},"52ef4e03cb13418b9d9a77495c4ecb30":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_ae6c9fe0543a43bda2b954ddc817a9f7","placeholder":"​","style":"IPY_MODEL_e6d93eaf796940bb98fd15b8aa06d5dd","value":"100%"}},"6793804a07864a419aa8a75c407ab0e7":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_2d88181801424457a19714631438cc6d","max":7038,"min":0,"orientation":"horizontal","style":"IPY_MODEL_228b8e4fa8264ad2a57b88b710cefbdb","value":7038}},"e089e5b5b37f48c8903082188781081d":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_ee8d478c95754cfe9590abda4bafb9cf","placeholder":"​","style":"IPY_MODEL_e25531fe6c3d4aba8599380188d5bdc2","value":" 7038/7038 [04:10&lt;00:00, 28.56imgs/s]"}},"a57dd27ffbc3426a97884e4d8e645b79":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"ae6c9fe0543a43bda2b954ddc817a9f7":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"e6d93eaf796940bb98fd15b8aa06d5dd":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"2d88181801424457a19714631438cc6d":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"228b8e4fa8264ad2a57b88b710cefbdb":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"ee8d478c95754cfe9590abda4bafb9cf":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"e25531fe6c3d4aba8599380188d5bdc2":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}}}},"language_info":{"name":""},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":30673,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Final Project - Advanced Topics in Deep Learning\n---\n<br>\n\n### Name and ID:\nStudent 1: Omer Maoz 206947186\n<br>\nStudent 2: Liav Nadam 208118877   \nStudent 3: Hila Bonder 322670720\n<br>\nStudent 4: Tal Sages 208754101","metadata":{"id":"XXqIDAd1SeMX"}},{"cell_type":"markdown","source":"**Introduction**\n<br>In recent years, significant progress has been made in the realm of deep learning, particularly in the realm of generative models. Among these models, Generative Adversarial Networks (GANs) have demonstrated remarkable efficacy in producing realistic new data based on existing examples. In this endeavor, we tackle the task of generating images in the style of Monet using GANs.<br>\n<br>Our objective is to produce novel images that closely resemble Monet's distinctive style, leveraging a dataset comprising 300 Monet images and 7078 images of regular scenery. To accomplish this, we employ architectures such as Cycle GAN<br>\n<br>**Related Works**<br>\nPrior research has delved into the utilization of GANs for image generation across diverse artistic styles. For example, Zhu et al. [1] introduced the original Cycle GAN, demonstrating its effectiveness in tasks related to style transfer. Other studies have focused on applying GANs to emulate specific styles such as Van Gogh's artworks, Japanese anime [2], and even graffiti [3].In the context of Monet's style, numerous researchers have explored GAN-based approaches. Li et al. [4] proposed a method centered around a hierarchical adversarial autoencoder, showcasing its efficacy in generating Monet-style images. However, their approach necessitated a substantial number of parameters and incurred high computational costs. In contrast, our method is founded on a Cycle GAN, offering computational efficiency and requiring fewer parameters.\n","metadata":{"id":"c48ywRJ7JLXo"}},{"cell_type":"markdown","source":"### Import Libraries","metadata":{"id":"iT8P_CHsbSIs"}},{"cell_type":"markdown","source":"","metadata":{"id":"EcECe0h8VITZ"}},{"cell_type":"code","source":"import os, random, json, PIL, shutil, re\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nimport tensorflow.keras.layers as L\nfrom tensorflow.keras import Model, losses, optimizers","metadata":{"id":"f0L2j5drJbLG","executionInfo":{"status":"ok","timestamp":1711044315820,"user_tz":-120,"elapsed":483,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Define consts","metadata":{"id":"QReFpU112hLT"}},{"cell_type":"code","source":"HEIGHT = 256\nWIDTH = 256\nCHANNELS = 3  #RGB\nEPOCHS = 30\nBATCH_SIZE = 50\nOUTPUT_CHANNELS = 3  #3 = RGB","metadata":{"id":"way7wWDnJclO","executionInfo":{"status":"ok","timestamp":1711044316778,"user_tz":-120,"elapsed":2,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"GOOGLE_DRIVE_PATH = \"/content/drive\"","metadata":{"id":"uN2Y4POHKCWk","executionInfo":{"status":"ok","timestamp":1711044316778,"user_tz":-120,"elapsed":2,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from google.colab import drive\ndrive.mount(GOOGLE_DRIVE_PATH)","metadata":{"id":"tOeenM_ooxXj","outputId":"e5359f88-e8b4-4fa1-ef67-6ef40a176325","executionInfo":{"status":"ok","timestamp":1711044319614,"user_tz":-120,"elapsed":2838,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"PROJECT_PATH = f'{GOOGLE_DRIVE_PATH}/MyDrive/Colab_Notebooks/advanced'\n\nMONET_FILENAMES = tf.io.gfile.glob(str(PROJECT_PATH + '/monet_tfrec/*.tfrec'))\nPHOTO_FILENAMES = tf.io.gfile.glob(str(PROJECT_PATH + '/photo_tfrec/*.tfrec'))","metadata":{"id":"II4p4Va1Jcfz","executionInfo":{"status":"ok","timestamp":1711044319614,"user_tz":-120,"elapsed":4,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Data Preprocessing","metadata":{"id":"hukY0MSDb4cs"}},{"cell_type":"markdown","source":"###Load data functions","metadata":{"id":"uah9ypXybfjM"}},{"cell_type":"code","source":"AUTO = tf.data.experimental.AUTOTUNE\n\ndef decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=CHANNELS)\n    image = tf.image.resize(image, [256, 256]) #resize\n    image = (tf.cast(image, tf.float32) / 127.5) - 1 #normalization\n    image = tf.reshape(image, [256, 256, 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\n\ndef load_dataset(filenames):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTO)\n    return dataset\n\ndef 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 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)\n    photo_ds = photo_ds.prefetch(AUTO)\n\n    gan_ds = tf.data.Dataset.zip((monet_ds, photo_ds)) #zip monets and photos ds together\n\n    return gan_ds","metadata":{"id":"3aLBh_ZQJccm","executionInfo":{"status":"ok","timestamp":1711044319614,"user_tz":-120,"elapsed":3,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###visualize images","metadata":{"id":"zpm5faIIcGtM"}},{"cell_type":"code","source":"def display_samples(ds, row, col):\n    ds_iter = iter(ds)\n    plt.figure(figsize=(15, int(15*row/col)))\n    for j in range(row*col):\n        example_sample = next(ds_iter)\n        plt.subplot(row,col,j+1)\n        plt.axis('off')\n        plt.imshow(example_sample[0] * 0.5 + 0.5)\n    plt.show()","metadata":{"id":"I06uFpnucJEB","executionInfo":{"status":"ok","timestamp":1711044319614,"user_tz":-120,"elapsed":3,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display_samples(load_dataset(MONET_FILENAMES).batch(1), 4, 6)","metadata":{"id":"nP7qpu8qM44d","outputId":"8ab0a7ca-2049-4297-ff4c-4de426e82b53","executionInfo":{"status":"ok","timestamp":1711044329961,"user_tz":-120,"elapsed":10350,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display_samples(load_dataset(PHOTO_FILENAMES).batch(1), 4, 6)","metadata":{"id":"OBKxNBluN4uN","outputId":"627c4106-6344-4426-ac82-68aff50134b8","executionInfo":{"status":"ok","timestamp":1711044339040,"user_tz":-120,"elapsed":9091,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#Base Model\nThe generator architecture employs a combination of downsampling and upsampling layers, each with a stride of 2. In the downsampling layers, the number of filters is doubled with each subsequent layer, while in the upsampling layers, it is halved.\n<br>As for the discriminator, its architecture resembles that of the generator, albeit without any upsampling layers. Utilizing a PatchGAN architecture, the discriminator classifies individual 34 x 34 patches of the image as real or fake, rather than assessing the entire image at once.<br>\n<br>The CycleGAN model comprises two generators and two discriminators, trained in an adversarial manner. Initially, the generators undergo training to translate images between domains. Subsequently, in the second stage, the generators are trained with cycle consistency loss, ensuring that translated images can be reverted back to their original domain without information loss, thus enhancing image quality.<br>\n<br>In the base model, after 30 epochs with a batch size of 50<br>","metadata":{"id":"BBKW6DBGd8gz"}},{"cell_type":"markdown","source":"##Build Model","metadata":{"id":"Aya5aDxieAUX"}},{"cell_type":"markdown","source":"###Model functions","metadata":{"id":"Bo_8hMJKeRyz"}},{"cell_type":"code","source":"def downsample(filters, size, strides=2):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = tf.keras.Sequential()\n    result.add(L.Conv2D(filters, size, strides=strides, padding='same',\n                        kernel_initializer=initializer, use_bias=False))\n    result.add(tf.keras.layers.BatchNormalization())\n    result.add(L.LeakyReLU())\n\n    return result\n\ndef upsample(filters, size, apply_dropout=False, strides=2):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = tf.keras.Sequential()\n    result.add(L.Conv2DTranspose(filters, size, strides=strides, padding='same',\n                                 kernel_initializer=initializer, use_bias=False))\n    result.add(tf.keras.layers.BatchNormalization())\n    if apply_dropout:\n        result.add(L.Dropout(0.5))\n    result.add(L.ReLU())\n\n    return result\n","metadata":{"id":"rR9-xbg-I4K0","executionInfo":{"status":"ok","timestamp":1711044339040,"user_tz":-120,"elapsed":14,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Generator","metadata":{"id":"tgF6TAb8eXe5"}},{"cell_type":"code","source":"def generator_fn():\n    inputs = L.Input(shape=[HEIGHT, WIDTH, CHANNELS])\n\n    down_stack = [\n        downsample(64, 4), # (bs, 160, 160, 64)\n        downsample(128, 4), # (bs, 80, 80, 128)\n        downsample(256, 4), # (bs, 40, 40, 256)\n    ]\n\n    up_stack = [\n        upsample(128, 4), # (bs, 80, 80, 128)\n        upsample(64, 4), # (bs, 160, 160, 64)\n    ]\n\n    last = L.Conv2DTranspose(OUTPUT_CHANNELS, 4,\n                                  strides=2,\n                                  padding='same',\n                                  kernel_initializer=tf.random_normal_initializer(0., 0.02),\n                                  activation='tanh') # (bs, 320, 320, 3)\n\n    x = inputs\n\n    # Downsampling through the model\n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips.append(x)\n\n    skips = reversed(skips[:-1])\n\n    # Upsampling and establishing the skip connections\n    for up, skip in zip(up_stack, skips):\n        x = up(x)\n        x = L.Concatenate()([x, skip])\n\n    # add last layer\n    x = last(x)\n\n    return Model(inputs=inputs, outputs=x)\n","metadata":{"id":"Q8HRsIjQN-g0","executionInfo":{"status":"ok","timestamp":1711044339040,"user_tz":-120,"elapsed":14,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"generator_fn().summary()","metadata":{"id":"Wb-ChQmTtOJS","outputId":"d79b27f3-fd0e-4c08-e91d-8a574c9f3ab3","executionInfo":{"status":"ok","timestamp":1711044339040,"user_tz":-120,"elapsed":14,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Discriminator","metadata":{"id":"aNY0xltQefB7"}},{"cell_type":"code","source":"def discriminator_fn():\n    initializer = tf.random_normal_initializer(0., 0.02)\n\n    inputs = L.Input(shape=[HEIGHT, WIDTH, CHANNELS], name='input_image')\n    x = inputs\n\n    down1 = downsample(64, 4, strides=2)(x) # (bs, 160, 160, 64)\n    down2 = downsample(128, 4, strides=2)(down1) # (bs, 80, 80, 128)\n\n    zero_pad1 = L.ZeroPadding2D()(down2) # (bs, 82, 82, 128)\n    conv = L.Conv2D(128, 4, strides=1,\n                    kernel_initializer=initializer,\n                    use_bias=False)(zero_pad1) # (bs, 79, 79, 128)\n\n    norm1 = tf.keras.layers.BatchNormalization()(conv)\n    leaky_relu = L.LeakyReLU()(norm1)\n\n    zero_pad2 = L.ZeroPadding2D()(leaky_relu) # (bs, 81, 81, 128)\n\n    last = L.Conv2D(1, 4, strides=1, kernel_initializer=initializer)(zero_pad2) # (bs, 78, 78, 1)\n\n    return Model(inputs=inputs, outputs=last)\n","metadata":{"id":"D8p5N_q6ODl4","executionInfo":{"status":"ok","timestamp":1711044339040,"user_tz":-120,"elapsed":10,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"discriminator_fn().summary()","metadata":{"id":"U8PL4MaTsnJW","outputId":"4f35492d-316e-4607-d128-c5ddd002b3f7","executionInfo":{"status":"ok","timestamp":1711044339041,"user_tz":-120,"elapsed":10,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Build the CycleGAN model","metadata":{"id":"cO6vS12cjCBD"}},{"cell_type":"code","source":"class CycleGan(Model):\n    def __init__(\n        self,\n        monet_generator,\n        photo_generator,\n        monet_discriminator,\n        photo_discriminator,\n        lambda_cycle=10,\n    ):\n        super(CycleGan, self).__init__()\n        self.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 call(self, inputs):\n        x = inputs\n        for layer in self.dense_layers:\n            x = layer(x)\n        return x\n\n    def train_step(self, batch_data):\n        real_monet, real_photo = batch_data\n\n        # The process in following function is:\n        #\n        # 1. Pass real images through the generators and get the generated images\n        # 2. Pass the generated images back to the generators to check if we\n        #    we can predict the original image from the generated image.\n        # 3. Do an identity mapping of the real images using the generators.\n        # 4. Pass the generated images in 1) to the corresponding discriminators.\n        # 5. Calculate the generators total loss (adverserial + cycle + identity)\n        # 6. Calculate the discriminators loss\n        # 7. Update the weights of the generators\n        # 8. Update the weights of the discriminators\n        # 9. Return the losses in a dictionary\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":{"id":"IqwlZK_lOHZ3","executionInfo":{"status":"ok","timestamp":1711044339041,"user_tz":-120,"elapsed":6,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Train the model","metadata":{"id":"5vsXhs_Bj6Zo"}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import losses\n# Discriminator loss {0: fake, 1: real} (The discriminator loss outputs the average of the real and generated loss)\ndef 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\ndef 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)\ndef 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))\ndef 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":{"id":"Xtkxi78iUcOb","executionInfo":{"status":"ok","timestamp":1711044339041,"user_tz":-120,"elapsed":6,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create GAN\nmonet_generator = generator_fn() # transforms photos to Monet-esque paintings\nphoto_generator = generator_fn() # transforms Monet paintings to be more like photos\n\nmonet_discriminator = discriminator_fn() # differentiates real Monet paintings and generated Monet paintings\nphoto_discriminator = discriminator_fn() # differentiates real photos and generated photos\n\nbase_model = CycleGan(monet_generator, photo_generator, monet_discriminator, photo_discriminator)\nbase_model.compile(m_gen_optimizer=optimizers.Adam(2e-4, beta_1=0.5),\n                  p_gen_optimizer=optimizers.Adam(2e-4, beta_1=0.5),\n                  m_disc_optimizer=optimizers.Adam(2e-4, beta_1=0.5),\n                  p_disc_optimizer=optimizers.Adam(2e-4, beta_1=0.5),\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\n\nhistory = base_model.fit(get_gan_dataset(MONET_FILENAMES, PHOTO_FILENAMES, batch_size=BATCH_SIZE),\n                        steps_per_epoch=(300//BATCH_SIZE),\n                        epochs=EPOCHS,\n                        verbose=2).history","metadata":{"id":"LnolsiGEUqRO","outputId":"3d1590c9-d2db-4923-e5f2-0da60860cec7","executionInfo":{"status":"ok","timestamp":1711044413660,"user_tz":-120,"elapsed":74625,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Save the model","metadata":{"id":"PE7lpvCyolO0"}},{"cell_type":"code","source":"base_model.m_gen.save(f'{GOOGLE_DRIVE_PATH}/MyDrive/DL/gan-getting-started/models/base_model',save_format='tf')","metadata":{"id":"RbRTjHYLohFP","outputId":"80e88fbb-7b1f-4182-da9b-c4724cea509e","executionInfo":{"status":"ok","timestamp":1711044415867,"user_tz":-120,"elapsed":2220,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Visualize loss","metadata":{"id":"_wyz9GGjxm9p"}},{"cell_type":"code","source":"keys = [\"photo_gen_loss\", \"photo_disc_loss\", \"monet_gen_loss\", \"monet_disc_loss\"]\n\nepoch_history = {\"photo\": {}, \"monet\": {}}\n\nfor key in keys:\n    img_type, model, _ = key.split(\"_\")\n    epoch_history[img_type][model] = np.array(\n        [tf.reduce_mean(loss).numpy() for loss in history[key]]\n    )\ntitles = {\"photo\": \"Photo\", \"monet\": \"Monet\"}\n\nf, axs = plt.subplots(1, len(epoch_history), figsize=(10, 5))\n\nfor (key, losses), ax in zip(epoch_history.items(), axs.flatten()):\n    ax.plot(epoch_history[key][\"gen\"], label=\"Generator\")\n    ax.plot(epoch_history[key][\"disc\"], label=\"Discriminator\")\n    ax.set_xlabel(\"Epochs\")\n    ax.set_ylabel(\"Loss\")\n    ax.set_title(titles[key])\n    ax.legend()\n\nplt.tight_layout()","metadata":{"id":"sygfZ01pxivq","executionInfo":{"status":"ok","timestamp":1711044415867,"user_tz":-120,"elapsed":5,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"ffa35e53-e38a-4280-a89f-2887fa2d0ea4"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Visualize predictions","metadata":{"id":"UbIODLlbdZIf"}},{"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":{"id":"xKECbMtdcvwS","executionInfo":{"status":"ok","timestamp":1711044415868,"user_tz":-120,"elapsed":5,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display_generated_samples(load_dataset(PHOTO_FILENAMES).batch(1), monet_generator, 8)\n","metadata":{"id":"sPwUGBYJtngt","executionInfo":{"status":"ok","timestamp":1711044426431,"user_tz":-120,"elapsed":10568,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"93143c5c-36d6-49f1-a4a9-f01a9c3d693d"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Make predictions","metadata":{"id":"P__oEiQjdnWY"}},{"cell_type":"code","source":"from tqdm.notebook import tqdm\nimport zipfile\nfrom PIL import Image\nimport io\n\nwith tqdm(load_dataset(PHOTO_FILENAMES).batch(1), unit=\"imgs\", total=7038) as ds, zipfile.ZipFile(f\"{PROJECT_PATH}/images.zip\", \"w\") as zf:\n    for idx, img in enumerate(ds):\n        prediction = monet_generator(img, training=False)[0].numpy()\n        prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n        img = Image.fromarray(prediction)\n        img.resize(size=(256,256))\n        dest = io.BytesIO()\n        img.save(dest, format=\"JPEG\")\n        zf.writestr(f\"{idx + 1}.jpg\", dest.getvalue())","metadata":{"id":"gxBRBg_eyuHB","executionInfo":{"status":"ok","timestamp":1711044547021,"user_tz":-120,"elapsed":120604,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"302ee4e0-ddcc-4ddb-ae92-7f5b7b0ea3a2"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#Experiment 1\nExperiment one introduces a variation to the base model by incorporating an additional set of downsample and upsample layers to the generator architecture. The objective is to evaluate whether augmenting the generator network with more layers enhances the quality of the generated images. These new layers maintain the same number of filters and kernel size as the existing layers.\n\nA key technical challenge in this experiment was striking a balance between expanding the generator network with additional layers and mitigating the risk of overfitting. The loss function employed for the generator remained consistent with the base model, utilizing binary cross-entropy loss. Furthermore, Adam optimizer was utilized with a learning rate set to 0.0002.\n","metadata":{"id":"oklu8WMztYxL"}},{"cell_type":"markdown","source":"##Build Model","metadata":{"id":"cxwhyxMBtYxM"}},{"cell_type":"markdown","source":"###Generator","metadata":{"id":"G_Iufg7DtYxM"}},{"cell_type":"code","source":"def generator_fn():\n    inputs = L.Input(shape=[HEIGHT, WIDTH, CHANNELS])\n\n    down_stack = [\n        downsample(64, 4), # (bs, 160, 160, 64)\n        downsample(128, 4), # (bs, 80, 80, 128)\n        downsample(256, 4), # (bs, 40, 40, 256)\n        downsample(512, 4), # (bs, 20, 40, 512)\n        downsample(512, 4), # (bs, 10, 10, 512)\n        downsample(512, 4), # (bs, 5, 5, 512)\n    ]\n\n    up_stack = [\n        upsample(512, 4, apply_dropout=True), # (bs, 10, 10, 1024)\n        upsample(512, 4, apply_dropout=True), # (bs, 20, 20, 1024)\n        upsample(256, 4), # (bs, 40, 40, 512)\n        upsample(128, 4), # (bs, 80, 80, 256)\n        upsample(64, 4), # (bs, 160, 160, 128)\n    ]\n\n    last = L.Conv2DTranspose(OUTPUT_CHANNELS, 4,\n                                  strides=2,\n                                  padding='same',\n                                  kernel_initializer=tf.random_normal_initializer(0., 0.02),\n                                  activation='tanh') # (bs, 320, 320, 3)\n\n    x = inputs\n\n    # Downsampling through the model\n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips.append(x)\n\n    skips = reversed(skips[:-1])\n\n    # Upsampling and establishing the skip connections\n    for up, skip in zip(up_stack, skips):\n        x = up(x)\n        x = L.Concatenate()([x, skip])\n\n    # add last layer\n    x = last(x)\n\n    return Model(inputs=inputs, outputs=x)","metadata":{"id":"JlcRFvLRtYxN","executionInfo":{"status":"ok","timestamp":1711044547021,"user_tz":-120,"elapsed":28,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"generator_fn().summary()","metadata":{"id":"Km-mri5etYxN","executionInfo":{"status":"ok","timestamp":1711044547021,"user_tz":-120,"elapsed":27,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"e0c5df08-ace1-4822-9c85-2985b2c34bd2"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Discriminator","metadata":{"id":"nWp-9HThtYxN"}},{"cell_type":"code","source":"def discriminator_fn():\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    inputs = L.Input(shape=[HEIGHT, WIDTH, CHANNELS], name='input_image')\n    x = inputs\n\n    down1 = downsample(64, 4)(x) # (bs, 160, 160, 64)\n    down2 = downsample(128, 4)(down1) # (bs, 80, 80, 128)\n    down3 = downsample(256, 4)(down2) # (bs, 40, 40, 256)\n    down4 = downsample(256, 4)(down3) # (bs, 20, 20, 512)\n\n    zero_pad1 = L.ZeroPadding2D()(down4) # (bs, 22, 22, 512)\n    conv = L.Conv2D(512, 4, strides=1,\n                    kernel_initializer=initializer,\n                    use_bias=False)(zero_pad1) # (bs, 19, 19, 512)\n\n    norm1 = tf.keras.layers.BatchNormalization(gamma_initializer=gamma_init)(conv)\n\n    leaky_relu = L.LeakyReLU()(norm1)\n\n    zero_pad2 = L.ZeroPadding2D()(leaky_relu) # (bs, 21, 21, 512)\n\n    last = L.Conv2D(1, 4, strides=1, kernel_initializer=initializer)(zero_pad2) # (bs, 18, 18, 1)\n\n    return Model(inputs=inputs, outputs=last)\n","metadata":{"id":"_ZgPUQBBtYxO","executionInfo":{"status":"ok","timestamp":1711044547021,"user_tz":-120,"elapsed":19,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"discriminator_fn().summary()","metadata":{"id":"XSlm-qnUtYxO","executionInfo":{"status":"ok","timestamp":1711044547021,"user_tz":-120,"elapsed":19,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"789bff19-1bb2-4967-a351-5fd0b91157d3"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Train the model","metadata":{"id":"p-r0r8aatYxP"}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import losses\n\n# Create GAN\nmonet_generator = generator_fn() # transforms photos to Monet-esque paintings\nphoto_generator = generator_fn() # transforms Monet paintings to be more like photos\n\nmonet_discriminator = discriminator_fn() # differentiates real Monet paintings and generated Monet paintings\nphoto_discriminator = discriminator_fn() # differentiates real photos and generated photos\n\nexp_one = CycleGan(monet_generator, photo_generator, monet_discriminator, photo_discriminator)\n\nexp_one.compile(m_gen_optimizer=optimizers.Adam(2e-4, beta_1=0.5),\n                  p_gen_optimizer=optimizers.Adam(2e-4, beta_1=0.5),\n                  m_disc_optimizer=optimizers.Adam(2e-4, beta_1=0.5),\n                  p_disc_optimizer=optimizers.Adam(2e-4, beta_1=0.5),\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\n\nhistory = exp_one.fit(get_gan_dataset(MONET_FILENAMES, PHOTO_FILENAMES, batch_size=BATCH_SIZE),\n                        steps_per_epoch=(300//BATCH_SIZE),\n                        epochs=EPOCHS,\n                        verbose=2).history","metadata":{"id":"c6TILA0ztYxP","executionInfo":{"status":"ok","timestamp":1711044732538,"user_tz":-120,"elapsed":185530,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"77abc196-2a68-48db-fc87-abca5e627cdf"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Save the model","metadata":{"id":"u1xySttCo3Ae"}},{"cell_type":"code","source":"exp_one.m_gen.save(f'{GOOGLE_DRIVE_PATH}/MyDrive/DL/gan-getting-started/exp_one')","metadata":{"id":"c9bHg6zlo3Ae","executionInfo":{"status":"ok","timestamp":1711044736244,"user_tz":-120,"elapsed":3718,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"8d4e8ec6-f3ac-4f53-c5de-54a35924b6fb"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Visualize loss","metadata":{"id":"51wRA7f3b3k3"}},{"cell_type":"code","source":"keys = [\"photo_gen_loss\", \"photo_disc_loss\", \"monet_gen_loss\", \"monet_disc_loss\"]\n\nepoch_history = {\"photo\": {}, \"monet\": {}}\n\nfor key in keys:\n    img_type, model, _ = key.split(\"_\")\n    epoch_history[img_type][model] = np.array(\n        [tf.reduce_mean(loss).numpy() for loss in history[key]]\n    )\ntitles = {\"photo\": \"Photo\", \"monet\": \"Monet\"}\n\nf, axs = plt.subplots(1, len(epoch_history), figsize=(10, 5))\n\nfor (key, losses), ax in zip(epoch_history.items(), axs.flatten()):\n    ax.plot(epoch_history[key][\"gen\"], label=\"Generator\")\n    ax.plot(epoch_history[key][\"disc\"], label=\"Discriminator\")\n    ax.set_xlabel(\"Epochs\")\n    ax.set_ylabel(\"Loss\")\n    ax.set_title(titles[key])\n    ax.legend()\n\nplt.tight_layout()","metadata":{"id":"1kTK3i2nb3k4","executionInfo":{"status":"ok","timestamp":1711044737763,"user_tz":-120,"elapsed":1529,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"371cb1de-51f9-4352-e8ce-325483314019"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Visualize predictions","metadata":{"id":"DqY2sKq_tYxP"}},{"cell_type":"code","source":"display_generated_samples(load_dataset(PHOTO_FILENAMES).batch(1), monet_generator, 8)\n","metadata":{"id":"dC-s7RwitYxQ","executionInfo":{"status":"ok","timestamp":1711044748949,"user_tz":-120,"elapsed":11189,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"44ed6a54-3d21-460e-cd10-09bfc2fd281f"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#Experiment 2\nIn the second experiment, we retained the architecture from the first experiment while adjusting certain hyperparameters to optimize our model:\n\n\n\n*   Learning rate: We increased the learning rate to 0.001. This adjustment was made to address the possibility of the optimization algorithm becoming trapped in a local minimum, hindering it from reaching the global minimum. By elevating the learning rate, we aim to enable the algorithm to escape local minima and explore other regions within the parameter space.\n*   Beta parameter in Adam optimizer: We augmented the beta parameter in the Adam optimizer. Increasing this parameter can enhance the optimizer's ability to adapt to the geometry of the loss surface, facilitating faster convergence towards the optimal solution.\n* Batch size: We decreased the batch size to 10. Smaller batch sizes can expedite model convergence by enabling more frequent weight updates. This leads to swifter adjustments in the model's weights, thereby hastening the convergence process.\n\nBy modifying these hyperparameters, we aimed to improve the efficiency and effectiveness of our model.\n","metadata":{"id":"r1vsajzEcZtj"}},{"cell_type":"markdown","source":"##Build Model","metadata":{"id":"87fO0Zn-cZtj"}},{"cell_type":"markdown","source":"###Train the model","metadata":{"id":"qcyOmn3hcZtl"}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import losses\n# Create GAN\nmonet_generator = generator_fn() # transforms photos to Monet-esque paintings\nphoto_generator = generator_fn() # transforms Monet paintings to be more like photos\n\nmonet_discriminator = discriminator_fn() # differentiates real Monet paintings and generated Monet paintings\nphoto_discriminator = discriminator_fn() # differentiates real photos and generated photos\n\nexp_two = CycleGan(monet_generator, photo_generator, monet_discriminator, photo_discriminator)\n\nexp_two.compile(m_gen_optimizer=optimizers.Adam(learning_rate=0.001, beta_1=0.9),\n                  p_gen_optimizer=optimizers.Adam(learning_rate=0.001, beta_1=0.9),\n                  m_disc_optimizer=optimizers.Adam(learning_rate=0.001, beta_1=0.9),\n                  p_disc_optimizer=optimizers.Adam(learning_rate=0.001, beta_1=0.9),\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\n\nhistory = exp_two.fit(get_gan_dataset(MONET_FILENAMES, PHOTO_FILENAMES, batch_size=10),\n                        steps_per_epoch=(300//10),\n                        epochs=EPOCHS,\n                        verbose=2).history\n","metadata":{"id":"uAIe8wndcZtl","executionInfo":{"status":"ok","timestamp":1711044911330,"user_tz":-120,"elapsed":162384,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"81b9e421-622e-485f-8163-3c0622580ad6"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###Save the model","metadata":{"id":"7BO5VqKJpK2p"}},{"cell_type":"code","source":"exp_two.m_gen.save(f'{GOOGLE_DRIVE_PATH}/MyDrive/DL/gan-getting-started/exp_two')","metadata":{"id":"kuMf01mUpK2p","executionInfo":{"status":"ok","timestamp":1711044917023,"user_tz":-120,"elapsed":5705,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"75a92dd0-fd6a-475d-9f7c-8f964b89831f"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Visualize loss","metadata":{"id":"13v5mcskcZtm"}},{"cell_type":"markdown","source":"##Visualize predictions","metadata":{"id":"6dWByrhacZtm"}},{"cell_type":"code","source":"display_generated_samples(load_dataset(PHOTO_FILENAMES).batch(1), monet_generator, 8)\n","metadata":{"id":"J_dBLE-VcZtm","executionInfo":{"status":"ok","timestamp":1711044928005,"user_tz":-120,"elapsed":10994,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"541d59a3-9644-4f57-8e75-32bf7bdf5a96"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Make predictions","metadata":{"id":"2mb8dIDgfIRR"}},{"cell_type":"code","source":"from tqdm.notebook import tqdm\nimport zipfile\nfrom PIL import Image\nimport io\n\nwith tqdm(load_dataset(PHOTO_FILENAMES).batch(1), unit=\"imgs\", total=7038) as ds, zipfile.ZipFile(f\"{PROJECT_PATH}/images.zip\", \"w\") as zf:\n    for idx, img in enumerate(ds):\n        prediction = monet_generator(img, training=False)[0].numpy()\n        prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n        img = Image.fromarray(prediction)\n        img.resize(size=(256,256))\n        dest = io.BytesIO()\n        img.save(dest, format=\"JPEG\")\n        zf.writestr(f\"{idx + 1}.jpg\", dest.getvalue())","metadata":{"id":"sm6UY0qTfIRS","executionInfo":{"status":"ok","timestamp":1711045170889,"user_tz":-120,"elapsed":242890,"user":{"displayName":"OMER MAOZ","userId":"05065424840214252684"}},"outputId":"578e358f-de70-41b2-b28c-701f0959723c"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Discussion**\nIn this project, we investigated the effectiveness of Generative Adversarial Networks (GANs) for producing images in the style of Monet. Leveraging a dataset comprising 300 Monet images and 7078 regular images, we embarked on our exploration.\n\nOur strategy involved employing a CycleGAN model, which features two generators and two discriminators. This model underwent training in two stages. Initially, we trained the generators to translate images between domains, followed by the integration of cycle consistency loss in the second stage to enhance the quality of the generated images.\n\nSubsequently, we conducted two experiments to refine our approach. In the first experiment, we enriched the generator architecture by introducing an additional set of downsample and upsample layers. This adjustment resulted in noticeable enhancements in the visual quality of the generated images compared to the base model. In the second experiment, we fine-tuned the hyperparameters, leading to\nfurther improvements in visual quality.\n\nIn summary, our findings indicate that the CycleGAN model hold promise for generating Monet-style images ,furthermore the CycleGAN model demonstrated superior visual quality in our experiments.\n","metadata":{"id":"hzdPnebaKzLa"}}]}