{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Import Packages & Load Data","metadata":{}},{"cell_type":"code","source":"import os\nimport zipfile\nimport shutil\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array\nfrom tensorflow.keras import layers, models","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-22T07:21:03.499764Z","iopub.execute_input":"2025-04-22T07:21:03.499967Z","iopub.status.idle":"2025-04-22T07:21:03.504322Z","shell.execute_reply.started":"2025-04-22T07:21:03.499951Z","shell.execute_reply":"2025-04-22T07:21:03.503615Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Unzip the training images\nzip_path = \"/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip\"\nextract_path = \"/kaggle/working/train\"\n\nwith zipfile.ZipFile(zip_path, 'r') as zip_ref:\n    zip_ref.extractall(extract_path)\n\n# Move images into cats/ and dogs/ folders\nos.makedirs(f\"{extract_path}/cats\", exist_ok=True)\nos.makedirs(f\"{extract_path}/dogs\", exist_ok=True)\n\nimage_dir = os.path.join(extract_path, \"train\")  # actual image folder inside zip\nfor fname in os.listdir(image_dir):\n    src = os.path.join(image_dir, fname)\n    if fname.startswith(\"cat\"):\n        shutil.move(src, os.path.join(extract_path, \"cats\", fname))\n    elif fname.startswith(\"dog\"):\n        shutil.move(src, os.path.join(extract_path, \"dogs\", fname))\n\n# Clean up leftover folder\nshutil.rmtree(image_dir)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T05:38:34.831593Z","iopub.execute_input":"2025-04-22T05:38:34.831865Z","iopub.status.idle":"2025-04-22T05:38:41.964002Z","shell.execute_reply.started":"2025-04-22T05:38:34.831836Z","shell.execute_reply":"2025-04-22T05:38:41.963076Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Preprocess with ImageDataGenerator","metadata":{}},{"cell_type":"code","source":"datagen = ImageDataGenerator(\n    rescale=1./255,\n    validation_split=0.2\n)\n\ntrain_gen = datagen.flow_from_directory(\n    extract_path,\n    target_size=(150, 150),\n    batch_size=32,\n    class_mode='binary',\n    subset='training'\n)\n\nval_gen = datagen.flow_from_directory(\n    extract_path,\n    target_size=(150, 150),\n    batch_size=32,\n    class_mode='binary',\n    subset='validation'\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:33:33.953063Z","iopub.execute_input":"2025-04-21T19:33:33.953681Z","iopub.status.idle":"2025-04-21T19:33:34.185421Z","shell.execute_reply.started":"2025-04-21T19:33:33.953658Z","shell.execute_reply":"2025-04-21T19:33:34.184894Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# CNN Model","metadata":{}},{"cell_type":"code","source":"model = models.Sequential([\n    layers.Input(shape=(150, 150, 3)),\n    \n    layers.Conv2D(32, (3, 3), activation='relu'),\n    layers.MaxPooling2D(2, 2),\n\n    layers.Conv2D(64, (3, 3), activation='relu'),\n    layers.MaxPooling2D(2, 2),\n\n    layers.Conv2D(128, (3, 3), activation='relu'),\n    layers.MaxPooling2D(2, 2),\n\n    layers.Flatten(),\n    layers.Dense(512, activation='relu'),\n    layers.Dense(1, activation='sigmoid')  # output is probability of dog\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T08:43:12.008767Z","iopub.execute_input":"2025-04-21T08:43:12.008962Z","iopub.status.idle":"2025-04-21T08:43:15.907503Z","shell.execute_reply.started":"2025-04-21T08:43:12.008946Z","shell.execute_reply":"2025-04-21T08:43:15.906737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.compile(\n    optimizer='adam',\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T08:44:38.000299Z","iopub.execute_input":"2025-04-21T08:44:38.001074Z","iopub.status.idle":"2025-04-21T08:44:38.013516Z","shell.execute_reply.started":"2025-04-21T08:44:38.001049Z","shell.execute_reply":"2025-04-21T08:44:38.012977Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Train the Model\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=10\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T08:44:54.417813Z","iopub.execute_input":"2025-04-21T08:44:54.418509Z","iopub.status.idle":"2025-04-21T08:50:36.086229Z","shell.execute_reply.started":"2025-04-21T08:44:54.418483Z","shell.execute_reply":"2025-04-21T08:50:36.085671Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visualize Training Curves\nimport matplotlib.pyplot as plt\n\n# Accuracy\nplt.plot(history.history['accuracy'], label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], label='Val Accuracy')\nplt.legend()\nplt.title(\"Model Accuracy Over Epochs\")\nplt.show()\n\n# Loss\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Val Loss')\nplt.legend()\nplt.title(\"Model Loss Over Epochs\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T08:50:36.087413Z","iopub.execute_input":"2025-04-21T08:50:36.087647Z","iopub.status.idle":"2025-04-21T08:50:36.456821Z","shell.execute_reply.started":"2025-04-21T08:50:36.087631Z","shell.execute_reply":"2025-04-21T08:50:36.455916Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Tuning","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import regularizers\n\nmodel = models.Sequential([\n    layers.Input(shape=(150, 150, 3)),\n    \n    layers.Conv2D(32, (3, 3), activation='relu'),\n    layers.MaxPooling2D(2, 2),\n\n    layers.Conv2D(64, (3, 3), activation='relu'),\n    layers.MaxPooling2D(2, 2),\n\n    layers.Conv2D(128, (3, 3), activation='relu'),\n    layers.MaxPooling2D(2, 2),\n\n    layers.Flatten(),\n    layers.Dropout(0.5),  # 🔥 NEW: turns off 50% neurons\n    layers.Dense(512, activation='relu', kernel_regularizer=regularizers.l2(0.001)),\n    layers.Dense(1, activation='sigmoid')\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T09:43:21.990139Z","iopub.execute_input":"2025-04-21T09:43:21.990420Z","iopub.status.idle":"2025-04-21T09:43:24.549300Z","shell.execute_reply.started":"2025-04-21T09:43:21.990397Z","shell.execute_reply":"2025-04-21T09:43:24.548750Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.compile(\n    optimizer='adam',\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T09:43:30.227554Z","iopub.execute_input":"2025-04-21T09:43:30.228285Z","iopub.status.idle":"2025-04-21T09:43:30.236157Z","shell.execute_reply.started":"2025-04-21T09:43:30.228257Z","shell.execute_reply":"2025-04-21T09:43:30.235443Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\ncallbacks = [\n    EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True),\n    ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=1)\n]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T09:43:32.004929Z","iopub.execute_input":"2025-04-21T09:43:32.005199Z","iopub.status.idle":"2025-04-21T09:43:32.010773Z","shell.execute_reply.started":"2025-04-21T09:43:32.005179Z","shell.execute_reply":"2025-04-21T09:43:32.010277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=20,  \n    callbacks=callbacks\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T09:43:35.081070Z","iopub.execute_input":"2025-04-21T09:43:35.081340Z","iopub.status.idle":"2025-04-21T09:54:08.416374Z","shell.execute_reply.started":"2025-04-21T09:43:35.081319Z","shell.execute_reply":"2025-04-21T09:54:08.415583Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visualize Training Curves\nimport matplotlib.pyplot as plt\n\n# Accuracy\nplt.plot(history.history['accuracy'], label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], label='Val Accuracy')\nplt.legend()\nplt.title(\"Model Accuracy Over Epochs\")\nplt.show()\n\n# Loss\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Val Loss')\nplt.legend()\nplt.title(\"Model Loss Over Epochs\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T09:54:08.417754Z","iopub.execute_input":"2025-04-21T09:54:08.418286Z","iopub.status.idle":"2025-04-21T09:54:08.767999Z","shell.execute_reply.started":"2025-04-21T09:54:08.418266Z","shell.execute_reply":"2025-04-21T09:54:08.767213Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Grab one batch from val_gen\nval_imgs, val_labels = next(val_gen)  # val_imgs.shape = (32, 150, 150, 3)\n\n# Predict probabilities\nval_preds = model.predict(val_imgs).flatten()\nimport matplotlib.pyplot as plt\n\nclass_names = ['Cat', 'Dog']\n\nplt.figure(figsize=(15, 10))\nfor i in range(10):\n    plt.subplot(2, 5, i+1)\n    plt.imshow(val_imgs[i])\n    plt.axis('off')\n    \n    true_label = class_names[int(val_labels[i])]\n    pred_prob = val_preds[i]\n    pred_label = class_names[int(pred_prob > 0.5)]\n    \n    plt.title(f\"Pred: {pred_label}\\nProb: {pred_prob:.2f}\\nTrue: {true_label}\")\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T09:54:08.768884Z","iopub.execute_input":"2025-04-21T09:54:08.769603Z","iopub.status.idle":"2025-04-21T09:54:10.260658Z","shell.execute_reply.started":"2025-04-21T09:54:08.769577Z","shell.execute_reply":"2025-04-21T09:54:10.259666Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Pre-trained models","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nimg_size = (300, 300)\n\ndatagen = ImageDataGenerator(\n    rescale=1./255,\n    validation_split=0.2,\n    horizontal_flip=True,\n    rotation_range=15,\n    zoom_range=0.2\n)\n\ntrain_gen = datagen.flow_from_directory(\n    \"/kaggle/working/train\",\n    target_size=img_size,\n    batch_size=32,\n    class_mode='binary',\n    subset='training'\n)\n\nval_gen = datagen.flow_from_directory(\n    \"/kaggle/working/train\",\n    target_size=img_size,\n    batch_size=32,\n    class_mode='binary',\n    subset='validation'\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:43:21.142326Z","iopub.execute_input":"2025-04-21T19:43:21.142970Z","iopub.status.idle":"2025-04-21T19:43:21.381158Z","shell.execute_reply.started":"2025-04-21T19:43:21.142948Z","shell.execute_reply":"2025-04-21T19:43:21.380630Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\n# Load base model with 300x300 input\nbase_model = EfficientNetB3(\n    include_top=False,\n    weights='imagenet',\n    input_shape=(300, 300, 3)\n)\nbase_model.trainable = False  # freeze initially\n\n# Add custom classification head\nx = base_model.output\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.Dropout(0.5)(x)\noutput = layers.Dense(1, activation='sigmoid')(x)\n\nmodel = models.Model(inputs=base_model.input, outputs=output)\n\n# Compile\nmodel.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n\n# Train head\ncallbacks = [\n    EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True),\n    ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2)\n]\n\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=5,\n    callbacks=callbacks\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:43:24.261132Z","iopub.execute_input":"2025-04-21T19:43:24.261400Z","iopub.status.idle":"2025-04-21T20:23:22.661156Z","shell.execute_reply.started":"2025-04-21T19:43:24.261380Z","shell.execute_reply":"2025-04-21T20:23:22.660572Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Unfreeze top layers\nbase_model.trainable = True\n\n# Freeze earlier layers, fine-tune last ~30\nfor layer in base_model.layers[:-30]:\n    layer.trainable = False\n\n# Re-compile with lower learning rate\nmodel.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n\n# Fine-tune entire model\nfine_tune_history = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=10,\n    callbacks=callbacks\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:31:09.827593Z","iopub.execute_input":"2025-04-21T19:31:09.827891Z","iopub.status.idle":"2025-04-21T19:31:09.834424Z","shell.execute_reply.started":"2025-04-21T19:31:09.827873Z","shell.execute_reply":"2025-04-21T19:31:09.833867Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ResNet50","metadata":{}},{"cell_type":"code","source":"# STEP 1: Data Generator with Augmentation\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nimg_size = (224, 224)\n\ndatagen = ImageDataGenerator(\n    rescale=1./255,\n    validation_split=0.2,\n    horizontal_flip=True,\n    rotation_range=15,\n    zoom_range=0.2,\n    shear_range=0.2\n)\n\ntrain_gen = datagen.flow_from_directory(\n    \"/kaggle/working/train\",   # Make sure cats/ and dogs/ are subfolders here\n    target_size=img_size,\n    batch_size=32,\n    class_mode='binary',\n    subset='training'\n)\n\nval_gen = datagen.flow_from_directory(\n    \"/kaggle/working/train\",\n    target_size=img_size,\n    batch_size=32,\n    class_mode='binary',\n    subset='validation'\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T07:21:24.283635Z","iopub.execute_input":"2025-04-22T07:21:24.284126Z","iopub.status.idle":"2025-04-22T07:21:24.518018Z","shell.execute_reply.started":"2025-04-22T07:21:24.284101Z","shell.execute_reply":"2025-04-22T07:21:24.517330Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# STEP 2: Build ResNet50V2 model with custom head\nfrom tensorflow.keras.applications import ResNet50V2\nfrom tensorflow.keras import layers, models\n\nbase_model = ResNet50V2(\n    include_top=False,\n    weights='imagenet',\n    input_shape=(224, 224, 3)\n)\nbase_model.trainable = False  # Freeze initial layers\n\n# Custom head\nx = base_model.output\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.BatchNormalization()(x)\nx = layers.Dense(512, activation='relu')(x)\nx = layers.Dropout(0.5)(x)\noutput = layers.Dense(1, activation='sigmoid')(x)\n\n# Final model\nmodel = models.Model(inputs=base_model.input, outputs=output)\n\n# STEP 3: Compile the model\nmodel.compile(\n    optimizer='adam',\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n# STEP 4: Add callbacks\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\ncallbacks = [\n    EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True),\n    ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=1)\n]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T07:21:28.180542Z","iopub.execute_input":"2025-04-22T07:21:28.180819Z","iopub.status.idle":"2025-04-22T07:21:30.512468Z","shell.execute_reply.started":"2025-04-22T07:21:28.180797Z","shell.execute_reply":"2025-04-22T07:21:30.511692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# STEP 5: Train the classifier head\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=5,\n    callbacks=callbacks\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T07:21:33.258763Z","iopub.execute_input":"2025-04-22T07:21:33.259365Z","iopub.status.idle":"2025-04-22T07:44:28.919586Z","shell.execute_reply.started":"2025-04-22T07:21:33.259331Z","shell.execute_reply":"2025-04-22T07:44:28.918873Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# STEP 6: Fine-tune the top layers of ResNet\nbase_model.trainable = True\n\n# Optionally freeze earlier layers\nfor layer in base_model.layers[:-30]:\n    layer.trainable = False\n\n# Re-compile with lower LR for fine-tuning\nmodel.compile(\n    optimizer='adam',\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n\n# Continue training\nfine_tune_history = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=10,\n    callbacks=callbacks\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T06:21:40.504883Z","iopub.execute_input":"2025-04-22T06:21:40.505137Z","iopub.status.idle":"2025-04-22T06:35:37.321047Z","shell.execute_reply.started":"2025-04-22T06:21:40.505120Z","shell.execute_reply":"2025-04-22T06:35:37.320466Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing import image\nimport numpy as np\nimport os\nfrom tqdm import tqdm\n\n# Path to test folder (after proper unzip)\ntest_dir = \"/kaggle/working/test_flat\"  # <- update this if different\ntest_files = sorted(os.listdir(test_dir))\n\nX_test = []\nids = []\n\nfor fname in tqdm(test_files):\n    img_path = os.path.join(test_dir, fname)\n    img = image.load_img(img_path, target_size=(224, 224))  # ✅ match ResNet input\n    img_array = image.img_to_array(img) / 255.0\n    X_test.append(img_array)\n    ids.append(int(fname.split('.')[0]))\n\nX_test = np.array(X_test)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T00:47:41.755749Z","iopub.execute_input":"2025-04-22T00:47:41.756189Z","iopub.status.idle":"2025-04-22T00:48:01.418047Z","shell.execute_reply.started":"2025-04-22T00:47:41.756165Z","shell.execute_reply":"2025-04-22T00:48:01.417467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Predict all at once\npreds = model.predict(X_test, batch_size=64).flatten()  # Binary probabilities\nimport pandas as pd\n\nsubmission = pd.DataFrame({\n    'id': ids,\n    'label': preds\n})\n\nsubmission = submission.sort_values('id')  # Ensure order\nsubmission.to_csv(\"submission.csv\", index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T07:20:24.140708Z","iopub.status.idle":"2025-04-22T07:20:24.140968Z","shell.execute_reply.started":"2025-04-22T07:20:24.140841Z","shell.execute_reply":"2025-04-22T07:20:24.140851Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Final (ResNet50V2 + EfficientNetB3)","metadata":{}},{"cell_type":"code","source":"# STEP 1: Imports\nfrom tensorflow.keras.applications import ResNet50V2, EfficientNetB3\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.preprocessing import image\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.losses import BinaryCrossentropy\nimport numpy as np\nimport pandas as pd\nimport os\nfrom tqdm import tqdm\n\n# STEP 2: Data Generators\nimg_size = (224, 224)\n\ndatagen = ImageDataGenerator(\n    rescale=1./255,\n    validation_split=0.2,\n    horizontal_flip=True,\n    rotation_range=15,\n    zoom_range=0.2,\n    shear_range=0.2\n)\n\ntrain_gen = datagen.flow_from_directory(\n    \"/kaggle/working/train\",\n    target_size=img_size,\n    batch_size=32,\n    class_mode='binary',\n    subset='training'\n)\n\nval_gen = datagen.flow_from_directory(\n    \"/kaggle/working/train\",\n    target_size=img_size,\n    batch_size=32,\n    class_mode='binary',\n    subset='validation'\n)\n\n# STEP 3: Callbacks\nearly_stop = EarlyStopping(monitor='val_loss', patience=2, restore_best_weights=True)\nreduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=1, verbose=1)\n\n# STEP 4: Build Base Model Function\ndef build_model(base_fn):\n    base_model = base_fn(include_top=False, weights='imagenet', input_shape=(224, 224, 3))\n    base_model.trainable = False\n    x = base_model.output\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dense(512, activation='relu')(x)\n    x = layers.Dropout(0.5)(x)\n    output = layers.Dense(1, activation='sigmoid')(x)\n    model = models.Model(inputs=base_model.input, outputs=output)\n    model.compile(optimizer='adam', loss=BinaryCrossentropy(label_smoothing=0.1), metrics=['accuracy'])\n    return model\n\n# STEP 5: Train ResNet50V2 (no fine-tuning)\nmodel_resnet = build_model(ResNet50V2)\nmodel_resnet.fit(train_gen, validation_data=val_gen, epochs=5, callbacks=[early_stop, reduce_lr])\n\n# STEP 6: Train EfficientNetB3 (no fine-tuning)\nmodel_effnet = build_model(EfficientNetB3)\nmodel_effnet.fit(train_gen, validation_data=val_gen, epochs=5, callbacks=[early_stop, reduce_lr])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T10:43:17.521437Z","iopub.execute_input":"2025-04-22T10:43:17.522193Z","iopub.status.idle":"2025-04-22T11:11:02.526692Z","shell.execute_reply.started":"2025-04-22T10:43:17.522166Z","shell.execute_reply":"2025-04-22T11:11:02.525446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"shutil.rmtree(\"/kaggle/working/test_flat\", ignore_errors=True)\n\nimport zipfile\n\n# Extract directly to /kaggle/working/test_flat\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# Rename from /test → /test_flat to make it clear\nimport os\nos.rename(\"/kaggle/working/test\", \"/kaggle/working/test_flat\")\n\n\n# STEP 7: TTA + Ensemble Predictions\ntest_dir = \"/kaggle/working/test_flat\"\ntest_files = sorted(os.listdir(test_dir))\nbatch_size = 500\nids = []\nensemble_preds = []\n\nfor i in tqdm(range(0, len(test_files), batch_size)):\n    batch_files = test_files[i:i+batch_size]\n    batch_imgs = []\n    batch_imgs_flipped = []\n    batch_ids = []\n\n    for fname in batch_files:\n        path = os.path.join(test_dir, fname)\n        img = image.load_img(path, target_size=img_size)\n        arr = image.img_to_array(img) / 255.0\n        flipped = np.fliplr(arr)\n\n        batch_imgs.append(arr)\n        batch_imgs_flipped.append(flipped)\n        batch_ids.append(int(fname.split('.')[0]))\n\n    batch_imgs = np.array(batch_imgs)\n    batch_imgs_flipped = np.array(batch_imgs_flipped)\n\n    resnet_orig = model_resnet.predict(batch_imgs, batch_size=64).flatten()\n    resnet_flip = model_resnet.predict(batch_imgs_flipped, batch_size=64).flatten()\n\n    effnet_orig = model_effnet.predict(batch_imgs, batch_size=64).flatten()\n    effnet_flip = model_effnet.predict(batch_imgs_flipped, batch_size=64).flatten()\n\n    resnet_tta = (resnet_orig + resnet_flip) / 2\n    effnet_tta = (effnet_orig + effnet_flip) / 2\n\n    ensemble_pred = (resnet_tta + effnet_tta) / 2\n    ensemble_pred = np.clip(ensemble_pred ** 1.2, 1e-5, 1 - 1e-5)\n\n    ids.extend(batch_ids)\n    ensemble_preds.extend(ensemble_pred)\n\n# STEP 8: Create Submission\nsubmission = pd.DataFrame({'id': ids, 'label': ensemble_preds})\nsubmission = submission.sort_values('id')\nsubmission.to_csv(\"submission_ensemble2.csv\", index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T11:11:02.528690Z","iopub.status.idle":"2025-04-22T11:11:02.529009Z","shell.execute_reply.started":"2025-04-22T11:11:02.528847Z","shell.execute_reply":"2025-04-22T11:11:02.528866Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"shutil.rmtree(\"/kaggle/working/test_flat\", ignore_errors=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T07:58:51.371309Z","iopub.execute_input":"2025-04-22T07:58:51.371965Z","iopub.status.idle":"2025-04-22T07:58:51.761830Z","shell.execute_reply.started":"2025-04-22T07:58:51.371931Z","shell.execute_reply":"2025-04-22T07:58:51.761259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"shutil.rmtree(\"/kaggle/working/test_flat\", ignore_errors=True)\n\nimport zipfile\n\n# Extract directly to /kaggle/working/test_flat\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# Rename from /test → /test_flat to make it clear\nimport os\nos.rename(\"/kaggle/working/test\", \"/kaggle/working/test_flat\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T07:58:53.107218Z","iopub.execute_input":"2025-04-22T07:58:53.108034Z","iopub.status.idle":"2025-04-22T07:58:55.687465Z","shell.execute_reply.started":"2025-04-22T07:58:53.108007Z","shell.execute_reply":"2025-04-22T07:58:55.686670Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing import image\nimport numpy as np\nimport pandas as pd\nimport os\nfrom tqdm import tqdm\n\n# Path to the test images (ensure this folder has all .jpgs flat)\ntest_dir = \"/kaggle/working/test_flat\"\ntest_files = sorted(os.listdir(test_dir))\n\n# Setup\nbatch_size = 500  # Number of images to load per mini-batch\nids = []\npreds = []\n\n# Predict in batches\nfor i in tqdm(range(0, len(test_files), batch_size)):\n    batch_files = test_files[i:i+batch_size]\n    batch_imgs = []\n    batch_ids = []\n\n    for fname in batch_files:\n        img_path = os.path.join(test_dir, fname)\n        img = image.load_img(img_path, target_size=(224, 224))  # match model input\n        img_array = image.img_to_array(img) / 255.0\n        batch_imgs.append(img_array)\n        batch_ids.append(int(fname.split('.')[0]))\n\n    batch_array = np.array(batch_imgs)\n    batch_preds = model.predict(batch_array, batch_size=64).flatten()\n\n    ids.extend(batch_ids)\n    preds.extend(batch_preds)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T07:58:58.671369Z","iopub.execute_input":"2025-04-22T07:58:58.671650Z","iopub.status.idle":"2025-04-22T08:00:27.654093Z","shell.execute_reply.started":"2025-04-22T07:58:58.671628Z","shell.execute_reply":"2025-04-22T08:00:27.653241Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame({\n    'id': ids,\n    'label': preds\n})\nsubmission = submission.sort_values('id')\nsubmission.to_csv(\"submission2.csv\", index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-22T08:22:55.103142Z","iopub.execute_input":"2025-04-22T08:22:55.103628Z","iopub.status.idle":"2025-04-22T08:22:55.134352Z","shell.execute_reply.started":"2025-04-22T08:22:55.103602Z","shell.execute_reply":"2025-04-22T08:22:55.133581Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dir = \"/kaggle/working/test_flat\"\n\nfrom tensorflow.keras.preprocessing import image\nimport numpy as np\nimport pandas as pd\nimport os\nfrom tqdm import tqdm\n\ntest_files = sorted(os.listdir(test_dir))\n\nX_test = []\nids = []\n\nfor fname in tqdm(test_files):\n    img_path = os.path.join(test_dir, fname)\n    img = image.load_img(img_path, target_size=(150, 150))\n    img_array = image.img_to_array(img) / 255.0\n    X_test.append(img_array)\n    ids.append(int(fname.split('.')[0]))\n\nX_test = np.array(X_test)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T10:07:10.901987Z","iopub.execute_input":"2025-04-21T10:07:10.902650Z","iopub.status.idle":"2025-04-21T10:07:10.905919Z","shell.execute_reply.started":"2025-04-21T10:07:10.902626Z","shell.execute_reply":"2025-04-21T10:07:10.905136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# Predict all at once\npreds = model.predict(X_test, batch_size=64).flatten()\n\n# Create submission\nsubmission = pd.DataFrame({'id': ids, 'label': preds})\nsubmission = submission.sort_values('id')\nsubmission.to_csv(\"submission.csv\", index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T10:07:50.550700Z","iopub.execute_input":"2025-04-21T10:07:50.551217Z","iopub.status.idle":"2025-04-21T10:08:19.352808Z","shell.execute_reply.started":"2025-04-21T10:07:50.551194Z","shell.execute_reply":"2025-04-21T10:08:19.352051Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Predict probabilities\npreds_effnet = model_effnet.predict(X_test, batch_size=64).flatten()\npreds_resnet = model_resnet.predict(X_test, batch_size=64).flatten()\n\n# Average (simple ensemble)\nfinal_preds = (preds_effnet + preds_resnet) / 2\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}