{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpuV5e8","dataSources":[{"sourceId":21154,"databundleVersionId":1243559,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"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\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":"2025-11-09T06:22:25.218455Z","iopub.execute_input":"2025-11-09T06:22:25.218716Z","iopub.status.idle":"2025-11-09T06:22:25.539589Z","shell.execute_reply.started":"2025-11-09T06:22:25.218694Z","shell.execute_reply":"2025-11-09T06:22:25.538746Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import math, re, os, random\nimport tensorflow as tf\nimport numpy as np\nfrom matplotlib import pyplot as plt\nfrom kaggle_datasets import KaggleDatasets\nfrom sklearn.metrics import f1_score, precision_score, recall_score, confusion_matrix\n\nprint(\"TF version \" + tf.__version__)\nAUTO = tf.data.experimental.AUTOTUNE","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:22:25.540090Z","iopub.execute_input":"2025-11-09T06:22:25.540333Z","iopub.status.idle":"2025-11-09T06:22:54.621606Z","shell.execute_reply.started":"2025-11-09T06:22:25.540316Z","shell.execute_reply":"2025-11-09T06:22:54.620617Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Dataset Structure","metadata":{}},{"cell_type":"markdown","source":"⚙️ 2. TPU / GPU Setup\n\nWe automatically detect TPU or fallback to GPU:","metadata":{}},{"cell_type":"code","source":"%%time\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\n    print(\"Running on TPU:\", tpu.master())\nexcept ValueError:\n    strategy = tf.distribute.get_strategy()  # CPU or GPU\nprint(\"REPLICAS:\", strategy.num_replicas_in_sync)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:22:54.622055Z","iopub.execute_input":"2025-11-09T06:22:54.622393Z","iopub.status.idle":"2025-11-09T06:22:54.626635Z","shell.execute_reply.started":"2025-11-09T06:22:54.622377Z","shell.execute_reply":"2025-11-09T06:22:54.626002Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data access and classes¶\n\nTPUs read data directly from Google Cloud Storage (GCS), so we need to copy the dataset to a GCS bucket co-located with the TPU. To do that, pass the name of a specific dataset to the get_gcs_path function. The name of the dataset is the name of the directory it is mounted in.","metadata":{}},{"cell_type":"code","source":"%%time\nimport tensorflow as tf\nfrom kaggle_datasets import KaggleDatasets\n\n# ====================================================\n# CONFIGURATION\n# ====================================================\nIMAGE_SIZE = [224, 224]   # You can switch to 331 or 512 later\nBATCH_SIZE = 16\n\n# ====================================================\n# DATA PATHS\n# ====================================================\ntry:\n    # Kaggle-only: detect TPU and load dataset path from GCS\n    GCS_DS_PATH = KaggleDatasets().get_gcs_path('tpu-getting-started')\n    USING_KAGGLE_GCS = True\n    print(\"✅ Using Kaggle GCS dataset\")\nexcept Exception as e:\n    # Local or GPU runtime\n    GCS_DS_PATH = \"/kaggle/input\"\n    USING_KAGGLE_GCS = False\n    print(\"⚠️ Using local dataset path:\", GCS_DS_PATH)\n\n# Map image size → folder\nGCS_PATH_SELECT = {\n    192: GCS_DS_PATH + '/tfrecords-jpeg-192x192',\n    224: GCS_DS_PATH + '/tfrecords-jpeg-224x224',\n    331: GCS_DS_PATH + '/tfrecords-jpeg-331x331',\n    512: GCS_DS_PATH + '/tfrecords-jpeg-512x512',\n}\nGCS_PATH = GCS_PATH_SELECT[IMAGE_SIZE[0]]\n\n# Define TFRecord files\nTRAINING_FILENAMES   = tf.io.gfile.glob(GCS_PATH + '/train/*.tfrec')\nVALIDATION_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/val/*.tfrec')\nTEST_FILENAMES       = tf.io.gfile.glob(GCS_PATH + '/test/*.tfrec')\n\nprint(f\"✅ Found {len(TRAINING_FILENAMES)} train, {len(VALIDATION_FILENAMES)} val, {len(TEST_FILENAMES)} test files\")\n\n# ====================================================\n# CLASS LABELS\n# ====================================================\nCLASSES = [\n    'pink primrose','hard-leaved pocket orchid','canterbury bells','sweet pea','wild geranium',\n    'tiger lily','moon orchid','bird of paradise','monkshood','globe thistle','snapdragon',\"colt's foot\",\n    'king protea','spear thistle','yellow iris','globe-flower','purple coneflower','peruvian lily','balloon flower',\n    'giant white arum lily','fire lily','pincushion flower','fritillary','red ginger','grape hyacinth','corn poppy',\n    'prince of wales feathers','stemless gentian','artichoke','sweet william','carnation','garden phlox',\n    'love in the mist','cosmos','alpine sea holly','ruby-lipped cattleya','cape flower','great masterwort',\n    'siam tulip','lenten rose','barberton daisy','daffodil','sword lily','poinsettia','bolero deep blue',\n    'wallflower','marigold','buttercup','daisy','common dandelion','petunia','wild pansy','primula','sunflower',\n    'lilac hibiscus','bishop of llandaff','gaura','geranium','orange dahlia','pink-yellow dahlia','cautleya spicata',\n    'japanese anemone','black-eyed susan','silverbush','californian poppy','osteospermum','spring crocus','iris',\n    'windflower','tree poppy','gazania','azalea','water lily','rose','thorn apple','morning glory','passion flower',\n    'lotus','toad lily','anthurium','frangipani','clematis','hibiscus','columbine','desert-rose','tree mallow',\n    'magnolia','cyclamen ','watercress','canna lily','hippeastrum ','bee balm','pink quill','foxglove',\n    'bougainvillea','camellia','mallow','mexican petunia','bromelia','blanket flower','trumpet creeper',\n    'blackberry lily','common tulip','wild rose'\n]\nNUM_CLASSES = len(CLASSES)\nprint(f\"🌸 Total flower classes: {NUM_CLASSES}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:22:54.627098Z","iopub.execute_input":"2025-11-09T06:22:54.627249Z","iopub.status.idle":"2025-11-09T06:22:54.678631Z","shell.execute_reply.started":"2025-11-09T06:22:54.627235Z","shell.execute_reply":"2025-11-09T06:22:54.677961Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# TFRecord Dataset Loading\n\nUniversal TFRecord loader (for train, val, test):","metadata":{}},{"cell_type":"code","source":"%%time\nTRAIN_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/train/*.tfrec')\nVALID_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/val/*.tfrec')\nTEST_FILENAMES  = tf.io.gfile.glob(GCS_PATH + '/test/*.tfrec')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:22:54.679202Z","iopub.execute_input":"2025-11-09T06:22:54.679365Z","iopub.status.idle":"2025-11-09T06:22:54.690904Z","shell.execute_reply.started":"2025-11-09T06:22:54.679350Z","shell.execute_reply":"2025-11-09T06:22:54.690124Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Universal TFRecord Loader¶","metadata":{}},{"cell_type":"code","source":"%%time\n# ====================================================\n# TFRecord Parsing & Dataset Loader\n# ====================================================\n\nAUTO = tf.data.AUTOTUNE\n\ndef decode_image(image_data):\n    \"\"\"Decode image bytes and normalize to [0,1].\"\"\"\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.image.resize(image, IMAGE_SIZE)\n    image = tf.cast(image, tf.float32) / 255.0\n    return image\n\ndef read_tfrecord(example, labeled=True):\n    \"\"\"Parse TFRecord example into (image, label) or (image, id).\"\"\"\n    if labeled:\n        tfrecord_format = {\n            \"image\": tf.io.FixedLenFeature([], tf.string),\n            \"class\": tf.io.FixedLenFeature([], tf.int64),\n            \"id\": tf.io.FixedLenFeature([], tf.string),\n        }\n    else:\n        tfrecord_format = {\n            \"image\": tf.io.FixedLenFeature([], tf.string),\n            \"id\": tf.io.FixedLenFeature([], tf.string),\n        }\n\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example[\"image\"])\n\n    if labeled:\n        label = tf.cast(example[\"class\"], tf.int32)\n        return image, label\n    else:\n        idnum = example[\"id\"]\n        return image, idnum\n\ndef load_dataset(filenames, labeled=True):\n    \"\"\"Load TFRecord dataset for train/val/test.\"\"\"\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)\n    dataset = dataset.map(lambda x: read_tfrecord(x, labeled), num_parallel_calls=AUTO)\n    return dataset\n\ndef get_training_dataset():\n    dataset = load_dataset(TRAINING_FILENAMES, labeled=True)\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.repeat()\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\ndef get_validation_dataset():\n    dataset = load_dataset(VALIDATION_FILENAMES, labeled=True)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.cache()\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\ndef get_test_dataset(ordered=False):\n    dataset = load_dataset(TEST_FILENAMES, labeled=False)\n    dataset = dataset.batch(BATCH_SIZE)\n    if not ordered:\n        options = tf.data.Options()\n        options.experimental_deterministic = False\n        dataset = dataset.with_options(options)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:22:54.691336Z","iopub.execute_input":"2025-11-09T06:22:54.691496Z","iopub.status.idle":"2025-11-09T06:22:54.697277Z","shell.execute_reply.started":"2025-11-09T06:22:54.691481Z","shell.execute_reply":"2025-11-09T06:22:54.696622Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Visual Check (Optional but Important)","metadata":{}},{"cell_type":"code","source":"%%time\nimport matplotlib.pyplot as plt\n\ntrain_ds = get_training_dataset()\nsample_images, sample_labels = next(iter(train_ds.take(1)))\n\nplt.figure(figsize=(10, 10))\nfor i in range(9):\n    ax = plt.subplot(3, 3, i + 1)\n    plt.imshow(sample_images[i].numpy())\n    label_index = sample_labels[i].numpy()\n    plt.title(CLASSES[label_index])\n    plt.axis(\"off\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:22:54.697780Z","iopub.execute_input":"2025-11-09T06:22:54.697962Z","iopub.status.idle":"2025-11-09T06:22:55.867624Z","shell.execute_reply.started":"2025-11-09T06:22:54.697947Z","shell.execute_reply":"2025-11-09T06:22:55.866714Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Preparation (GPU-based EfficientNetB0).","metadata":{}},{"cell_type":"markdown","source":"### EfficientNet B","metadata":{}},{"cell_type":"code","source":"!pip install -q efficientnet\nimport efficientnet.tfkeras as efn\n\nfrom tensorflow.keras import layers, models","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:22:55.868167Z","iopub.execute_input":"2025-11-09T06:22:55.868333Z","iopub.status.idle":"2025-11-09T06:23:00.280954Z","shell.execute_reply.started":"2025-11-09T06:22:55.868318Z","shell.execute_reply":"2025-11-09T06:23:00.279934Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\n# ====================================================\n# STEP 2 — MODEL PREPARATION (TFRecord format, GPU)\n# ====================================================\n\nimport tensorflow as tf\nimport tensorflow.keras as keras\nfrom tensorflow.keras import layers, models\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os\n\n# Paths\nDATA_DIR = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-224x224\"\nIMAGE_SIZE = [224, 224]\nBATCH_SIZE = 32\nAUTO = tf.data.AUTOTUNE\n\n# ====================================================\n# TFRecord utilities\n# ====================================================\n\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.image.resize(image, IMAGE_SIZE)\n    image = tf.cast(image, tf.float32) / 255.0\n    return image\n\ndef read_tfrecord(example, labeled=True):\n    if labeled:\n        tfrecord_format = {\n            \"image\": tf.io.FixedLenFeature([], tf.string),\n            \"class\": tf.io.FixedLenFeature([], tf.int64),\n        }\n        example = tf.io.parse_single_example(example, tfrecord_format)\n        image = decode_image(example[\"image\"])\n        label = tf.cast(example[\"class\"], tf.int32)\n        return image, label\n    else:\n        tfrecord_format = {\"image\": tf.io.FixedLenFeature([], tf.string)}\n        example = tf.io.parse_single_example(example, tfrecord_format)\n        image = decode_image(example[\"image\"])\n        return image\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)\n    dataset = dataset.with_options(ignore_order)\n    dataset = dataset.map(lambda x: read_tfrecord(x, labeled), num_parallel_calls=AUTO)\n    return dataset\n\n# ====================================================\n# Load TFRecord files\n# ====================================================\n\nTRAINING_FILENAMES = tf.io.gfile.glob(f\"{DATA_DIR}/train/*.tfrec\")\nVALIDATION_FILENAMES = tf.io.gfile.glob(f\"{DATA_DIR}/val/*.tfrec\")\nTEST_FILENAMES = tf.io.gfile.glob(f\"{DATA_DIR}/test/*.tfrec\")\n\nprint(\"Train TFRecords:\", len(TRAINING_FILENAMES))\nprint(\"Val TFRecords:\", len(VALIDATION_FILENAMES))\nprint(\"Test TFRecords:\", len(TEST_FILENAMES))\n\n# ====================================================\n# Create datasets\n# ====================================================\n\ntrain_ds = (\n    load_dataset(TRAINING_FILENAMES, labeled=True)\n    .shuffle(2048)\n    .batch(BATCH_SIZE)\n    .prefetch(AUTO)\n)\n\nval_ds = (\n    load_dataset(VALIDATION_FILENAMES, labeled=True)\n    .batch(BATCH_SIZE)\n    .prefetch(AUTO)\n)\n\n# ====================================================\n# Visual check — display few flowers\n# ====================================================\n\nfor imgs, labels in train_ds.take(1):\n    plt.figure(figsize=(10, 10))\n    for i in range(9):\n        plt.subplot(3, 3, i + 1)\n        plt.imshow(imgs[i])\n        plt.title(f\"Label: {labels[i].numpy()}\")\n        plt.axis(\"off\")\n    plt.show()\n\n# ====================================================\n# Build EfficientNetB0 model\n# ====================================================\n\n# ====================================================\n# Build EfficientNetB0 model (fixed Sequential version)\n# ====================================================\n\nfrom tensorflow.keras.applications.efficientnet import EfficientNetB0, preprocess_input\n\nbase_model = EfficientNetB0(include_top=False, input_shape=IMAGE_SIZE + [3], weights=\"imagenet\")\nbase_model.trainable = False\n\n# wrap preprocess_input into a Lambda layer\nmodel = keras.Sequential([\n    layers.Lambda(preprocess_input, name=\"preprocessing\"),\n    base_model,\n    layers.GlobalAveragePooling2D(),\n    layers.Dropout(0.3),\n    layers.Dense(104, activation=\"softmax\")\n])\n\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-3),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:23:00.281586Z","iopub.execute_input":"2025-11-09T06:23:00.282174Z","iopub.status.idle":"2025-11-09T06:23:02.313985Z","shell.execute_reply.started":"2025-11-09T06:23:00.282156Z","shell.execute_reply":"2025-11-09T06:23:02.313175Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training + Saving the Best Model (best_model.h5).","metadata":{}},{"cell_type":"code","source":"%%time\n# ====================================================\n# STEP 3 — TRAINING EfficientNetB0 BASELINE\n# ====================================================\n\nfrom tensorflow.keras import callbacks\n\n# Callbacks: Save best model & reduce LR if plateau\ncheckpoint_cb = callbacks.ModelCheckpoint(\n    \"best_model.h5\",\n    monitor=\"val_accuracy\",\n    save_best_only=True,\n    mode=\"max\",\n    verbose=1\n)\n\nearlystop_cb = callbacks.EarlyStopping(\n    monitor=\"val_accuracy\",\n    patience=2,\n    restore_best_weights=True,\n    verbose=1\n)\n\nreduce_lr_cb = callbacks.ReduceLROnPlateau(\n    monitor=\"val_loss\",\n    factor=0.2,\n    patience=1,\n    verbose=1,\n    min_lr=1e-6\n)\n\n# ====================================================\n# Train the model\n# ====================================================\n\nEPOCHS = 5  # (you can increase later if TPU time allows)\n\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=[checkpoint_cb, earlystop_cb, reduce_lr_cb],\n    verbose=1\n)\n\n# ====================================================\n# Visualize training progress\n# ====================================================\n\nimport matplotlib.pyplot as plt\n\nplt.figure(figsize=(12, 5))\n\nplt.subplot(1, 2, 1)\nplt.plot(history.history[\"accuracy\"], label=\"Train Accuracy\")\nplt.plot(history.history[\"val_accuracy\"], label=\"Val Accuracy\")\nplt.legend()\nplt.title(\"Accuracy\")\n\nplt.subplot(1, 2, 2)\nplt.plot(history.history[\"loss\"], label=\"Train Loss\")\nplt.plot(history.history[\"val_loss\"], label=\"Val Loss\")\nplt.legend()\nplt.title(\"Loss\")\n\nplt.show()\n\nprint(\"✅ Training finished. Best model saved as best_model.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:23:02.314461Z","iopub.execute_input":"2025-11-09T06:23:02.314623Z","iopub.status.idle":"2025-11-09T06:30:23.677232Z","shell.execute_reply.started":"2025-11-09T06:23:02.314608Z","shell.execute_reply":"2025-11-09T06:30:23.676329Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ✅ Step 1: Reload best model","metadata":{}},{"cell_type":"code","source":"%%time\nimport tensorflow as tf\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\n\nMODEL_PATH = \"/kaggle/working/best_model.h5\"\n\n# Dummy function placeholder to avoid preprocess_input loading issue\ndef preprocess_input_fn(x): \n    return preprocess_input(x)\n\nmodel = tf.keras.models.load_model(\n    MODEL_PATH,\n    custom_objects={\"preprocess_input\": preprocess_input_fn}\n)\nprint(\"✅ Model loaded successfully from\", MODEL_PATH)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:30:23.677958Z","iopub.execute_input":"2025-11-09T06:30:23.678129Z","iopub.status.idle":"2025-11-09T06:30:24.424311Z","shell.execute_reply.started":"2025-11-09T06:30:23.678113Z","shell.execute_reply":"2025-11-09T06:30:24.423547Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ✅ Step 2: Load test TFRecords","metadata":{}},{"cell_type":"code","source":"%%time\nTEST_FILENAMES = tf.io.gfile.glob(\"/kaggle/input/tpu-getting-started/tfrecords-jpeg-224x224/test/*.tfrec\")\nAUTO = tf.data.AUTOTUNE\nIMAGE_SIZE = [224, 224]\nBATCH_SIZE = 32\n\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.image.resize(image, IMAGE_SIZE)\n    image = tf.cast(image, tf.float32) / 255.0\n    return image\n\ndef read_unlabeled_tfrecord(example):\n    tfrecord_format = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"id\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example[\"image\"])\n    idnum = example[\"id\"]\n    return image, idnum\n\ndef load_dataset(filenames, ordered=False):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)\n    dataset = dataset.with_options(ignore_order)\n    dataset = dataset.map(read_unlabeled_tfrecord, num_parallel_calls=AUTO)\n    return dataset\n\ntest_dataset = load_dataset(TEST_FILENAMES, ordered=True).batch(BATCH_SIZE)\nprint(\"✅ Test dataset ready.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:30:24.425005Z","iopub.execute_input":"2025-11-09T06:30:24.425179Z","iopub.status.idle":"2025-11-09T06:30:24.464274Z","shell.execute_reply.started":"2025-11-09T06:30:24.425163Z","shell.execute_reply":"2025-11-09T06:30:24.463538Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ✅ Step 3: Make predictions & create submission","metadata":{}},{"cell_type":"code","source":"%%time\nimport pandas as pd\nimport numpy as np\n\nimage_ids = []\npreds = []\n\nfor imgs, ids in test_dataset:\n    p = model.predict(imgs, verbose=0)\n    preds.extend(np.argmax(p, axis=-1))\n    image_ids.extend([id.decode(\"utf-8\") for id in ids.numpy()])\n\nsubmission = pd.DataFrame({\"id\": image_ids, \"label\": preds})\nsubmission.to_csv(\"/kaggle/working/submission.csv\", index=False)\nprint(\"✅ Submission file saved:\", submission.shape)\nsubmission.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-09T06:30:24.464630Z","iopub.execute_input":"2025-11-09T06:30:24.464790Z","iopub.status.idle":"2025-11-09T06:31:47.285024Z","shell.execute_reply.started":"2025-11-09T06:30:24.464776Z","shell.execute_reply":"2025-11-09T06:31:47.284054Z"}},"outputs":[],"execution_count":null}]}