{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:25.751751Z","iopub.execute_input":"2026-06-22T01:59:25.752016Z","iopub.status.idle":"2026-06-22T01:59:29.495136Z","shell.execute_reply.started":"2026-06-22T01:59:25.751994Z","shell.execute_reply":"2026-06-22T01:59:29.494283Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Imports","metadata":{}},{"cell_type":"code","source":"import os\nimport glob\nimport time\nimport zipfile\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom PIL import Image\nimport matplotlib.pyplot as plt\n\nprint('TensorFlow version:', tf.__version__)\nprint('GPUs:', tf.config.list_physical_devices('GPU'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:29.496611Z","iopub.execute_input":"2026-06-22T01:59:29.496909Z","iopub.status.idle":"2026-06-22T01:59:29.502644Z","shell.execute_reply.started":"2026-06-22T01:59:29.496886Z","shell.execute_reply":"2026-06-22T01:59:29.501894Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"configuration","metadata":{}},{"cell_type":"code","source":"IMG_HEIGHT = 256\nIMG_WIDTH  = 256\nCHANNELS   = 3\nBATCH_SIZE      = 1\nEPOCHS          = 40       # increase to 80 for better MiFID\nSTEPS_PER_EPOCH = 300\nLAMBDA_CYCLE = 10.0\nLAMBDA_ID    = 0.5\nLEARNING_RATE = 2e-4\nBETA_1        = 0.5\n\nMONET_TFREC = '/kaggle/input/competitions/gan-getting-started/monet_tfrec'\nPHOTO_TFREC = '/kaggle/input/competitions/gan-getting-started/photo_tfrec'\nOUTPUT_DIR  = '/kaggle/working/images'\nVIS_DIR     = '/kaggle/working/visuals'\nCKPT_DIR    = '/kaggle/working/checkpoints'\nZIP_PATH    = '/kaggle/working/images.zip'\n\nfor d in [OUTPUT_DIR, VIS_DIR, CKPT_DIR]:\n    os.makedirs(d, exist_ok=True)\n\nAUTOTUNE = tf.data.AUTOTUNE\nprint('Config ready.')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:29.503515Z","iopub.execute_input":"2026-06-22T01:59:29.503747Z","iopub.status.idle":"2026-06-22T01:59:29.518731Z","shell.execute_reply.started":"2026-06-22T01:59:29.503727Z","shell.execute_reply":"2026-06-22T01:59:29.518044Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"3. Custom Instance Normalization","metadata":{}},{"cell_type":"code","source":"class InstanceNormalization(tf.keras.layers.Layer):\n    def __init__(self, epsilon=1e-5, **kwargs):\n        super().__init__(**kwargs)\n        self.epsilon = epsilon\n\n    def build(self, input_shape):\n        n_channels = input_shape[-1]  # integer — fixes Keras 3.x TypeError\n        self.scale = self.add_weight(\n            name='scale',\n            shape=(n_channels,),\n            initializer='ones',\n            trainable=True\n        )\n        self.offset = self.add_weight(\n            name='offset',\n            shape=(n_channels,),\n            initializer='zeros',\n            trainable=True\n        )\n        super().build(input_shape)\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.math.rsqrt(var + self.epsilon) + self.offset\n\n\ndef instance_norm(x):\n    return InstanceNormalization()(x)\n\nprint('InstanceNormalization defined.')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:29.520452Z","iopub.execute_input":"2026-06-22T01:59:29.520752Z","iopub.status.idle":"2026-06-22T01:59:29.532889Z","shell.execute_reply.started":"2026-06-22T01:59:29.520731Z","shell.execute_reply":"2026-06-22T01:59:29.532086Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- Auto-detect correct TFRecord paths ---\nimport os, glob as pyglob\n\ndef find_tfrec_files(base_path):\n    \"\"\"Recursively find all .tfrec files under a directory.\"\"\"\n    # Try direct glob first\n    direct = tf.io.gfile.glob(base_path)\n    # Filter to actual files (not directories)\n    files = [f for f in direct if not os.path.isdir(f) and f.endswith('.tfrec')]\n    if files:\n        return files\n    # If none found, walk subdirectories\n    found = []\n    base_dir = base_path.replace('/*.tfrec', '').replace('*.tfrec', '')\n    for root, dirs, filenames in os.walk(base_dir):\n        for fname in filenames:\n            if fname.endswith('.tfrec'):\n                found.append(os.path.join(root, fname))\n    return found\n\n# Find actual files\nmonet_files = find_tfrec_files(MONET_TFREC)\nphoto_files = find_tfrec_files(PHOTO_TFREC)\n\nprint(f\"Monet files found: {len(monet_files)}\")\nprint(f\"Photo files found: {len(photo_files)}\")\nif monet_files: print(f\"  Sample: {monet_files[0]}\")\nif photo_files: print(f\"  Sample: {photo_files[0]}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:29.533966Z","iopub.execute_input":"2026-06-22T01:59:29.534209Z","iopub.status.idle":"2026-06-22T01:59:29.549896Z","shell.execute_reply.started":"2026-06-22T01:59:29.53419Z","shell.execute_reply":"2026-06-22T01:59:29.549161Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"4. Data Pipeline","metadata":{}},{"cell_type":"code","source":"def decode_image(image_bytes):\n    image = tf.image.decode_jpeg(image_bytes, channels=3)\n    image = tf.cast(image, tf.float32)\n    image = (image / 127.5) - 1.0\n    image = tf.reshape(image, [IMG_HEIGHT, IMG_WIDTH, CHANNELS])\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    return decode_image(example['image'])\n\ndef random_jitter(image):\n    image = tf.image.resize(image, [286, 286],\n                            method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n    image = tf.image.random_crop(image, size=[IMG_HEIGHT, IMG_WIDTH, CHANNELS])\n    image = tf.image.random_flip_left_right(image)\n    return image\n\ndef make_dataset(file_list, train=True, repeat=True, shuffle_buf=1024):\n    \"\"\"Build dataset from an explicit list of .tfrec file paths.\"\"\"\n    ds = tf.data.TFRecordDataset(file_list)\n    ds = ds.map(read_tfrecord, num_parallel_calls=AUTOTUNE)\n    if train:\n        ds = ds.map(random_jitter, num_parallel_calls=AUTOTUNE)\n    if shuffle_buf:\n        ds = ds.shuffle(shuffle_buf)\n    if repeat:\n        ds = ds.repeat()\n    ds = ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)\n    return ds\n\nprint(f\"make_dataset ready. Monet: {len(monet_files)} files, Photo: {len(photo_files)} files\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:29.550883Z","iopub.execute_input":"2026-06-22T01:59:29.551196Z","iopub.status.idle":"2026-06-22T01:59:29.562614Z","shell.execute_reply.started":"2026-06-22T01:59:29.551168Z","shell.execute_reply":"2026-06-22T01:59:29.561813Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"5. Model Architecture","metadata":{}},{"cell_type":"code","source":"INIT = tf.random_normal_initializer(0.0, 0.02)\n\ndef residual_block(x, filters):\n    shortcut = x\n    x = layers.Conv2D(filters, 3, padding='same',\n                      kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x)\n    x = layers.ReLU()(x)\n    x = layers.Conv2D(filters, 3, padding='same',\n                      kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x)\n    return layers.Add()([shortcut, x])\n\ndef build_generator(name='generator'):\n    inputs = layers.Input(shape=[IMG_HEIGHT, IMG_WIDTH, CHANNELS])\n    # Encoder\n    x = layers.Conv2D(64, 7, padding='same', kernel_initializer=INIT, use_bias=False)(inputs)\n    x = instance_norm(x); x = layers.ReLU()(x)\n    x = layers.Conv2D(128, 3, strides=2, padding='same', kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x); x = layers.ReLU()(x)\n    x = layers.Conv2D(256, 3, strides=2, padding='same', kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x); x = layers.ReLU()(x)\n    # 9 Residual blocks\n    for _ in range(9):\n        x = residual_block(x, 256)\n    # Decoder\n    x = layers.Conv2DTranspose(128, 3, strides=2, padding='same', kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x); x = layers.ReLU()(x)\n    x = layers.Conv2DTranspose(64, 3, strides=2, padding='same', kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x); x = layers.ReLU()(x)\n    outputs = layers.Conv2D(3, 7, padding='same', activation='tanh', kernel_initializer=INIT)(x)\n    return keras.Model(inputs, outputs, name=name)\n\ndef build_discriminator(name='discriminator'):\n    inp = layers.Input(shape=[IMG_HEIGHT, IMG_WIDTH, CHANNELS])\n    x = layers.Conv2D(64, 4, strides=2, padding='same', kernel_initializer=INIT)(inp)\n    x = layers.LeakyReLU(0.2)(x)\n    x = layers.Conv2D(128, 4, strides=2, padding='same', kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x); x = layers.LeakyReLU(0.2)(x)\n    x = layers.Conv2D(256, 4, strides=2, padding='same', kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x); x = layers.LeakyReLU(0.2)(x)\n    x = layers.Conv2D(512, 4, strides=1, padding='same', kernel_initializer=INIT, use_bias=False)(x)\n    x = instance_norm(x); x = layers.LeakyReLU(0.2)(x)\n    out = layers.Conv2D(1, 4, strides=1, padding='same', kernel_initializer=INIT)(x)\n    return keras.Model(inp, out, name=name)\n\nG_photo_to_monet = build_generator('G_photo_to_monet')\nG_monet_to_photo = build_generator('G_monet_to_photo')\nD_monet          = build_discriminator('D_monet')\nD_photo          = build_discriminator('D_photo')\nprint(f'Generator params    : {G_photo_to_monet.count_params():,}')\nprint(f'Discriminator params: {D_monet.count_params():,}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:29.563497Z","iopub.execute_input":"2026-06-22T01:59:29.563827Z","iopub.status.idle":"2026-06-22T01:59:30.39726Z","shell.execute_reply.started":"2026-06-22T01:59:29.563789Z","shell.execute_reply":"2026-06-22T01:59:30.396395Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"6. Loss Functions and Optimizers","metadata":{}},{"cell_type":"code","source":"mse = keras.losses.MeanSquaredError()\nmae = keras.losses.MeanAbsoluteError()\n\ndef discriminator_loss(real, generated):\n    return 0.5 * (mse(tf.ones_like(real), real) +\n                  mse(tf.zeros_like(generated), generated))\n\ndef generator_loss(generated):\n    return mse(tf.ones_like(generated), generated)\n\ndef cycle_loss(real_image, cycled_image):\n    return LAMBDA_CYCLE * mae(real_image, cycled_image)\n\ndef identity_loss(real_image, same_image):\n    return LAMBDA_CYCLE * LAMBDA_ID * mae(real_image, same_image)\n\nopt_G  = keras.optimizers.Adam(LEARNING_RATE, beta_1=BETA_1)\nopt_F  = keras.optimizers.Adam(LEARNING_RATE, beta_1=BETA_1)\nopt_Dm = keras.optimizers.Adam(LEARNING_RATE, beta_1=BETA_1)\nopt_Dp = keras.optimizers.Adam(LEARNING_RATE, beta_1=BETA_1)\nprint('Loss functions and optimizers ready.')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:30.398314Z","iopub.execute_input":"2026-06-22T01:59:30.398653Z","iopub.status.idle":"2026-06-22T01:59:30.414109Z","shell.execute_reply.started":"2026-06-22T01:59:30.398628Z","shell.execute_reply":"2026-06-22T01:59:30.413498Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"7. Training Step","metadata":{}},{"cell_type":"code","source":"@tf.function\ndef train_step(real_photo, real_monet):\n    with tf.GradientTape(persistent=True) as tape:\n        fake_monet   = G_photo_to_monet(real_photo,  training=True)\n        cycled_photo = G_monet_to_photo(fake_monet,  training=True)\n        fake_photo   = G_monet_to_photo(real_monet,  training=True)\n        cycled_monet = G_photo_to_monet(fake_photo,  training=True)\n        same_monet   = G_photo_to_monet(real_monet,  training=True)\n        same_photo   = G_monet_to_photo(real_photo,  training=True)\n\n        disc_real_monet = D_monet(real_monet, training=True)\n        disc_fake_monet = D_monet(fake_monet, training=True)\n        disc_real_photo = D_photo(real_photo, training=True)\n        disc_fake_photo = D_photo(fake_photo, training=True)\n\n        g_loss = generator_loss(disc_fake_monet)\n        f_loss = generator_loss(disc_fake_photo)\n        total_cycle = (cycle_loss(real_photo, cycled_photo) +\n                       cycle_loss(real_monet, cycled_monet))\n        total_g_loss = g_loss + total_cycle + identity_loss(real_monet, same_monet)\n        total_f_loss = f_loss + total_cycle + identity_loss(real_photo, same_photo)\n        dm_loss = discriminator_loss(disc_real_monet, disc_fake_monet)\n        dp_loss = discriminator_loss(disc_real_photo, disc_fake_photo)\n\n    opt_G.apply_gradients(zip(\n        tape.gradient(total_g_loss, G_photo_to_monet.trainable_variables),\n        G_photo_to_monet.trainable_variables))\n    opt_F.apply_gradients(zip(\n        tape.gradient(total_f_loss, G_monet_to_photo.trainable_variables),\n        G_monet_to_photo.trainable_variables))\n    opt_Dm.apply_gradients(zip(\n        tape.gradient(dm_loss, D_monet.trainable_variables),\n        D_monet.trainable_variables))\n    opt_Dp.apply_gradients(zip(\n        tape.gradient(dp_loss, D_photo.trainable_variables),\n        D_photo.trainable_variables))\n    del tape\n    return {'G_loss': total_g_loss, 'F_loss': total_f_loss,\n            'Dm_loss': dm_loss, 'Dp_loss': dp_loss, 'cycle': total_cycle}\n\nprint('train_step compiled.')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:30.415248Z","iopub.execute_input":"2026-06-22T01:59:30.415626Z","iopub.status.idle":"2026-06-22T01:59:30.534239Z","shell.execute_reply.started":"2026-06-22T01:59:30.415544Z","shell.execute_reply":"2026-06-22T01:59:30.533159Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"8. Visualization Helper","metadata":{}},{"cell_type":"code","source":"def denorm(img_tensor):\n    \"\"\"Convert [-1, 1] tensor to [0, 1] numpy array for display.\"\"\"\n    img = (img_tensor * 0.5) + 0.5\n    return tf.clip_by_value(img, 0.0, 1.0).numpy()\n\n\ndef save_sample_grid(epoch, n=4):\n    \"\"\"Save a side-by-side grid of input photos and generated Monet paintings.\"\"\"\n    # ✅ Use photo_files list — NOT PHOTO_TFREC pattern\n    test_ds = make_dataset(photo_files, train=False, repeat=False, shuffle_buf=0)\n    fig, axes = plt.subplots(n, 2, figsize=(8, 4 * n))\n    for i, batch in enumerate(test_ds.take(n)):\n        pred = G_photo_to_monet(batch, training=False)\n        axes[i, 0].imshow(denorm(batch[0]))\n        axes[i, 0].set_title('Input Photo', fontsize=10, fontweight='bold')\n        axes[i, 0].axis('off')\n        axes[i, 1].imshow(denorm(pred[0]))\n        axes[i, 1].set_title('Generated Monet', fontsize=10, fontweight='bold')\n        axes[i, 1].axis('off')\n    plt.suptitle(f'CycleGAN — Epoch {epoch}', fontsize=12)\n    plt.tight_layout()\n    path = os.path.join(VIS_DIR, f'epoch_{epoch:03d}.png')\n    plt.savefig(path, dpi=120, bbox_inches='tight')\n    plt.show()\n    plt.close(fig)   # ✅ free memory after saving\n    print(f'  Visualization saved: {path}')\n\n\nprint('Visualization helper ready.')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:30.540019Z","iopub.execute_input":"2026-06-22T01:59:30.54052Z","iopub.status.idle":"2026-06-22T01:59:30.549429Z","shell.execute_reply.started":"2026-06-22T01:59:30.540496Z","shell.execute_reply":"2026-06-22T01:59:30.548429Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"9. Training Loop","metadata":{}},{"cell_type":"code","source":"monet_ds = make_dataset(monet_files, train=True, repeat=True)\nphoto_ds = make_dataset(photo_files, train=True, repeat=True)\ntrain_ds = tf.data.Dataset.zip((photo_ds, monet_ds))\n\nprint(f\"Starting training: {EPOCHS} epochs x {STEPS_PER_EPOCH} steps\")\n\nfor epoch in range(1, EPOCHS + 1):\n    t0 = time.time()\n    totals = {k: 0.0 for k in ['G_loss','F_loss','Dm_loss','Dp_loss','cycle']}\n    step = 0\n    for real_photo, real_monet in train_ds:\n        losses = train_step(real_photo, real_monet)\n        for k in totals:\n            totals[k] += float(losses[k])\n        step += 1\n        if step >= STEPS_PER_EPOCH:\n            break\n    for k in totals:\n        totals[k] /= STEPS_PER_EPOCH\n    print(f\"Ep {epoch:03d}/{EPOCHS} | G:{totals['G_loss']:.3f}  F:{totals['F_loss']:.3f}  \"\n          f\"Dm:{totals['Dm_loss']:.3f}  Dp:{totals['Dp_loss']:.3f}  \"\n          f\"Cyc:{totals['cycle']:.3f} | {time.time()-t0:.1f}s\")\n    if epoch % 5 == 0:\n        save_sample_grid(epoch)\n    if epoch % 10 == 0:\n        G_photo_to_monet.save_weights(os.path.join(CKPT_DIR, f'G_P2M_ep{epoch}.weights.h5'))\n        G_monet_to_photo.save_weights(os.path.join(CKPT_DIR, f'G_M2P_ep{epoch}.weights.h5'))\n        print(f'  Checkpoint saved at epoch {epoch}')\n\nG_photo_to_monet.save_weights(os.path.join(CKPT_DIR, 'G_P2M_final.weights.h5'))\nprint(\"Training complete!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T01:59:30.550531Z","iopub.execute_input":"2026-06-22T01:59:30.551577Z","iopub.status.idle":"2026-06-22T03:27:25.795317Z","shell.execute_reply.started":"2026-06-22T01:59:30.551519Z","shell.execute_reply":"2026-06-22T03:27:25.794645Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"10. Generate Images and Create Zip File","metadata":{}},{"cell_type":"code","source":"NUM_IMAGES = 10000\ngen_ds = make_dataset(photo_files, train=False, repeat=True, shuffle_buf=0)\ngen_iter = iter(gen_ds)\n\nprint(f'Generating {NUM_IMAGES} Monet-style images...')\nfor i in range(NUM_IMAGES):\n    batch = next(gen_iter)\n    pred  = G_photo_to_monet(batch, training=False)\n    img   = (denorm(pred[0]) * 255).astype(np.uint8)\n    Image.fromarray(img).save(os.path.join(OUTPUT_DIR, f'{i:05d}.jpg'))\n    if (i + 1) % 1000 == 0:\n        print(f'  {i+1}/{NUM_IMAGES}')\n\njpg_files = sorted(glob.glob(os.path.join(OUTPUT_DIR, '*.jpg')))\nwith zipfile.ZipFile(ZIP_PATH, 'w', compression=zipfile.ZIP_DEFLATED) as zf:\n    for fp in jpg_files:\n        zf.write(fp, arcname=os.path.basename(fp))\n\nassert os.path.exists(ZIP_PATH)\nprint(f'images.zip ready: {os.path.getsize(ZIP_PATH)/1e6:.1f} MB  |  {len(jpg_files)} images')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T03:27:25.79647Z","iopub.execute_input":"2026-06-22T03:27:25.796877Z","iopub.status.idle":"2026-06-22T03:39:01.736219Z","shell.execute_reply.started":"2026-06-22T03:27:25.796851Z","shell.execute_reply":"2026-06-22T03:39:01.735554Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"11. Final Visualization (8 Examples)","metadata":{}},{"cell_type":"code","source":"test_ds = make_dataset(photo_files, train=False, repeat=False, shuffle_buf=0)\nfig, axes = plt.subplots(8, 2, figsize=(10, 40))\nfor i, batch in enumerate(test_ds.take(8)):\n    pred = G_photo_to_monet(batch, training=False)\n    axes[i, 0].imshow(denorm(batch[0]))\n    axes[i, 0].set_title('Original Photo', fontsize=12, fontweight='bold')\n    axes[i, 0].axis('off')\n    axes[i, 1].imshow(denorm(pred[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-22T03:43:07.262062Z","iopub.execute_input":"2026-06-22T03:43:07.262532Z","iopub.status.idle":"2026-06-22T03:43:07.269852Z","shell.execute_reply.started":"2026-06-22T03:43:07.262502Z","shell.execute_reply":"2026-06-22T03:43:07.268928Z"}},"outputs":[],"execution_count":null}]}