{"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"},{"sourceId":267394589,"sourceType":"kernelVersion"}],"dockerImageVersionId":31089,"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\ncurrent_directory = os.getcwd()\nprint(\"Current working directory:\", current_directory)\nos.chdir(\"/kaggle/input/gan-getting-started\")\nprint(\"Current working directory:\", current_directory)\nos.listdir(current_directory)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-12T01:52:50.956453Z","iopub.execute_input":"2025-10-12T01:52:50.957371Z","iopub.status.idle":"2025-10-12T01:52:50.964686Z","shell.execute_reply.started":"2025-10-12T01:52:50.957345Z","shell.execute_reply":"2025-10-12T01:52:50.963906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- 1. SETUP AND IMPORTS ---\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nimport zipfile\n#!pip install tensorflow-addons -q\n\n# --- 2. EXPLORATORY DATA ANALYSIS (EDA) ---\n\n# Define paths \nMONET_JPG_DIR = './monet_jpg/'\nPHOTO_JPG_DIR = './photo_jpg/'\n\n# Count the number of images\nnum_monet_images = len(os.listdir(MONET_JPG_DIR))\nnum_photo_images = len(os.listdir(PHOTO_JPG_DIR))\n\nprint(f\"Number of Monet paintings: {num_monet_images}\")\nprint(f\"Number of photos: {num_photo_images}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T01:40:35.939683Z","iopub.execute_input":"2025-10-12T01:40:35.940045Z","iopub.status.idle":"2025-10-12T01:40:55.146489Z","shell.execute_reply.started":"2025-10-12T01:40:35.940022Z","shell.execute_reply":"2025-10-12T01:40:55.145883Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# display sample images\ndef display_samples(directory, num_samples=3):\n    plt.figure(figsize=(12, 4))\n    image_files = os.listdir(directory)[:num_samples]\n    for i, file_name in enumerate(image_files):\n        img = tf.keras.utils.load_img(os.path.join(directory, file_name))\n        plt.subplot(1, num_samples, i + 1)\n        plt.imshow(img)\n        plt.axis('off')\n    plt.show()\n\nprint(\"\\nSample Monet Paintings:\")\ndisplay_samples(MONET_JPG_DIR)\nprint(\"Sample Photos:\")\ndisplay_samples(PHOTO_JPG_DIR)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T01:40:57.065725Z","iopub.execute_input":"2025-10-12T01:40:57.066716Z","iopub.status.idle":"2025-10-12T01:40:57.790098Z","shell.execute_reply.started":"2025-10-12T01:40:57.066678Z","shell.execute_reply":"2025-10-12T01:40:57.789494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#plot color channel histograms\ndef plot_histograms(directory, title):\n    # Load one image to analyze\n    image_file = os.listdir(directory)[0]\n    img = plt.imread(os.path.join(directory, image_file))\n    \n    plt.figure(figsize=(10, 5))\n    plt.title(title)\n    \n    # Plot histograms for each channel\n    plt.hist(img[:, :, 0].ravel(), bins=256, color='red', alpha=0.5, label='Red')\n    plt.hist(img[:, :, 1].ravel(), bins=256, color='green', alpha=0.5, label='Green')\n    plt.hist(img[:, :, 2].ravel(), bins=256, color='blue', alpha=0.5, label='Blue')\n    \n    plt.xlabel('Pixel Intensity')\n    plt.ylabel('Frequency')\n    plt.legend()\n    plt.grid(True, alpha=0.3)\n    plt.show()\n\nprint(\"\\nColor Distribution Analysis:\")\nplot_histograms(MONET_JPG_DIR, \"Monet Painting - Color Histogram\")\nplot_histograms(PHOTO_JPG_DIR, \"Photo - Color Histogram\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T01:41:01.142010Z","iopub.execute_input":"2025-10-12T01:41:01.142512Z","iopub.status.idle":"2025-10-12T01:41:03.422742Z","shell.execute_reply.started":"2025-10-12T01:41:01.142485Z","shell.execute_reply":"2025-10-12T01:41:03.421948Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Architecture","metadata":{}},{"cell_type":"code","source":"# --- 3. DATA PIPELINE (TFRecord Version) ---\nimport tensorflow as tf\nimport numpy as np\nimport random, glob\n\n# --- Constants ---\nAUTOTUNE = tf.data.AUTOTUNE\nIMG_SIZE = 256\nBATCH = 4\nSEED = 1337\nrandom.seed(SEED); np.random.seed(SEED); tf.random.set_seed(SEED)\n\n# --- Define TFRecord paths using glob to find all .tfrec files ---\n# This is robust and will work even if you have multiple TFRecord files (shards)\nMONET_TFREC_PATHS = glob.glob('./monet_tfrec/*.tfrec')\nPHOTO_TFREC_PATHS = glob.glob('./photo_tfrec/*.tfrec')\nprint(f\"Found Monet TFRecords: {len(MONET_TFREC_PATHS)} | Photo TFRecords: {len(PHOTO_TFREC_PATHS)}\")\n\n# --- Preprocessing functions (these are the same as your original script) ---\ndef decode_center_crop_resize(img_bytes):\n    img = tf.io.decode_jpeg(img_bytes, channels=3)\n    img = tf.image.convert_image_dtype(img, tf.float32)\n    h, w = tf.shape(img)[0], tf.shape(img)[1]\n    side = tf.minimum(h, w)\n    img = tf.image.crop_to_bounding_box(img, (h - side)//2, (w - side)//2, side, side)\n    img = tf.image.resize(img, [IMG_SIZE, IMG_SIZE], method=\"bicubic\")\n    return img\n\ndef preprocess_train(img):\n    img = tf.image.resize(img, [286, 286], method=\"bicubic\")\n    img = tf.image.random_crop(img, size=[IMG_SIZE, IMG_SIZE, 3])\n    img = tf.image.random_flip_left_right(img)\n    img = (img * 2.0) - 1.0\n    return img\n\ndef preprocess_infer(img):\n    img = (img * 2.0) - 1.0\n    return img\n\n# --- function to parse the serialized data from TFRecords ---\ndef _parse_function(example_proto):\n    feature_description = {'image': tf.io.FixedLenFeature([], tf.string)}\n    features = tf.io.parse_single_example(example_proto, feature_description)\n    return features['image']\n\n\ndef make_ds(tfrec_paths, training=True):\n    if not tfrec_paths:\n        return None\n\n    # 1. Create dataset from TFRecord files for high performance\n    ds = tf.data.TFRecordDataset(tfrec_paths, num_parallel_reads=AUTOTUNE)\n\n    # 2. Extract the raw image bytes from the TFRecord structure\n    ds = ds.map(_parse_function, num_parallel_calls=AUTOTUNE)\n\n    # 3. Apply your existing decoding and preprocessing\n    def _load(img_bytes):\n        img = decode_center_crop_resize(img_bytes)\n        img = preprocess_train(img) if training else preprocess_infer(img)\n        return img\n\n    if training:\n        # For a robust shuffle, we need the total number of images\n        # NOTE: This count assumes all TFRecords are from the same dataset (e.g., monet)\n        # Kaggle datasets often provide this number. A rough estimate is also fine.\n        # Let's assume 300 for Monet and 7000 for Photos as an example.\n        if 'monet' in tfrec_paths[0]:\n            count = 300\n        else:\n            count = 7038\n        ds = ds.shuffle(count, seed=SEED, reshuffle_each_iteration=True)\n\n    ds = ds.map(_load, num_parallel_calls=AUTOTUNE).batch(BATCH).prefetch(AUTOTUNE)\n    return ds\n\n# --- Create the final datasets from TFRecords ---\nds_monet = make_ds(MONET_TFREC_PATHS, training=True)\nds_photo = make_ds(PHOTO_TFREC_PATHS, training=True)\n\nif ds_monet is None or ds_photo is None:\n    raise ValueError(\"Could not build datasets; check your TFRecord directories.\")\nelse:\n    print(\"Successfully created datasets from TFRecord files.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T01:41:10.835309Z","iopub.execute_input":"2025-10-12T01:41:10.835590Z","iopub.status.idle":"2025-10-12T01:41:14.838581Z","shell.execute_reply.started":"2025-10-12T01:41:10.835569Z","shell.execute_reply":"2025-10-12T01:41:14.837747Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- 4. MODEL (CycleGAN) ---\ntry:\n    import tensorflow_addons as tfa\n    NormLayer = tfa.layers.InstanceNormalization\n    print(\"Using InstanceNormalization from tensorflow_addons.\")\nexcept Exception:\n    from tensorflow.keras.layers import LayerNormalization\n    def NormLayer():\n        # Normalize across H, W per channel (approx InstanceNorm behavior)\n        return LayerNormalization(axis=[1,2])\n    print(\"tensorflow_addons not found; using LayerNormalization fallback.\")\n\ndef conv7(filters):\n    return tf.keras.layers.Conv2D(filters, 7, padding='same',\n                                  kernel_initializer=tf.random_normal_initializer(0., 0.02), use_bias=False)\ndef conv3s2(filters):\n    return tf.keras.layers.Conv2D(filters, 3, strides=2, padding='same',\n                                  kernel_initializer=tf.random_normal_initializer(0., 0.02), use_bias=False)\ndef deconv3s2(filters):\n    return tf.keras.layers.Conv2DTranspose(filters, 3, strides=2, padding='same',\n                                           kernel_initializer=tf.random_normal_initializer(0., 0.02), use_bias=False)\n\nclass ResBlock(tf.keras.Model):\n    def __init__(self, filters):\n        super().__init__()\n        self.c1 = tf.keras.layers.Conv2D(filters, 3, padding='same',\n                                         kernel_initializer=tf.random_normal_initializer(0., 0.02), use_bias=False)\n        self.n1 = NormLayer()\n        self.c2 = tf.keras.layers.Conv2D(filters, 3, padding='same',\n                                         kernel_initializer=tf.random_normal_initializer(0., 0.02), use_bias=False)\n        self.n2 = NormLayer()\n        self.relu = tf.keras.layers.ReLU()\n    def call(self, x, training=False):\n        y = self.c1(x); y = self.n1(y, training=training); y = self.relu(y)\n        y = self.c2(y); y = self.n2(y, training=training)\n        return x + y\n\ndef build_generator():\n    inp = tf.keras.Input(shape=(IMG_SIZE, IMG_SIZE, 3))\n    x = conv7(64)(inp); x = NormLayer()(x); x = tf.keras.layers.ReLU()(x)\n    x = conv3s2(128)(x); x = NormLayer()(x); x = tf.keras.layers.ReLU()(x)\n    x = conv3s2(256)(x); x = NormLayer()(x); x = tf.keras.layers.ReLU()(x)\n    RES_BLOCKS = 6  # was 9\n    for _ in range(RES_BLOCKS): x = ResBlock(256)(x)\n    x = deconv3s2(128)(x); x = NormLayer()(x); x = tf.keras.layers.ReLU()(x)\n    x = deconv3s2(64)(x);  x = NormLayer()(x); x = tf.keras.layers.ReLU()(x)\n    x = tf.keras.layers.Conv2D(3, 7, padding='same',\n                               kernel_initializer=tf.random_normal_initializer(0.,0.02))(x)\n    out = tf.keras.layers.Activation('tanh')(x)\n    return tf.keras.Model(inp, out, name=\"Generator\")\n\ndef build_discriminator():\n    inp = tf.keras.Input(shape=(IMG_SIZE, IMG_SIZE, 3))\n    x = tf.keras.layers.Conv2D(64, 4, strides=2, padding='same',\n                               kernel_initializer=tf.random_normal_initializer(0.,0.02))(inp)\n    x = tf.keras.layers.LeakyReLU(0.2)(x)\n\n    def block(x, f, s):\n        x = tf.keras.layers.Conv2D(f, 4, strides=s, padding='same',\n                                   kernel_initializer=tf.random_normal_initializer(0.,0.02), use_bias=False)(x)\n        x = NormLayer()(x); x = tf.keras.layers.LeakyReLU(0.2)(x); return x\n    x = block(x, 128, 2)\n    x = block(x, 256, 2)\n    x = block(x, 512, 1)\n    out = tf.keras.layers.Conv2D(1, 4, strides=1, padding='same',\n                                 kernel_initializer=tf.random_normal_initializer(0.,0.02))(x)\n    return tf.keras.Model(inp, out, name=\"Discriminator\")\n\nG_X2Y = build_generator()   # Monet-style from Photo\nG_Y2X = build_generator()   # Photo-style from Monet (used for cycle)\nD_X = build_discriminator() # Discriminate Monet\nD_Y = build_discriminator() # Discriminate Photo\n\nbce_logits = tf.keras.losses.BinaryCrossentropy(from_logits=True)\ndef gan_loss(pred, real=True):\n    tgt = tf.ones_like(pred) if real else tf.zeros_like(pred)\n    return bce_logits(tgt, pred)\n\nLAMBDA_CYCLE = 10.0\nLAMBDA_ID = 0.5\n\ngen_opt = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\ndisc_opt = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T01:43:01.551433Z","iopub.execute_input":"2025-10-12T01:43:01.551980Z","iopub.status.idle":"2025-10-12T01:43:02.781082Z","shell.execute_reply.started":"2025-10-12T01:43:01.551957Z","shell.execute_reply":"2025-10-12T01:43:02.780540Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@tf.function\ndef train_step(batch_x, batch_y):\n    # This single, persistent tape will watch all operations.\n    with tf.GradientTape(persistent=True) as tape:\n        # ----- 1. ALL FORWARD PASSES (inside the tape's scope) -----\n        fake_y   = G_X2Y(batch_y, training=True)\n        cycled_x = G_Y2X(fake_y,   training=True)\n        fake_x   = G_Y2X(batch_x, training=True)\n        cycled_y = G_X2Y(fake_x,   training=True)\n        same_x   = G_Y2X(batch_x, training=True)\n        same_y   = G_X2Y(batch_y, training=True)\n        dx_real  = D_X(batch_x, training=True)\n        dx_fake  = D_X(fake_x,  training=True)\n        dy_real  = D_Y(batch_y, training=True)\n        dy_fake  = D_Y(fake_y,  training=True)\n\n        # ----- 2. ALL LOSS CALCULATIONS (also inside the tape's scope) -----\n        g_x2y_gan = gan_loss(dy_fake, True)\n        g_y2x_gan = gan_loss(dx_fake, True)\n        cycle = (tf.reduce_mean(tf.abs(batch_x - cycled_x)) +\n                 tf.reduce_mean(tf.abs(batch_y - cycled_y))) * LAMBDA_CYCLE\n        ident = (tf.reduce_mean(tf.abs(batch_x - same_x)) +\n                 tf.reduce_mean(tf.abs(batch_y - same_y))) * LAMBDA_ID\n        g_total = g_x2y_gan + g_y2x_gan + cycle + ident\n\n        d_x_loss = 0.5 * (gan_loss(dx_real, True) + gan_loss(dx_fake, False))\n        d_y_loss = 0.5 * (gan_loss(dy_real, True) + gan_loss(dy_fake, False))\n        d_total  = d_x_loss + d_y_loss\n\n    # ----- 3. GRADIENT CALCULATION (outside the 'with' block) -----\n    g_vars = G_X2Y.trainable_variables + G_Y2X.trainable_variables\n    d_vars = D_X.trainable_variables + D_Y.trainable_variables\n\n    # Calculate gradients using the persistent tape\n    gen_grads  = tape.gradient(g_total, g_vars)\n    disc_grads = tape.gradient(d_total, d_vars)\n\n    # ----- 4. APPLY GRADIENTS -----\n    gen_opt.apply_gradients(zip(gen_grads, g_vars))\n    disc_opt.apply_gradients(zip(disc_grads, d_vars))\n    \n    # It's good practice to release the persistent tape's resources\n    del tape\n\n    # ----- 5. RETURN TENSORS -----\n    return {\n        \"g_total\": g_total,\n        \"g_x2y\":   g_x2y_gan,\n        \"g_y2x\":   g_y2x_gan,\n        \"cycle\":   cycle,\n        \"id\":      ident,\n        \"dx\":      d_x_loss,\n        \"dy\":      d_y_loss\n    }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T01:43:10.433805Z","iopub.execute_input":"2025-10-12T01:43:10.434049Z","iopub.status.idle":"2025-10-12T01:43:10.442410Z","shell.execute_reply.started":"2025-10-12T01:43:10.434032Z","shell.execute_reply":"2025-10-12T01:43:10.441668Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ===== TRAIN DATASET =====\n\ncheckpoint_path = \"/kaggle/working/checkpoints/train\"\nckpt = tf.train.Checkpoint(G_X2Y=G_X2Y,\n                           G_Y2X=G_Y2X,\n                           D_X=D_X,\n                           D_Y=D_Y,\n                           gen_opt=gen_opt,\n                           disc_opt=disc_opt)\n\nmanager = tf.train.CheckpointManager(ckpt, checkpoint_path, max_to_keep=3)\n\n\nfrom tqdm import trange\ntrain_monet = ds_monet.repeat()\ntrain_photo = ds_photo.repeat()\ntrain_ds = tf.data.Dataset.zip((train_monet, train_photo)).prefetch(AUTOTUNE)\n\n# Cap steps per epoch as you like; repeating avoids exhaustion\nSTEPS_PER_EPOCH = 400\nEPOCHS = 8\n\nPATIENCE = 3\nIMPROVE_TOL = 0.01\nbest_score = float('inf')\nno_improve = 0\n\ndef proxy_score(logs_avg):\n    return logs_avg[\"cycle\"] + logs_avg[\"id\"] + 0.5*(logs_avg[\"g_x2y\"]+logs_avg[\"g_y2x\"])\n\nfor epoch in range(1, EPOCHS + 1):\n    sums = {k:0.0 for k in [\"g_total\",\"g_x2y\",\"g_y2x\",\"cycle\",\"id\",\"dx\",\"dy\"]}\n\n    for step, (bx, by) in enumerate(train_ds.take(STEPS_PER_EPOCH)):\n        # train_step now returns a dictionary of Tensors\n        loss_tensors = train_step(bx, by)\n        \n        # Convert to numpy/float here, outside the @tf.function\n        for k in sums:\n            sums[k] += loss_tensors[k].numpy()\n\n    logs_avg = {k: v/max(1,STEPS_PER_EPOCH) for k,v in sums.items()}\n    s = proxy_score(logs_avg)\n    print(f\"Epoch {epoch}: { {k:round(v,4) for k,v in logs_avg.items()} }, proxy={s:.4f}\")\n\n    if s < best_score*(1 - IMPROVE_TOL):\n        best_score = s\n        no_improve = 0\n    # The 'manager' variable is now defined and this will work\n        print(\"Validation score improved, saving checkpoint.\")\n        manager.save()\n    else:\n        no_improve += 1\n        if no_improve >= PATIENCE:\n            print(f\"Early stopping at epoch {epoch} .\")\n            break\n\nckpt.restore(manager.latest_checkpoint)\nprint(\"Restored model from the latest checkpoint.\")","metadata":{"trusted":true,"execution":{"execution_failed":"2025-10-07T06:04:13.690Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#os.chdir(\"/kaggle/input/gan-getting-started\")\n#os.chdir(\"/kaggle/input/dtsa5511-week5-project\")\n#current_directory = os.getcwd()\n#print(\"Current working directory:\", current_directory)\n#os.listdir(current_directory)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T02:08:23.234233Z","iopub.execute_input":"2025-10-12T02:08:23.234485Z","iopub.status.idle":"2025-10-12T02:08:23.240705Z","shell.execute_reply.started":"2025-10-12T02:08:23.234467Z","shell.execute_reply":"2025-10-12T02:08:23.240162Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define your models and checkpoint objects\n\n#checkpoint_path = \"/kaggle/input/dtsa5511-week5-project/checkpoints/train\"\n#ckpt = tf.train.Checkpoint(G_X2Y=G_X2Y,\n#                           G_Y2X=G_Y2X,\n#                           D_X=D_X,\n#                           D_Y=D_Y,\n#                           gen_opt=gen_opt,\n#                           disc_opt=disc_opt)\n\n#manager = tf.train.CheckpointManager(ckpt, checkpoint_path, max_to_keep=3)\n\n\n# --- 2. THE FIX: Build the relevant model first ---\n# Create a dummy input with the correct shape (batch, height, width, channels)\n#dummy_input = tf.zeros((1, IMG_SIZE, IMG_SIZE, 3)) \n\n# Pass it through the generator you need for the export.\n# This forces TensorFlow to build the model's layers and variables.\n#_ = G_X2Y(dummy_input) \n#print(\"Model G_X2Y has been built.\")\n\n\n# --- 3. Now, restore the weights ---\n# Using .expect_partial() is good practice here, as you only need G_X2Y's weights\n# and don't care if the other model/optimizer weights aren't restored.\n#status = ckpt.restore(manager.latest_checkpoint).expect_partial()\n#print(\"Checkpoint restored successfully.\")\n\n# You can optionally assert that the weights were loaded\n#status.assert_existing_objects_matched()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T02:04:54.388581Z","iopub.execute_input":"2025-10-12T02:04:54.389059Z","iopub.status.idle":"2025-10-12T02:04:54.688841Z","shell.execute_reply.started":"2025-10-12T02:04:54.389036Z","shell.execute_reply":"2025-10-12T02:04:54.688155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"OUT_ZIP = \"/kaggle/working/images.zip\"\nN_OUT = 8000  # 7000–10000 per the rules\n\n@tf.function(experimental_relax_shapes=True)\ndef _infer(batch):\n    # You can keep @tf.function here for speed if you want; it's optional.\n    return G_X2Y(batch, training=False)\n\ndef to_jpeg_bytes(t):  # t in [-1,1]\n    x = tf.clip_by_value((t + 1.0) / 2.0, 0.0, 1.0)\n    x = tf.image.convert_image_dtype(x, tf.uint8)\n    return tf.io.encode_jpeg(x, format='rgb', quality=95)\n\ndef generate_zip(infer_ds, n_out=N_OUT, zip_name=OUT_ZIP):\n    import zipfile\n    count = 0\n    with zipfile.ZipFile(zip_name, 'w', compression=zipfile.ZIP_STORED) as zf:\n        for batch in infer_ds:\n            gen = _infer(batch)  # works in eager or graph\n            gen_np = gen.numpy() # safe here because we're in Python/eager context now\n            for i in range(gen_np.shape[0]):\n                if count >= n_out:\n                    break\n                jpeg = to_jpeg_bytes(gen_np[i]).numpy()\n                zf.writestr(f\"image_{count:05d}.jpg\", jpeg)\n                count += 1\n            if count >= n_out:\n                break\n    print(f\"Wrote {count} images to {zip_name}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T02:04:57.280570Z","iopub.execute_input":"2025-10-12T02:04:57.281315Z","iopub.status.idle":"2025-10-12T02:04:57.288488Z","shell.execute_reply.started":"2025-10-12T02:04:57.281287Z","shell.execute_reply":"2025-10-12T02:04:57.287750Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create the dataset for inference \nimport glob\n\n# The generator G_X2Y turns photos into Monet paintings,\n# so we need to feed it the photo dataset.\n# Make sure to replace <YOUR-TRAINING-NOTEBOOK-NAME> with the actual notebook name!\nprint(\"Creating inference dataset from photos...\")\nPHOTO_TFREC_PATHS = glob.glob('/kaggle/input/gan-getting-started/photo_tfrec/*.tfrec')\ninfer_ds = make_ds(PHOTO_TFREC_PATHS, training=False)\n\n# --- 2. Call the function to start generating images ---\nif infer_ds:\n    print(\"Dataset created successfully. Starting image generation...\")\n    generate_zip(infer_ds)\n    print(\"Process complete.\")\nelse:\n    print(\"Could not create inference dataset. Please check the TFRecord paths.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T02:09:33.513327Z","iopub.execute_input":"2025-10-12T02:09:33.513596Z","iopub.status.idle":"2025-10-12T02:12:46.510862Z","shell.execute_reply.started":"2025-10-12T02:09:33.513574Z","shell.execute_reply":"2025-10-12T02:12:46.510046Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}