{"metadata":{"kernelspec":{"display_name":"Python (mymeditruth)","language":"python","name":"mymeditruth"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.16"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Submitted by - Mayank Singh (Section 01)","metadata":{}},{"cell_type":"markdown","source":"## Final Summary: Dogs vs. Cats Image Classification\n\nThis notebook explored a comprehensive set of techniques for binary image classification—from transfer learning and CNN fine-tuning to feature extraction with ensemble learning using tabular models.\n\nThe modeling journey covered:\n- Augmentation and image generator setup\n- Feature extraction from pretrained CNNs like EfficientNet and ConvNeXt\n- Classifier training with Logistic Regression and XGBoost\n- Hyperparameter tuning using GridSearchCV\n- Ensemble techniques including weighted averaging and stacking\n- Submission generation for Kaggle evaluation\n\n---\n\n## Techniques Explored\n\n### 1. **Transfer Learning & Fine-Tuning**\n- Models: `MobileNetV2`, `EfficientNetB3/B5`, `DenseNet201`, `Xception`, `ResNet152`, `InceptionV3`, `ConvNeXt`\n- Strategy: Used pretrained weights from ImageNet, froze convolutional base, added dropout + fully connected head, optionally fine-tuned deeper layers\n\n### 2. **Feature Extraction + Tabular Models**\n- Extracted high-level image features using `EfficientNetB7` and `EfficientNetB3`\n- Used `Logistic Regression`, `XGBoost`, and their ensemble\n- Explored extensive hyperparameter spaces\n\n### 3. **Ensemble Methods**\n- Weighted average ensembling (grid search over LR/XGB blend ratios)\n- Stacking (meta-model: logistic regression)\n\n---\n\n## Results Summary\n\n| Technique                                 | Kaggle Log Loss |\n|-------------------------------------------|------------------|\n| <span style=\"color:green; font-weight:bold\">Champion Model! -> \"EB7 + Blended Ensemble (LR + XGB)\"      | <span style=\"color:green; font-weight:bold\">**0.03929**      |\n| EB7 + Logistic Regression                 | 0.04112          |\n| ConvNeXt (Fine-Tuned)                       | 0.04906          |\n| EB7 + Stacked Ensemble (LR + XGB)                          | 0.05040          |\n| EB3 + Blended Ensemble (LR + XGB)                    | 0.05164          |\n| Xception (Fine-Tuned)                                  | 0.05517          |\n| InceptionV3 (Fine-Tuned)                               | 0.07372          |\n\n![image.png](attachment:1899389f-c4ea-4d6d-a40f-edf85725954a.png)\n---\n\n## Conclusion\n- **Best Model**: EB7 + Blended Ensemble (LR + XGBoost)\n- **Key Learning**: While transfer learning models perform very well, combining traditional classifiers with strong embeddings from EfficientNetB7 led to the best result in terms of log loss.\n\n---\n\n## Extras:\nThe idea:  \nIf the model is **highly confident** about certain predictions (i.e., probabilities very close to `0` or `1`), then we can **manually convert them to 0 or 1**, since the log loss penalizes overconfident wrong predictions but rewards confident correct ones.\n\nAfter analyzing the distribution of predicted probabilities, I applied **clipping and thresholding** to carefully adjust the prediction values:\n\n- Applied clipping to bound all predictions between a safe range\n- Converted extremely confident predictions into hard `0` or `1`\n- Tuned the thresholds manually using trial and error\n\n![image.png](attachment:4a094775-354a-4508-9a7e-578961ecebf6.png)\n\nThis technique helped me **improve the log loss from `0.03929` to `0.03445`**, demonstrating how fine-grained calibration of outputs can make a real difference in ranking.\n\nThis exploration reflects how top Kaggle solutions often go beyond modeling — diving into clever optimization of final predictions.\n\n![image.png](attachment:fd2be2b0-811e-41fa-b9bb-1233b543a6a6.png)\n","metadata":{},"attachments":{"1899389f-c4ea-4d6d-a40f-edf85725954a.png":{"image/png":"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"},"4a094775-354a-4508-9a7e-578961ecebf6.png":{"image/png":"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"},"fd2be2b0-811e-41fa-b9bb-1233b543a6a6.png":{"image/png":"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"}}},{"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","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2025-04-20T14:20:51.150610Z","iopub.status.busy":"2025-04-20T14:20:51.150307Z","iopub.status.idle":"2025-04-20T14:20:51.445012Z","shell.execute_reply":"2025-04-20T14:20:51.444360Z","shell.execute_reply.started":"2025-04-20T14:20:51.150588Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Baseline Model – EfficientNetB0 (Frozen)\n\nAs a starting point, I implemented a baseline image classification model using **EfficientNetB0**, pretrained on ImageNet.\n\n- The base model was loaded with `include_top=False`, and a simple classification head was added:\n  - `GlobalAveragePooling2D → Dropout(0.3) → Dense(1, sigmoid)`\n- The base was **frozen** to prevent updating pretrained weights during training.\n- Data augmentation was applied using rotation, zoom, and horizontal flips.\n- Trained for 5 epochs using Adam optimizer with a learning rate of 0.001.\n- Input size used: (224, 224), Batch size: 32\n\n#### Results:\n| Metric         | Value     |\n|----------------|-----------|\n| Final Val Accuracy | **0.5000** |\n| Final Val Loss     | **~0.694** |\n\nThis result is close to **random guessing** (binary classification with 50% accuracy), suggesting that the model:\n- **Didn't learn meaningful features** from the dataset\n- Was unable to separate the two classes without fine-tuning\n\nThis confirms that freezing the base model entirely is insufficient, and motivates deeper modeling or fine-tuning for further improvement.\n","metadata":{}},{"cell_type":"code","source":"import zipfile\n\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')","metadata":{"execution":{"iopub.execute_input":"2025-04-20T14:20:52.364785Z","iopub.status.busy":"2025-04-20T14:20:52.364110Z","iopub.status.idle":"2025-04-20T14:21:04.598193Z","shell.execute_reply":"2025-04-20T14:21:04.597457Z","shell.execute_reply.started":"2025-04-20T14:20:52.364762Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\ntrain_dir = \"/kaggle/working/train\"\nfiles = os.listdir(train_dir)\nprint(\"Total images:\", len(files))\nprint(\"Example filenames:\", files[:5])\n","metadata":{"execution":{"iopub.execute_input":"2025-04-20T14:21:04.599775Z","iopub.status.busy":"2025-04-20T14:21:04.599321Z","iopub.status.idle":"2025-04-20T14:21:04.616982Z","shell.execute_reply":"2025-04-20T14:21:04.616449Z","shell.execute_reply.started":"2025-04-20T14:21:04.599748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nimport shutil\nimport os\n\n# Step 1: Create target folders\nbase_dir = \"/kaggle/working/dataset\"\nfor folder in [\"train/dog\", \"train/cat\", \"val/dog\", \"val/cat\"]:\n    os.makedirs(os.path.join(base_dir, folder), exist_ok=True)\n\n# Step 2: Get filenames\nimage_files = os.listdir(\"/kaggle/working/train\")\ndog_imgs = [f for f in image_files if \"dog\" in f]\ncat_imgs = [f for f in image_files if \"cat\" in f]\n\n# Step 3: Train/val split\ntrain_dogs, val_dogs = train_test_split(dog_imgs, test_size=0.1, random_state=42)\ntrain_cats, val_cats = train_test_split(cat_imgs, test_size=0.1, random_state=42)\n\n# Step 4: Move to correct folders\ndef move_images(images, label, split):\n    for img in images:\n        src = f\"/kaggle/working/train/{img}\"\n        dst = f\"{base_dir}/{split}/{label}/{img}\"\n        shutil.copy2(src, dst)\n\nmove_images(train_dogs, \"dog\", \"train\")\nmove_images(val_dogs, \"dog\", \"val\")\nmove_images(train_cats, \"cat\", \"train\")\nmove_images(val_cats, \"cat\", \"val\")","metadata":{"execution":{"iopub.execute_input":"2025-04-20T05:50:01.057094Z","iopub.status.busy":"2025-04-20T05:50:01.056400Z","iopub.status.idle":"2025-04-20T05:50:04.626761Z","shell.execute_reply":"2025-04-20T05:50:04.626175Z","shell.execute_reply.started":"2025-04-20T05:50:01.057069Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls /kaggle/working/dataset/train\n!ls /kaggle/working/dataset/val","metadata":{"execution":{"iopub.execute_input":"2025-04-20T05:50:04.628768Z","iopub.status.busy":"2025-04-20T05:50:04.627894Z","iopub.status.idle":"2025-04-20T05:50:04.888098Z","shell.execute_reply":"2025-04-20T05:50:04.887362Z","shell.execute_reply.started":"2025-04-20T05:50:04.628746Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n# Constants\nIMG_SIZE = (224, 224)\nBATCH_SIZE = 32\n\n# Train data generator with augmentation\ntrain_datagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=20,\n    horizontal_flip=True,\n    zoom_range=0.2\n)\n\n# Validation generator — no augmentation!\nval_datagen = ImageDataGenerator(rescale=1./255)\n\n# Flow images from directory\ntrain_generator = train_datagen.flow_from_directory(\n    '/kaggle/working/dataset/train',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='binary'  # dog=1, cat=0\n)\n\nval_generator = val_datagen.flow_from_directory(\n    '/kaggle/working/dataset/val',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='binary'\n)","metadata":{"execution":{"iopub.execute_input":"2025-04-20T04:36:49.034911Z","iopub.status.busy":"2025-04-20T04:36:49.034130Z","iopub.status.idle":"2025-04-20T04:36:49.239709Z","shell.execute_reply":"2025-04-20T04:36:49.238962Z","shell.execute_reply.started":"2025-04-20T04:36:49.034885Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.applications import EfficientNetB0\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout\nfrom tensorflow.keras.optimizers import Adam\n\nbase_model = EfficientNetB0(weights='imagenet', include_top=False, input_shape=(224, 224, 3))\nbase_model.trainable = False  # freeze base\n\n# Add custom classifier head\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\nx = Dropout(0.3)(x)\noutput = Dense(1, activation='sigmoid')(x) \n\nmodel = Model(inputs=base_model.input, outputs=output)\n\n# Compile\nmodel.compile(\n    optimizer=Adam(learning_rate=0.001),\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n\n# Train\nhistory = model.fit(\n    train_generator,\n    epochs=5,\n    validation_data=val_generator\n)","metadata":{"execution":{"iopub.execute_input":"2025-04-20T04:36:54.137327Z","iopub.status.busy":"2025-04-20T04:36:54.136736Z","iopub.status.idle":"2025-04-20T04:56:50.273713Z","shell.execute_reply":"2025-04-20T04:56:50.273048Z","shell.execute_reply.started":"2025-04-20T04:36:54.137304Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Transfer Learning: Exploring Pretrained CNN Architectures\n\nAfter testing a baseline model (EfficientNetB0), I moved on to **transfer learning**, where I leveraged pretrained convolutional neural networks (CNNs) trained on ImageNet.\n\n#### Objective:\nEvaluate and compare the performance of various popular pretrained architectures by freezing the convolutional base and training a lightweight custom classification head.\n\n#### Experimental Setup:\nFor each model, I explored combinations of the following hyperparameters:\n- **Dropout**: 0.3 and 0.5\n- **Image rotation**: 20° and 30°\n- **Learning rate**: 0.001 and 0.0005\n- **Input image size**: based on model requirement (e.g., 160x160 for MobileNet, 456x456 for EfficientNetB5)\n\nAll models were trained for 2 epochs each to quickly identify strong candidates.\n\n---\n\n### Models Evaluated (So far):\n\n| Model          | Input Size | Best Val Accuracy | Best Val Loss |\n|----------------|------------|-------------------|----------------|\n| **MobileNetV2** | (160, 160) | **0.9832**         | 0.0495         |\n| EfficientNetB5 | (456, 456) | 0.5092             | 0.6920         |\n\n---\n\n### Observations:\n- **MobileNetV2** performed exceptionally well, even with a smaller input size and shallower architecture, reaching **98.3% validation accuracy** in just 2 epochs.\n- **EfficientNetB5**, despite being a deeper and more powerful model, performed no better than random guessing (val_acc ~0.50). This could be due to:\n  - Mismatch in augmentation/input configuration\n  - Need for fine-tuning (as only top layers were trained)\n  - Higher image size increasing memory but not adding new learning under frozen base\n- MobileNetV2's lighter architecture is likely better suited to quick transfer learning in low-epoch regimes.\n\n---\n\nNext, I plan to continue this exploration with additional models including:\n- EfficientNetB3\n- DenseNet201\n- Xception\n- ResNet152\n- InceptionV3\n\nEach will be evaluated similarly.\n","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications import (\n    EfficientNetB5, EfficientNetB3,\n    DenseNet201, Xception,\n    ResNet152, MobileNetV2, InceptionV3\n)\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, Dropout, GlobalAveragePooling2D\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nimport tensorflow as tf\nimport pandas as pd\n\n# Model configurations\nmodels_to_test = [\n    {\"name\": \"MobileNetV2\", \"model\": MobileNetV2, \"input_size\": (160, 160)},\n    {\"name\": \"EfficientNetB5\", \"model\": EfficientNetB5, \"input_size\": (456, 456)},\n    # {\"name\": \"EfficientNetB3\", \"model\": EfficientNetB3, \"input_size\": (300, 300)},\n    # {\"name\": \"DenseNet201\", \"model\": DenseNet201, \"input_size\": (224, 224)},\n    # {\"name\": \"Xception\", \"model\": Xception, \"input_size\": (299, 299)},\n    # {\"name\": \"ResNet152\", \"model\": ResNet152, \"input_size\": (224, 224)},\n    # {\"name\": \"InceptionV3\", \"model\": InceptionV3, \"input_size\": (299, 299)}\n]\n\n# Feature settings and learning rates to explore\nfeature_settings = [\n    {\"dropout\": 0.3, \"rotation\": 20},\n    {\"dropout\": 0.5, \"rotation\": 30}\n]\nlearning_rates = [0.001, 0.0005]\n\nresults = []\n\n# Experiment loop\nfor model_cfg in models_to_test:\n    for feature_cfg in feature_settings:\n        for lr in learning_rates:\n            print(f\"Testing: {model_cfg['name']} | Dropout: {feature_cfg['dropout']} | Rotation: {feature_cfg['rotation']} | LR: {lr}\")\n\n            train_datagen = ImageDataGenerator(\n                rescale=1./255,\n                rotation_range=feature_cfg[\"rotation\"],\n                zoom_range=0.2,\n                horizontal_flip=True\n            )\n            val_datagen = ImageDataGenerator(rescale=1./255)\n\n            train_generator = train_datagen.flow_from_directory(\n                '/kaggle/working/dataset/train',\n                target_size=model_cfg[\"input_size\"],\n                batch_size=32,\n                class_mode='binary'\n            )\n            val_generator = val_datagen.flow_from_directory(\n                '/kaggle/working/dataset/val',\n                target_size=model_cfg[\"input_size\"],\n                batch_size=32,\n                class_mode='binary'\n            )\n\n            try:\n                base_model = model_cfg[\"model\"](weights='imagenet', include_top=False,\n                                                input_shape=(*model_cfg[\"input_size\"], 3))\n                base_model.trainable = False\n\n                x = base_model.output\n                x = GlobalAveragePooling2D()(x)\n                x = Dropout(feature_cfg[\"dropout\"])(x)\n                output = Dense(1, activation='sigmoid')(x)\n\n                model = Model(inputs=base_model.input, outputs=output)\n                model.compile(optimizer=Adam(learning_rate=lr),\n                              loss='binary_crossentropy',\n                              metrics=['accuracy'])\n\n                history = model.fit(train_generator, epochs=2,\n                                    validation_data=val_generator, verbose=1)\n\n                val_acc = history.history['val_accuracy'][-1]\n                val_loss = history.history['val_loss'][-1]\n\n                results.append({\n                    \"model\": model_cfg[\"name\"],\n                    \"img_size\": model_cfg[\"input_size\"],\n                    \"dropout\": feature_cfg[\"dropout\"],\n                    \"rotation\": feature_cfg[\"rotation\"],\n                    \"lr\": lr,\n                    \"val_acc\": round(val_acc, 4),\n                    \"val_loss\": round(val_loss, 4)\n                })\n\n            except Exception as e:\n                print(f\"Failed on {model_cfg['name']} — {e}\")\n                results.append({\n                    \"model\": model_cfg[\"name\"],\n                    \"img_size\": model_cfg[\"input_size\"],\n                    \"dropout\": feature_cfg[\"dropout\"],\n                    \"rotation\": feature_cfg[\"rotation\"],\n                    \"lr\": lr,\n                    \"val_acc\": None,\n                    \"val_loss\": None,\n                    \"error\": str(e)\n                })\n\n# Summary table\ndf_results1 = pd.DataFrame(results)\ndf_results1.sort_values(\"val_acc\", ascending=False, inplace=True)\nprint(df_results1)","metadata":{"execution":{"iopub.execute_input":"2025-04-20T05:50:23.856728Z","iopub.status.busy":"2025-04-20T05:50:23.855971Z","iopub.status.idle":"2025-04-20T08:27:22.209034Z","shell.execute_reply":"2025-04-20T08:27:22.208200Z","shell.execute_reply.started":"2025-04-20T05:50:23.856700Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Transfer Learning – Round 2: DenseNet201 & EfficientNetB3\n\nBuilding on my previous MobileNetV2 and EfficientNetB5 experiments, I extended the transfer learning exploration by comparing:\n- **DenseNet201** with input size (224, 224)\n- **EfficientNetB3** with input size (300, 300)\n\n#### Setup:\nFor each model, I varied:\n- Dropout: 0.3 and 0.5\n- Rotation range: 20° and 30°\n- Learning rate: 0.001 and 0.0005\n\nAll models were trained with a frozen base and custom head, using 2 epochs of training to benchmark quickly.\n\n---\n\n### Results:\n\n| Model          | Best Val Accuracy | Best Val Loss |\n|----------------|-------------------|----------------|\n| **DenseNet201** | **0.9916**         | **0.0227**      |\n| EfficientNetB3 | 0.5472             | 0.6884         |\n\n---\n\n### Insights:\n- **DenseNet201** significantly outperformed EfficientNetB3 in all configurations.\n  - Achieved **>99% accuracy** across multiple runs\n  - Performed consistently with both dropout settings and learning rates\n- **EfficientNetB3**, like B5, performed poorly (~50–55% accuracy), suggesting:\n  - The model may require fine-tuning (base unfreezing)\n  - Larger input size alone didn’t contribute to better learning under a frozen base\n- DenseNet201 is a strong candidate for final model selection or ensembling due to its reliability and high accuracy.\n\nNext, I’ll evaluate **Xception**, **ResNet152**, and **InceptionV3** to complete the benchmark set before selecting models for full fine-tuning or blending.\n","metadata":{}},{"cell_type":"code","source":"# Model configurations\nmodels_to_test = [\n    # {\"name\": \"MobileNetV2\", \"model\": MobileNetV2, \"input_size\": (160, 160)},\n    # {\"name\": \"EfficientNetB5\", \"model\": EfficientNetB5, \"input_size\": (456, 456)},\n    {\"name\": \"EfficientNetB3\", \"model\": EfficientNetB3, \"input_size\": (300, 300)},\n    {\"name\": \"DenseNet201\", \"model\": DenseNet201, \"input_size\": (224, 224)},\n    # {\"name\": \"Xception\", \"model\": Xception, \"input_size\": (299, 299)},\n    # {\"name\": \"ResNet152\", \"model\": ResNet152, \"input_size\": (224, 224)},\n    # {\"name\": \"InceptionV3\", \"model\": InceptionV3, \"input_size\": (299, 299)}\n]\n\n# Feature settings and learning rates to explore\nfeature_settings = [\n    {\"dropout\": 0.3, \"rotation\": 20},\n    {\"dropout\": 0.5, \"rotation\": 30}\n]\nlearning_rates = [0.001, 0.0005]\n\nresults = []\n\n# Experiment loop\nfor model_cfg in models_to_test:\n    for feature_cfg in feature_settings:\n        for lr in learning_rates:\n            print(f\"Testing: {model_cfg['name']} | Dropout: {feature_cfg['dropout']} | Rotation: {feature_cfg['rotation']} | LR: {lr}\")\n\n            train_datagen = ImageDataGenerator(\n                rescale=1./255,\n                rotation_range=feature_cfg[\"rotation\"],\n                zoom_range=0.2,\n                horizontal_flip=True\n            )\n            val_datagen = ImageDataGenerator(rescale=1./255)\n\n            train_generator = train_datagen.flow_from_directory(\n                '/kaggle/working/dataset/train',\n                target_size=model_cfg[\"input_size\"],\n                batch_size=32,\n                class_mode='binary'\n            )\n            val_generator = val_datagen.flow_from_directory(\n                '/kaggle/working/dataset/val',\n                target_size=model_cfg[\"input_size\"],\n                batch_size=32,\n                class_mode='binary'\n            )\n\n            try:\n                base_model = model_cfg[\"model\"](weights='imagenet', include_top=False,\n                                                input_shape=(*model_cfg[\"input_size\"], 3))\n                base_model.trainable = False\n\n                x = base_model.output\n                x = GlobalAveragePooling2D()(x)\n                x = Dropout(feature_cfg[\"dropout\"])(x)\n                output = Dense(1, activation='sigmoid')(x)\n\n                model = Model(inputs=base_model.input, outputs=output)\n                model.compile(optimizer=Adam(learning_rate=lr),\n                              loss='binary_crossentropy',\n                              metrics=['accuracy'])\n\n                history = model.fit(train_generator, epochs=2,\n                                    validation_data=val_generator, verbose=1)\n\n                val_acc = history.history['val_accuracy'][-1]\n                val_loss = history.history['val_loss'][-1]\n\n                results.append({\n                    \"model\": model_cfg[\"name\"],\n                    \"img_size\": model_cfg[\"input_size\"],\n                    \"dropout\": feature_cfg[\"dropout\"],\n                    \"rotation\": feature_cfg[\"rotation\"],\n                    \"lr\": lr,\n                    \"val_acc\": round(val_acc, 4),\n                    \"val_loss\": round(val_loss, 4)\n                })\n\n            except Exception as e:\n                print(f\"Failed on {model_cfg['name']} — {e}\")\n                results.append({\n                    \"model\": model_cfg[\"name\"],\n                    \"img_size\": model_cfg[\"input_size\"],\n                    \"dropout\": feature_cfg[\"dropout\"],\n                    \"rotation\": feature_cfg[\"rotation\"],\n                    \"lr\": lr,\n                    \"val_acc\": None,\n                    \"val_loss\": None,\n                    \"error\": str(e)\n                })\n\n# Summary table\ndf_results2 = pd.DataFrame(results)\ndf_results2.sort_values(\"val_acc\", ascending=False, inplace=True)\nprint(df_results2)","metadata":{"execution":{"iopub.execute_input":"2025-04-20T08:27:22.210437Z","iopub.status.busy":"2025-04-20T08:27:22.210206Z","iopub.status.idle":"2025-04-20T10:07:33.609079Z","shell.execute_reply":"2025-04-20T10:07:33.608420Z","shell.execute_reply.started":"2025-04-20T08:27:22.210414Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Transfer Learning – Round 3: Xception vs. ResNet152\n\nContinuing the comparative evaluation of pretrained CNN architectures, this round focused on:\n\n- **Xception**: A depthwise separable CNN with strong performance on image classification tasks.\n- **ResNet152**: A very deep residual network designed to avoid vanishing gradients through skip connections.\n\n#### Experimental Setup:\nConsistent with earlier rounds, I explored:\n- Dropout: 0.3 and 0.5\n- Rotation: 20° and 30°\n- Learning Rates: 0.001 and 0.0005\n- Input sizes: 299×299 (Xception), 224×224 (ResNet152)\n- Base layers were **frozen**; only the custom classification head was trained.\n\n---\n\n### Results Summary:\n\n| Model      | Best Val Accuracy | Best Val Loss |\n|------------|-------------------|---------------|\n| **Xception**   | **0.9972**         | **0.0160**     |\n| ResNet152  | 0.6240             | 0.6640         |\n\n---\n\n### Observations:\n\n- **Xception** emerged as one of the top-performing models across all experiments so far, consistently achieving **>99.6% accuracy** and low validation loss.\n- Its performance is comparable to DenseNet201, and could be a strong candidate for:\n  - Final fine-tuning\n  - Ensemble modeling\n- **ResNet152**, while deeper, underperformed with validation accuracy around **60–62%**. This might be due to:\n  - Model complexity not translating well under frozen transfer learning\n  - Need for deeper tuning or more aggressive regularization\n\n---\n\nWith Xception performing this well, it joins DenseNet201 and MobileNetV2 as leading contenders for final model selection.\n","metadata":{}},{"cell_type":"code","source":"# Model configurations\nmodels_to_test = [\n    # {\"name\": \"MobileNetV2\", \"model\": MobileNetV2, \"input_size\": (160, 160)},\n    # {\"name\": \"EfficientNetB5\", \"model\": EfficientNetB5, \"input_size\": (456, 456)},\n    # {\"name\": \"EfficientNetB3\", \"model\": EfficientNetB3, \"input_size\": (300, 300)},\n    # {\"name\": \"DenseNet201\", \"model\": DenseNet201, \"input_size\": (224, 224)},\n    {\"name\": \"Xception\", \"model\": Xception, \"input_size\": (299, 299)},\n    {\"name\": \"ResNet152\", \"model\": ResNet152, \"input_size\": (224, 224)},\n    # {\"name\": \"InceptionV3\", \"model\": InceptionV3, \"input_size\": (299, 299)}\n]\n\n# Feature settings and learning rates to explore\nfeature_settings = [\n    {\"dropout\": 0.3, \"rotation\": 20},\n    {\"dropout\": 0.5, \"rotation\": 30}\n]\nlearning_rates = [0.001, 0.0005]\n\nresults = []\n\n# Experiment loop\nfor model_cfg in models_to_test:\n    for feature_cfg in feature_settings:\n        for lr in learning_rates:\n            print(f\"Testing: {model_cfg['name']} | Dropout: {feature_cfg['dropout']} | Rotation: {feature_cfg['rotation']} | LR: {lr}\")\n\n            train_datagen = ImageDataGenerator(\n                rescale=1./255,\n                rotation_range=feature_cfg[\"rotation\"],\n                zoom_range=0.2,\n                horizontal_flip=True\n            )\n            val_datagen = ImageDataGenerator(rescale=1./255)\n\n            train_generator = train_datagen.flow_from_directory(\n                '/kaggle/working/dataset/train',\n                target_size=model_cfg[\"input_size\"],\n                batch_size=32,\n                class_mode='binary'\n            )\n            val_generator = val_datagen.flow_from_directory(\n                '/kaggle/working/dataset/val',\n                target_size=model_cfg[\"input_size\"],\n                batch_size=32,\n                class_mode='binary'\n            )\n\n            try:\n                base_model = model_cfg[\"model\"](weights='imagenet', include_top=False,\n                                                input_shape=(*model_cfg[\"input_size\"], 3))\n                base_model.trainable = False\n\n                x = base_model.output\n                x = GlobalAveragePooling2D()(x)\n                x = Dropout(feature_cfg[\"dropout\"])(x)\n                output = Dense(1, activation='sigmoid')(x)\n\n                model = Model(inputs=base_model.input, outputs=output)\n                model.compile(optimizer=Adam(learning_rate=lr),\n                              loss='binary_crossentropy',\n                              metrics=['accuracy'])\n\n                history = model.fit(train_generator, epochs=2,\n                                    validation_data=val_generator, verbose=1)\n\n                val_acc = history.history['val_accuracy'][-1]\n                val_loss = history.history['val_loss'][-1]\n\n                results.append({\n                    \"model\": model_cfg[\"name\"],\n                    \"img_size\": model_cfg[\"input_size\"],\n                    \"dropout\": feature_cfg[\"dropout\"],\n                    \"rotation\": feature_cfg[\"rotation\"],\n                    \"lr\": lr,\n                    \"val_acc\": round(val_acc, 4),\n                    \"val_loss\": round(val_loss, 4)\n                })\n\n            except Exception as e:\n                print(f\"Failed on {model_cfg['name']} — {e}\")\n                results.append({\n                    \"model\": model_cfg[\"name\"],\n                    \"img_size\": model_cfg[\"input_size\"],\n                    \"dropout\": feature_cfg[\"dropout\"],\n                    \"rotation\": feature_cfg[\"rotation\"],\n                    \"lr\": lr,\n                    \"val_acc\": None,\n                    \"val_loss\": None,\n                    \"error\": str(e)\n                })\n\n# Summary table\ndf_results3 = pd.DataFrame(results)\ndf_results3.sort_values(\"val_acc\", ascending=False, inplace=True)\nprint(df_results3)","metadata":{"execution":{"iopub.execute_input":"2025-04-20T10:07:33.610104Z","iopub.status.busy":"2025-04-20T10:07:33.609883Z","iopub.status.idle":"2025-04-20T11:42:04.244561Z","shell.execute_reply":"2025-04-20T11:42:04.243954Z","shell.execute_reply.started":"2025-04-20T10:07:33.610081Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Transfer Learning – Round 4: InceptionV3\n\nTo complete the benchmarking of popular pretrained CNN architectures, I evaluated **InceptionV3**, a well-established model known for its balance of depth and efficiency, especially in image classification.\n\n#### Setup:\n- Input size: 299×299\n- Dropout: 0.3 and 0.5\n- Rotation: 20° and 30°\n- Learning rates: 0.001 and 0.0005\n- Trained using frozen base model with a simple custom head\n- Each configuration was run for 2 epochs\n\n---\n\n### Results:\n\n| Best Val Accuracy | Best Val Loss |\n|-------------------|----------------|\n| **0.9968**         | **0.0134 – 0.0180** |\n\n---\n\n### Observations:\n\n- **InceptionV3 consistently performed well across all settings**, joining DenseNet201 and Xception in the top-performing group.\n- Minor variance in performance was observed between different hyperparameter settings, but all achieved **>99.6% accuracy**.\n- This further strengthens the case for considering InceptionV3 in the final fine-tuning or ensembling phase.\n\n---\n\nAt this point, the strongest transfer learning candidates (based on validation performance alone) are:\n- DenseNet201\n- Xception\n- InceptionV3\n- MobileNetV2 (fast + effective)\n\nThese will now be further refined through fine-tuning and/or used in ensemble models.\n","metadata":{}},{"cell_type":"code","source":"# Model configurations\nmodels_to_test = [\n    # {\"name\": \"MobileNetV2\", \"model\": MobileNetV2, \"input_size\": (160, 160)},\n    # {\"name\": \"EfficientNetB5\", \"model\": EfficientNetB5, \"input_size\": (456, 456)},\n    # {\"name\": \"EfficientNetB3\", \"model\": EfficientNetB3, \"input_size\": (300, 300)},\n    # {\"name\": \"DenseNet201\", \"model\": DenseNet201, \"input_size\": (224, 224)},\n    # {\"name\": \"Xception\", \"model\": Xception, \"input_size\": (299, 299)},\n    # {\"name\": \"ResNet152\", \"model\": ResNet152, \"input_size\": (224, 224)},\n    {\"name\": \"InceptionV3\", \"model\": InceptionV3, \"input_size\": (299, 299)}\n]\n\n# Feature settings and learning rates to explore\nfeature_settings = [\n    {\"dropout\": 0.3, \"rotation\": 20},\n    {\"dropout\": 0.5, \"rotation\": 30}\n]\nlearning_rates = [0.001, 0.0005]\n\nresults = []\n\n# Experiment loop\nfor model_cfg in models_to_test:\n    for feature_cfg in feature_settings:\n        for lr in learning_rates:\n            print(f\"Testing: {model_cfg['name']} | Dropout: {feature_cfg['dropout']} | Rotation: {feature_cfg['rotation']} | LR: {lr}\")\n\n            train_datagen = ImageDataGenerator(\n                rescale=1./255,\n                rotation_range=feature_cfg[\"rotation\"],\n                zoom_range=0.2,\n                horizontal_flip=True\n            )\n            val_datagen = ImageDataGenerator(rescale=1./255)\n\n            train_generator = train_datagen.flow_from_directory(\n                '/kaggle/working/dataset/train',\n                target_size=model_cfg[\"input_size\"],\n                batch_size=32,\n                class_mode='binary'\n            )\n            val_generator = val_datagen.flow_from_directory(\n                '/kaggle/working/dataset/val',\n                target_size=model_cfg[\"input_size\"],\n                batch_size=32,\n                class_mode='binary'\n            )\n\n            try:\n                base_model = model_cfg[\"model\"](weights='imagenet', include_top=False,\n                                                input_shape=(*model_cfg[\"input_size\"], 3))\n                base_model.trainable = False\n\n                x = base_model.output\n                x = GlobalAveragePooling2D()(x)\n                x = Dropout(feature_cfg[\"dropout\"])(x)\n                output = Dense(1, activation='sigmoid')(x)\n\n                model = Model(inputs=base_model.input, outputs=output)\n                model.compile(optimizer=Adam(learning_rate=lr),\n                              loss='binary_crossentropy',\n                              metrics=['accuracy'])\n\n                history = model.fit(train_generator, epochs=2,\n                                    validation_data=val_generator, verbose=1)\n\n                val_acc = history.history['val_accuracy'][-1]\n                val_loss = history.history['val_loss'][-1]\n\n                results.append({\n                    \"model\": model_cfg[\"name\"],\n                    \"img_size\": model_cfg[\"input_size\"],\n                    \"dropout\": feature_cfg[\"dropout\"],\n                    \"rotation\": feature_cfg[\"rotation\"],\n                    \"lr\": lr,\n                    \"val_acc\": round(val_acc, 4),\n                    \"val_loss\": round(val_loss, 4)\n                })\n\n            except Exception as e:\n                print(f\"Failed on {model_cfg['name']} — {e}\")\n                results.append({\n                    \"model\": model_cfg[\"name\"],\n                    \"img_size\": model_cfg[\"input_size\"],\n                    \"dropout\": feature_cfg[\"dropout\"],\n                    \"rotation\": feature_cfg[\"rotation\"],\n                    \"lr\": lr,\n                    \"val_acc\": None,\n                    \"val_loss\": None,\n                    \"error\": str(e)\n                })\n\n# Summary table\ndf_results4 = pd.DataFrame(results)\ndf_results4.sort_values(\"val_acc\", ascending=False, inplace=True)\nprint(df_results4)","metadata":{"execution":{"iopub.execute_input":"2025-04-20T11:42:04.246003Z","iopub.status.busy":"2025-04-20T11:42:04.245789Z","iopub.status.idle":"2025-04-20T12:41:09.230504Z","shell.execute_reply":"2025-04-20T12:41:09.229652Z","shell.execute_reply.started":"2025-04-20T11:42:04.245986Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Transfer Learning Checkpoint: Xception (Full Dataset + Fine-Tuning)\n\nFollowing strong validation results in earlier experiments, I selected **Xception** as a promising model for a full-dataset training and test-time evaluation.\n\n#### Strategy:\n- Used **all 25,000 training images** (cats and dogs) without hold-out validation\n- Applied data augmentation to prevent overfitting: rotation, zoom, horizontal flip\n- Model structure:\n  - Pretrained **Xception** base (frozen initially)\n  - Custom head: `GlobalAvgPooling → Dropout(0.3) → Dense(1, sigmoid)`\n  \n#### Training Procedure:\n- **Phase 1 – Frozen Base**: 5 epochs, learning rate = 0.001\n- **Phase 2 – Fine-Tuning**: Base model unfrozen, 5 additional epochs, learning rate = 1e-5\n\n#### Test Set Predictions:\n- Predictions were generated using the fine-tuned model\n- Saved in submission format: `MSBA.Session01.MayankSingh.Xception.csv`\n\n---\n\n### Submission Score:\n| Metric         | Value     |\n|----------------|-----------|\n| **Kaggle Log Loss** | **0.05517** |\n\nThis is one of the lowest log-loss values among all tested models so far.  \nHowever, **this is not the final model** — the next step is to:\n- Experiment with **InceptionV3**, **ConvNeXtV2**, **EfficientNetB7**, and other modern backbones\n- Consider **ensembling** or **meta-modeling** based on feature extraction\n\nThis Xception model serves as a **strong intermediate benchmark** for further exploration.\n\n![image.png](attachment:64aa56ff-8e64-456f-8a56-c5644e863288.png)","metadata":{},"attachments":{"64aa56ff-8e64-456f-8a56-c5644e863288.png":{"image/png":"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"}}},{"cell_type":"code","source":"# === STEP 1: UNZIP FILES ===\nimport zipfile\nimport os\n\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\n# === STEP 2: SPLIT CAT/DOGS INTO FOLDERS FOR FULL TRAINING ===\nimport shutil\n\nfull_train_dir = \"/kaggle/working/full_data\"\nos.makedirs(full_train_dir + \"/cat\", exist_ok=True)\nos.makedirs(full_train_dir + \"/dog\", exist_ok=True)\n\nall_images = os.listdir('/kaggle/working/train')\n\nfor img in all_images:\n    label = \"dog\" if \"dog\" in img else \"cat\"\n    shutil.move(f\"/kaggle/working/train/{img}\", f\"{full_train_dir}/{label}/{img}\")\n\n# === STEP 3: SETUP IMAGE GENERATOR ===\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nIMG_SIZE = (299, 299)\nBATCH_SIZE = 32\n\ndatagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=20,\n    zoom_range=0.2,\n    horizontal_flip=True\n)\n\ntrain_generator = datagen.flow_from_directory(\n    full_train_dir,\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='binary'\n)\n\n# === STEP 4: BUILD & TRAIN XCEPTION MODEL ===\nfrom tensorflow.keras.applications import Xception\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dropout, Dense\nfrom tensorflow.keras.optimizers import Adam\n\nbase_model = Xception(weights='imagenet', include_top=False, input_shape=(299, 299, 3))\nbase_model.trainable = False\n\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\nx = Dropout(0.3)(x)\noutput = Dense(1, activation='sigmoid')(x)\n\nmodel = Model(inputs=base_model.input, outputs=output)\nmodel.compile(optimizer=Adam(learning_rate=0.001), loss='binary_crossentropy', metrics=['accuracy'])\n\n# Initial training\nmodel.fit(train_generator, epochs=5)\n\n# Optional: Fine-tune\nbase_model.trainable = True\nmodel.compile(optimizer=Adam(learning_rate=1e-5), loss='binary_crossentropy', metrics=['accuracy'])\nmodel.fit(train_generator, epochs=5)\n\n# === STEP 5: MAKE TEST PREDICTIONS ===\nimport numpy as np\nfrom tensorflow.keras.preprocessing import image\nimport pandas as pd\n\ntest_dir = '/kaggle/working/test'\ntest_images = sorted(os.listdir(test_dir))  # maintain order\n\ndef load_and_prep(img_path):\n    img = image.load_img(img_path, target_size=IMG_SIZE)\n    x = image.img_to_array(img) / 255.0\n    return np.expand_dims(x, axis=0)\n\npreds = []\nids = []\n\nfor fname in test_images:\n    img_path = os.path.join(test_dir, fname)\n    prob = model.predict(load_and_prep(img_path), verbose=0)[0][0]\n    preds.append(prob)\n    ids.append(fname.split(\".\")[0])  # remove .jpg\n","metadata":{"execution":{"iopub.execute_input":"2025-04-20T20:42:13.384420Z","iopub.status.busy":"2025-04-20T20:42:13.383715Z","iopub.status.idle":"2025-04-20T22:17:31.972750Z","shell.execute_reply":"2025-04-20T22:17:31.971902Z","shell.execute_reply.started":"2025-04-20T20:42:13.384390Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === STEP 6: CREATE SUBMISSION CSV ===\nsubmission1 = pd.DataFrame({\"id\": ids, \"label\": preds})\nsubmission1.to_csv(\"MSBA.Session01.MayankSingh.Xception.csv\", index=False)","metadata":{"execution":{"iopub.execute_input":"2025-04-20T22:17:31.974295Z","iopub.status.busy":"2025-04-20T22:17:31.974033Z","iopub.status.idle":"2025-04-20T22:17:32.006072Z","shell.execute_reply":"2025-04-20T22:17:32.005542Z","shell.execute_reply.started":"2025-04-20T22:17:31.974270Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Transfer Learning Checkpoint: InceptionV3 (Full Dataset + Fine-Tuning)\n\nFollowing a strong validation performance in earlier model sweeps, **InceptionV3** was selected for full training and evaluation. The objective was to benchmark how well it generalizes when trained on the complete dataset and fine-tuned.\n\n#### Strategy:\n- **Full dataset (25,000 images)** used for training (no validation split)\n- Pretrained **InceptionV3** model (weights from ImageNet)\n- Custom classification head:\n  - GlobalAveragePooling2D\n  - Dropout(0.3)\n  - Dense(1, sigmoid)\n\n#### Training Procedure:\n- **Phase 1 (Frozen base):** 5 epochs, learning rate = 0.001\n- **Phase 2 (Unfrozen base):** 3 epochs, learning rate = 1e-5\n\n#### Test Prediction:\n- Generated predictions for all 12,500 test images\n- Submission file: `MSBA.Session01.MayankSingh.Inception.csv`\n\n---\n\n### Kaggle Evaluation:\n| Model        | Log Loss  |\n|--------------|-----------|\n| **InceptionV3** | **0.07372** |\n\nAlthough the log loss is slightly higher than Xception (0.05517), InceptionV3 still performs exceptionally well.\n\nThis checkpoint contributes valuable insight to our broader **model benchmarking effort** and will help guide further tuning or ensemble strategies.\n\n![image.png](attachment:9ebe33eb-47eb-4274-98cb-9d3e40e64f07.png)","metadata":{},"attachments":{"9ebe33eb-47eb-4274-98cb-9d3e40e64f07.png":{"image/png":"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"}}},{"cell_type":"code","source":"# === STEP 1: UNZIP FILES ===\nimport zipfile, os, shutil\n\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\n# === STEP 2: ORGANIZE FULL TRAIN DATA ===\nfull_train_dir = \"/kaggle/working/full_data\"\nos.makedirs(full_train_dir + \"/cat\", exist_ok=True)\nos.makedirs(full_train_dir + \"/dog\", exist_ok=True)\n\nfor img in os.listdir('/kaggle/working/train'):\n    label = \"dog\" if \"dog\" in img else \"cat\"\n    shutil.move(f\"/kaggle/working/train/{img}\", f\"{full_train_dir}/{label}/{img}\")\n\n# === STEP 3: DATA GENERATOR ===\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nIMG_SIZE = (299, 299)\nBATCH_SIZE = 32\n\ndatagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=20,\n    zoom_range=0.2,\n    horizontal_flip=True\n)\n\ntrain_generator = datagen.flow_from_directory(\n    full_train_dir,\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='binary'\n)\n\n# === STEP 4: INCEPTIONV3 MODEL ===\nfrom tensorflow.keras.applications import InceptionV3\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dropout, Dense\nfrom tensorflow.keras.optimizers import Adam\n\nbase_model = InceptionV3(weights='imagenet', include_top=False, input_shape=(299, 299, 3))\nbase_model.trainable = False\n\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\nx = Dropout(0.3)(x)\noutput = Dense(1, activation='sigmoid')(x)\n\nmodel = Model(inputs=base_model.input, outputs=output)\nmodel.compile(optimizer=Adam(learning_rate=0.001), loss='binary_crossentropy', metrics=['accuracy'])\n\n# Initial training: 5 epochs (frozen)\nmodel.fit(train_generator, epochs=5)\n\n# Fine-tuning: unfreeze & 3 more epochs\nbase_model.trainable = True\nmodel.compile(optimizer=Adam(learning_rate=1e-5), loss='binary_crossentropy', metrics=['accuracy'])\nmodel.fit(train_generator, epochs=3)\n\n# === STEP 5: PREDICT ON TEST SET ===\nimport numpy as np\nfrom tensorflow.keras.preprocessing import image\nimport pandas as pd\n\ntest_dir = '/kaggle/working/test'\ntest_images = sorted(os.listdir(test_dir))\n\ndef load_and_prep(img_path):\n    img = image.load_img(img_path, target_size=IMG_SIZE)\n    x = image.img_to_array(img) / 255.0\n    return np.expand_dims(x, axis=0)\n\npreds = []\nids = []\n\nfor fname in test_images:\n    prob = model.predict(load_and_prep(os.path.join(test_dir, fname)), verbose=0)[0][0]\n    preds.append(prob)\n    ids.append(fname.split(\".\")[0])","metadata":{"execution":{"iopub.execute_input":"2025-04-20T18:41:28.651140Z","iopub.status.busy":"2025-04-20T18:41:28.650858Z","iopub.status.idle":"2025-04-20T20:02:16.960853Z","shell.execute_reply":"2025-04-20T20:02:16.960050Z","shell.execute_reply.started":"2025-04-20T18:41:28.651117Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === STEP 6: GENERATE SUBMISSION FILE ===\nsubmission2 = pd.DataFrame({\"id\": ids, \"label\": preds})\nsubmission2.to_csv(\"MSBA.Session01.MayankSingh.Inception.csv\", index=False)","metadata":{"execution":{"iopub.execute_input":"2025-04-20T20:02:16.962638Z","iopub.status.busy":"2025-04-20T20:02:16.961919Z","iopub.status.idle":"2025-04-20T20:02:16.998756Z","shell.execute_reply":"2025-04-20T20:02:16.998220Z","shell.execute_reply.started":"2025-04-20T20:02:16.962617Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### EfficientNetB7 + Logistic Regression (Feature-Based Modeling)\n\n#### Objective\nI explored a two-stage modeling pipeline where I used a pre-trained EfficientNetB7 model for feature extraction and trained a Logistic Regression classifier on top to distinguish between cats and dogs.\n\n#### Methodology\n- **Feature Extraction**:\n  - I used EfficientNetB7 (`include_top=False`, `pooling='avg'`) pretrained on ImageNet.\n  - Resized all images to `600x600` before extracting features.\n  - I kept the CNN frozen and extracted high-level feature vectors from all training and test images.\n\n- **Classifier**:\n  - I trained a `LogisticRegression` model on the extracted features.\n  - I performed hyperparameter tuning using `GridSearchCV` with the following parameter grid:\n    - `C`: [0.01, 0.1, 1, 10]\n    - `penalty`: ['l2']\n    - `solver`: ['lbfgs']\n  - I used nested cross-validation to ensure stable results and then evaluated the best model on a 10% hold-out test set.\n\n#### Results\n\n| Metric           | Value    |\n|------------------|----------|\n| Accuracy         | 0.9900   |\n| Log Loss         | 0.0176   |\n| AUC (ROC)        | 0.9997   |\n| F1-score (macro) | 0.9900   |\n\n- **Best Parameters**: `{'C': 0.1, 'penalty': 'l2', 'solver': 'lbfgs'}`\n- **Classification Report**:\n  - Cat: Precision = 1.00, Recall = 0.99\n  - Dog: Precision = 0.99, Recall = 1.00\n\n#### Visual Evaluation\n- The confusion matrix shows the model misclassified only 13 out of 2500 examples.\n- The ROC curve indicates near-perfect separability (AUC = 0.9997).\n- Precision-Recall curve confirms excellent balance between precision and recall.\n\n#### Kaggle Submission\n- Private Leaderboard Log Loss: `0.04112`\n\n#### Conclusion\nThis modeling strategy gave me strong performance using simple logistic regression on top of high-quality CNN features. It’s a great baseline and very efficient compared to full fine-tuned CNNs.\n\n![image.png](attachment:1cb61802-1f6a-4e77-9202-b0f7c8158695.png)","metadata":{},"attachments":{"1cb61802-1f6a-4e77-9202-b0f7c8158695.png":{"image/png":"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"}}},{"cell_type":"code","source":"# === SETUP ===\nprint(\"Starting setup...\")\nimport zipfile, os, glob, shutil\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split, GridSearchCV, StratifiedKFold\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.metrics import log_loss, classification_report, confusion_matrix\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras.applications import EfficientNetB7\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\n\n# === STEP 1: UNZIP DATA ===\nprint(\"Unzipping data...\")\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\n# === STEP 2: LOAD EB7 FOR FEATURE EXTRACTION ===\nprint(\"Loading EfficientNetB7 model...\")\nIMG_SIZE = (600, 600)\nbase_model = EfficientNetB7(weights='imagenet', include_top=False, pooling='avg', input_shape=(600, 600, 3))\nmodel = Model(inputs=base_model.input, outputs=base_model.output)\n\ndef extract_features(image_paths):\n    print(f\"Extracting features from {len(image_paths)} images...\")\n    features = []\n    for idx, path in enumerate(image_paths):\n        if idx % 500 == 0:\n            print(f\"Processed {idx}/{len(image_paths)} images...\")\n        img = load_img(path, target_size=IMG_SIZE)\n        x = img_to_array(img)\n        x = preprocess_input(x)\n        x = np.expand_dims(x, axis=0)\n        feat = model.predict(x, verbose=0)\n        features.append(feat.squeeze())\n    print(\"Feature extraction complete.\")\n    return np.array(features)\n\n# === STEP 3: EXTRACT FEATURES FROM TRAIN IMAGES ===\nprint(\"Loading and labeling training data...\")\ntrain_files = glob.glob(\"/kaggle/working/train/*.jpg\")\ntrain_labels = [1 if 'dog' in fname else 0 for fname in train_files]\nX = extract_features(train_files)\ny = np.array(train_labels)\n\n# === STEP 4: SPLIT OFF TEST SET FOR FINAL EVALUATION ===\nprint(\"Splitting data into train and holdout test sets...\")\nX_train_cv, X_test_final, y_train_cv, y_test_final = train_test_split(X, y, test_size=0.1, stratify=y, random_state=42)\n\n# === STEP 5: NESTED CV WITH GRID SEARCH FOR LOGISTIC REGRESSION ===\nprint(\"Starting GridSearchCV with nested cross-validation...\")\nparam_grid = {\n    'C': [0.01, 0.1, 1, 10],\n    'penalty': ['l2'],\n    'solver': ['lbfgs']\n}\ninner_cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)\nlogreg = LogisticRegression(max_iter=1000)\ngrid = GridSearchCV(logreg, param_grid, cv=inner_cv, scoring='neg_log_loss', n_jobs=-1, verbose=1)\ngrid.fit(X_train_cv, y_train_cv)\n\n# === STEP 6: EVALUATE BEST MODEL ON FINAL TEST SET ===\nprint(\"Evaluating best model on test set...\")\nbest_lr = grid.best_estimator_\ny_pred_proba = best_lr.predict_proba(X_test_final)[:, 1]\ny_pred = best_lr.predict(X_test_final)\n\nprint(\"Best Parameters:\", grid.best_params_)\nprint(\"Test Log Loss:\", round(log_loss(y_test_final, y_pred_proba), 4))\nprint(\"Classification Report:\\n\", classification_report(y_test_final, y_pred))\n\n# === CONFUSION MATRIX ===\nprint(\"Displaying confusion matrix...\")\ncm = confusion_matrix(y_test_final, y_pred)\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['Cat', 'Dog'], yticklabels=['Cat', 'Dog'])\nplt.xlabel('Predicted')\nplt.ylabel('Actual')\nplt.title('Confusion Matrix - Logistic Regression')\nplt.show()\n","metadata":{"execution":{"iopub.execute_input":"2025-04-20T23:15:26.299594Z","iopub.status.busy":"2025-04-20T23:15:26.299079Z","iopub.status.idle":"2025-04-21T00:02:27.098246Z","shell.execute_reply":"2025-04-21T00:02:27.097498Z","shell.execute_reply.started":"2025-04-20T23:15:26.299571Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import roc_curve, auc, precision_recall_curve\n\n# === ROC CURVE ===\nfpr, tpr, _ = roc_curve(y_test_final, y_pred_proba)\nroc_auc = auc(fpr, tpr)\n\nplt.figure(figsize=(6, 5))\nplt.plot(fpr, tpr, label=f'ROC curve (AUC = {roc_auc:.4f})')\nplt.plot([0, 1], [0, 1], linestyle='--', color='gray')\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('ROC Curve - Logistic Regression')\nplt.legend(loc='lower right')\nplt.grid()\nplt.show()\n\n# === PRECISION-RECALL CURVE ===\nprecision, recall, _ = precision_recall_curve(y_test_final, y_pred_proba)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, label='Precision-Recall curve')\nplt.xlabel('Recall')\nplt.ylabel('Precision')\nplt.title('Precision-Recall Curve - Logistic Regression')\nplt.legend()\nplt.grid()\nplt.show()","metadata":{"execution":{"iopub.execute_input":"2025-04-21T00:10:02.146414Z","iopub.status.busy":"2025-04-21T00:10:02.146107Z","iopub.status.idle":"2025-04-21T00:10:02.500480Z","shell.execute_reply":"2025-04-21T00:10:02.499680Z","shell.execute_reply.started":"2025-04-21T00:10:02.146389Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === STEP 7: PREDICT ON KAGGLE TEST SET ===\nprint(\"Extracting test features and predicting...\")\n\n# Get test image paths\ntest_dir = \"/kaggle/working/test\"\ntest_files = sorted(glob.glob(f\"{test_dir}/*.jpg\"))\ntest_ids = [os.path.basename(f).split('.')[0] for f in test_files]\n\n# Extract features from test images\nX_test_submission = extract_features(test_files)\n\n# Predict probabilities\ntest_preds = best_lr.predict_proba(X_test_submission)[:, 1]\n\n# Create submission DataFrame\nsubmission = pd.DataFrame({\n    \"id\": test_ids,\n    \"label\": test_preds\n})\n\n# Save to CSV\nsubmission_file = \"MSBA.SessionX.MayankSingh.EB7_LR.csv\"\nsubmission.to_csv(submission_file, index=False)\n\nprint(f\"Submission file saved as: {submission_file}\")","metadata":{"execution":{"iopub.execute_input":"2025-04-21T00:11:51.505601Z","iopub.status.busy":"2025-04-21T00:11:51.505052Z","iopub.status.idle":"2025-04-21T00:35:52.322944Z","shell.execute_reply":"2025-04-21T00:35:52.322221Z","shell.execute_reply.started":"2025-04-21T00:11:51.505576Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### EfficientNetB7 Features + Logistic Regression & XGBoost Blended Ensemble\n\n#### Objective\nAs part of model experimentation, I investigated whether ensembling two high-performing models could outperform either alone. Specifically, I blended predictions from **Logistic Regression** and **XGBoost**, both trained on features extracted from **EfficientNetB7**, a powerful pretrained CNN architecture.\n\n---\n\n#### Step 1: Feature Extraction\n- Used EfficientNetB7 (`include_top=False`, `pooling='avg'`) pretrained on ImageNet.\n- Extracted 2560-dimensional feature vectors from `600x600` resized training images.\n- EfficientNetB7 was kept frozen; only used for embedding generation.\n\n---\n\n#### Step 2: Base Model Training & Tuning\n\n**Logistic Regression**  \n- Tuned `C` over `[0.01, 0.1, 1, 10]` using 3-fold Stratified CV.  \n- Best configuration: `C = 0.1`, solver = `lbfgs`.  \n- Validation Log Loss: **0.01761**.\n\n**XGBoost**  \n- Tuned over 16 hyperparameter combinations:  \n  - `n_estimators`  [100, 200]  \n  - `learning_rate`  [0.01, 0.1]  \n  - `max_depth`  [3, 5]  \n  - `subsample`  [0.8, 1.0]  \n- Validation Log Loss: **0.01989**.\n\n---\n\n#### Step 3: Ensemble via Weighted Blending\n- Blended LR and XGB predicted probabilities using different weight combinations.\n- Searched for the best `w` in:  \n  `blended_pred = w * LR_prob + (1 - w) * XGB_prob`\n- Best result:  \n  **Weight LR = 0.75, XGB = 0.25** → **Blended Log Loss: 0.01731**\n\n---\n\n#### Step 4: Final Training & Submission\n- Retrained both Logistic Regression and XGBoost on **full training data** (entire feature set).\n- Used the optimized weights (LR: 0.75, XGB: 0.25) to blend predictions on the test set.\n\n---\n\n#### Summary\nThis experiment confirmed that carefully tuning and combining base learners (even simple ones like Logistic Regression) can outperform more complex standalone models. \n","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split\nfrom sklearn.linear_model import LogisticRegression\nfrom xgboost import XGBClassifier\nfrom sklearn.metrics import log_loss\nimport numpy as np\n\n# === 0. SPLIT DATA ===\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.1, stratify=y, random_state=42)\n\n# === 1. LOGISTIC REGRESSION with GRID SEARCH ===\nprint(\"Training Logistic Regression...\")\nlr_param_grid = {\n    'C': [0.01, 0.1, 1, 10],\n    'penalty': ['l2'],\n    'solver': ['lbfgs']\n}\nlr_model = LogisticRegression(max_iter=1000)\nlr_cv = GridSearchCV(lr_model, lr_param_grid, cv=StratifiedKFold(n_splits=3, shuffle=True, random_state=42),\n                     scoring='neg_log_loss', verbose=1, n_jobs=-1)\nlr_cv.fit(X_train, y_train)\nbest_lr = lr_cv.best_estimator_\nlr_val_pred = best_lr.predict_proba(X_val)[:, 1]\nlr_logloss = log_loss(y_val, lr_val_pred)\nprint(f\"Logistic Regression Log Loss: {lr_logloss:.5f}\")\n\n# === 2. XGBOOST with GRID SEARCH ===\nprint(\"\\nTraining XGBoost...\")\nxgb_param_grid = {\n    'n_estimators': [100, 200],\n    'learning_rate': [0.01, 0.1],\n    'max_depth': [3, 5],\n    'subsample': [0.8, 1.0]\n}\nxgb_model = XGBClassifier(use_label_encoder=False, eval_metric='logloss')\nxgb_cv = GridSearchCV(xgb_model, xgb_param_grid, cv=StratifiedKFold(n_splits=3, shuffle=True, random_state=42),\n                      scoring='neg_log_loss', verbose=1, n_jobs=-1)\nxgb_cv.fit(X_train, y_train)\nbest_xgb = xgb_cv.best_estimator_\nxgb_val_pred = best_xgb.predict_proba(X_val)[:, 1]\nxgb_logloss = log_loss(y_val, xgb_val_pred)\nprint(f\"XGBoost Log Loss: {xgb_logloss:.5f}\")\n\n# === 3. ENSEMBLE BLENDING (GRID SEARCH OVER WEIGHTS) ===\nprint(\"\\nSearching for best ensemble weights...\")\nbest_score = float('inf')\nbest_weight = None\n\nfor w in np.linspace(0, 1, 21):\n    blended_pred = w * lr_val_pred + (1 - w) * xgb_val_pred\n    blended_logloss = log_loss(y_val, blended_pred)\n    print(f\"Weight LR: {w:.2f}, XGB: {1 - w:.2f} => Log Loss: {blended_logloss:.5f}\")\n    \n    if blended_logloss < best_score:\n        best_score = blended_logloss\n        best_weight = w\n\nprint(f\"\\nBest Ensemble Weight\\n LR: {best_weight:.2f}, XGB: {1 - best_weight:.2f} | Log Loss: {best_score:.5f}\")","metadata":{"execution":{"iopub.execute_input":"2025-04-21T00:38:12.997329Z","iopub.status.busy":"2025-04-21T00:38:12.996553Z","iopub.status.idle":"2025-04-21T01:01:47.473871Z","shell.execute_reply":"2025-04-21T01:01:47.473009Z","shell.execute_reply.started":"2025-04-21T00:38:12.997300Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### EB7 Feature Ensemble: Logistic Regression + XGBoost\n\nIn this experiment, I extracted deep visual features from all images using the **EfficientNetB7** model pre-trained on ImageNet. These 600×600 pooled representations served as input to two separate classifiers: **Logistic Regression (LR)** and **XGBoost (XGB)**.\n\n#### Approach:\n- **Nested cross-validation** was used to tune hyperparameters:\n  - Best LR: `C=0.1`, `solver=lbfgs` → Log Loss: **0.0176**\n  - Best XGB: `n_estimators=200`, `learning_rate=0.1`, `max_depth=3`, `subsample=0.8` → Log Loss: **0.0199**\n\n- Next, I performed **ensemble blending** by averaging their predicted probabilities with different weight ratios.\n\n#### Result:\n- The optimal blending weight was **LR: 0.50**, **XGB: 0.50**\n- Final validation log loss of the ensemble: **0.01374**\n\nThis model was then used to generate test predictions and submit to Kaggle, resulting in a **public score of 0.03929**.\n\n![image.png](attachment:587eea7c-850a-4123-b206-457252e5f6ce.png)","metadata":{},"attachments":{"587eea7c-850a-4123-b206-457252e5f6ce.png":{"image/png":"iVBORw0KGgoAAAANSUhEUgAABJcAAABVCAYAAAD0ZgVEAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAADeHSURBVHhe7d1/WJRV/v/xp4vr5K+xtHFtA80FrQWtkD6mfAxhTSU/KrUG1WpuYrai5qbmZlSr9IPIMtvUtEjbEMtgzVBDwAw0v1CuYJZQm5AhlOakxojaoDTfP+4ZGIYfAlpr9Xpc11zXzH2f+8ec+wyX5+0579PG4XA4EBERERERERERaYVfeW4QERERERERERFpLgWXRERERERERESk1RRcEhERERERERGRVlNwSUREREREREREWk3BJRERERERERERaTUFl0REREREREREpNUUXBIRERERERERkVZTcElERERERERERFpNwSUREREREREREWk1BZdERERERERERKTVFFwSEREREREREZFWU3BJRERERERERERaTcElERERERERERFpNQWXRERERERERESk1RRckh+V/ZgNe7XnVvlJs9uw2uyeW0VEREREROQXoo3D4XB4bvzFKUplXvJu432HwUx/eAw+nmUO5bDo+SysAHgzctY0wiyunXbKcpJZtiqV3C/s0MGH4D9GM/3PofiYXGWKeCM2GedVanXwJfSPkYT7mz33GNzuzTJsFnOG1Vz07OzlZL/6Ai+/mUfZSTBdMZio6GlMDPWm5rZ+RNZ/TWHg33Jg1DI+XTrih7mHahuF61ey9PUNFFrBEjCWKfOmEd6r/tXspVksfTEHK+A9YhYzQhup23NuHxcWa84LLMoqh6snkHC7v+fuOmxFqSQuSSWt0ApYCLglkhl3RRJwiavEbuKvjyLR6s2MTdnMafp0reD63QRyZ3wkAZ67f6EK18ay+qOztNsWlBMRERERETkXCi4BbJ1N7ykbnR+8mZGWzZz+dYtY10Qx8BFXaMif2Ow0pvQCqktIvCOC+F0NjNywjGHVlmcJMwPkcN/vppDmWcYpYF4Gm+7x9dxM7iNBjF9jMz70msam7FnN62CXrGTc6AQKGrqtWxJ5Z1EojYSzfjD2nFhuiE6F6BR2PhzoufvcNfosLEQkZvDcMNc3tlO8ZgZ/WpCD1TmKKmBeNpvu8XY/qNa5tI8LUNlLEYQkFMEtiexfFOq528lGdmw40WuNcFkdXv7EZqQx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91meR6PHnYsGnkfBoptJ6PIMKXc3N7dT81WdqqKdW36lVmmsnqqrqKJdbf3kLOCGNwNJez6Cmslv1VVUVjSzrYqIiIiIiMgvzo8eXAKosFXybYWNyy/7DW3buvf67djKS8j/qByb3Y7J1Bmfqwfg523G5FYK4MyZar48+DUXdzHTxeyWEPoCVxtcqjcP7sdVXcSbzxXS/95I+ihicG6qinhzyW76ThtPv+YEAC8YVRQsieahXYFMmRhIt1OlvP1yKtydzMLRyqskIiIiIiIizfNfCS4BHD1WwfHKE/ym+6V1RjA1x3f2Kr4+/A2dO3Wk6yVdPHdf0Kr2f8BeAhnQu2XfWeSHUcXBXWlsevcAR9v3YNCImxh6pQJLIiIiIiIi0nz/teASzhFM1m+O0q3rxVzcpTNt2rTxLFKHw+Hg24rjHDn6LZZLu/6kRiyJiIiIiIiIiPwc/VeDSzhzMB05+i0nT32HuXNHZ3Ludnh5GbnGq6u/x26v4uSpUxyvPEmH9hfRrevF5y/HkoiIiIiIiIiItNp/PbjkUlV1mhMnT3Ly5HdUnT5NdfX3AHh5/Ypf//rXdGx/ER07dji3VeFEREREREREROS8umCCSyIiIiIiIiIi8tNjzD0TERERERERERFpBQWXRERERERERESk1RRcEhERERERERGRVlNwSUREREREREREWk3BJRERERERERERaTUFl0REREREREREpNUUXBIRERERERERkVZTcElERERERERERFpNwSUREREREREREWk1BZdERERERERERKTVFFwSEREREREREZFWU3BJRERERERERERaTcElERERERERERFpNQWXRERERERERESk1RRcEhERERERERGRVmvzWfEXDs+NIiIiIiIiIiIizdHG4XAouCQiIiIiIiIiIq2iaXEiIiIiIiIiItJqCi6JiIiIiIiIiEirKbgkIiIiIiIiIiKtpuCSiIiIiIiIiIi0moJLIiIiIiIiIiLSagouiYiIiIiIiIhIqym4JCIiIiIiIiIirabgkoiIiIiIiIiItJqCSyIiIiIiIiIi0mr/H56BAT/sKDXaAAAAAElFTkSuQmCC"}}},{"cell_type":"code","source":"# === SETUP ===\nimport zipfile, os, glob, shutil\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split, GridSearchCV, StratifiedKFold\nfrom sklearn.linear_model import LogisticRegression\nfrom xgboost import XGBClassifier\nfrom sklearn.metrics import log_loss\nfrom tensorflow.keras.applications import EfficientNetB7\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\n\n# === UNZIP ===\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\n# === EB7 FEATURE EXTRACTOR ===\nIMG_SIZE = (600, 600)\nbase_model = EfficientNetB7(weights='imagenet', include_top=False, pooling='avg', input_shape=(600, 600, 3))\nmodel = Model(inputs=base_model.input, outputs=base_model.output)\n\ndef extract_features(image_paths):\n    features = []\n    for idx, path in enumerate(image_paths):\n        if idx % 500 == 0:\n            print(f\"Processed {idx}/{len(image_paths)}\")\n        img = load_img(path, target_size=IMG_SIZE)\n        x = img_to_array(img)\n        x = preprocess_input(x)\n        x = np.expand_dims(x, axis=0)\n        feat = model.predict(x, verbose=0)\n        features.append(feat.squeeze())\n    return np.array(features)\n\n# === EXTRACT FEATURES FOR TRAIN ===\ntrain_files = glob.glob(\"/kaggle/working/train/*.jpg\")\ntrain_labels = [1 if 'dog' in fname else 0 for fname in train_files]\nX = extract_features(train_files)\ny = np.array(train_labels)\n\n# === TRAIN/VALIDATION SPLIT ===\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.1, stratify=y, random_state=42)\n\n# === LOGISTIC REGRESSION TUNING ===\nprint(\"\\nTraining Logistic Regression...\")\nlr_param_grid = {'C': [0.01, 0.1, 1, 10], 'penalty': ['l2'], 'solver': ['lbfgs']}\nlr_model = LogisticRegression(max_iter=1000)\nlr_cv = GridSearchCV(lr_model, lr_param_grid, cv=StratifiedKFold(3, shuffle=True, random_state=42),\n                     scoring='neg_log_loss', n_jobs=-1, verbose=1)\nlr_cv.fit(X_train, y_train)\nbest_lr = lr_cv.best_estimator_\nlr_val_pred = best_lr.predict_proba(X_val)[:, 1]\nlr_loss = log_loss(y_val, lr_val_pred)\nprint(f\"Log Loss (LR): {lr_loss:.5f}\")\n\n# === XGBOOST TUNING ===\nprint(\"\\nTraining XGBoost...\")\nxgb_param_grid = {\n    'n_estimators': [100, 200],\n    'learning_rate': [0.01, 0.1],\n    'max_depth': [3, 5],\n    'subsample': [0.8, 1.0]\n}\nxgb_model = XGBClassifier(use_label_encoder=False, eval_metric='logloss')\nxgb_cv = GridSearchCV(xgb_model, xgb_param_grid, cv=StratifiedKFold(3, shuffle=True, random_state=42),\n                      scoring='neg_log_loss', n_jobs=-1, verbose=1)\nxgb_cv.fit(X_train, y_train)\nbest_xgb = xgb_cv.best_estimator_\nxgb_val_pred = best_xgb.predict_proba(X_val)[:, 1]\nxgb_loss = log_loss(y_val, xgb_val_pred)\nprint(f\"Log Loss (XGB): {xgb_loss:.5f}\")\n\n# === ENSEMBLE GRID SEARCH ===\nprint(\"\\nGrid search for best ensemble weights...\")\nbest_score = float('inf')\nbest_weight = None\nfor w in np.linspace(0, 1, 21):\n    blended = w * lr_val_pred + (1 - w) * xgb_val_pred\n    loss = log_loss(y_val, blended)\n    print(f\"LR: {w:.2f} | XGB: {1-w:.2f} => Log Loss: {loss:.5f}\")\n    if loss < best_score:\n        best_score = loss\n        best_weight = w\n\nprint(f\"\\nBest Blending Weight LR: {best_weight:.2f} | XGB: {1-best_weight:.2f} | Log Loss: {best_score:.5f}\")\n\n# === PREDICT ON TEST SET & SUBMIT ===\nprint(\"\\nExtracting test features and generating submission...\")\ntest_files = sorted(glob.glob(\"/kaggle/working/test/*.jpg\"))\ntest_ids = [os.path.basename(f).split('.')[0] for f in test_files]\nX_test_submission = extract_features(test_files)\n\nlr_test_pred = best_lr.predict_proba(X_test_submission)[:, 1]\nxgb_test_pred = best_xgb.predict_proba(X_test_submission)[:, 1]\nensemble_test_pred = best_weight * lr_test_pred + (1 - best_weight) * xgb_test_pred\n\nsubmission = pd.DataFrame({\"id\": test_ids, \"label\": ensemble_test_pred})\nsubmission_file = \"MSBA.Session01.MayankSingh_ensemble.csv\"\nsubmission.to_csv(submission_file, index=False)\nprint(f\"Submission saved: {submission_file}\")","metadata":{"execution":{"iopub.execute_input":"2025-04-21T02:23:18.006594Z","iopub.status.busy":"2025-04-21T02:23:18.006038Z","iopub.status.idle":"2025-04-21T03:58:56.336133Z","shell.execute_reply":"2025-04-21T03:58:56.335323Z","shell.execute_reply.started":"2025-04-21T02:23:18.006569Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### EB7 Feature Stacking Ensemble (LR + XGBoost)\n\nIn this experiment, I used **stacked generalization** to combine the strengths of Logistic Regression and XGBoost on top of **EfficientNetB7 feature embeddings**.\n\n#### Model Setup:\n- **Base learners**:\n  - `LogisticRegression(C=0.02)`\n  - `XGBClassifier(n_estimators=300, learning_rate=0.1, max_depth=4, subsample=0.85)`\n- **Meta-learner**: Logistic Regression\n- `passthrough=True` was used to let the meta-learner access original EB7 features in addition to base model predictions.\n\n#### Performance:\n- Validation Log Loss: **0.01775**\n\nThis model was then used to generate predictions on the Kaggle test set.\n\nWhile the stacked model showed strong validation performance, its public leaderboard score was **0.05040**, slightly worse than the manually weighted ensemble. Further tuning of the meta-learner or base model diversity might improve this.\n\n![image.png](attachment:7e068563-f9bc-4337-9a85-52d38bf1a014.png)","metadata":{},"attachments":{"7e068563-f9bc-4337-9a85-52d38bf1a014.png":{"image/png":"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"}}},{"cell_type":"code","source":"from sklearn.ensemble import StackingClassifier\n\nbase_learners = [\n    ('lr', LogisticRegression(C=0.02, solver='lbfgs', max_iter=10000)),\n    ('xgb', XGBClassifier(n_estimators=300, learning_rate=0.1, max_depth=4, subsample=0.85, use_label_encoder=False, eval_metric='logloss'))\n]\n\nmeta_learner = LogisticRegression(max_iter=1000)\nstacked_model = StackingClassifier(estimators=base_learners, final_estimator=meta_learner, cv=3, passthrough=True)\nstacked_model.fit(X_train, y_train)\n\n# === VALIDATION PERFORMANCE ===\nval_pred_proba = stacked_model.predict_proba(X_val)[:, 1]\nval_logloss = log_loss(y_val, val_pred_proba)\nprint(f\"Stacked Model Log Loss on Validation Set: {val_logloss:.5f}\")","metadata":{"execution":{"iopub.execute_input":"2025-04-21T04:13:08.725609Z","iopub.status.busy":"2025-04-21T04:13:08.725062Z","iopub.status.idle":"2025-04-21T04:17:13.499191Z","shell.execute_reply":"2025-04-21T04:17:13.498367Z","shell.execute_reply.started":"2025-04-21T04:13:08.725586Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === PREDICT ON TEST DATA ===\ntest_files = sorted(glob.glob(\"/kaggle/working/test/*.jpg\"))\ntest_ids = [os.path.basename(f).split('.')[0] for f in test_files]\nX_test_submission = extract_features(test_files)\ntest_preds = stacked_model.predict_proba(X_test_submission)[:, 1]\n\n# === GENERATE SUBMISSION FILE ===\nsubmission = pd.DataFrame({\"id\": test_ids, \"label\": test_preds})\nsubmission_file = \"MSBA.Session01.MayankSingh_stacked.csv\"\nsubmission.to_csv(submission_file, index=False)\nprint(f\"Submission saved: {submission_file}\")","metadata":{"execution":{"iopub.execute_input":"2025-04-21T04:19:27.364834Z","iopub.status.busy":"2025-04-21T04:19:27.364156Z","iopub.status.idle":"2025-04-21T04:43:54.228558Z","shell.execute_reply":"2025-04-21T04:43:54.227800Z","shell.execute_reply.started":"2025-04-21T04:19:27.364796Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### EfficientNetB3 Feature Ensemble (Logistic Regression + XGBoost)\n\nIn this experiment, I extracted image embeddings using **EfficientNetB3** and trained two classifiers — Logistic Regression and XGBoost — followed by a **weighted ensemble** of their predicted probabilities.\n\n#### Training Process:\n- **EfficientNetB3** (pretrained on ImageNet) used as a frozen feature extractor on (300×300) resized images.\n- Extracted features were split into training and validation sets.\n\n#### Models:\n- **Logistic Regression**:\n  - Extensive grid search on regularization strength `C` (10 values between 1e-4 and 100)\n  - Optimized using `GridSearchCV` with 3-fold Stratified CV\n- **XGBoost**:\n  - Grid search on `n_estimators`, `learning_rate`, `max_depth`, `subsample`\n\n#### Ensemble Strategy:\n- Blended final validation predictions using weights ranging from 0 to 1\n- Best log loss observed at:\n  - **Weight LR = 0.55**, **XGB = 0.45**\n  - **Validation Log Loss = 0.02319**\n\n#### Kaggle Performance:\n- Private Leaderboard Score: **0.05164**\n\nWhile EB3 features were lighter to compute than EB7, they performed slightly worse on the leaderboard. This setup still gave valuable insight into the tradeoff between model depth and performance.\n\n![image.png](attachment:5744664a-852b-49e3-af9b-5791d7533ccc.png)","metadata":{},"attachments":{"5744664a-852b-49e3-af9b-5791d7533ccc.png":{"image/png":"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"}}},{"cell_type":"code","source":"# === SETUP ===\nimport zipfile, os, glob\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split, GridSearchCV, StratifiedKFold\nfrom sklearn.linear_model import LogisticRegression\nfrom xgboost import XGBClassifier\nfrom sklearn.metrics import log_loss\nfrom tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\n\n# === UNZIP ===\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\n# === EB3 MODEL ===\nIMG_SIZE = (300, 300)\nbase_model = EfficientNetB3(weights='imagenet', include_top=False, pooling='avg', input_shape=(300, 300, 3))\nmodel = Model(inputs=base_model.input, outputs=base_model.output)\n\ndef extract_features(image_paths):\n    features = []\n    for idx, path in enumerate(image_paths):\n        if idx % 500 == 0:\n            print(f\"Processed {idx}/{len(image_paths)}\")\n        img = load_img(path, target_size=IMG_SIZE)\n        x = img_to_array(img)\n        x = preprocess_input(x)\n        x = np.expand_dims(x, axis=0)\n        feat = model.predict(x, verbose=0)\n        features.append(feat.squeeze())\n    return np.array(features)\n\n# === EXTRACT TRAIN FEATURES ===\ntrain_files = glob.glob(\"/kaggle/working/train/*.jpg\")\ntrain_labels = [1 if 'dog' in fname else 0 for fname in train_files]\nX = extract_features(train_files)\ny = np.array(train_labels)\n\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.1, stratify=y, random_state=42)\n\n# === LOGISTIC REGRESSION (EXTENSIVE GRID) ===\nprint(\"Training Logistic Regression...\")\nlr_param_grid = {\n    'C': np.logspace(-4, 2, 10),  # C from 0.0001 to 100\n    'penalty': ['l2'],\n    'solver': ['lbfgs']\n}\nlr_model = LogisticRegression(max_iter=10000)\nlr_cv = GridSearchCV(lr_model, lr_param_grid, cv=StratifiedKFold(3, shuffle=True, random_state=42),\n                     scoring='neg_log_loss', n_jobs=-1, verbose=1)\nlr_cv.fit(X_train, y_train)\nbest_lr = lr_cv.best_estimator_\nlr_val_pred = best_lr.predict_proba(X_val)[:, 1]\nprint(f\"Log Loss (LR): {log_loss(y_val, lr_val_pred):.5f}\")\n\n# === XGBOOST (NORMAL GRID) ===\nprint(\"Training XGBoost...\")\nxgb_param_grid = {\n    'n_estimators': [100, 200],\n    'learning_rate': [0.001, 0.01, 0.1],\n    'max_depth': [3, 5],\n    'subsample': [0.8,1.0]\n}\nxgb_model = XGBClassifier(use_label_encoder=False, eval_metric='logloss')\nxgb_cv = GridSearchCV(xgb_model, xgb_param_grid, cv=StratifiedKFold(3, shuffle=True, random_state=42),\n                      scoring='neg_log_loss', n_jobs=-1, verbose=1)\nxgb_cv.fit(X_train, y_train)\nbest_xgb = xgb_cv.best_estimator_\nxgb_val_pred = best_xgb.predict_proba(X_val)[:, 1]\nprint(f\"Log Loss (XGB): {log_loss(y_val, xgb_val_pred):.5f}\")\n\n# === GRID SEARCH OVER BLENDING WEIGHTS ===\nprint(\"Blending models...\")\nbest_score = float('inf')\nbest_wt = None\n\nfor w in np.linspace(0, 1, 21):\n    blended = w * lr_val_pred + (1 - w) * xgb_val_pred\n    loss = log_loss(y_val, blended)\n    print(f\"LR: {w:.2f} | XGB: {1-w:.2f} => Log Loss: {loss:.5f}\")\n    if loss < best_score:\n        best_score = loss\n        best_wt = w\n\nprint(f\"\\nBest Blend → LR: {best_wt:.2f} | XGB: {1-best_wt:.2f} | Log Loss: {best_score:.5f}\")\n","metadata":{"execution":{"iopub.execute_input":"2025-04-21T14:50:55.357556Z","iopub.status.busy":"2025-04-21T14:50:55.357066Z","iopub.status.idle":"2025-04-21T15:43:53.663665Z","shell.execute_reply":"2025-04-21T15:43:53.662692Z","shell.execute_reply.started":"2025-04-21T14:50:55.357532Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === TEST PREDICTION AND SUBMISSION ===\nprint(\"Extracting test features...\")\ntest_files = sorted(glob.glob(\"/kaggle/working/test/*.jpg\"))\ntest_ids = [os.path.basename(f).split('.')[0] for f in test_files]\nX_test_submission = extract_features(test_files)\n\nlr_test_pred = best_lr.predict_proba(X_test_submission)[:, 1]\nxgb_test_pred = best_xgb.predict_proba(X_test_submission)[:, 1]\nfinal_test_pred = best_wt * lr_test_pred + (1 - best_wt) * xgb_test_pred\n\nsubmission = pd.DataFrame({\"id\": test_ids, \"label\": final_test_pred})\nsubmission.to_csv(\"MSBA.SessionX.MayankSingh_EB3_ensemble.csv\", index=False)\nprint(\"Submission file saved.\")","metadata":{"execution":{"iopub.execute_input":"2025-04-21T15:50:18.271313Z","iopub.status.busy":"2025-04-21T15:50:18.270172Z","iopub.status.idle":"2025-04-21T16:05:56.274917Z","shell.execute_reply":"2025-04-21T16:05:56.274308Z","shell.execute_reply.started":"2025-04-21T15:50:18.271282Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### ConvNeXtV2 Fine-tuned on Augmented Image Data\n\nAfter trying out multiple pretrained CNNs, I decided to explore the ConvNeXtV2 architecture to see if a more modern backbone could improve classification performance. \n\n**Steps Taken:**\n- Used `ConvNeXtBase` pretrained on ImageNet as the feature extractor, excluding the top classification layer.\n- Applied data augmentation during training via `ImageDataGenerator` with:\n  - `rotation_range=20`\n  - `horizontal_flip=True`\n- Split the original dataset into 90% training and 10% validation using `train_test_split` per class.\n- Appended a custom classification head with `GlobalAveragePooling`, `Dropout(0.3)`, and a final `Dense(1, activation='sigmoid')` layer.\n- Trained the model for 5 epochs (with frozen base layers).\n- Did **not fine-tune** further as early results were promising and val loss remained stable.\n\n**Observations:**\n- Validation accuracy plateaued at **0.9964–0.9972** after 2 epochs, with **val_loss dropping to as low as 0.0090**.\n- The model showed excellent generalization with stable performance in the last few epochs, indicating it wasn’t overfitting despite high accuracy.\n- Final test predictions were generated and saved in the submission file.\n\n**Kaggle Score:**  \nThe submission file `MSBA.Session01.MayankSingh_ConvNeXt.csv` achieved a **log loss of 0.04906**, confirming that ConvNeXt is a strong backbone in this binary classification setup.\n\nThis experiment reinforces that modern transformer-inspired CNNs like ConvNeXt can outperform legacy architectures with minimal tuning.\n\n![image.png](attachment:c9b377e4-8bb5-448d-b20f-9d896627d8cf.png)","metadata":{},"attachments":{"c9b377e4-8bb5-448d-b20f-9d896627d8cf.png":{"image/png":"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"}}},{"cell_type":"code","source":"import zipfile, os\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import ConvNeXtBase\nfrom tensorflow.keras.applications.convnext import preprocess_input\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dropout, Dense\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\n\n# === UNZIP ===\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\n# === ORGANIZE FOLDERS ===\nimport shutil\ntrain_dir = '/kaggle/working/train'\nbase_dir = '/kaggle/working/dataset'\nfor label in ['dog', 'cat']:\n    os.makedirs(f\"{base_dir}/train/{label}\", exist_ok=True)\n    os.makedirs(f\"{base_dir}/val/{label}\", exist_ok=True)\n\nfrom sklearn.model_selection import train_test_split\nall_files = os.listdir(train_dir)\ndog_files = [f for f in all_files if f.startswith(\"dog\")]\ncat_files = [f for f in all_files if f.startswith(\"cat\")]\ntrain_dog, val_dog = train_test_split(dog_files, test_size=0.1, random_state=42)\ntrain_cat, val_cat = train_test_split(cat_files, test_size=0.1, random_state=42)\n\nfor f in train_dog: shutil.copy2(f\"{train_dir}/{f}\", f\"{base_dir}/train/dog/{f}\")\nfor f in val_dog: shutil.copy2(f\"{train_dir}/{f}\", f\"{base_dir}/val/dog/{f}\")\nfor f in train_cat: shutil.copy2(f\"{train_dir}/{f}\", f\"{base_dir}/train/cat/{f}\")\nfor f in val_cat: shutil.copy2(f\"{train_dir}/{f}\", f\"{base_dir}/val/cat/{f}\")\n\n# === DATA AUGMENTATION ===\nIMG_SIZE = (224, 224)\nBATCH_SIZE = 32\n\ntrain_datagen = ImageDataGenerator(\n    preprocessing_function=preprocess_input,\n    rotation_range=20,\n    horizontal_flip=True\n)\n\nval_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)\n\ntrain_gen = train_datagen.flow_from_directory(\n    f\"{base_dir}/train\", target_size=IMG_SIZE, batch_size=BATCH_SIZE, class_mode='binary'\n)\nval_gen = val_datagen.flow_from_directory(\n    f\"{base_dir}/val\", target_size=IMG_SIZE, batch_size=BATCH_SIZE, class_mode='binary'\n)\n\n# === ConvNeXtV2 MODEL ===\nbase_model = ConvNeXtBase(weights='imagenet', include_top=False, input_shape=(224, 224, 3))\nbase_model.trainable = False  # Freeze for now\n\nx = GlobalAveragePooling2D()(base_model.output)\nx = Dropout(0.3)(x)\noutput = Dense(1, activation='sigmoid')(x)\n\nmodel = Model(inputs=base_model.input, outputs=output)\nmodel.compile(optimizer=Adam(learning_rate=0.001), loss='binary_crossentropy', metrics=['accuracy'])\n\n# === TRAIN ===\nhistory = model.fit(train_gen, validation_data=val_gen, epochs=5)\n\n# Optionally unfreeze and fine-tune:\n# base_model.trainable = True\n# model.compile(optimizer=Adam(1e-5), loss='binary_crossentropy', metrics=['accuracy'])\n# model.fit(train_gen, validation_data=val_gen, epochs=3)\n","metadata":{"execution":{"iopub.execute_input":"2025-04-21T17:41:04.061741Z","iopub.status.busy":"2025-04-21T17:41:04.061561Z","iopub.status.idle":"2025-04-21T18:02:02.455304Z","shell.execute_reply":"2025-04-21T18:02:02.454715Z","shell.execute_reply.started":"2025-04-21T17:41:04.061725Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zipfile\nimport os\n\n# Force re-extraction to /kaggle/working/\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\n# Check the result\nprint(\"Total files in /kaggle/working/test:\", len(os.listdir(\"/kaggle/working/test\")) if os.path.exists(\"/kaggle/working/test\") else \"Folder missing\")\nprint(\"Total JPGs in /kaggle/working:\", len([f for f in os.listdir(\"/kaggle/working\") if f.endswith('.jpg')]))\n","metadata":{"execution":{"iopub.execute_input":"2025-04-21T18:26:45.948410Z","iopub.status.busy":"2025-04-21T18:26:45.947693Z","iopub.status.idle":"2025-04-21T18:26:48.506089Z","shell.execute_reply":"2025-04-21T18:26:48.505359Z","shell.execute_reply.started":"2025-04-21T18:26:45.948383Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\nimport pandas as pd\nimport numpy as np\nfrom tensorflow.keras.preprocessing import image\n\n# === LOAD TEST IMAGES ===\ntest_dir = '/kaggle/working/test'\ntest_files = sorted(glob.glob(f\"{test_dir}/*.jpg\"))\ntest_ids = [os.path.basename(f).split('.')[0] for f in test_files]\n\n# === Predict for each test image ===\npreds = []\n\nfor idx, filepath in enumerate(test_files):\n    if idx % 500 == 0:\n        print(f\"Processing {idx}/{len(test_files)}...\")\n    img = image.load_img(filepath, target_size=IMG_SIZE)\n    img_array = image.img_to_array(img)\n    img_array = preprocess_input(img_array)\n    img_array = np.expand_dims(img_array, axis=0)\n    prob = model.predict(img_array, verbose=0)[0][0]\n    preds.append(prob)\n\n# === CREATE SUBMISSION ===\nsubmission = pd.DataFrame({\n    \"id\": test_ids,\n    \"label\": preds\n})\n\nsubmission_file = \"MSBA.Session01.MayankSingh_ConvNeXt.csv\"\nsubmission.to_csv(submission_file, index=False)\nprint(f\"Submission saved: {submission_file}\")","metadata":{"execution":{"iopub.execute_input":"2025-04-21T18:27:03.273446Z","iopub.status.busy":"2025-04-21T18:27:03.272879Z","iopub.status.idle":"2025-04-21T18:41:33.672168Z","shell.execute_reply":"2025-04-21T18:41:33.671429Z","shell.execute_reply.started":"2025-04-21T18:27:03.273423Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Experiment: ConvNeXtV2 – Raw Inference Without Training\n\nAs part of my model experimentation process, I also attempted to directly use the pretrained **ConvNeXtBase** model for predictions without any training or fine-tuning.\n\n**Steps Taken:**\n- Loaded `ConvNeXtBase` with pretrained ImageNet weights and appended a custom head:\n  - `GlobalAveragePooling2D → Dropout(0.3) → Dense(1, activation='sigmoid')`\n- Did **not** compile or train the model.\n- Ran inference directly on the test set using `preprocess_input` and `image.load_img`.\n\n**Purpose:**\nThe goal was to **benchmark how poorly an untrained head** would perform—essentially, to establish a lower bound baseline for ConvNeXt when used without any supervision.\n\n**Results:**\n- The submission `MSBA.Session01.MayankSingh_ConvNeXt_raw.csv` scored **0.88195** log loss on the Kaggle private leaderboard.\n- As expected, this is significantly worse than any of the trained/fine-tuned models, confirming that training on the labeled dataset is essential.\n\nThis experiment validated the intuition that although pretrained CNNs offer powerful feature representations, the classification head must still be trained to align with the target task.\n","metadata":{}},{"cell_type":"code","source":"IMG_SIZE = (224, 224)\nbase_model = ConvNeXtBase(weights='imagenet', include_top=False, input_shape=(224, 224, 3))\nx = GlobalAveragePooling2D()(base_model.output)\nx = Dropout(0.3)(x)\noutput = Dense(1, activation='sigmoid')(x)\nmodel = Model(inputs=base_model.input, outputs=output)\n\ntest_dir = '/kaggle/working/test'\ntest_files = sorted(glob.glob(f\"{test_dir}/*.jpg\"))\ntest_ids = [os.path.basename(f).split('.')[0] for f in test_files]\n\nprint(f\"Total test images found: {len(test_files)}\")\n\npreds = []\nfor idx, fpath in enumerate(test_files):\n    if idx % 500 == 0:\n        print(f\"Predicting {idx}/{len(test_files)}...\")\n    img = image.load_img(fpath, target_size=IMG_SIZE)\n    img_array = image.img_to_array(img)\n    img_array = preprocess_input(img_array)\n    img_array = np.expand_dims(img_array, axis=0)\n    prob = model.predict(img_array, verbose=0)[0][0]\n    preds.append(prob)\n\n# === CREATE SUBMISSION ===\nsubmission = pd.DataFrame({\n    \"id\": test_ids,\n    \"label\": preds\n})\n\nsubmission_file = \"MSBA.Session01.MayankSingh_ConvNeXt_raw.csv\"\nsubmission.to_csv(submission_file, index=False)\nprint(f\"Submission saved: {submission_file}\")","metadata":{"execution":{"iopub.execute_input":"2025-04-21T18:49:02.031989Z","iopub.status.busy":"2025-04-21T18:49:02.031695Z","iopub.status.idle":"2025-04-21T19:03:28.142654Z","shell.execute_reply":"2025-04-21T19:03:28.141828Z","shell.execute_reply.started":"2025-04-21T18:49:02.031966Z"}},"outputs":[],"execution_count":null}]}