{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"},{"sourceId":1457241,"sourceType":"datasetVersion","datasetId":854421}],"dockerImageVersionId":30786,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Cyclic Gans Art Generator ","metadata":{}},{"cell_type":"markdown","source":"## Setting up the file ","metadata":{}},{"cell_type":"code","source":"tf.keras.backend.clear_session()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:19.945877Z","iopub.execute_input":"2024-11-16T14:01:19.94628Z","iopub.status.idle":"2024-11-16T14:01:20.237932Z","shell.execute_reply.started":"2024-11-16T14:01:19.94624Z","shell.execute_reply":"2024-11-16T14:01:20.236899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf \nfrom tensorflow import keras \nfrom tensorflow.keras import layers \n# import tensorflow_addons as tfa\nfrom kaggle_datasets import KaggleDatasets\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns \nsns.set()\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print('Device:', tpu.master())\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nexcept:\n    strategy = tf.distribute.get_strategy()\nprint('Number of replicas:', strategy.num_replicas_in_sync)\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n    \nprint(tf.__version__)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.243643Z","iopub.execute_input":"2024-11-16T14:01:20.243969Z","iopub.status.idle":"2024-11-16T14:01:20.254456Z","shell.execute_reply.started":"2024-11-16T14:01:20.243936Z","shell.execute_reply":"2024-11-16T14:01:20.253572Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Importing the dataset using TFRecords since theres alot of data","metadata":{}},{"cell_type":"code","source":"import os\nprint(os.listdir('/kaggle/input'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.255616Z","iopub.execute_input":"2024-11-16T14:01:20.256529Z","iopub.status.idle":"2024-11-16T14:01:20.276426Z","shell.execute_reply.started":"2024-11-16T14:01:20.256469Z","shell.execute_reply":"2024-11-16T14:01:20.27543Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path('gan-getting-started')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.27905Z","iopub.execute_input":"2024-11-16T14:01:20.279425Z","iopub.status.idle":"2024-11-16T14:01:20.57025Z","shell.execute_reply.started":"2024-11-16T14:01:20.279382Z","shell.execute_reply":"2024-11-16T14:01:20.569341Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"MONET_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nprint('Monet TFRecord Files:', len(MONET_FILENAMES))\n\nPHOTO_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\nprint('Photo TFRecord Files:', len(PHOTO_FILENAMES))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.571965Z","iopub.execute_input":"2024-11-16T14:01:20.572635Z","iopub.status.idle":"2024-11-16T14:01:20.671386Z","shell.execute_reply.started":"2024-11-16T14:01:20.57258Z","shell.execute_reply":"2024-11-16T14:01:20.670359Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Parsing the TFrecord","metadata":{}},{"cell_type":"code","source":"def parse_tfrecord(example):\n    # Define features in TFRecord\n    feature_description = {\n        'image': tf.io.FixedLenFeature([], tf.string),  # Serialized image data\n    }\n    parsed_example = tf.io.parse_single_example(example, feature_description)\n    \n    # Decode the image\n    image = tf.io.decode_jpeg(parsed_example['image'], channels=3)\n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.672841Z","iopub.execute_input":"2024-11-16T14:01:20.673231Z","iopub.status.idle":"2024-11-16T14:01:20.679191Z","shell.execute_reply.started":"2024-11-16T14:01:20.673188Z","shell.execute_reply":"2024-11-16T14:01:20.678186Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preprocessing Images, normalizing it to [-1,1] scale ideal for GANS.","metadata":{}},{"cell_type":"code","source":"def preprocess_image(image, img_size=128):\n    # Resize image\n    image = tf.image.resize(image, [img_size, img_size])\n    \n    # Normalize to [-1, 1] for GANs\n    image = (image / 127.5) - 1.0\n\n    return image\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.680462Z","iopub.execute_input":"2024-11-16T14:01:20.680884Z","iopub.status.idle":"2024-11-16T14:01:20.698625Z","shell.execute_reply.started":"2024-11-16T14:01:20.68084Z","shell.execute_reply":"2024-11-16T14:01:20.69777Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_and_preprocess_dataset(filenames, batch_size=32, img_size=128):\n    dataset = tf.data.TFRecordDataset(filenames)\n    \n    # Map parsing and preprocessing\n    dataset = dataset.map(\n        lambda x: preprocess_image(parse_tfrecord(x), img_size),\n        num_parallel_calls=tf.data.AUTOTUNE\n    )\n    \n    # Shuffle, batch, and prefetch for performance\n    dataset = dataset.shuffle(buffer_size=1000).batch(batch_size).prefetch(tf.data.AUTOTUNE)\n    \n    return dataset\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.699812Z","iopub.execute_input":"2024-11-16T14:01:20.700845Z","iopub.status.idle":"2024-11-16T14:01:20.718239Z","shell.execute_reply.started":"2024-11-16T14:01:20.700797Z","shell.execute_reply":"2024-11-16T14:01:20.717203Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"monet_dataset = load_and_preprocess_dataset(MONET_FILENAMES,batch_size=32)\nphotos_dataset = load_and_preprocess_dataset(PHOTO_FILENAMES,batch_size=32)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.719385Z","iopub.execute_input":"2024-11-16T14:01:20.719779Z","iopub.status.idle":"2024-11-16T14:01:20.901549Z","shell.execute_reply.started":"2024-11-16T14:01:20.719735Z","shell.execute_reply":"2024-11-16T14:01:20.900539Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"example_monet = next(iter(monet_dataset))\nexample_photo = next(iter(photos_dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:20.904756Z","iopub.execute_input":"2024-11-16T14:01:20.905072Z","iopub.status.idle":"2024-11-16T14:01:22.830298Z","shell.execute_reply.started":"2024-11-16T14:01:20.905039Z","shell.execute_reply":"2024-11-16T14:01:22.829242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.subplot(121)\nplt.title('Monet')\nplt.imshow(example_monet[0] * 0.5 + 0.5)\n\nplt.subplot(122)\nplt.title('Photo')\nplt.imshow(example_photo[0] * 0.5 + 0.5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:22.831641Z","iopub.execute_input":"2024-11-16T14:01:22.831969Z","iopub.status.idle":"2024-11-16T14:01:23.485818Z","shell.execute_reply.started":"2024-11-16T14:01:22.831936Z","shell.execute_reply":"2024-11-16T14:01:23.484782Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Creating a Generator \nSo a generator will map the images from domain A (monet paintings) to domain B (photos dataset).\nA generator is like a U-Net model built with residual blocks for better feature preservation.","metadata":{}},{"cell_type":"code","source":"def build_generator():\n    inputs = layers.Input(shape=(128, 128, 3))\n\n    # Encoder (downsampling)\n    x = layers.Conv2D(64, (4, 4), strides=2, padding=\"same\")(inputs)\n    x = layers.ReLU()(x)\n    x = layers.Conv2D(128, (4, 4), strides=2, padding=\"same\")(x)\n    x = layers.ReLU()(x)\n\n    # Residual blocks\n    for _ in range(6):  # Add 6 residual blocks\n        res = x\n        x = layers.Conv2D(128, (3, 3), padding=\"same\")(x)\n        x = layers.ReLU()(x)\n        x = layers.Conv2D(128, (3, 3), padding=\"same\")(x)\n        x = layers.Add()([x, res])\n\n    # Decoder (upsampling)\n    x = layers.Conv2DTranspose(64, (4, 4), strides=2, padding=\"same\")(x)\n    x = layers.ReLU()(x)\n    x = layers.Conv2DTranspose(3, (4, 4), strides=2, padding=\"same\")(x)\n    outputs = layers.Activation(\"tanh\")(x)\n\n    return tf.keras.Model(inputs, outputs)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:23.487244Z","iopub.execute_input":"2024-11-16T14:01:23.487664Z","iopub.status.idle":"2024-11-16T14:01:23.499547Z","shell.execute_reply.started":"2024-11-16T14:01:23.487619Z","shell.execute_reply":"2024-11-16T14:01:23.498519Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Creating a Discriminator \nIts a patchGAN that focuses on small patches and features for realism of the images. ","metadata":{}},{"cell_type":"code","source":"def build_discriminator():\n    inputs = layers.Input(shape=(128, 128, 3))\n\n    # Convolutional layers\n    x = layers.Conv2D(64, (4, 4), strides=2, padding=\"same\")(inputs)\n    x = layers.LeakyReLU(0.2)(x)\n    x = layers.Conv2D(128, (4, 4), strides=2, padding=\"same\")(x)\n    x = layers.LeakyReLU(0.2)(x)\n\n    # Final layer\n    x = layers.Conv2D(1, (4, 4), padding=\"same\")(x)\n    outputs = layers.Activation(\"sigmoid\")(x)\n\n    return tf.keras.Model(inputs, outputs)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:23.500844Z","iopub.execute_input":"2024-11-16T14:01:23.501206Z","iopub.status.idle":"2024-11-16T14:01:23.513625Z","shell.execute_reply.started":"2024-11-16T14:01:23.501163Z","shell.execute_reply":"2024-11-16T14:01:23.512753Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Building the CycleGAN model ","metadata":{}},{"cell_type":"code","source":"def build_cyclegan(generator_a_to_b, generator_b_to_a, discriminator_a, discriminator_b):\n    # Inputs\n    input_a = layers.Input(shape=(128, 128, 3))  # Monet image\n    input_b = layers.Input(shape=(128, 128, 3))  # Photo\n\n    # Generated images\n    fake_b = generator_a_to_b(input_a)  # Monet -> Photo\n    fake_a = generator_b_to_a(input_b)  # Photo -> Monet\n\n    # Cycled images\n    cycled_a = generator_b_to_a(fake_b)  # Monet -> Photo -> Monet\n    cycled_b = generator_a_to_b(fake_a)  # Photo -> Monet -> Photo\n\n    # Discriminators\n    valid_a = discriminator_a(fake_a)  # Discriminator for Monet\n    valid_b = discriminator_b(fake_b)  # Discriminator for Photos\n\n    return tf.keras.Model(inputs=[input_a, input_b], outputs=[fake_a, fake_b, cycled_a, cycled_b, valid_a, valid_b])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:23.514975Z","iopub.execute_input":"2024-11-16T14:01:23.515326Z","iopub.status.idle":"2024-11-16T14:01:23.531278Z","shell.execute_reply.started":"2024-11-16T14:01:23.515285Z","shell.execute_reply":"2024-11-16T14:01:23.530455Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Defining Loss Functions ","metadata":{}},{"cell_type":"code","source":"def adversarial_loss(y_true, y_pred):\n    return tf.keras.losses.MeanSquaredError()(y_true, y_pred)\n\ndef cycle_consistency_loss(real, cycled):\n    return tf.keras.losses.MeanAbsoluteError()(real, cycled)\n\ndef identity_loss(real, same):\n    return tf.keras.losses.MeanAbsoluteError()(real, same)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:23.532738Z","iopub.execute_input":"2024-11-16T14:01:23.533091Z","iopub.status.idle":"2024-11-16T14:01:23.548987Z","shell.execute_reply.started":"2024-11-16T14:01:23.533051Z","shell.execute_reply":"2024-11-16T14:01:23.548132Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training the CycleGan Model ","metadata":{}},{"cell_type":"code","source":"# Hyperparameters\nEPOCHS = 200\nBATCH_SIZE = 1  # CycleGANs are typically trained with a batch size of 1\nLEARNING_RATE = 2e-4\nLAMBDA_CYCLE = 10.0  # Weight for cycle consistency loss\nLAMBDA_IDENTITY = 0.5  # Weight for identity loss\n\n# Build models\ngenerator_a_to_b = build_generator()  # Monet -> Photo\ngenerator_b_to_a = build_generator()  # Photo -> Monet\ndiscriminator_a = build_discriminator()  # For Monet\ndiscriminator_b = build_discriminator()  # For Photos\n\n\n# Separate optimizers for each model\ngen_a_to_b_optimizer = tf.keras.optimizers.Adam(LEARNING_RATE, beta_1=0.5)\ngen_b_to_a_optimizer = tf.keras.optimizers.Adam(LEARNING_RATE, beta_1=0.5)\ndisc_a_optimizer = tf.keras.optimizers.Adam(LEARNING_RATE, beta_1=0.5)\ndisc_b_optimizer = tf.keras.optimizers.Adam(LEARNING_RATE, beta_1=0.5)\n\n\n# Training loop\nfor epoch in range(EPOCHS):\n    for real_a, real_b in zip(monet_dataset, photos_dataset):\n        with tf.GradientTape(persistent=True) as tape:\n            # Generate fake images\n            fake_b = generator_a_to_b(real_a)\n            fake_a = generator_b_to_a(real_b)\n\n            # Cycle back\n            cycled_a = generator_b_to_a(fake_b)\n            cycled_b = generator_a_to_b(fake_a)\n\n            # Identity mapping\n            same_a = generator_b_to_a(real_a)\n            same_b = generator_a_to_b(real_b)\n\n            # Discriminator outputs\n            valid_a = discriminator_a(fake_a)\n            valid_b = discriminator_b(fake_b)\n            real_a_valid = discriminator_a(real_a)\n            real_b_valid = discriminator_b(real_b)\n\n            # Losses\n            gen_a_to_b_loss = adversarial_loss(tf.ones_like(valid_b), valid_b)\n            gen_b_to_a_loss = adversarial_loss(tf.ones_like(valid_a), valid_a)\n\n            cycle_loss = (\n                cycle_consistency_loss(real_a, cycled_a)\n                + cycle_consistency_loss(real_b, cycled_b)\n            ) * LAMBDA_CYCLE\n\n            identity_loss_a = identity_loss(real_a, same_a) * LAMBDA_IDENTITY\n            identity_loss_b = identity_loss(real_b, same_b) * LAMBDA_IDENTITY\n\n            total_gen_loss = (\n                gen_a_to_b_loss\n                + gen_b_to_a_loss\n                + cycle_loss\n                + identity_loss_a\n                + identity_loss_b\n            )\n\n            disc_a_loss = adversarial_loss(tf.ones_like(real_a_valid), real_a_valid) + \\\n                          adversarial_loss(tf.zeros_like(valid_a), valid_a)\n            disc_b_loss = adversarial_loss(tf.ones_like(real_b_valid), real_b_valid) + \\\n                          adversarial_loss(tf.zeros_like(valid_b), valid_b)\n\n            # Update generators\n            gen_a_to_b_grads = tape.gradient(total_gen_loss, generator_a_to_b.trainable_variables)\n            gen_b_to_a_grads = tape.gradient(total_gen_loss, generator_b_to_a.trainable_variables)\n\n            gen_a_to_b_optimizer.apply_gradients(zip(gen_a_to_b_grads, generator_a_to_b.trainable_variables))\n            gen_b_to_a_optimizer.apply_gradients(zip(gen_b_to_a_grads, generator_b_to_a.trainable_variables))\n\n            # Update discriminators\n            disc_a_grads = tape.gradient(disc_a_loss, discriminator_a.trainable_variables)\n            disc_b_grads = tape.gradient(disc_b_loss, discriminator_b.trainable_variables)\n\n            disc_a_optimizer.apply_gradients(zip(disc_a_grads, discriminator_a.trainable_variables))\n            disc_b_optimizer.apply_gradients(zip(disc_b_grads, discriminator_b.trainable_variables))\n\n\n    print(f\"Epoch {epoch+1}/{EPOCHS} completed.\")\n    # checkpoint = tf.train.Checkpoint(generator=generator_a_to_b, optimizer=gen_optimizer)\n    # checkpoint.save('./checkpoints/my_checkpoint')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:01:23.551464Z","iopub.execute_input":"2024-11-16T14:01:23.551836Z","iopub.status.idle":"2024-11-16T14:55:30.129038Z","shell.execute_reply.started":"2024-11-16T14:01:23.551793Z","shell.execute_reply":"2024-11-16T14:55:30.127986Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Save Photo → Monet generator\ngenerator_b_to_a.save('generator_b_to_a.h5')\n\n# Save Monet → Photo generator\ngenerator_a_to_b.save('generator_a_to_b.h5')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:55:30.130459Z","iopub.execute_input":"2024-11-16T14:55:30.13089Z","iopub.status.idle":"2024-11-16T14:55:30.246461Z","shell.execute_reply.started":"2024-11-16T14:55:30.130846Z","shell.execute_reply":"2024-11-16T14:55:30.245551Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport zipfile\nimport tensorflow as tf\nfrom tensorflow.keras.utils import array_to_img\n\n# Directory to save generated images temporarily\ntemp_dir = \"../tmp/monet_images\"\nos.makedirs(temp_dir, exist_ok=True)\n\n# Define number of images to generate\nNUM_IMAGES = 8000  # Adjust as needed between 7000 and 10000\n\n# Generate and save Monet-style images\nfor i, photo_batch in enumerate(photos_dataset.take(NUM_IMAGES)):\n    # Ensure batch size is 1 for individual image generation\n    photo = tf.expand_dims(photo_batch[0], axis=0)  # Add batch dimension\n    monet_image = generator_b_to_a(photo, training=False)  # Generate Monet-style image\n    monet_image = array_to_img((monet_image[0] + 1.0) * 127.5)  # Denormalize to [0, 255]\n    \n    # Save the image in the temporary directory\n    monet_image.save(f\"{temp_dir}/monet_{i + 1}.jpg\")\n    print(f\"Saved: monet_{i + 1}.jpg\")\n    \n    # Stop if we've generated enough images\n    if i + 1 >= NUM_IMAGES:\n        break\n\n# Create a zip file containing all generated images\noutput_zip = \"images.zip\"\nwith zipfile.ZipFile(output_zip, 'w') as zf:\n    for root, _, files in os.walk(temp_dir):\n        for file in files:\n            zf.write(os.path.join(root, file), arcname=file)  # Add to zip, without folder structure\n\nprint(f\"Zip file {output_zip} created successfully.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:55:30.247598Z","iopub.execute_input":"2024-11-16T14:55:30.247888Z","iopub.status.idle":"2024-11-16T14:55:39.190757Z","shell.execute_reply.started":"2024-11-16T14:55:30.247856Z","shell.execute_reply":"2024-11-16T14:55:39.189791Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Iterate over a few images in the photo dataset\nfor photo_batch in photos_dataset.take(3):  # Take 5 batches of photos\n    # Extract a single photo tensor from the batch\n    photo = photo_batch[0]  \n    photo = tf.expand_dims(photo, axis=0)  # Add batch dimension for generator\n\n    # Generate Monet-style image using the generator\n    monet_image = generator_b_to_a(photo, training=False)  \n\n    # Remove batch dimension and denormalize for visualization\n    photo = (photo[0] + 1.0) / 2.0  \n    monet_image = (monet_image[0] + 1.0) / 2.0  \n\n    # Plot original photo and Monet-style image\n    plt.figure(figsize=(10, 5))\n    \n  \n    plt.subplot(1, 2, 1)\n    plt.imshow(photo)\n    plt.title(\"Original Photo\")\n    \n  \n    plt.subplot(1, 2, 2)\n    plt.imshow(monet_image)\n    plt.title(\"Monet-Style Image\")\n    \n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-16T14:55:39.196192Z","iopub.execute_input":"2024-11-16T14:55:39.196491Z","iopub.status.idle":"2024-11-16T14:55:42.411693Z","shell.execute_reply.started":"2024-11-16T14:55:39.196458Z","shell.execute_reply":"2024-11-16T14:55:42.410657Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}