{"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":31194,"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-07T14:01:20.57182Z","iopub.execute_input":"2025-11-07T14:01:20.572086Z","iopub.status.idle":"2025-11-07T14:01:20.575862Z","shell.execute_reply.started":"2025-11-07T14:01:20.572069Z","shell.execute_reply":"2025-11-07T14:01:20.574797Z"}},"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-07T14:01:20.576264Z","iopub.execute_input":"2025-11-07T14:01:20.576421Z","iopub.status.idle":"2025-11-07T14:01:20.588261Z","shell.execute_reply.started":"2025-11-07T14:01:20.576408Z","shell.execute_reply":"2025-11-07T14:01:20.587525Z"}},"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-07T14:01:20.589109Z","iopub.execute_input":"2025-11-07T14:01:20.589261Z","iopub.status.idle":"2025-11-07T14:01:20.596237Z","shell.execute_reply.started":"2025-11-07T14:01:20.589248Z","shell.execute_reply":"2025-11-07T14:01:20.595419Z"}},"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-07T14:01:20.596648Z","iopub.execute_input":"2025-11-07T14:01:20.596808Z","iopub.status.idle":"2025-11-07T14:01:20.624408Z","shell.execute_reply.started":"2025-11-07T14:01:20.596794Z","shell.execute_reply":"2025-11-07T14:01:20.623562Z"}},"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-07T14:01:20.721258Z","iopub.execute_input":"2025-11-07T14:01:20.721434Z","iopub.status.idle":"2025-11-07T14:01:20.73396Z","shell.execute_reply.started":"2025-11-07T14:01:20.72142Z","shell.execute_reply":"2025-11-07T14:01:20.733048Z"}},"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-07T14:01:20.734271Z","iopub.execute_input":"2025-11-07T14:01:20.734436Z","iopub.status.idle":"2025-11-07T14:01:20.741476Z","shell.execute_reply.started":"2025-11-07T14:01:20.734422Z","shell.execute_reply":"2025-11-07T14:01:20.740644Z"}},"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-07T14:01:20.742144Z","iopub.execute_input":"2025-11-07T14:01:20.742304Z","iopub.status.idle":"2025-11-07T14:01:21.420269Z","shell.execute_reply.started":"2025-11-07T14:01:20.74229Z","shell.execute_reply":"2025-11-07T14:01:21.419165Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Preparation (GPU-based EfficientNetB0).","metadata":{}},{"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-07T14:01:21.420948Z","iopub.execute_input":"2025-11-07T14:01:21.421128Z","iopub.status.idle":"2025-11-07T14:01:23.193293Z","shell.execute_reply.started":"2025-11-07T14:01:21.421105Z","shell.execute_reply":"2025-11-07T14:01:23.192232Z"}},"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-07T14:01:23.193977Z","iopub.execute_input":"2025-11-07T14:01:23.194163Z","iopub.status.idle":"2025-11-07T14:08:33.396447Z","shell.execute_reply.started":"2025-11-07T14:01:23.194147Z","shell.execute_reply":"2025-11-07T14:08:33.395627Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":" # Predict & Create Submission","metadata":{}},{"cell_type":"code","source":"%%time\n# ====================================================\n# STEP 4 — INFERENCE AND SUBMISSION (manual preprocessing)\n# ====================================================\n\nimport tensorflow as tf\nimport pandas as pd\nimport numpy as np\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\n\n# Reload model safely (ignore missing preprocess_input)\nMODEL_PATH = \"/kaggle/working/best_model.h5\"\nmodel = tf.keras.models.load_model(MODEL_PATH, compile=False, custom_objects={\"preprocess_input\": lambda x: x})\nprint(\"✅ Model loaded successfully.\")\n\n# ====================================================\n# Prepare test dataset\n# ====================================================\n\nBATCH_SIZE = 32\nAUTO = tf.data.AUTOTUNE\n\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.image.resize(image, [224, 224])\n    image = tf.cast(image, tf.float32)\n    return image\n\ndef read_tfrecord(example):\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):\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTO)\n    return dataset\n\n# Load test TFRecords\nTEST_FILENAMES = tf.io.gfile.glob(\"/kaggle/input/tpu-getting-started/tfrecords-jpeg-224x224/test/*.tfrec\")\nprint(f\"✅ Found {len(TEST_FILENAMES)} test TFRecord files\")\n\ntest_ds = load_dataset(TEST_FILENAMES)\ntest_ds = test_ds.map(lambda x: preprocess_input(x))  # manual preprocessing\ntest_ds = test_ds.batch(BATCH_SIZE).prefetch(AUTO)\n\n# ====================================================\n# Run inference\n# ====================================================\npreds = model.predict(test_ds, verbose=1)\npredicted_labels = np.argmax(preds, axis=1)\n\n# ====================================================\n# Create submission\n# ====================================================\nsubmission = pd.DataFrame({\n    \"id\": range(len(predicted_labels)),\n    \"label\": predicted_labels\n})\n\nsubmission_path = \"/kaggle/working/submission.csv\"\nsubmission.to_csv(submission_path, index=False)\nprint(\"✅ Submission saved to\", submission_path)\nsubmission.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-07T14:08:33.397416Z","iopub.execute_input":"2025-11-07T14:08:33.397701Z","iopub.status.idle":"2025-11-07T14:09:43.274103Z","shell.execute_reply.started":"2025-11-07T14:08:33.39768Z","shell.execute_reply":"2025-11-07T14:09:43.273335Z"}},"outputs":[],"execution_count":null}]}