{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n#for dirname, _, filenames in os.walk('/kaggle/input'):\n    #for filename in filenames:\n        #print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-08T00:23:22.052043Z","iopub.execute_input":"2026-01-08T00:23:22.05228Z","iopub.status.idle":"2026-01-08T00:23:23.637521Z","shell.execute_reply.started":"2026-01-08T00:23:22.052255Z","shell.execute_reply":"2026-01-08T00:23:23.636362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# CycleGAN – Kaggle \"I'm Something of a Painter Myself\"\n# FULL IMPROVED SCRIPT \n# ============================================================\n\nimport os, glob, zipfile\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\n\nAUTOTUNE = tf.data.AUTOTUNE\n\n# ============================================================\n# CONFIG\n# ============================================================\nIMG_SIZE = 256\nBATCH_SIZE = 1\n#EPOCHS = 25\nEPOCHS = 20\nSTEPS_PER_EPOCH = 1000\nLAMBDA = 10\nOUTPUT_DIR = \"generated_monet\"\n\n# ============================================================\n# DATA LOADING\n# ============================================================\ndef parse_tfrecord(example):\n    features = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n    }\n    example = tf.io.parse_single_example(example, features)\n    image = tf.image.decode_jpeg(example[\"image\"], channels=3)\n    return image\n\ndef load_tfrecord_dataset(directory):\n    files = tf.io.gfile.glob(directory + \"/*.tfrec\")\n    dataset = tf.data.TFRecordDataset(files)\n    dataset = dataset.map(parse_tfrecord, num_parallel_calls=AUTOTUNE)\n    return dataset\n\ndef load_jpg_dataset(directory):\n    files = tf.data.Dataset.list_files(directory + \"/*.jpg\")\n    dataset = files.map(\n        lambda x: tf.image.decode_jpeg(tf.io.read_file(x), channels=3),\n        num_parallel_calls=AUTOTUNE,\n    )\n    return dataset\n\n# ============================================================\n# PREPROCESSING\n# ============================================================\ndef preprocess(image):\n    image = tf.image.resize(image, [IMG_SIZE, IMG_SIZE])\n    image = (image / 127.5) - 1.0\n    return image\n\ndef prepare_dataset(ds):\n    return (\n        ds.map(preprocess, num_parallel_calls=AUTOTUNE)\n        .shuffle(1024)\n        .batch(BATCH_SIZE)\n        .prefetch(AUTOTUNE)\n    )\n\n# ============================================================\n# LOAD DATASETS\n# ============================================================\nmonet_ds = prepare_dataset(\n    load_tfrecord_dataset(\"/kaggle/input/gan-getting-started/monet_tfrec\")\n)\nphoto_ds = prepare_dataset(\n    load_tfrecord_dataset(\"/kaggle/input/gan-getting-started/photo_tfrec\")\n)\nphoto_jpg = prepare_dataset(\n    load_jpg_dataset(\"/kaggle/input/gan-getting-started/photo_jpg\")\n)\n\n# ============================================================\n# COUNT ORIGINAL IMAGES\n# ============================================================\ndef count_images(dataset, name):\n    count = 0\n    for _ in dataset.unbatch():\n        count += 1\n    print(f\"{name}: {count} images\")\n    return count\n\n\nnum_photos = count_images(photo_ds, \"Photos\")\nnum_monets = count_images(monet_ds, \"Monet paintings\")\n\n# ============================================================\n# VISUALIZE ORIGINAL DATA (2 Photos, 2 Monets)\n# ============================================================\ndef show_original_samples(photo_ds, monet_ds):\n    fig = plt.figure(figsize=(8, 6))\n    gs = fig.add_gridspec(2, 2, hspace=0.3, wspace=0.05)\n\n    # Take 2 photos and 2 monets\n    photos = list(photo_ds.unbatch().take(2))\n    monets = list(monet_ds.unbatch().take(2))\n\n    # Top row: Photos\n    for i, img in enumerate(photos):\n        ax = fig.add_subplot(gs[0, i])\n        ax.imshow((img + 1) / 2)\n        ax.axis(\"off\")\n        if i == 0:\n            ax.set_title(\"Photos (Original)\", fontsize=12)\n\n    # Bottom row: Monets\n    for i, img in enumerate(monets):\n        ax = fig.add_subplot(gs[1, i])\n        ax.imshow((img + 1) / 2)\n        ax.axis(\"off\")\n        if i == 0:\n            ax.set_title(\"Monet Paintings (Original)\", fontsize=12)\n\n    plt.show()\n\n\n# Call visualization\nshow_original_samples(photo_ds, monet_ds)\n\n\n# ============================================================\n# INSTANCE NORMALIZATION\n# ============================================================\nclass InstanceNormalization(tf.keras.layers.Layer):\n    def __init__(self, epsilon=1e-5):\n        super().__init__()\n        self.epsilon = epsilon\n\n    def build(self, shape):\n        self.scale = self.add_weight(\n            shape=(shape[-1],), initializer=\"ones\", trainable=True\n        )\n        self.offset = self.add_weight(\n            shape=(shape[-1],), initializer=\"zeros\", trainable=True\n        )\n\n    def call(self, x):\n        mean, var = tf.nn.moments(x, axes=[1, 2], keepdims=True)\n        return self.scale * (x - mean) / tf.sqrt(var + self.epsilon) + self.offset\n\n# ============================================================\n# MODEL ARCHITECTURE\n# ============================================================\ndef resnet_block(x):\n    y = tf.keras.layers.Conv2D(256, 3, padding=\"same\", use_bias=False)(x)\n    y = InstanceNormalization()(y)\n    y = tf.keras.layers.ReLU()(y)\n    y = tf.keras.layers.Conv2D(256, 3, padding=\"same\", use_bias=False)(y)\n    y = InstanceNormalization()(y)\n    return tf.keras.layers.Add()([x, y])\n\ndef build_generator():\n    inputs = tf.keras.Input(shape=(IMG_SIZE, IMG_SIZE, 3))\n    x = tf.keras.layers.Conv2D(64, 7, padding=\"same\", use_bias=False)(inputs)\n    x = InstanceNormalization()(x)\n    x = tf.keras.layers.ReLU()(x)\n\n    for f in [128, 256]:\n        x = tf.keras.layers.Conv2D(f, 3, strides=2, padding=\"same\", use_bias=False)(x)\n        x = InstanceNormalization()(x)\n        x = tf.keras.layers.ReLU()(x)\n\n    for _ in range(9):\n        x = resnet_block(x)\n\n    for f in [128, 64]:\n        x = tf.keras.layers.Conv2DTranspose(f, 3, strides=2, padding=\"same\", use_bias=False)(x)\n        x = InstanceNormalization()(x)\n        x = tf.keras.layers.ReLU()(x)\n\n    outputs = tf.keras.layers.Conv2D(3, 7, padding=\"same\", activation=\"tanh\")(x)\n    return tf.keras.Model(inputs, outputs)\n\ndef build_discriminator():\n    inputs = tf.keras.Input(shape=(IMG_SIZE, IMG_SIZE, 3))\n    x = inputs\n    for f in [64, 128, 256, 512]:\n        x = tf.keras.layers.Conv2D(f, 4, strides=2, padding=\"same\")(x)\n        x = tf.keras.layers.LeakyReLU(0.2)(x)\n    outputs = tf.keras.layers.Conv2D(1, 4, padding=\"same\")(x)\n    return tf.keras.Model(inputs, outputs)\n\n# ============================================================\n# INITIALIZE MODELS\n# ============================================================\nG_photo2monet = build_generator()\nG_monet2photo = build_generator()\nD_monet = build_discriminator()\nD_photo = build_discriminator()\n\n# ============================================================\n# LOSSES\n# ============================================================\nmse = tf.keras.losses.MeanSquaredError()\n\ndef generator_loss(fake):\n    return mse(tf.ones_like(fake), fake)\n\ndef discriminator_loss(real, fake):\n    return 0.5 * (mse(tf.ones_like(real), real) + mse(tf.zeros_like(fake), fake))\n\ndef identity_loss(real, same):\n    return tf.reduce_mean(tf.abs(real - same))\n\n# ============================================================\n# OPTIMIZERS\n# ============================================================\nG_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\nD_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n\n# ============================================================\n# TRAIN STEP\n# ============================================================\n@tf.function\ndef train_step(real_monet, real_photo):\n    with tf.GradientTape(persistent=True) as tape:\n\n        fake_monet = G_photo2monet(real_photo, training=True)\n        fake_photo = G_monet2photo(real_monet, training=True)\n\n        cycled_photo = G_monet2photo(fake_monet, training=True)\n        cycled_monet = G_photo2monet(fake_photo, training=True)\n\n        same_monet = G_photo2monet(real_monet, training=True)\n        same_photo = G_monet2photo(real_photo, training=True)\n\n        d_real_monet = D_monet(real_monet, training=True)\n        d_fake_monet = D_monet(fake_monet, training=True)\n        d_real_photo = D_photo(real_photo, training=True)\n        d_fake_photo = D_photo(fake_photo, training=True)\n\n        gen_loss = (\n            generator_loss(d_fake_monet)\n            + generator_loss(d_fake_photo)\n            + LAMBDA * (\n                tf.reduce_mean(tf.abs(real_photo - cycled_photo))\n                + tf.reduce_mean(tf.abs(real_monet - cycled_monet))\n            )\n            + 0.5 * LAMBDA * (\n                identity_loss(real_monet, same_monet)\n                + identity_loss(real_photo, same_photo)\n            )\n        )\n\n        disc_loss = (\n            discriminator_loss(d_real_monet, d_fake_monet)\n            + discriminator_loss(d_real_photo, d_fake_photo)\n        )\n\n    G_vars = G_photo2monet.trainable_variables + G_monet2photo.trainable_variables\n    D_vars = D_monet.trainable_variables + D_photo.trainable_variables\n\n    G_optimizer.apply_gradients(zip(tape.gradient(gen_loss, G_vars), G_vars))\n    D_optimizer.apply_gradients(zip(tape.gradient(disc_loss, D_vars), D_vars))\n\n    return gen_loss, disc_loss\n\n# ============================================================\n# TRAIN LOOP + LOSS LOGGING\n# ============================================================\ngen_losses, disc_losses = [], []\n\nmonet_iter = iter(monet_ds.repeat())\nphoto_iter = iter(photo_ds.repeat())\n\nfor epoch in range(EPOCHS):\n    for step in range(STEPS_PER_EPOCH):\n        g_loss, d_loss = train_step(next(monet_iter), next(photo_iter))\n        gen_losses.append(g_loss)\n        disc_losses.append(d_loss)\n    print(f\"Epoch {epoch+1}/{EPOCHS} | G: {g_loss:.3f} | D: {d_loss:.3f}\")\n\n# ============================================================\n# PLOT LOSSES\n# ============================================================\nplt.figure(figsize=(10,5))\nplt.plot(gen_losses, label=\"Generator\")\nplt.plot(disc_losses, label=\"Discriminator\")\nplt.legend()\nplt.title(\"CycleGAN Training Loss\")\nplt.xlabel(\"Steps\")\nplt.ylabel(\"Loss\")\nplt.show()\n\n# ============================================================\n# GENERATE IMAGES\n# ============================================================\nos.makedirs(OUTPUT_DIR, exist_ok=True)\ncount = 0\nfor batch in photo_jpg:\n    fake = G_photo2monet(batch, training=False)\n    for img in fake:\n        img = tf.cast((img + 1) * 127.5, tf.uint8)\n        tf.keras.preprocessing.image.save_img(f\"{OUTPUT_DIR}/{count}.jpg\", img)\n        count += 1\n        if count == 10000:\n            break\n    if count == 10000:\n        break\n\n# ============================================================\n# ZIP SUBMISSION\n# ============================================================\nwith zipfile.ZipFile(\"images.zip\", \"w\") as z:\n    for f in sorted(glob.glob(f\"{OUTPUT_DIR}/*.jpg\")):\n        z.write(f, arcname=os.path.basename(f))\n\nprint(\"images.zip ready for Kaggle submission\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T00:23:23.638941Z","iopub.execute_input":"2026-01-08T00:23:23.639301Z","iopub.status.idle":"2026-01-08T01:00:51.096934Z","shell.execute_reply.started":"2026-01-08T00:23:23.639279Z","shell.execute_reply":"2026-01-08T01:00:51.096168Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# VISUALIZATION – BEFORE / AFTER (10 SAMPLES)\n# ============================================================\ndef show_samples(dataset, generator):\n    fig = plt.figure(figsize=(20, 5))\n\n    # Create grid with extra vertical space\n    gs = fig.add_gridspec(\n        2, 10,\n        height_ratios=[1, 1],\n        hspace=0.35  # 👈 increase this for larger gap\n    )\n\n    images = list(dataset.unbatch().take(5))\n\n    for i, img in enumerate(images):\n        # Top row: Photo\n        ax_top = fig.add_subplot(gs[0, i])\n        ax_top.imshow((img + 1) / 2)\n        ax_top.axis(\"off\")\n\n        # Bottom row: Photo → Monet\n        fake = generator(tf.expand_dims(img, 0), training=False)[0]\n        ax_bottom = fig.add_subplot(gs[1, i])\n        ax_bottom.imshow((fake + 1) / 2)\n        ax_bottom.axis(\"off\")\n\n    # Row labels\n    fig.text(0.3, 0.85, \"Photo (Input)\", ha=\"center\", fontsize=12)\n    fig.text(0.3, 0.40, \"Photo → Monet (Generated)\", ha=\"center\", fontsize=12)\n\n    plt.show()\n\nshow_samples(photo_jpg, G_photo2monet)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T01:00:51.097915Z","iopub.execute_input":"2026-01-08T01:00:51.098112Z","iopub.status.idle":"2026-01-08T01:00:52.454996Z","shell.execute_reply.started":"2026-01-08T01:00:51.098097Z","shell.execute_reply":"2026-01-08T01:00:52.454351Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# VISUALIZATION – BEFORE / AFTER (10 SAMPLES)\n# ============================================================\ndef show_samples(dataset, generator):\n    fig = plt.figure(figsize=(20, 5))\n\n    # Create grid with extra vertical space\n    gs = fig.add_gridspec(\n        2, 10,\n        height_ratios=[1, 1],\n        hspace=0.35  # 👈 increase this for larger gap\n    )\n\n    images = list(dataset.unbatch().take(5))\n\n    for i, img in enumerate(images):\n        # Top row: Photo\n        ax_top = fig.add_subplot(gs[0, i])\n        ax_top.imshow((img + 1) / 2)\n        ax_top.axis(\"off\")\n\n        # Bottom row: Photo → Monet\n        fake = generator(tf.expand_dims(img, 0), training=False)[0]\n        ax_bottom = fig.add_subplot(gs[1, i])\n        ax_bottom.imshow((fake + 1) / 2)\n        ax_bottom.axis(\"off\")\n\n    # Row labels\n    fig.text(0.3, 0.85, \"Photo (Input)\", ha=\"center\", fontsize=12)\n    fig.text(0.3, 0.40, \"Photo → Monet (Generated)\", ha=\"center\", fontsize=12)\n\n    plt.show()\n\nshow_samples(photo_jpg, G_photo2monet)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# VISUALIZATION – BEFORE / AFTER (10 SAMPLES)\n# ============================================================\ndef show_samples(dataset, generator):\n    fig = plt.figure(figsize=(20, 5))\n\n    # Create grid with extra vertical space\n    gs = fig.add_gridspec(\n        2, 10,\n        height_ratios=[1, 1],\n        hspace=0.35  # 👈 increase this for larger gap\n    )\n\n    images = list(dataset.unbatch().take(5))\n\n    for i, img in enumerate(images):\n        # Top row: Photo\n        ax_top = fig.add_subplot(gs[0, i])\n        ax_top.imshow((img + 1) / 2)\n        ax_top.axis(\"off\")\n\n        # Bottom row: Photo → Monet\n        fake = generator(tf.expand_dims(img, 0), training=False)[0]\n        ax_bottom = fig.add_subplot(gs[1, i])\n        ax_bottom.imshow((fake + 1) / 2)\n        ax_bottom.axis(\"off\")\n\n    # Row labels\n    fig.text(0.3, 0.85, \"Photo (Input)\", ha=\"center\", fontsize=12)\n    fig.text(0.3, 0.40, \"Photo → Monet (Generated)\", ha=\"center\", fontsize=12)\n\n    plt.show()\n\nshow_samples(photo_jpg, G_photo2monet)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}