{"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":"gpu","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":525409,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":412180,"modelId":429965}],"dockerImageVersionId":31089,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## 🚀 Inference Notebook Strategy for Kaggle Competitions\n\nFor Kaggle competitions that involve models with long training times, like deep learning models for image classification, the best practice is to separate your workflow into two distinct notebooks:\n\n---\n\n### 1. **Training Notebook**\n\nThis notebook is where you'll do all the heavy lifting of training your model.  \n[Cassava Leaf train using EfficientNetB3 fine tuning](https://www.kaggle.com/code/amirmohamadaskari/cassava-leaf-train-efficientnetb3-fine-tuning/edit)\n\n* It should leverage powerful accelerators like **TPUs** or **GPUs** to speed up the training process.\n* Once your model is trained, you'll **save the trained model file** (e.g., as a `.keras` or `SavedModel` format) to your notebook's output directory.\n* After the training is complete and the model is saved, you then **save a version** of this notebook. The output of this notebook (your trained model) becomes a permanent asset on Kaggle.\n\n---\n\n### 2. **Inference Notebook (This Notebook)**\n\nThis notebook is designed specifically for making predictions and generating your submission file. It's built to be lightweight and fast.\n\n* It **doesn't perform any training**. Instead, it **loads the pre-trained model** that you saved as an output from your Training Notebook. To do this, you'll add the Training Notebook's output as an **input dataset** to this Inference Notebook.\n* It then focuses solely on **loading the test data**, using the loaded model to make predictions, and **formatting these predictions into the required `submission.csv` file**.\n* This separation ensures that your submission run is quick and reliable, as it doesn't need to re-train the model every time. Kaggle's submission system will run this entire notebook to generate your final submission.","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nimport pandas as pd\nimport random","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:46:32.626893Z","iopub.execute_input":"2025-12-10T15:46:32.627074Z","iopub.status.idle":"2025-12-10T15:46:50.828933Z","shell.execute_reply.started":"2025-12-10T15:46:32.627057Z","shell.execute_reply":"2025-12-10T15:46:50.828143Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Some configuration based on training\nAUTO = tf.data.AUTOTUNE\nIMAGE_SIZE = (512, 512)\nBATCH_SIZE_PER_REPLICA = 8\nNUM_CLASSES = 5\nBATCH_SIZE = 8","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:46:55.691117Z","iopub.execute_input":"2025-12-10T15:46:55.691444Z","iopub.status.idle":"2025-12-10T15:46:55.695793Z","shell.execute_reply.started":"2025-12-10T15:46:55.691419Z","shell.execute_reply":"2025-12-10T15:46:55.695045Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"These two lines define **key file paths** for your Kaggle notebook:\n\n---\n\n* **`DATA_DIR = '/kaggle/input/cassava-leaf-disease-classification'`**\n    This points to the **competition's dataset**, which Kaggle automatically makes available in the `/kaggle/input/` directory. It's where your notebook finds the images and other data it needs.\n\n---\n\n* **`MODEL_DIR = '/kaggle/input/cassava-leaf-model/tensorflow2/default/1/final_model_cassava.keras'`**\n    This specifies the location of your **trained model file**. On Kaggle, if you train a model in one notebook and want to use it in another (like an inference notebook), you save the training notebook, and its output (your model) can then be added as an **input dataset** to your current notebook. This makes the model accessible via the `/kaggle/input/` path.","metadata":{}},{"cell_type":"code","source":"DATA_DIR = '/kaggle/input/cassava-leaf-disease-classification'\nMODEL_DIR = '/kaggle/input/cassava-leaf-model/tensorflow2/default/1/final_model_cassava.keras'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:46:58.545841Z","iopub.execute_input":"2025-12-10T15:46:58.546111Z","iopub.status.idle":"2025-12-10T15:46:58.549939Z","shell.execute_reply.started":"2025-12-10T15:46:58.546090Z","shell.execute_reply":"2025-12-10T15:46:58.549180Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Loading model\n\nLet's load our model.  \nI saved my model from training script. \n**Note**: Always save models is good approach cause somehow we can't import output of older versions of our notebook as input. so I uploaded there and use it for inference but main approach that I explained doesn't change at all:      \n[Cassava Leaf Model](https://www.kaggle.com/models/amirmohamadaskari/cassava-leaf-model)","metadata":{}},{"cell_type":"code","source":"model = load_model(MODEL_DIR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:47:02.257126Z","iopub.execute_input":"2025-12-10T15:47:02.257837Z","iopub.status.idle":"2025-12-10T15:47:11.305910Z","shell.execute_reply.started":"2025-12-10T15:47:02.257811Z","shell.execute_reply":"2025-12-10T15:47:11.305162Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔍 Inference on Test Set\n\nWith our model trained and fine-tuned, it's time to generate predictions on the unseen test data. We'll first create a data pipeline for the test set, similar to the one we built for training and validation.","metadata":{}},{"cell_type":"code","source":"# This function decodes examples from the test TFRecord files.\n# Note that test files contain an 'image_name' instead of a 'target' label.\ndef decode_test_example(example):\n    feature_description = {\n        'image': tf.io.FixedLenFeature([], tf.string),\n        'image_name': tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, feature_description)\n    \n    # Decode and process the image\n    image = tf.image.decode_jpeg(example['image'], channels=3)\n    image = tf.image.resize(image, IMAGE_SIZE)\n    \n    # Return the image and its ID\n    return image, example['image_name']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:48:35.527814Z","iopub.execute_input":"2025-12-10T15:48:35.528554Z","iopub.status.idle":"2025-12-10T15:48:35.533443Z","shell.execute_reply.started":"2025-12-10T15:48:35.528518Z","shell.execute_reply":"2025-12-10T15:48:35.532665Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.applications.efficientnet import preprocess_input\n\ndef preprocess(image, label):\n    # Apply the specific preprocessing required by the EfficientNet model\n    image = preprocess_input(image)\n    return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:48:36.367726Z","iopub.execute_input":"2025-12-10T15:48:36.367969Z","iopub.status.idle":"2025-12-10T15:48:36.378539Z","shell.execute_reply.started":"2025-12-10T15:48:36.367951Z","shell.execute_reply":"2025-12-10T15:48:36.377895Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Test-Time Augmentation (TTA)\n\nTo potentially improve our prediction accuracy, we will use **Test-Time Augmentation (TTA)**. This technique involves creating multiple augmented versions of each test image, making a prediction for each version, and then averaging these predictions. This process can lead to more robust and accurate results as it reduces the impact of random variations in the test images.","metadata":{}},{"cell_type":"code","source":"data_augmentation = tf.keras.Sequential([\n    # Geometric Transformations\n    tf.keras.layers.RandomRotation(40/ 360), # Randomly rotate images\n    tf.keras.layers.RandomTranslation(0.2, 0.2), # Randomly shift images horizontally and vertically\n    tf.keras.layers.RandomZoom(0.2, 0.2), # Randomly zoom into images\n    tf.keras.layers.RandomFlip('horizontal'), # Randomly flip images horizontally\n    tf.keras.layers.RandomFlip('vertical') # Randomly flip images vertically\n], name=\"data_augmentation\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:48:38.738517Z","iopub.execute_input":"2025-12-10T15:48:38.738770Z","iopub.status.idle":"2025-12-10T15:48:38.757177Z","shell.execute_reply.started":"2025-12-10T15:48:38.738750Z","shell.execute_reply":"2025-12-10T15:48:38.756578Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create the test dataset pipeline\nTEST_DIR = os.path.join(DATA_DIR, 'test_tfrecords')\ntest_files = tf.io.gfile.glob(os.path.join(TEST_DIR, '*.tfrec'))\ntest_dataset = tf.data.TFRecordDataset(test_files, num_parallel_reads= AUTO)\ntest_dataset = (test_dataset\n                .map(decode_test_example, num_parallel_calls= AUTO)\n                .map(lambda image, image_id: (preprocess_input(image), image_id), num_parallel_calls= AUTO)\n                .batch(BATCH_SIZE)\n                .prefetch(AUTO))\n\nprint('Test dataset created Successfully !')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:48:40.136029Z","iopub.execute_input":"2025-12-10T15:48:40.136681Z","iopub.status.idle":"2025-12-10T15:48:40.245145Z","shell.execute_reply.started":"2025-12-10T15:48:40.136649Z","shell.execute_reply":"2025-12-10T15:48:40.244387Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Function to get TTA predictions for a full batch of images\ndef tta_predict_batch(model, images, ids, num_tta):\n    \"\"\"\n    Run Test-Time Augmentation (TTA) on a batch of images.\n\n    Args:\n        model: Trained keras model\n        images: Batch of test images\n        ids: Batch of image ids (filenames)\n        num_tta: Number of augmentations per image\n\n    Returns:\n        preds: Averaged predictions for this batch\n        names: List of image ids corresponding to preds\n    \"\"\"\n    all_preds = []\n\n    for _ in range(num_tta):\n        # Apply augmentation to the *whole batch*\n        augmented = data_augmentation(images, training=True)\n\n        # Preprocess for EfficientNet\n        preprocessed = preprocess_input(augmented)\n\n        # Run inference on GPU (much faster in batch mode)\n        preds = model.predict(preprocessed, verbose=1)\n\n        all_preds.append(preds)\n\n    # Average predictions across TTA rounds\n    mean_preds = np.mean(all_preds, axis=0)\n\n    # Convert ids from tf.Tensors to python strings\n    names = [img_id.numpy().decode(\"utf-8\") for img_id in ids]\n\n    return mean_preds, names\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:48:42.287951Z","iopub.execute_input":"2025-12-10T15:48:42.288221Z","iopub.status.idle":"2025-12-10T15:48:42.293557Z","shell.execute_reply.started":"2025-12-10T15:48:42.288200Z","shell.execute_reply":"2025-12-10T15:48:42.292736Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport pandas as pd\n\n# Full Name Mapping for Cassava Leaf Diseases\nLABEL_MAP = {\n    0: \"Cassava Bacterial Blight (CBB)\",\n    1: \"Cassava Brown Streak Disease (CBSD)\",\n    2: \"Cassava Green Mottle (CGM)\",\n    3: \"Cassava Mosaic Disease (CMD)\",\n    4: \"Healthy\"\n}\n\ndef check_model_on_train_images(num_samples=3, tta_runs=5):\n    \"\"\"\n    Loads random images from train_images, runs your TTA function, \n    and compares predictions to ground truth with large visualizations.\n    \"\"\"\n    # 1. Load the training CSV to get IDs and Labels\n    train_csv_path = os.path.join(DATA_DIR, 'train.csv')\n    train_df = pd.read_csv(train_csv_path)\n    \n    # 2. Sample random rows\n    samples = train_df.sample(num_samples)\n    \n    # Lists to store batch data\n    batch_images = []\n    batch_ids = []\n    ground_truths = []\n    \n    print(f\"Loading {num_samples} training images for inference check...\")\n    \n    # 3. Load and prepare images\n    for idx, row in samples.iterrows():\n        img_id = row['image_id']\n        label = row['label']\n        ground_truths.append(label)\n        \n        # Load image file\n        img_path = os.path.join(DATA_DIR, 'train_images', img_id)\n        img = tf.io.read_file(img_path)\n        img = tf.image.decode_jpeg(img, channels=3)\n        img = tf.image.resize(img, IMAGE_SIZE) \n        \n        batch_images.append(img)\n        batch_ids.append(img_id)\n\n    # Convert to Tensor Batch\n    batch_images = tf.stack(batch_images)\n    batch_ids = tf.constant(batch_ids)\n\n    # 4. Run Inference using YOUR defined TTA function\n    print(f\"Running TTA ({tta_runs} augmentations per image)...\")\n    preds, names = tta_predict_batch(model, batch_images, batch_ids, tta_runs)\n    \n    # 5. Visualize Results (Individual large plots)\n    for i in range(num_samples):\n        # Create a large figure for each image\n        plt.figure(figsize=(10, 10))\n        \n        # Get labels and confidence\n        pred_idx = np.argmax(preds[i])\n        true_idx = ground_truths[i]\n        confidence = preds[i][pred_idx]\n        \n        # Determine Color: Green if correct, Red if wrong\n        color_code = 'green' if pred_idx == true_idx else 'red'\n        status = \"CORRECT\" if pred_idx == true_idx else \"WRONG\"\n        \n        # Display image (normalized 0-1 for matplotlib)\n        img_disp = batch_images[i].numpy() / 255.0\n        plt.imshow(img_disp)\n        \n        # Create detailed title\n        title_text = (\n            f\"Image ID: {batch_ids[i].numpy().decode('utf-8')}\\n\"\n            f\"Prediction: {LABEL_MAP[pred_idx]} ({confidence:.2%})\\n\"\n            f\"Ground Truth: {LABEL_MAP[true_idx]}\\n\"\n            f\"[{status}]\"\n        )\n        \n        plt.title(title_text, color=color_code, fontsize=16, fontweight='bold', loc='left')\n        plt.axis(\"off\")\n        plt.show()\n\n# --- Run the check ---\n# I reduced num_samples to 3 so it doesn't clutter your notebook too much,\n# but since they are individual plots, you can increase it if you like.\ncheck_model_on_train_images(num_samples=6, tta_runs=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-10T15:53:24.128857Z","iopub.execute_input":"2025-12-10T15:53:24.129551Z","iopub.status.idle":"2025-12-10T15:53:28.944123Z","shell.execute_reply.started":"2025-12-10T15:53:24.129527Z","shell.execute_reply":"2025-12-10T15:53:28.943418Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Iterate through the test dataset and generate TTA predictions for each image\ntta_num_augmentations = 10  # Number of augmented images to create per test image\ntta_predictions = []\ntta_image_names = []\n\nfor images, ids in test_dataset:\n    # Run TTA for this *batch* (instead of each image individually)\n    mean_preds, names = tta_predict_batch(model, images, ids, tta_num_augmentations)\n\n    # Save results\n    tta_predictions.extend(mean_preds)\n    tta_image_names.extend(names)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T18:19:44.560091Z","iopub.execute_input":"2025-08-19T18:19:44.560808Z","iopub.status.idle":"2025-08-19T18:19:53.640502Z","shell.execute_reply.started":"2025-08-19T18:19:44.560783Z","shell.execute_reply":"2025-08-19T18:19:53.639907Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 📝 Create Submission File\n\nFinally, we process our predictions and format them into a `submission.csv` file as required by the competition. We take the `argmax` of our averaged predictions to get the final predicted class label for each image.","metadata":{}},{"cell_type":"code","source":"tta_predictions = np.array(tta_predictions)\n# Find the index of the highest probability for each prediction to get the final label\nfinal_tta_labels = tf.argmax(tta_predictions, axis=1)\npred_labels = final_tta_labels.numpy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T18:20:03.453827Z","iopub.execute_input":"2025-08-19T18:20:03.454566Z","iopub.status.idle":"2025-08-19T18:20:03.473306Z","shell.execute_reply.started":"2025-08-19T18:20:03.454541Z","shell.execute_reply":"2025-08-19T18:20:03.472592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a new DataFrame with the correct order\nsubmission_df = pd.DataFrame({\n    'image_id': tta_image_names,\n    'label': pred_labels\n})\n\n# Save the final submission file\nsubmission_df.to_csv('submission.csv', index=False)\n\nprint('Submission file created successfully!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T18:20:05.474997Z","iopub.execute_input":"2025-08-19T18:20:05.475285Z","iopub.status.idle":"2025-08-19T18:20:05.486807Z","shell.execute_reply.started":"2025-08-19T18:20:05.475265Z","shell.execute_reply":"2025-08-19T18:20:05.485968Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}