{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":3978,"databundleVersionId":32719,"sourceType":"competition"},{"sourceId":13923297,"sourceType":"datasetVersion","datasetId":8872395},{"sourceId":13936966,"sourceType":"datasetVersion","datasetId":8881954},{"sourceId":13937054,"sourceType":"datasetVersion","datasetId":8882022},{"sourceId":13937066,"sourceType":"datasetVersion","datasetId":8882031},{"sourceId":13937082,"sourceType":"datasetVersion","datasetId":8882045},{"sourceId":13937100,"sourceType":"datasetVersion","datasetId":8882053},{"sourceId":13937204,"sourceType":"datasetVersion","datasetId":8882122},{"sourceId":13939907,"sourceType":"datasetVersion","datasetId":8883970},{"sourceId":13939982,"sourceType":"datasetVersion","datasetId":8884021}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!rm -rf /kaggle/working/train\n!mkdir -p /kaggle/working/train\n!unzip -q /kaggle/input/datasciencebowl/train.zip -d /kaggle/working/train\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T04:54:49.690628Z","iopub.execute_input":"2025-12-01T04:54:49.690997Z","iopub.status.idle":"2025-12-01T04:54:52.733236Z","shell.execute_reply.started":"2025-12-01T04:54:49.690972Z","shell.execute_reply":"2025-12-01T04:54:52.732242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\n# ====================== CONFIG ======================\n# 🔴 CHANGE THIS to your dataset path name 🔴\nDATA_DIR = \"/kaggle/working/train/train\"   # <--- Modify this\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16   # If kernel crashes → reduce to 8\nEPOCHS = 15\nSEED = 42\n# =====================================================\n\n# GPU memory safe mode (no crash)\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    for gpu in gpus:\n        tf.config.experimental.set_memory_growth(gpu, True)\n    print(\"GPU Enabled:\", gpus)\nelse:\n    print(\"⚠ Running on CPU (still works but slower).\")\n\n\n# ====== Load Dataset with built-in Train/Validation Split ======\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nclass_names = train_ds.class_names\nNUM_CLASSES = len(class_names)\n\nprint(\"\\nClasses Detected:\", NUM_CLASSES)\nprint(class_names[:10], \" ...\")   # Preview first 10\n\n\n# ================= Model — MobileNetV2 (Pretrained) ================\ndata_aug = keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.05),\n    layers.RandomZoom(0.1),\n])\n\nbase = tf.keras.applications.MobileNetV2(\n    input_shape=IMAGE_SIZE + (3,),\n    include_top=False,\n    weights=\"imagenet\"\n)\nbase.trainable = False  # Freeze first phase\n\ninputs = keras.Input(shape=IMAGE_SIZE + (3,))\nx = data_aug(inputs)\nx = tf.keras.applications.mobilenet_v2.preprocess_input(x)\nx = base(x, training=False)\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.Dropout(0.25)(x)\noutputs = layers.Dense(NUM_CLASSES, activation=\"softmax\")(x)\n\nmodel = keras.Model(inputs, outputs)\nmodel.compile(optimizer=keras.optimizers.Adam(1e-4),\n              loss=\"sparse_categorical_crossentropy\",\n              metrics=[\"accuracy\"])\n\nmodel.summary()\n\n\n# ================= Train (Stage 1: Head Only) =================\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=[\n        keras.callbacks.ModelCheckpoint(\"best_model.keras\",\n                                        save_best_only=True,\n                                        monitor='val_accuracy')\n    ]\n)\n\n# ================= Fine-Tune (More Accuracy) =================\nprint(\"\\n🔥 Fine-tuning top MobileNet layers ...\")\n\nbase.trainable = True\nfor layer in base.layers[:100]:\n    layer.trainable = False  # Keep early layers frozen\n\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-5),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nfine_tune = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=10,  # You can increase to 20–30 for best results\n    callbacks=[\n        keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),\n        keras.callbacks.ModelCheckpoint(\"final_model.keras\", save_best_only=True)\n    ]\n)\n\nprint(\"\\n✅ Model Training Completed Successfully 🎉\")\nprint(\"📁 Saved Files:\")\nprint(\" ├─ best_model.keras\")\nprint(\" └─ final_model.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T09:54:43.468035Z","iopub.execute_input":"2025-11-29T09:54:43.46832Z","iopub.status.idle":"2025-11-29T16:42:08.452661Z","shell.execute_reply.started":"2025-11-29T09:54:43.468288Z","shell.execute_reply":"2025-11-29T16:42:08.451549Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nDATASET_PATH =\"/kaggle/working/train/train\"\nOUTPUT_FILE = \"/kaggle/working/train/train.txt\"\n\nclasses = sorted(os.listdir(DATASET_PATH))\n\nwith open(OUTPUT_FILE, \"w\") as f:\n    for cname in classes:\n        f.write(cname + \"\\n\")\n\nprint(\"✔ class_names.txt created successfully!\")\nprint(f\"Total classes found: {len(classes)}\")\nprint(\"\\nSaved at → class_names.txt\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T18:08:05.563785Z","iopub.execute_input":"2025-11-29T18:08:05.564086Z","iopub.status.idle":"2025-11-29T18:08:05.571365Z","shell.execute_reply.started":"2025-11-29T18:08:05.564067Z","shell.execute_reply":"2025-11-29T18:08:05.570719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport os\nfrom collections import Counter\n\nclass_counts = Counter()\nfor cls in class_names:\n    class_counts[cls] = len(os.listdir(os.path.join(DATA_DIR, cls)))\n\nplt.figure(figsize=(12,5))\nplt.bar(class_counts.keys(), class_counts.values())\nplt.xticks(rotation=90)\nplt.title(\"Class Distribution\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:02:37.187039Z","iopub.execute_input":"2025-11-29T17:02:37.18749Z","iopub.status.idle":"2025-11-29T17:02:38.249209Z","shell.execute_reply.started":"2025-11-29T17:02:37.187457Z","shell.execute_reply":"2025-11-29T17:02:38.247401Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nplt.figure(figsize=(12,8))\nfor images, labels in train_ds.take(1):\n    for i in range(12):\n        ax = plt.subplot(3,4,i+1)\n        plt.imshow(images[i].numpy().astype(\"uint8\"))\n        plt.title(class_names[labels[i]])\n        plt.axis(\"off\")\n    break\n\n# Augmented images\nplt.figure(figsize=(12,8))\naugmented = data_aug(images)\nfor i in range(12):\n    ax = plt.subplot(3,4,i+1)\n    plt.imshow(augmented[i].numpy().astype(\"uint8\"))\n    plt.title(\"Augmented\")\n    plt.axis(\"off\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:02:49.823513Z","iopub.execute_input":"2025-11-29T17:02:49.824728Z","iopub.status.idle":"2025-11-29T17:02:51.727041Z","shell.execute_reply.started":"2025-11-29T17:02:49.824687Z","shell.execute_reply":"2025-11-29T17:02:51.725907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"acc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nplt.figure(figsize=(14,5))\nplt.subplot(1,2,1)\nplt.plot(acc, label='Train Acc')\nplt.plot(val_acc, label='Val Acc')\nplt.legend(); plt.title(\"Accuracy Curve\")\n\nplt.subplot(1,2,2)\nplt.plot(loss, label='Train Loss')\nplt.plot(val_loss, label='Val Loss')\nplt.legend(); plt.title(\"Loss Curve\")\nplt.show()\n\n# Combined Graph\nplt.figure(figsize=(10,5))\nplt.plot(acc, label='Train Acc')\nplt.plot(val_acc, label='Val Acc')\nplt.plot(loss, label='Train Loss')\nplt.plot(val_loss, label='Val Loss')\nplt.legend(); plt.title(\"Combined Performance\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:03:00.609514Z","iopub.execute_input":"2025-11-29T17:03:00.610863Z","iopub.status.idle":"2025-11-29T17:03:01.230701Z","shell.execute_reply.started":"2025-11-29T17:03:00.610812Z","shell.execute_reply":"2025-11-29T17:03:01.229186Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport numpy as np\nimport seaborn as sns\n\ny_true = []\ny_pred = []\n\nfor imgs, labels in val_ds:\n    preds = model.predict(imgs)\n    y_true.extend(labels.numpy())\n    y_pred.extend(np.argmax(preds, axis=1))\n\ncm = confusion_matrix(y_true, y_pred)\nplt.figure(figsize=(12,10))\nsns.heatmap(cm, annot=False, cmap=\"Blues\")\nplt.title(\"Confusion Matrix\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:03:09.241843Z","iopub.execute_input":"2025-11-29T17:03:09.243192Z","iopub.status.idle":"2025-11-29T17:06:25.124561Z","shell.execute_reply.started":"2025-11-29T17:03:09.243142Z","shell.execute_reply":"2025-11-29T17:06:25.123498Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport os\nimport random\n\nmissing_class = \"appendicularian_fritillaridae\"\npath = f\"{DATA_DIR}/{missing_class}\"\n\nimages = os.listdir(path)\nsample = random.sample(images, 9)  # 9 images grid\n\nplt.figure(figsize=(10,10))\n\nfor i, img in enumerate(sample):\n    img_path = os.path.join(path, img)\n    image = plt.imread(img_path)\n    plt.subplot(3,3,i+1)\n    plt.imshow(image)\n    plt.axis(\"off\")\n\nplt.suptitle(f\"Sample Images from Missing Class → {missing_class}\", fontsize=14, fontweight='bold')\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:10:49.413887Z","iopub.execute_input":"2025-11-29T17:10:49.414258Z","iopub.status.idle":"2025-11-29T17:10:49.732317Z","shell.execute_reply.started":"2025-11-29T17:10:49.414233Z","shell.execute_reply":"2025-11-29T17:10:49.731386Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nmodel = tf.keras.models.load_model(\"/kaggle/input/knlmh/final_model.keras\")\nprint(model.output_shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T18:18:35.368127Z","iopub.execute_input":"2025-11-29T18:18:35.368538Z","iopub.status.idle":"2025-11-29T18:18:37.502031Z","shell.execute_reply.started":"2025-11-29T18:18:35.368504Z","shell.execute_reply":"2025-11-29T18:18:37.501106Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# STEP 1 — Get unique labels from y_true & y_pred\nunique_labels = sorted(set(y_true) | set(y_pred))   # automatic safe handling\n\n# STEP 2 — Check mismatch\nprint(\"Total folders detected in dataset:\", len(class_names))\nprint(\"Unique classes in predictions:\", len(unique_labels))\n\n# STEP 3 — Find missing classes (not predicted even once)\nmissing = set(range(len(class_names))) - set(unique_labels)\n\nprint(\"\\n⚠ Missing class index:\", missing)\nfor m in missing:\n    print(\"Missing class name:\", class_names[m])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:12:03.510807Z","iopub.execute_input":"2025-11-29T17:12:03.511091Z","iopub.status.idle":"2025-11-29T17:12:03.518936Z","shell.execute_reply.started":"2025-11-29T17:12:03.511074Z","shell.execute_reply":"2025-11-29T17:12:03.517564Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nremove_idx = 4  # class to drop\nmask = np.array(y_true) != remove_idx  \n\ny_true_f = np.array(y_true)[mask]\ny_pred_f = np.array(y_pred)[mask]\n\nclass_names_f = [c for i,c in enumerate(class_names) if i != remove_idx]\n\nprint(\"✔ Final evaluated classes =\", len(class_names_f))\nprint(\"❌ Removed class:\", class_names[4])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:14:45.269142Z","iopub.execute_input":"2025-11-29T17:14:45.269489Z","iopub.status.idle":"2025-11-29T17:14:45.279915Z","shell.execute_reply.started":"2025-11-29T17:14:45.269467Z","shell.execute_reply":"2025-11-29T17:14:45.27849Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report\nprint(classification_report(y_true_f, y_pred_f, target_names=class_names_f))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:14:53.977009Z","iopub.execute_input":"2025-11-29T17:14:53.977346Z","iopub.status.idle":"2025-11-29T17:14:54.013245Z","shell.execute_reply.started":"2025-11-29T17:14:53.977324Z","shell.execute_reply":"2025-11-29T17:14:54.011917Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cm = confusion_matrix(y_true_f, y_pred_f)\nsns.heatmap(cm, cmap=\"Blues\")\nplt.title(\"Confusion Matrix (120 classes)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:15:06.948252Z","iopub.execute_input":"2025-11-29T17:15:06.949176Z","iopub.status.idle":"2025-11-29T17:15:07.341205Z","shell.execute_reply.started":"2025-11-29T17:15:06.94914Z","shell.execute_reply":"2025-11-29T17:15:07.340227Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cm_norm = cm.astype(\"float\") / cm.sum(axis=1)[:,None]\nsns.heatmap(cm_norm, cmap=\"viridis\")\nplt.title(\"Normalized CM\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:15:13.493944Z","iopub.execute_input":"2025-11-29T17:15:13.494282Z","iopub.status.idle":"2025-11-29T17:15:13.911504Z","shell.execute_reply.started":"2025-11-29T17:15:13.49426Z","shell.execute_reply":"2025-11-29T17:15:13.910335Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"recall = cm.diagonal()/cm.sum(axis=1)\nplt.bar(range(len(recall)), recall)\nplt.title(\"Recall per Class\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:15:19.678682Z","iopub.execute_input":"2025-11-29T17:15:19.679945Z","iopub.status.idle":"2025-11-29T17:15:19.971044Z","shell.execute_reply.started":"2025-11-29T17:15:19.679861Z","shell.execute_reply":"2025-11-29T17:15:19.970117Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\ndf = pd.DataFrame({\"class\":class_names_f,\"recall\":recall})\ndf.sort_values(\"recall\", ascending=False).head(20).plot(kind=\"bar\",x=\"class\",y=\"recall\")\nplt.title(\"Top 20 Performing Classes\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:15:24.652386Z","iopub.execute_input":"2025-11-29T17:15:24.652834Z","iopub.status.idle":"2025-11-29T17:15:25.025155Z","shell.execute_reply.started":"2025-11-29T17:15:24.652815Z","shell.execute_reply":"2025-11-29T17:15:25.024173Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.sort_values(\"recall\").head(20).plot(kind=\"bar\",x=\"class\",y=\"recall\")\nplt.title(\"Worst 20 Classes\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:15:29.840257Z","iopub.execute_input":"2025-11-29T17:15:29.840573Z","iopub.status.idle":"2025-11-29T17:15:30.131179Z","shell.execute_reply.started":"2025-11-29T17:15:29.840554Z","shell.execute_reply":"2025-11-29T17:15:30.130247Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import precision_recall_fscore_support\np,r,f,_=precision_recall_fscore_support(y_true_f,y_pred_f)\n\nplt.scatter(p,r)\nplt.xlabel(\"Precision\")\nplt.ylabel(\"Recall\")\nplt.title(\"Precision vs Recall (All Classes)\")\nplt.show()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:15:37.853334Z","iopub.execute_input":"2025-11-29T17:15:37.854028Z","iopub.status.idle":"2025-11-29T17:15:38.043551Z","shell.execute_reply.started":"2025-11-29T17:15:37.854001Z","shell.execute_reply":"2025-11-29T17:15:38.04236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.hist(f,bins=30)\nplt.title(\"F1 Score Distribution Across Classes\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:15:42.744367Z","iopub.execute_input":"2025-11-29T17:15:42.744756Z","iopub.status.idle":"2025-11-29T17:15:42.910028Z","shell.execute_reply.started":"2025-11-29T17:15:42.744734Z","shell.execute_reply":"2025-11-29T17:15:42.908963Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"support = cm.sum(axis=1)\nplt.bar(range(len(support)),support)\nplt.title(\"Support per Class\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:15:48.352228Z","iopub.execute_input":"2025-11-29T17:15:48.352505Z","iopub.status.idle":"2025-11-29T17:15:48.637165Z","shell.execute_reply.started":"2025-11-29T17:15:48.352487Z","shell.execute_reply":"2025-11-29T17:15:48.636218Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Convert validation DS into numpy arrays for image visualization\nval_images = []\nval_labels = []\n\nfor imgs, labels in val_ds:\n    val_images.append(imgs.numpy())\n    val_labels.append(labels.numpy())\n\nval_images = np.concatenate(val_images)\nval_labels = np.concatenate(val_labels)\n\nprint(\"Validation Images Ready:\", val_images.shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:17:02.718401Z","iopub.execute_input":"2025-11-29T17:17:02.718788Z","iopub.status.idle":"2025-11-29T17:17:07.309231Z","shell.execute_reply.started":"2025-11-29T17:17:02.718765Z","shell.execute_reply":"2025-11-29T17:17:07.307531Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import random, matplotlib.pyplot as plt\n\nplt.figure(figsize=(10,10))\nfor i in range(9):\n    idx = random.randint(0,len(val_images)-1)\n    plt.subplot(3,3,i+1)\n    plt.imshow(val_images[idx].astype('uint8'))\n    plt.title(class_names[val_labels[idx]])\n    plt.axis(\"off\")\nplt.suptitle(\"Random Samples from Dataset\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:17:13.22446Z","iopub.execute_input":"2025-11-29T17:17:13.2249Z","iopub.status.idle":"2025-11-29T17:17:13.842514Z","shell.execute_reply.started":"2025-11-29T17:17:13.224873Z","shell.execute_reply":"2025-11-29T17:17:13.840684Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"correct = [i for i in range(len(y_true_f)) if y_true_f[i]==y_pred_f[i]]\nsample = random.sample(correct,9)\n\nplt.figure(figsize=(10,10))\nfor i,j in enumerate(sample):\n    plt.subplot(3,3,i+1)\n    plt.imshow(val_images[j].astype(\"uint8\"))\n    plt.title(f\"Correct → {class_names_f[y_true_f[j]]}\")\n    plt.axis(\"off\")\nplt.suptitle(\"Correct Predictions\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:17:19.4919Z","iopub.execute_input":"2025-11-29T17:17:19.492283Z","iopub.status.idle":"2025-11-29T17:17:20.102449Z","shell.execute_reply.started":"2025-11-29T17:17:19.492253Z","shell.execute_reply":"2025-11-29T17:17:20.101327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mix = correct[:4] + wrong[:5]\n\nplt.figure(figsize=(10,10))\nfor i,j in enumerate(mix):\n    plt.subplot(3,3,i+1)\n    plt.imshow(val_images[j].astype(\"uint8\"))\n    typ = \"✔\" if y_true_f[j]==y_pred_f[j] else \"❌\"\n    plt.title(f\"{typ} {class_names_f[y_pred_f[j]]}\")\n    plt.axis(\"off\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:17:44.945121Z","iopub.execute_input":"2025-11-29T17:17:44.945971Z","iopub.status.idle":"2025-11-29T17:17:45.51921Z","shell.execute_reply.started":"2025-11-29T17:17:44.945942Z","shell.execute_reply":"2025-11-29T17:17:45.518137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"remove_idx = 4  # class to delete\n\nmask = np.array(y_true) != remove_idx\n\ny_true_f = np.array(y_true)[mask]\ny_pred_f = np.array(y_pred)[mask]\n\n# convert labels to NEW index after removal\ny_true_f = np.where(y_true_f > remove_idx, y_true_f - 1, y_true_f)\ny_pred_f = np.where(y_pred_f > remove_idx, y_pred_f - 1, y_pred_f)\n\nclass_names_f = [c for i,c in enumerate(class_names) if i != remove_idx]\n\nprint(\"✔ Final Classes =\", len(class_names_f))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:18:35.125248Z","iopub.execute_input":"2025-11-29T17:18:35.125697Z","iopub.status.idle":"2025-11-29T17:18:35.136946Z","shell.execute_reply.started":"2025-11-29T17:18:35.125665Z","shell.execute_reply":"2025-11-29T17:18:35.135494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"wrong = [i for i in range(len(y_true_f)) if y_true_f[i]!=y_pred_f[i]]\nsample = random.sample(wrong,9)\n\nplt.figure(figsize=(10,10))\nfor i,j in enumerate(sample):\n    plt.subplot(3,3,i+1)\n    plt.imshow(val_images[j].astype(\"uint8\"))\n    plt.title(\n        f\"TRUE : {class_names_f[y_true_f[j]]}\\n\"\n        f\"PRED : {class_names_f[y_pred_f[j]]}\",\n        fontsize=8\n    )\n    plt.axis(\"off\")\nplt.suptitle(\"Wrong Predictions (Clean)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:18:39.638049Z","iopub.execute_input":"2025-11-29T17:18:39.639157Z","iopub.status.idle":"2025-11-29T17:18:40.842562Z","shell.execute_reply.started":"2025-11-29T17:18:39.639111Z","shell.execute_reply":"2025-11-29T17:18:40.841278Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"recall = cm.diagonal() / cm.sum(axis=1)\nplt.figure(figsize=(14,8))\nplt.bar(class_names_f, recall)\nplt.xticks(rotation=90)\nplt.ylabel(\"Recall\")\nplt.title(\"Class-wise Recall Score\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:21:55.872003Z","iopub.execute_input":"2025-11-29T17:21:55.872397Z","iopub.status.idle":"2025-11-29T17:21:56.846553Z","shell.execute_reply.started":"2025-11-29T17:21:55.872366Z","shell.execute_reply":"2025-11-29T17:21:56.845228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"correct = sum(y_true_f==y_pred_f)\nwrong   = len(y_true_f)-correct\n\nplt.pie([correct,wrong],labels=[\"Correct\",\"Wrong\"],autopct=\"%1.1f%%\",explode=[0.05,0])\nplt.title(\"Overall Model Success Ratio\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:21:56.973268Z","iopub.execute_input":"2025-11-29T17:21:56.973622Z","iopub.status.idle":"2025-11-29T17:21:57.07879Z","shell.execute_reply.started":"2025-11-29T17:21:56.973595Z","shell.execute_reply":"2025-11-29T17:21:57.077776Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\ncm_hot = np.where(cm_norm>0.15,cm_norm,0)   # only heavy confusions visible\nsns.heatmap(cm_hot,cmap=\"inferno\")\nplt.title(\"High-Intensity Error Hotspots\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:21:57.451241Z","iopub.execute_input":"2025-11-29T17:21:57.451717Z","iopub.status.idle":"2025-11-29T17:21:57.83037Z","shell.execute_reply.started":"2025-11-29T17:21:57.451689Z","shell.execute_reply":"2025-11-29T17:21:57.829014Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(6,6))\nplt.boxplot(recall,vert=True)\nplt.title(\"Model Stability Across Classes (Recall Spread)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T17:21:57.895835Z","iopub.execute_input":"2025-11-29T17:21:57.896114Z","iopub.status.idle":"2025-11-29T17:21:58.022084Z","shell.execute_reply.started":"2025-11-29T17:21:57.896097Z","shell.execute_reply":"2025-11-29T17:21:58.020972Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\nOLD = \"/kaggle/input/knlmh/final_model.keras\"   # correct path\nNEW = \"/kaggle/working/model.h5\"                # output file\n\nprint(\"\\n🔄 Converting .keras → .h5\")\n\nmodel = tf.keras.models.load_model(OLD, compile=False)\nmodel.save(NEW)\n\nprint(\"\\n✅ DONE — model.h5 created successfully!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T19:29:19.660301Z","iopub.execute_input":"2025-11-29T19:29:19.660688Z","iopub.status.idle":"2025-11-29T19:29:20.931013Z","shell.execute_reply.started":"2025-11-29T19:29:19.660661Z","shell.execute_reply":"2025-11-29T19:29:20.930256Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Yo Yo Honey Singh","metadata":{}},{"cell_type":"code","source":"import os\nimport json\nfrom collections import Counter\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport numpy as np\n\n# ------------------ CONFIG ------------------\n# Path to your dataset folder (folder that contains class subfolders)\nBASE_DIR = \"/kaggle/working/train\"\nDATA_DIR = \"/kaggle/working/train/train\"\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 4\nSEED = 42\nINITIAL_EPOCHS = 8\nFINE_TUNE_EPOCHS = 20\nFINAL_MODEL_PATH = os.path.join(BASE_DIR, \"final2.keras\")\nCLASSES_JSON = os.path.join(BASE_DIR, \"classes.json\")\n\n\ndef enable_gpu_memory_growth():\n    gpus = tf.config.list_physical_devices('GPU')\n    if gpus:\n        try:\n            for gpu in gpus:\n                tf.config.experimental.set_memory_growth(gpu, True)\n            print(\"GPU Enabled:\", gpus)\n        except Exception as e:\n            print(\"Could not set memory growth:\", e)\n    else:\n        print(\"⚠ Running on CPU (still works but slower).\")\n\n\ndef make_datasets(data_dir, image_size, batch_size, seed):\n    train_ds = tf.keras.utils.image_dataset_from_directory(\n        data_dir,\n        validation_split=0.2,\n        subset=\"training\",\n        seed=seed,\n        image_size=image_size,\n        batch_size=batch_size\n    )\n\n    val_ds = tf.keras.utils.image_dataset_from_directory(\n        data_dir,\n        validation_split=0.2,\n        subset=\"validation\",\n        seed=seed,\n        image_size=image_size,\n        batch_size=batch_size\n    )\n\n    class_names = train_ds.class_names\n    return train_ds, val_ds, class_names\n\n\ndef compute_class_weights(data_dir, class_names):\n    counts = []\n    for cname in class_names:\n        cpath = os.path.join(data_dir, cname)\n        total = 0\n        for root, _, files in os.walk(cpath):\n            for f in files:\n                if f.lower().endswith((\".jpg\", \".jpeg\", \".png\", \".bmp\")):\n                    total += 1\n        counts.append(total)\n    counts = np.array(counts, dtype=np.float32)\n    # Avoid division by zero\n    counts[counts == 0] = 1.0\n    total_images = counts.sum()\n    # Weight = total / (num_classes * count)\n    weights = total_images / (len(counts) * counts)\n    class_weight = {i: float(w) for i, w in enumerate(weights)}\n    return class_weight\n\n\ndef build_model(num_classes, input_shape=(224, 224, 3), dropout_rate=0.25):\n    data_augmentation = keras.Sequential([\n        layers.RandomFlip(\"horizontal\"),\n        layers.RandomRotation(0.05),\n        layers.RandomZoom(0.08),\n    ], name=\"data_augmentation\")\n\n    base = tf.keras.applications.MobileNetV2(\n        input_shape=input_shape,\n        include_top=False,\n        weights=\"imagenet\"\n    )\n    base.trainable = False\n\n    inputs = keras.Input(shape=input_shape)\n    x = data_augmentation(inputs)\n    x = tf.keras.applications.mobilenet_v2.preprocess_input(x)\n    x = base(x, training=False)\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dropout(dropout_rate)(x)\n    outputs = layers.Dense(num_classes, activation=\"softmax\")(x)\n    model = keras.Model(inputs, outputs)\n    return model, base\n\n\ndef prepare_for_performance(ds):\n    AUTOTUNE = tf.data.AUTOTUNE\n    return ds.cache().prefetch(buffer_size=AUTOTUNE)\n\n\ndef main():\n    enable_gpu_memory_growth()\n\n    if not os.path.isdir(DATA_DIR):\n        raise SystemExit(f\"Dataset folder not found: {DATA_DIR}\\nPlease set DATA_DIR correctly in the script.\")\n\n    train_ds, val_ds, class_names = make_datasets(DATA_DIR, IMAGE_SIZE, BATCH_SIZE, SEED)\n    NUM_CLASSES = len(class_names)\n    print('\\nClasses Detected:', NUM_CLASSES)\n\n    # Save class names so test script can map indices -> labels\n    with open(CLASSES_JSON, 'w', encoding='utf-8') as f:\n        json.dump(class_names, f, ensure_ascii=False, indent=2)\n    print(f\"Saved class names to: {CLASSES_JSON}\")\n\n    class_weight = compute_class_weights(DATA_DIR, class_names)\n    print('Class weights sample (first 6):', dict(list(class_weight.items())[:6]))\n\n    train_ds = prepare_for_performance(train_ds)\n    val_ds = prepare_for_performance(val_ds)\n\n    model, base = build_model(NUM_CLASSES, input_shape=IMAGE_SIZE + (3,))\n\n    model.compile(\n        optimizer=keras.optimizers.Adam(1e-4),\n        loss=\"sparse_categorical_crossentropy\",\n        metrics=[\"accuracy\"]\n    )\n\n    callbacks = [\n        keras.callbacks.ModelCheckpoint(FINAL_MODEL_PATH, save_best_only=True, monitor='val_accuracy'),\n        keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=1),\n        keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)\n    ]\n\n    print('\\n=== Stage 1: Train head only ===')\n    history1 = model.fit(\n        train_ds,\n        validation_data=val_ds,\n        epochs=INITIAL_EPOCHS,\n        class_weight=class_weight,\n        callbacks=callbacks\n    )\n\n    print('\\n=== Stage 2: Fine-tune top layers of MobileNetV2 ===')\n    base.trainable = True\n    # Freeze most layers, keep top layers trainable\n    for layer in base.layers[:-100]:\n        layer.trainable = False\n\n    model.compile(\n        optimizer=keras.optimizers.Adam(1e-5),\n        loss=\"sparse_categorical_crossentropy\",\n        metrics=[\"accuracy\"]\n    )\n\n    history2 = model.fit(\n        train_ds,\n        validation_data=val_ds,\n        epochs=FINE_TUNE_EPOCHS,\n        class_weight=class_weight,\n        callbacks=callbacks\n    )\n\n    # Ensure final model saved (ModelCheckpoint already saves best)\n    try:\n        model.save(FINAL_MODEL_PATH)\n        print(f\"Saved final model to: {FINAL_MODEL_PATH}\")\n    except Exception as e:\n        print(\"Warning: could not save final model via model.save():\", e)\n\n    print('\\n✅ Training complete')\n\n\nif __name__ == '__main__':\n    main()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T21:13:26.197774Z","iopub.execute_input":"2025-11-29T21:13:26.198098Z","execution_failed":"2025-11-29T21:15:43.538Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\n# ====================== CONFIG ======================\nDATA_DIR = \"/kaggle/working/train/train\"   # CHANGE THIS\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nEPOCHS = 15\nSEED = 42\n# =====================================================\n\n# ===== GPU safe =====\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    for g in gpus:\n        tf.config.experimental.set_memory_growth(g, True)\n    print(\"GPU Enabled:\", gpus)\nelse:\n    print(\"⚠ Running on CPU\")\n\n# ===== Load dataset =====\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nclass_names = train_ds.class_names\nNUM_CLASSES = len(class_names)\nprint(\"Classes:\", class_names)\n\n# CACHE + PREFETCH for speed\ntrain_ds = train_ds.prefetch(tf.data.AUTOTUNE)\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ================= Model — MobileNetV2 =================\ndata_aug = keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.05),\n    layers.RandomZoom(0.1),\n])\n\nbase = tf.keras.applications.MobileNetV2(\n    input_shape=IMAGE_SIZE + (3,),\n    include_top=False,\n    weights=\"imagenet\"\n)\nbase.trainable = False\n\ninputs = keras.Input(shape=IMAGE_SIZE + (3,))\nx = data_aug(inputs)\nx = tf.keras.applications.mobilenet_v2.preprocess_input(x)\nx = base(x, training=False)\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.Dropout(0.25)(x)\noutputs = layers.Dense(NUM_CLASSES, activation=\"softmax\")(x)\n\nmodel = keras.Model(inputs, outputs)\n\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-4),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nmodel.summary()\n\n# ================= Train (Stage 1) =================\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=[\n        keras.callbacks.ModelCheckpoint(\"best_model.keras\", save_best_only=True)\n    ]\n)\n\n# ================= Fine-Tune =================\nprint(\"\\n🔥 Fine-tuning model...\")\n\nbase.trainable = True\nfor layer in base.layers[:100]:  # freeze early layers\n    layer.trainable = False\n\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-5),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nfine_history = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=10,\n    callbacks=[\n        keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),\n        keras.callbacks.ModelCheckpoint(\"final_model.keras\", save_best_only=True)\n    ]\n)\n\nprint(\"\\n🎉 Training Complete\")\n\n# ============================================================\n#   ⭐⭐ CREATE CLEAN INFERENCE MODEL (NO AUGMENTATION) ⭐⭐\n# ============================================================\n\nprint(\"\\n🧼 Creating clean inference model...\")\n\nclean_inputs = keras.Input(shape=IMAGE_SIZE + (3,))\ny = tf.keras.applications.mobilenet_v2.preprocess_input(clean_inputs)\n\nclean_base = tf.keras.applications.MobileNetV2(\n    input_shape=IMAGE_SIZE + (3,),\n    include_top=False,\n    weights=\"imagenet\"\n)\nclean_base.trainable = True\n\ny = clean_base(y, training=False)\ny = layers.GlobalAveragePooling2D()(y)\ny = layers.Dropout(0.25)(y)\nclean_outputs = layers.Dense(NUM_CLASSES, activation=\"softmax\")(y)\n\nclean_model = keras.Model(clean_inputs, clean_outputs)\n\n# Copy weights\nclean_model.set_weights(model.get_weights())\n\n# Save clean version\nclean_model.save(\"model_clean.keras\")\nclean_model.save(\"model_clean.h5\")\n\nprint(\"\\n✅ Saved CLEAN models:\")\nprint(\" - model_clean.keras\")\nprint(\" - model_clean.h5\")\nprint(\"Load them safely without config warnings!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T21:18:41.042959Z","iopub.execute_input":"2025-11-29T21:18:41.043428Z","iopub.status.idle":"2025-11-29T21:43:59.025569Z","shell.execute_reply.started":"2025-11-29T21:18:41.043397Z","shell.execute_reply":"2025-11-29T21:43:59.024826Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport os\n\n# ====================== UPDATED CONFIG ======================\nDATA_DIR = \"/kaggle/working/train/train\"  \nIMAGE_SIZE = (256, 256)   # 224 → improved clarity\nBATCH_SIZE = 16           # 32 → more stable, prevent OOM\nEPOCHS_STAGE1 = 20\nEPOCHS_STAGE2 = 20\nLABEL_SMOOTH = 0.1        # 👈 new\nMIXUP_ALPHA = 0.15        # optional\nSEED = 42\n# ===========================================================\n\n# GPU MODE\ngpus = tf.config.list_physical_devices(\"GPU\")\nif gpus:\n    for g in gpus:\n        tf.config.experimental.set_memory_growth(g, True)\n    print(\"GPU Enabled:\", gpus)\nelse:\n    print(\"CPU Mode\")\n\n# ============= DATA LOADING =============\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR, validation_split=0.2, subset=\"training\",\n    seed=SEED, image_size=IMAGE_SIZE, batch_size=BATCH_SIZE\n)\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR, validation_split=0.2, subset=\"validation\",\n    seed=SEED, image_size=IMAGE_SIZE, batch_size=BATCH_SIZE\n)\n\nNUM_CLASSES = len(train_ds.class_names)\nprint(\"\\n📌 Classes:\", NUM_CLASSES)\n\ntrain_ds = train_ds.shuffle(1000).prefetch(tf.data.AUTOTUNE)\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# =========== MixUp (Optional — Highly Recommended) ===========\ndef mixup(batch_x, batch_y, alpha=MIXUP_ALPHA):\n    lam = tf.random.uniform([], 0, alpha)\n    idx = tf.random.shuffle(tf.range(tf.shape(batch_x)[0]))\n    mixed_x = lam * batch_x + (1-lam) * tf.gather(batch_x, idx)\n    y1 = tf.one_hot(batch_y, NUM_CLASSES)\n    y2 = tf.one_hot(tf.gather(batch_y, idx), NUM_CLASSES)\n    mixed_y = lam * y1 + (1-lam) * y2\n    return mixed_x, mixed_y\n\ndef apply_mixup(ds):\n    return ds.map(lambda x,y: mixup(x,y))\n\ntrain_ds = apply_mixup(train_ds)  # ⬅ MIXUP Enabled\n\n# ============ EfficientNetV2-S ============\nbase = keras.applications.EfficientNetV2S(\n    include_top=False, weights=\"imagenet\",\n    input_shape=IMAGE_SIZE + (3,)\n)\nbase.trainable = False\n\ninputs = keras.Input(shape=IMAGE_SIZE + (3,))\nx = keras.layers.Rescaling(1/255)(inputs)\n\nx = keras.layers.RandomFlip(\"horizontal\")(x)\nx = keras.layers.RandomRotation(0.15)(x)\nx = keras.layers.RandomZoom(0.2)(x)\nx = keras.layers.RandomContrast(0.2)(x)\n\nx = keras.applications.efficientnet_v2.preprocess_input(x)\nx = base(x, training=False)\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.Dropout(0.3)(x)\noutputs = layers.Dense(NUM_CLASSES, activation=\"softmax\",\n                       kernel_regularizer=keras.regularizers.l2(1e-5))(x)\n\nmodel = keras.Model(inputs, outputs)\n\nmodel.compile(\n    optimizer=keras.optimizers.AdamW(learning_rate=1e-4, weight_decay=1e-5),\n    loss=keras.losses.CategoricalCrossentropy(label_smoothing=LABEL_SMOOTH),\n    metrics=[\"accuracy\"]\n)\n\nprint(\"\\n🔷 Stage 1 Training Started...\")\nhist1 = model.fit(\n    train_ds, validation_data=val_ds,\n    epochs=EPOCHS_STAGE1,\n    callbacks=[keras.callbacks.ModelCheckpoint(\"stage1.keras\", save_best_only=True)]\n)\n\n# ============ Stage-2 Fine Tuning ============\nprint(\"\\n🔥 Fine-tuning started...\")\nbase.trainable = True\nfor layer in base.layers[:150]:\n    layer.trainable = False\n\nmodel.compile(\n    optimizer=keras.optimizers.AdamW(learning_rate=5e-5, weight_decay=1e-6),\n    loss=keras.losses.CategoricalCrossentropy(label_smoothing=LABEL_SMOOTH),\n    metrics=[\"accuracy\"]\n)\n\nhist2 = model.fit(\n    train_ds, validation_data=val_ds,\n    epochs=EPOCHS_STAGE2,\n    callbacks=[\n        keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True),\n        keras.callbacks.ModelCheckpoint(\"best_finetuned.keras\", save_best_only=True)\n    ]\n)\n\n# ================== EXPORT CLEAN MODEL ==================\nprint(\"\\n🧼 Building final inference model...\")\n\nclean_model = keras.models.clone_model(model)\nclean_model.set_weights(model.get_weights())\nclean_model.save(\"final_model3.keras\")\nclean_model.save(\"final_model3.h5\")\n\nprint(\"\\n🎉 Training Completed Successfully!\")\nprint(\"📦 Saved → final_model.keras + final_model.h5\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T21:47:18.127138Z","iopub.execute_input":"2025-11-29T21:47:18.128085Z","iopub.status.idle":"2025-11-29T21:49:52.349323Z","shell.execute_reply.started":"2025-11-29T21:47:18.128054Z","shell.execute_reply":"2025-11-29T21:49:52.348141Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\n\n# ======================= CONFIG =======================\nDATA_DIR = \"/kaggle/working/train/train\"\nIMAGE_SIZE = (224, 224)     # MobileNetV2 default & optimal\nBATCH_SIZE = 32             # You can reduce if OOM\nEPOCHS_STAGE1 = 20\nEPOCHS_STAGE2 = 15\nLABEL_SMOOTH = 0.1\nMIXUP_ALPHA = 0.15\nSEED = 42\n# ======================================================\n\n\n# GPU SAFETY\ngpus = tf.config.list_physical_devices(\"GPU\")\nif gpus:\n    for g in gpus:\n        tf.config.experimental.set_memory_growth(g, True)\n    print(\"GPU ENABLED\")\nelse:\n    print(\"CPU MODE\")\n\n# ================== LOAD DATA ======================\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR, validation_split=0.2, subset=\"training\",\n    seed=SEED, image_size=IMAGE_SIZE, batch_size=BATCH_SIZE\n)\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR, validation_split=0.2, subset=\"validation\",\n    seed=SEED, image_size=IMAGE_SIZE, batch_size=BATCH_SIZE\n)\n\nCLASSES = train_ds.class_names\nNUM_CLASSES = len(CLASSES)\nprint(\"\\n📌 Classes:\", NUM_CLASSES)\n\ntrain_ds = train_ds.shuffle(1000).prefetch(tf.data.AUTOTUNE)\nval_ds  = val_ds.prefetch(tf.data.AUTOTUNE)\n\n\n# =================== MixUp Augmentation ===================\ndef mixup(x, y, a=MIXUP_ALPHA):\n    lam = tf.random.uniform([], 0, 1)\n    idx = tf.random.shuffle(tf.range(tf.shape(x)[0]))\n    mixed_x = lam * x + (1-lam) * tf.gather(x, idx)\n    y1 = tf.one_hot(y, NUM_CLASSES)\n    y2 = tf.one_hot(tf.gather(y, idx), NUM_CLASSES)\n    mixed_y = lam*y1 + (1-lam)*y2\n    return mixed_x, mixed_y\n\ndef apply_mix(ds):\n    return ds.map(lambda x,y: mixup(x,y))\n\ntrain_ds = apply_mix(train_ds)  # ⭐ Enabled MixUp Regularization\n\n\n# =================== MobileNetV2 BASE ===================\nbase = keras.applications.MobileNetV2(\n    include_top=False, input_shape=IMAGE_SIZE+(3,),\n    weights=\"imagenet\", alpha=1.0\n)\nbase.trainable = False   # Stage-1 Freeze\n\ninputs = keras.Input(shape=IMAGE_SIZE + (3,))\nx = layers.Rescaling(1/255.0)(inputs)\n\n# Strong Augmentation\nx = layers.RandomFlip(\"horizontal\")(x)\nx = layers.RandomRotation(0.15)(x)\nx = layers.RandomZoom(0.20)(x)\nx = layers.RandomContrast(0.20)(x)\n\nx = keras.applications.mobilenet_v2.preprocess_input(x)\nx = base(x, training=False)\n\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.Dropout(0.4)(x)                     # Increased dropout\noutputs = layers.Dense(NUM_CLASSES, activation=\"softmax\",\n                       kernel_regularizer=keras.regularizers.l2(1e-5))(x)\n\nmodel = keras.Model(inputs, outputs)\n\nmodel.compile(\n    optimizer=keras.optimizers.AdamW(1e-4, weight_decay=1e-5),\n    loss=keras.losses.CategoricalCrossentropy(label_smoothing=LABEL_SMOOTH),\n    metrics=[\"accuracy\"]\n)\n\nprint(\"\\n🔷 Stage-1 Training (Head Only)\")\nmodel.fit(\n    train_ds, validation_data=val_ds,\n    epochs=EPOCHS_STAGE1,\n    callbacks=[keras.callbacks.ModelCheckpoint(\"mnet_stage1.keras\", save_best_only=True)]\n)\n\n\n# =================== Fine-tuning Stage-2 ===================\nprint(\"\\n🔥 Fine-tuning MobileNetV2...\")\nbase.trainable = True\n\n# Freeze very early low-level layers\nfor layer in base.layers[:80]:\n    layer.trainable = False\n\nmodel.compile(\n    optimizer=keras.optimizers.AdamW(5e-5, weight_decay=1e-6),\n    loss=keras.losses.CategoricalCrossentropy(label_smoothing=LABEL_SMOOTH),\n    metrics=[\"accuracy\"]\n)\n\nmodel.fit(\n    train_ds, validation_data=val_ds,\n    epochs=EPOCHS_STAGE2,\n    callbacks=[\n        keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True),\n        keras.callbacks.ModelCheckpoint(\"best_mobilenet_finetuned.keras\", save_best_only=True)\n    ]\n)\n\n\n# =================== Export Final Model ====================\nprint(\"\\n📦 Building final export model...\")\n\nexport = keras.models.clone_model(model)\nexport.set_weights(model.get_weights())\nexport.save(\"mobilenet_final.keras\")\nexport.save(\"mobilenet_final.h5\")\n\nprint(\"\\n🎉 DONE! SAVED → mobilenet_final.keras + mobilenet_final.h5\")\nprint(\"✔ Ready for Flask API 🚀\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T21:50:10.637326Z","iopub.execute_input":"2025-11-29T21:50:10.638047Z","iopub.status.idle":"2025-11-29T21:51:02.535859Z","shell.execute_reply.started":"2025-11-29T21:50:10.638021Z","shell.execute_reply":"2025-11-29T21:51:02.53481Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# full_pipeline_train.py\nimport os\nimport math\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport itertools\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom sklearn.metrics import confusion_matrix, classification_report\n\n# ====================== CONFIG ======================\n# 🔴 CHANGE THIS to your dataset path name 🔴\nDATA_DIR = \"/kaggle/working/train/train\"   # <--- Modify this\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 4   # If kernel crashes → reduce to 8 or 4\nEPOCHS_HEAD = 15\nEPOCHS_FINE = 10\nSEED = 42\nAUTOTUNE = tf.data.AUTOTUNE\n\n# Model choice: \"mobilenetv2\" or \"efficientnetb0\"\nBASE_MODEL = \"mobilenetv2\"\n# =====================================================\n\n# Reproducibility\ntf.random.set_seed(SEED)\nnp.random.seed(SEED)\n\n# GPU memory safe mode\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    try:\n        for gpu in gpus:\n            tf.config.experimental.set_memory_growth(gpu, True)\n        print(\"GPU Enabled:\", gpus)\n    except RuntimeError as e:\n        print(\"GPU config error:\", e)\nelse:\n    print(\"⚠ Running on CPU (still works but slower).\")\n\n# ================= Load Dataset with built-in Train/Validation Split =================\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nclass_names = train_ds.class_names\nNUM_CLASSES = len(class_names)\n\nprint(\"\\nClasses Detected:\", NUM_CLASSES)\nprint(class_names[:20], \" ...\")\n\n# Optimize pipeline\ntrain_ds = train_ds.cache().prefetch(AUTOTUNE)\nval_ds = val_ds.cache().prefetch(AUTOTUNE)\n\n# ================= Check Class Imbalance and compute class weights =================\n# We'll compute class counts from the training dataset.\ncounts = np.zeros(NUM_CLASSES, dtype=int)\nfor images, labels in train_ds.unbatch():\n    counts[int(labels.numpy())] += 1\n\nprint(\"Class counts:\", dict(zip(class_names, counts.tolist())))\n\n# Compute class weights (balanced)\ntotal = counts.sum()\nclass_weight = {i: (total / (NUM_CLASSES * counts[i])) if counts[i] > 0 else 1.0\n                for i in range(NUM_CLASSES)}\nprint(\"Class weights:\", class_weight)\n\n# ================= Data Augmentation / Preprocessing =================\ndata_augmentation = keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.06),\n    layers.RandomZoom(0.08),\n    layers.RandomContrast(0.06),\n], name=\"data_augmentation\")\n\n# Preprocessing: use the appropriate preprocess_input for the chosen base model\nif BASE_MODEL == \"mobilenetv2\":\n    preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input\nelif BASE_MODEL == \"efficientnetb0\":\n    preprocess_input = tf.keras.applications.efficientnet.preprocess_input\nelse:\n    raise ValueError(\"Unknown BASE_MODEL: choose 'mobilenetv2' or 'efficientnetb0'\")\n\n# ================= Build Model =================\ndef build_model(image_size=IMAGE_SIZE + (3,), num_classes=NUM_CLASSES, base_name=BASE_MODEL):\n    inputs = keras.Input(shape=image_size)\n    x = data_augmentation(inputs)\n    x = preprocess_input(x)\n    if base_name == \"mobilenetv2\":\n        base = tf.keras.applications.MobileNetV2(\n            input_shape=image_size,\n            include_top=False,\n            weights=\"imagenet\"\n        )\n    else:  # efficientnetb0\n        base = tf.keras.applications.EfficientNetB0(\n            input_shape=image_size,\n            include_top=False,\n            weights=\"imagenet\"\n        )\n    base.trainable = False\n    x = base(x, training=False)\n    x = layers.GlobalAveragePooling2D(name=\"gap\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.3)(x)  # stronger dropout\n    outputs = layers.Dense(num_classes, activation=\"softmax\", name=\"predictions\")(x)\n    model = keras.Model(inputs, outputs, name=f\"clf_{base_name}\")\n    return model, base\n\nmodel, base_model = build_model()\nmodel.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=1e-4),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nmodel.summary()\n\n# ================= Callbacks =================\ncheckpoint_cb = keras.callbacks.ModelCheckpoint(\n    \"best_model.keras\",\n    monitor=\"val_accuracy\",\n    save_best_only=True,\n    save_weights_only=False\n)\n\nearlystop_cb = keras.callbacks.EarlyStopping(\n    monitor=\"val_loss\",\n    patience=4,\n    restore_best_weights=True\n)\n\nreduce_lr_cb = keras.callbacks.ReduceLROnPlateau(\n    monitor=\"val_loss\",\n    factor=0.3,\n    patience=2,\n    min_lr=1e-7,\n    verbose=1\n)\n\ntensorboard_cb = keras.callbacks.TensorBoard(log_dir=\"./logs\", histogram_freq=1)\n\n# ================= Train Stage 1: Head Only =================\nhistory_head = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS_HEAD,\n    class_weight=class_weight,\n    callbacks=[checkpoint_cb, reduce_lr_cb, tensorboard_cb]\n)\n\n# Save head snapshot\nmodel.save(\"model_head_snapshot.h5\")\n\n# ================= Fine-Tune (Unfreeze top layers) =================\nprint(\"\\n🔥 Fine-tuning top layers ...\")\n# Unfreeze last N layers of the base\nbase_model.trainable = True\n# Freeze earlier layers to keep low-level features stable (tune last ~100 layers or based on size)\nfreeze_until = -100 if len(base_model.layers) > 100 else 0\nfor layer in base_model.layers[:freeze_until]:\n    layer.trainable = False\nfor layer in base_model.layers[freeze_until:]:\n    layer.trainable = True\n\n# Lower LR for fine-tuning\nmodel.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=1e-5),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nfine_tune_history = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS_FINE,\n    class_weight=class_weight,\n    callbacks=[earlystop_cb, keras.callbacks.ModelCheckpoint(\"final_model.keras\", save_best_only=True), reduce_lr_cb]\n)\n\n# Save final models (both formats)\nmodel.save(\"final_model.h5\")\nmodel.save(\"final_model.keras\")\n\nprint(\"\\n✅ Model Training Completed Successfully 🎉\")\nprint(\"📁 Saved Files:\")\nprint(\" ├─ best_model.keras\")\nprint(\" ├─ model_head_snapshot.h5\")\nprint(\" ├─ final_model.h5\")\nprint(\" └─ final_model.keras\")\n\n# ================= Plotting utilities =================\ndef plot_history(h1, h2=None, title_suffix=\"\"):\n    # h1, h2 are History objects (can be None)\n    plt.figure(figsize=(14,5))\n    # Accuracy\n    plt.subplot(1,2,1)\n    if h1:\n        plt.plot(h1.history.get('accuracy', []), label='train_acc_head')\n        plt.plot(h1.history.get('val_accuracy', []), label='val_acc_head')\n    if h2:\n        plt.plot(h2.history.get('accuracy', []), label='train_acc_ft')\n        plt.plot(h2.history.get('val_accuracy', []), label='val_acc_ft')\n    plt.title('Accuracy ' + title_suffix)\n    plt.legend()\n    # Loss\n    plt.subplot(1,2,2)\n    if h1:\n        plt.plot(h1.history.get('loss', []), label='train_loss_head')\n        plt.plot(h1.history.get('val_loss', []), label='val_loss_head')\n    if h2:\n        plt.plot(h2.history.get('loss', []), label='train_loss_ft')\n        plt.plot(h2.history.get('val_loss', []), label='val_loss_ft')\n    plt.title('Loss ' + title_suffix)\n    plt.legend()\n    plt.tight_layout()\n    plt.show()\n\nplot_history(history_head, fine_tune_history, title_suffix=f\"({BASE_MODEL})\")\n\n# ================= Evaluate on Validation set & Confusion Matrix =================\n# Build arrays for true labels and predictions\ny_true = []\ny_pred = []\ny_probs = []\nfor images, labels in val_ds.unbatch():\n    imgs = tf.expand_dims(images, axis=0)\n    probs = model.predict(imgs, verbose=0)[0]\n    pred = np.argmax(probs)\n    y_true.append(int(labels.numpy()))\n    y_pred.append(int(pred))\n    y_probs.append(probs)\n\ny_true = np.array(y_true)\ny_pred = np.array(y_pred)\n\nprint(\"\\nClassification Report:\")\nprint(classification_report(y_true, y_pred, target_names=class_names, digits=4))\n\ndef plot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix'):\n    if normalize:\n        cm = cm.astype('float') / (cm.sum(axis=1)[:, np.newaxis] + 1e-12)\n    plt.figure(figsize=(8, 6))\n    plt.imshow(cm, interpolation='nearest')\n    plt.title(title)\n    plt.colorbar()\n    tick_marks = np.arange(len(classes))\n    plt.xticks(tick_marks, classes, rotation=90)\n    plt.yticks(tick_marks, classes)\n    fmt = '.2f' if normalize else 'd'\n    thresh = cm.max() / 2.\n    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n        plt.text(j, i, format(cm[i, j], fmt),\n                 horizontalalignment=\"center\",\n                 color=\"white\" if cm[i, j] > thresh else \"black\")\n    plt.ylabel('True label')\n    plt.xlabel('Predicted label')\n    plt.tight_layout()\n    plt.show()\n\ncm = confusion_matrix(y_true, y_pred)\nplot_confusion_matrix(cm, class_names, normalize=False, title='Confusion matrix (counts)')\nplot_confusion_matrix(cm, class_names, normalize=True, title='Confusion matrix (normalized)')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T17:39:19.531155Z","iopub.execute_input":"2025-11-30T17:39:19.531735Z","execution_failed":"2025-11-30T17:41:50.3Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ======================= IMPORTS =======================\nimport os\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport itertools\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom sklearn.metrics import confusion_matrix, classification_report\n\n\n# ======================= CONFIG =======================\nDATA_DIR = \"/kaggle/working/train/train\"  # 🔴 CHANGE THIS\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 8   # memory-safe\nEPOCHS_HEAD = 10\nEPOCHS_FINE = 10\nSEED = 42\nAUTOTUNE = tf.data.AUTOTUNE\n\n\n# ======================= GPU SAFE MODE =======================\ngpus = tf.config.list_physical_devices(\"GPU\")\nif gpus:\n    for g in gpus:\n        tf.config.experimental.set_memory_growth(g, True)\n    print(\"GPU Enabled:\", gpus)\nelse:\n    print(\"⚠ Using CPU\")\n\n\n# ======================= LOAD DATA =======================\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nclass_names = train_ds.class_names\nNUM_CLASSES = len(class_names)\nprint(\"Classes:\", class_names)\n\n# MEMORY SAFE — NO CACHE\ntrain_ds = train_ds.prefetch(AUTOTUNE)\nval_ds = val_ds.prefetch(AUTOTUNE)\n\n\n# ======================= CLASS WEIGHTS =======================\ncounts = np.zeros(NUM_CLASSES, dtype=int)\nfor _, labels in train_ds.unbatch():\n    counts[int(labels.numpy())] += 1\n\ntotal = counts.sum()\nclass_weight = {i: total / (NUM_CLASSES * counts[i]) for i in range(NUM_CLASSES)}\n\nprint(\"Class Weights:\", class_weight)\n\n\n# ======================= DATA AUGMENTATION =======================\ndata_aug = keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.05),\n    layers.RandomZoom(0.1)\n])\n\n\n# ======================= BUILD MODEL =======================\ndef build_model():\n    inputs = keras.Input(shape=IMAGE_SIZE + (3,))\n    x = data_aug(inputs)\n    x = tf.keras.applications.mobilenet_v2.preprocess_input(x)\n\n    base = tf.keras.applications.MobileNetV2(\n        include_top=False,\n        weights=\"imagenet\",\n        input_shape=IMAGE_SIZE + (3,)\n    )\n\n    base.trainable = False\n\n    x = base(x, training=False)\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dropout(0.3)(x)\n    outputs = layers.Dense(NUM_CLASSES, activation=\"softmax\")(x)\n\n    model = keras.Model(inputs, outputs)\n    return model, base\n\n\nmodel, base = build_model()\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-4),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nmodel.summary()\n\n\n# ======================= CALLBACKS =======================\nckpt = keras.callbacks.ModelCheckpoint(\n    \"best_model.keras\",\n    monitor=\"val_accuracy\",\n    save_best_only=True\n)\n\nearly = keras.callbacks.EarlyStopping(\n    monitor=\"val_loss\",\n    patience=3,\n    restore_best_weights=True\n)\n\nreduceLR = keras.callbacks.ReduceLROnPlateau(\n    monitor=\"val_loss\",\n    factor=0.3,\n    patience=2,\n    min_lr=1e-7\n)\n\n\n# ======================= TRAIN — HEAD ONLY =======================\nhistory_head = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS_HEAD,\n    class_weight=class_weight,\n    callbacks=[ckpt, reduceLR]\n)\n\n\n# ======================= FINE-TUNE =======================\nprint(\"\\n🔥 Fine-Tuning...\")\n\nbase.trainable = True\n\n# Keep first 100 layers frozen\nfor layer in base.layers[:100]:\n    layer.trainable = False\n\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-5),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nhistory_fine = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS_FINE,\n    class_weight=class_weight,\n    callbacks=[early, keras.callbacks.ModelCheckpoint(\"final_model.keras\", save_best_only=True)]\n)\n\nmodel.save(\"final_model.h5\")\n\n\n# ======================= PLOT TRAINING =======================\ndef plot_history(h1, h2):\n    plt.figure(figsize=(14,5))\n\n    # ACC\n    plt.subplot(1,2,1)\n    plt.plot(h1.history[\"accuracy\"], label=\"train_head\")\n    plt.plot(h1.history[\"val_accuracy\"], label=\"val_head\")\n    plt.plot(h2.history[\"accuracy\"], label=\"train_fine\")\n    plt.plot(h2.history[\"val_accuracy\"], label=\"val_fine\")\n    plt.legend()\n    plt.title(\"Accuracy\")\n\n    # LOSS\n    plt.subplot(1,2,2)\n    plt.plot(h1.history[\"loss\"], label=\"train_head\")\n    plt.plot(h1.history[\"val_loss\"], label=\"val_head\")\n    plt.plot(h2.history[\"loss\"], label=\"train_fine\")\n    plt.plot(h2.history[\"val_loss\"], label=\"val_fine\")\n    plt.legend()\n    plt.title(\"Loss\")\n\n    plt.show()\n\nplot_history(history_head, history_fine)\n\n\n# ======================= CONFUSION MATRIX =======================\ny_true = []\ny_pred = []\n\nfor imgs, labels in val_ds.unbatch():\n    probs = model.predict(np.expand_dims(imgs, 0), verbose=0)[0]\n    y_pred.append(np.argmax(probs))\n    y_true.append(int(labels.numpy()))\n\ncm = confusion_matrix(y_true, y_pred)\n\nprint(\"\\nClassification Report\")\nprint(classification_report(y_true, y_pred, target_names=class_names))\n\n\ndef plot_cm(cm, classes):\n    plt.figure(figsize=(8,6))\n    plt.imshow(cm)\n    plt.title(\"Confusion Matrix\")\n    plt.colorbar()\n    ticks = np.arange(len(classes))\n    plt.xticks(ticks, classes, rotation=90)\n    plt.yticks(ticks, classes)\n    thresh = cm.max()/2.\n\n    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n        plt.text(j, i, cm[i,j],\n                 horizontalalignment=\"center\",\n                 color=\"white\" if cm[i,j] > thresh else \"black\")\n    plt.tight_layout()\n    plt.show()\n\nplot_cm(cm, class_names)\n\n\n# ======================= GRAD-CAM =======================\ndef make_gradcam_heatmap(img_array, model, last_conv_layer_name):\n    last_conv_layer = model.get_layer(last_conv_layer_name)\n    grad_model = keras.Model([model.inputs], [last_conv_layer.output, model.output])\n\n    with tf.GradientTape() as tape:\n        conv_out, preds = grad_model(img_array)\n        class_index = tf.argmax(preds[0])\n        class_output = preds[:, class_index]\n\n    grads = tape.gradient(class_output, conv_out)[0]\n    pooled_grads = tf.reduce_mean(grads, axis=(0,1,2))\n    conv_out = conv_out[0]\n\n    heatmap = conv_out @ pooled_grads[..., tf.newaxis]\n    heatmap = tf.squeeze(heatmap)\n    heatmap = tf.maximum(heatmap, 0) / (tf.reduce_max(heatmap) + 1e-10)\n    return heatmap.numpy()\n\n\nlast_conv = None\nfor layer in reversed(model.layers):\n    if len(layer.output_shape) == 4:\n        last_conv = layer.name\n        break\n\nprint(\"Last Conv Layer:\", last_conv)\n\n\ndef visualize_gradcam(img, heatmap):\n    import cv2\n    img_uint8 = (img * 255).astype(np.uint8)\n    heatmap = cv2.resize(heatmap, (img_uint8.shape[1], img_uint8.shape[0]))\n\n    heatmap_color = cv2.applyColorMap(np.uint8(255 * heatmap), cv2.COLORMAP_JET)\n    superimposed = cv2.addWeighted(img_uint8, 0.6, heatmap_color, 0.4, 0)\n\n    plt.figure(figsize=(6,6))\n    plt.imshow(superimposed)\n    plt.axis(\"off\")\n    plt.show()\n\n\ncount = 0\nfor imgs, _ in val_ds.take(1):\n    for i in range(3):\n        img = imgs[i].numpy()\n        img_in = np.expand_dims(tf.keras.applications.mobilenet_v2.preprocess_input(img), 0)\n        heat = make_gradcam_heatmap(img_in, model, last_conv)\n        visualize_gradcam(img, heat)\n\n\nprint(\"\\n🎉 Training Finished — Models Saved!\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\n# ====================== CONFIG ======================\n# 🔴 CHANGE THIS to your dataset path name 🔴\nDATA_DIR = \"/kaggle/working/train/train\"   # <--- Modify this\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16   # If kernel crashes → reduce to 8\nEPOCHS = 30\nSEED = 42\n# =====================================================\n\n# GPU memory safe mode (no crash)\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    for gpu in gpus:\n        tf.config.experimental.set_memory_growth(gpu, True)\n    print(\"GPU Enabled:\", gpus)\nelse:\n    print(\"⚠ Running on CPU (still works but slower).\")\n\n\n# ====== Load Dataset with built-in Train/Validation Split ======\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nclass_names = train_ds.class_names\nNUM_CLASSES = len(class_names)\n\nprint(\"\\nClasses Detected:\", NUM_CLASSES)\nprint(class_names[:10], \" ...\")   # Preview first 10\n\n\n# ================= Model — MobileNetV2 (Pretrained) ================\ndata_aug = keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.05),\n    layers.RandomZoom(0.1),\n])\n\nbase = tf.keras.applications.MobileNetV2(\n    input_shape=IMAGE_SIZE + (3,),\n    include_top=False,\n    weights=\"imagenet\"\n)\nbase.trainable = False  # Freeze first phase\n\ninputs = keras.Input(shape=IMAGE_SIZE + (3,))\nx = data_aug(inputs)\nx = tf.keras.applications.mobilenet_v2.preprocess_input(x)\nx = base(x, training=False)\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.Dropout(0.25)(x)\noutputs = layers.Dense(NUM_CLASSES, activation=\"softmax\")(x)\n\nmodel = keras.Model(inputs, outputs)\nmodel.compile(optimizer=keras.optimizers.Adam(1e-4),\n              loss=\"sparse_categorical_crossentropy\",\n              metrics=[\"accuracy\"])\n\nmodel.summary()\n\n\n# ================= Train (Stage 1: Head Only) =================\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=[\n        keras.callbacks.ModelCheckpoint(\"best_modellllll.keras\",\n                                        save_best_only=True,\n                                        monitor='val_accuracy')\n    ]\n)\n\n# ================= Fine-Tune (More Accuracy) =================\nprint(\"\\n🔥 Fine-tuning top MobileNet layers ...\")\n\nbase.trainable = True\nfor layer in base.layers[:100]:\n    layer.trainable = False  # Keep early layers frozen\n\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-5),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nfine_tune = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=30,  # You can increase to 20–30 for best results\n    callbacks=[\n        keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),\n        keras.callbacks.ModelCheckpoint(\"final_modelllllll.keras\", save_best_only=True)\n    ]\n)\n\nprint(\"\\n✅ Model Training Completed Successfully 🎉\")\nprint(\"📁 Saved Files:\")\nprint(\" ├─ best_model.keras\")\nprint(\" └─ final_model.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T18:15:52.324984Z","iopub.execute_input":"2025-11-30T18:15:52.325832Z","iopub.status.idle":"2025-11-30T19:06:00.745183Z","shell.execute_reply.started":"2025-11-30T18:15:52.325798Z","shell.execute_reply":"2025-11-30T19:06:00.744369Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\ncomplex_cnn_eval.py\n\nLoads your trained model (.keras) and computes:\n- Accuracy\n- Confusion matrix\n- Precision, recall, F1-score (per class)\n- Plots:\n    1) Confusion matrix heatmap\n    2) ROC curve (for binary classification)\n    3) Precision-Recall curve (for binary classification)\n\"\"\"\n\nimport tensorflow as tf\nfrom tensorflow import keras\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    confusion_matrix,\n    classification_report,\n    accuracy_score,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\n\n# ====================== CONFIG ======================\n# 🔴 CHANGE THESE PATHS ACCORDING TO YOUR KAGGLE DATASETS\nDATA_DIR   = \"/kaggle/working/train/train\"     # same folder structure as training\nMODEL_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"  # <--- set to your model\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ====================== LOAD DATA (VALIDATION SPLIT) ======================\nval_ds = keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    batch_size=BATCH_SIZE,\n    image_size=IMAGE_SIZE,\n)\n\nclass_names = val_ds.class_names\nnum_classes = len(class_names)\nprint(\"Classes detected:\", num_classes)\nprint(\"Class names example:\", class_names[:10], \"...\")\n\n# Optional prefetch (doesn't break evaluation)\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ====================== LOAD TRAINED MODEL ======================\nprint(\"\\nLoading model from:\", MODEL_PATH)\nmodel = keras.models.load_model(MODEL_PATH)\nmodel.summary()\n\n# ====================== PREDICTIONS & TRUE LABELS ======================\ny_true = []\ny_prob = []\n\nprint(\"\\nRunning predictions on validation set...\")\nfor batch_images, batch_labels in val_ds:\n    y_true.append(batch_labels.numpy())\n    batch_pred = model.predict(batch_images, verbose=0)\n    y_prob.append(batch_pred)\n\ny_true = np.concatenate(y_true, axis=0)\ny_prob = np.concatenate(y_prob, axis=0)\n\n# Convert probabilities to predicted labels\nif num_classes == 2:\n    # Binary: model output shape (N, 1) with sigmoid\n    y_scores = y_prob.ravel()\n    y_pred = (y_scores >= 0.5).astype(int)\nelse:\n    # Multi-class: model output shape (N, C) with softmax\n    y_scores = y_prob\n    y_pred = np.argmax(y_scores, axis=1)\n\n# ====================== METRICS (FIXED) ======================\nlabels = np.arange(num_classes)  # [0, 1, 2, ..., num_classes-1]\n\nacc = accuracy_score(y_true, y_pred)\nprint(f\"\\n✅ Validation Accuracy: {acc * 100:.2f}%\\n\")\n\nprint(\"📊 Classification Report:\")\nprint(classification_report(\n    y_true,\n    y_pred,\n    labels=labels,             # <-- force all class indices\n    target_names=class_names,  # <-- same length as labels\n    zero_division=0            # <-- avoid warnings for empty classes\n))\n\ncm = confusion_matrix(y_true, y_pred, labels=labels)\nprint(\"Confusion Matrix (raw counts):\")\nprint(cm)\n\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\nfig, ax = plt.subplots(figsize=(6, 5))\nim = ax.imshow(cm, interpolation=\"nearest\")\nax.figure.colorbar(im, ax=ax)\nax.set(\n    xticks=np.arange(num_classes),\n    yticks=np.arange(num_classes),\n    xticklabels=class_names,\n    yticklabels=class_names,\n    ylabel=\"True label\",\n    xlabel=\"Predicted label\",\n    title=\"Confusion Matrix\"\n)\n\nplt.setp(ax.get_xticklabels(), rotation=45, ha=\"right\", rotation_mode=\"anchor\")\n\n# Annotate cells\nthresh = cm.max() / 2.0 if cm.max() > 0 else 0.5\nfor i in range(cm.shape[0]):\n    for j in range(cm.shape[1]):\n        ax.text(\n            j, i, format(cm[i, j], \"d\"),\n            ha=\"center\", va=\"center\",\n            color=\"white\" if cm[i, j] > thresh else \"black\"\n        )\n\nplt.tight_layout()\nplt.show()\n\n# ====================== PLOT 2 & 3: ROC + PR (BINARY ONLY) ======================\nif num_classes == 2:\n    # --------- ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true, y_scores)\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"ROC curve (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(y_true, y_scores)\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"PR curve (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\nelse:\n    print(\"\\n(ROC / PR curves are only implemented for binary classification in this script.)\")\n\nprint(\"\\n🎯 Evaluation complete.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T05:45:23.465056Z","iopub.execute_input":"2025-12-01T05:45:23.465727Z","iopub.status.idle":"2025-12-01T05:46:21.161325Z","shell.execute_reply.started":"2025-12-01T05:45:23.465696Z","shell.execute_reply":"2025-12-01T05:46:21.160605Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\ncomplex_cnn_eval.py\n\nLoads your trained model (.keras) and computes:\n- Accuracy\n- Confusion matrix\n- Precision, recall, F1-score (per class)\n- Plots:\n    1) Confusion matrix heatmap\n    2) ROC curve\n    3) Precision-Recall curve\nWorks for BOTH binary and multi-class (micro-averaged for multi-class).\n\"\"\"\n\nimport tensorflow as tf\nfrom tensorflow import keras\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    confusion_matrix,\n    classification_report,\n    accuracy_score,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\n\n# ====================== CONFIG ======================\n# 🔴 CHANGE THESE PATHS ACCORDING TO YOUR KAGGLE DATASETS\nDATA_DIR   = \"/kaggle/working/train/train\"     # same folder structure as training\nMODEL_PATH = \"/kaggle/input/dadadadad/final_modelllllll.keras\"  # <--- set to your model\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ====================== LOAD DATA (VALIDATION SPLIT) ======================\nval_ds = keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    batch_size=BATCH_SIZE,\n    image_size=IMAGE_SIZE,\n)\n\nclass_names = val_ds.class_names\nnum_classes = len(class_names)\nprint(\"Classes detected:\", num_classes)\nprint(\"Class names example:\", class_names[:10], \"...\")\n\n# Optional prefetch (doesn't break evaluation)\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ====================== LOAD TRAINED MODEL ======================\nprint(\"\\nLoading model from:\", MODEL_PATH)\nmodel = keras.models.load_model(MODEL_PATH)\nmodel.summary()\n\n# ====================== PREDICTIONS & TRUE LABELS ======================\ny_true = []\ny_prob = []\n\nprint(\"\\nRunning predictions on validation set...\")\nfor batch_images, batch_labels in val_ds:\n    y_true.append(batch_labels.numpy())\n    batch_pred = model.predict(batch_images, verbose=0)\n    y_prob.append(batch_pred)\n\ny_true = np.concatenate(y_true, axis=0)   # shape (N,)\ny_prob = np.concatenate(y_prob, axis=0)   # shape (N, num_classes) or (N, 1)\n\n# Convert probabilities to predicted labels\nif num_classes == 2:\n    # Binary: model output shape (N, 1) with sigmoid OR (N, 2) with softmax\n    if y_prob.shape[1] == 1:\n        # Sigmoid\n        y_scores = y_prob.ravel()\n        y_pred = (y_scores >= 0.5).astype(int)\n    else:\n        # Softmax with 2 outputs\n        y_scores = y_prob[:, 1]          # probability of positive class\n        y_pred = np.argmax(y_prob, axis=1)\nelse:\n    # Multi-class: model output shape (N, C) with softmax\n    y_scores = y_prob                    # shape (N, num_classes)\n    y_pred = np.argmax(y_scores, axis=1)\n\n# ====================== METRICS ======================\nlabels = np.arange(num_classes)  # [0, 1, 2, ..., num_classes-1]\n\nacc = accuracy_score(y_true, y_pred)\nprint(f\"\\n✅ Validation Accuracy: {acc * 100:.2f}%\\n\")\n\nprint(\"📊 Classification Report:\")\nprint(classification_report(\n    y_true,\n    y_pred,\n    labels=labels,             # force all class indices\n    target_names=class_names,  # same length as labels\n    zero_division=0            # avoid warnings for empty classes\n))\n\ncm = confusion_matrix(y_true, y_pred, labels=labels)\nprint(\"Confusion Matrix (raw counts):\")\nprint(cm)\n\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\nfig, ax = plt.subplots(figsize=(7, 6))\nim = ax.imshow(cm, interpolation=\"nearest\")\nax.figure.colorbar(im, ax=ax)\n\nax.set_xlabel(\"Predicted label\")\nax.set_ylabel(\"True label\")\nax.set_title(\"Confusion Matrix (121 classes)\")\n\n# Remove tick labels (too many to show nicely)\nax.set_xticks([])\nax.set_yticks([])\n\nplt.tight_layout()\nplt.show()\n\n# For many classes (121), x/y labels will clutter – you can comment next 2 lines if needed\nplt.setp(ax.get_xticklabels(), rotation=90, ha=\"right\", rotation_mode=\"anchor\")\nplt.setp(ax.get_yticklabels(), rotation=0)\n\n# Annotate cells (only makes sense if num_classes is not huge,\n# but we keep it generic)\nthresh = cm.max() / 2.0 if cm.max() > 0 else 0.5\nfor i in range(cm.shape[0]):\n    for j in range(cm.shape[1]):\n        ax.text(\n            j, i, format(cm[i, j], \"d\"),\n            ha=\"center\", va=\"center\",\n            color=\"white\" if cm[i, j] > thresh else \"black\"\n        )\n\nplt.tight_layout()\nplt.show()\n\n# ====================== PLOT 2 & 3: ROC + PR (BINARY or MULTI-CLASS) ======================\nif num_classes == 2:\n    print(\"\\n📈 Plotting binary ROC & PR curves ...\")\n\n    # --------- ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true, y_scores)\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"ROC curve (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(y_true, y_scores)\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"PR curve (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\n\nelse:\n    print(\"\\n📈 Multi-class detected – plotting MICRO-AVERAGED ROC & PR curves ...\")\n\n    # Binarize the labels for micro-averaging\n    y_true_bin = label_binarize(y_true, classes=labels)   # shape (N, C)\n    # y_scores already shape (N, C)\n\n    # --------- Micro-averaged ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"Micro-averaged ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Micro-averaged Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(\n        y_true_bin.ravel(), y_scores.ravel()\n    )\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Micro-averaged Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\n\nprint(\"\\n🎯 Evaluation complete.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:04:53.813437Z","iopub.execute_input":"2025-12-01T06:04:53.813977Z","iopub.status.idle":"2025-12-01T06:05:37.476829Z","shell.execute_reply.started":"2025-12-01T06:04:53.813953Z","shell.execute_reply":"2025-12-01T06:05:37.47609Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\ncomplex_cnn_eval.py\n\nLoads your trained model (.keras) and computes:\n- Accuracy\n- Confusion matrix\n- Precision, recall, F1-score (per class)\n- Plots:\n    1) Confusion matrix heatmap\n    2) ROC curve\n    3) Precision-Recall curve\nWorks for BOTH binary and multi-class (micro-averaged for multi-class).\n\"\"\"\n\nimport tensorflow as tf\nfrom tensorflow import keras\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    confusion_matrix,\n    classification_report,\n    accuracy_score,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\n\n# ====================== CONFIG ======================\n# 🔴 CHANGE THESE PATHS ACCORDING TO YOUR KAGGLE DATASETS\nDATA_DIR   = \"/kaggle/working/train/train\"     # same folder structure as training\nMODEL_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"  # <--- set to your model\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ====================== LOAD DATA (VALIDATION SPLIT) ======================\nval_ds = keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    batch_size=BATCH_SIZE,\n    image_size=IMAGE_SIZE,\n)\n\nclass_names = val_ds.class_names\nnum_classes = len(class_names)\nprint(\"Classes detected:\", num_classes)\nprint(\"Class names example:\", class_names[:10], \"...\")\n\n# Optional prefetch (doesn't break evaluation)\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ====================== LOAD TRAINED MODEL ======================\nprint(\"\\nLoading model from:\", MODEL_PATH)\nmodel = keras.models.load_model(MODEL_PATH)\nmodel.summary()\n\n# ====================== PREDICTIONS & TRUE LABELS ======================\ny_true = []\ny_prob = []\n\nprint(\"\\nRunning predictions on validation set...\")\nfor batch_images, batch_labels in val_ds:\n    y_true.append(batch_labels.numpy())\n    batch_pred = model.predict(batch_images, verbose=0)\n    y_prob.append(batch_pred)\n\ny_true = np.concatenate(y_true, axis=0)   # shape (N,)\ny_prob = np.concatenate(y_prob, axis=0)   # shape (N, num_classes) or (N, 1)\n\n# Convert probabilities to predicted labels\nif num_classes == 2:\n    # Binary: model output shape (N, 1) with sigmoid OR (N, 2) with softmax\n    if y_prob.shape[1] == 1:\n        # Sigmoid\n        y_scores = y_prob.ravel()\n        y_pred = (y_scores >= 0.5).astype(int)\n    else:\n        # Softmax with 2 outputs\n        y_scores = y_prob[:, 1]          # probability of positive class\n        y_pred = np.argmax(y_prob, axis=1)\nelse:\n    # Multi-class: model output shape (N, C) with softmax\n    y_scores = y_prob                    # shape (N, num_classes)\n    y_pred = np.argmax(y_scores, axis=1)\n\n# ====================== METRICS ======================\nlabels = np.arange(num_classes)  # [0, 1, 2, ..., num_classes-1]\n\nacc = accuracy_score(y_true, y_pred)\nprint(f\"\\n✅ Validation Accuracy: {acc * 100:.2f}%\\n\")\n\nprint(\"📊 Classification Report:\")\nprint(classification_report(\n    y_true,\n    y_pred,\n    labels=labels,             # force all class indices\n    target_names=class_names,  # same length as labels\n    zero_division=0            # avoid warnings for empty classes\n))\n\ncm = confusion_matrix(y_true, y_pred, labels=labels)\nprint(\"Confusion Matrix (raw counts):\")\nprint(cm)\n\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\nfig, ax = plt.subplots(figsize=(7, 6))\nim = ax.imshow(cm, interpolation=\"nearest\")\nax.figure.colorbar(im, ax=ax)\n\nax.set_xlabel(\"Predicted label\")\nax.set_ylabel(\"True label\")\nax.set_title(\"Confusion Matrix (121 classes)\")\n\n# Remove tick labels (too many to show nicely)\nax.set_xticks([])\nax.set_yticks([])\n\nplt.tight_layout()\nplt.show()\n\n# For many classes (121), x/y labels will clutter – you can comment next 2 lines if needed\nplt.setp(ax.get_xticklabels(), rotation=90, ha=\"right\", rotation_mode=\"anchor\")\nplt.setp(ax.get_yticklabels(), rotation=0)\n\n# Annotate cells (only makes sense if num_classes is not huge,\n# but we keep it generic)\nthresh = cm.max() / 2.0 if cm.max() > 0 else 0.5\nfor i in range(cm.shape[0]):\n    for j in range(cm.shape[1]):\n        ax.text(\n            j, i, format(cm[i, j], \"d\"),\n            ha=\"center\", va=\"center\",\n            color=\"white\" if cm[i, j] > thresh else \"black\"\n        )\n\nplt.tight_layout()\nplt.show()\n\n# ====================== PLOT 2 & 3: ROC + PR (BINARY or MULTI-CLASS) ======================\nif num_classes == 2:\n    print(\"\\n📈 Plotting binary ROC & PR curves ...\")\n\n    # --------- ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true, y_scores)\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"ROC curve (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(y_true, y_scores)\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"PR curve (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\n\nelse:\n    print(\"\\n📈 Multi-class detected – plotting MICRO-AVERAGED ROC & PR curves ...\")\n\n    # Binarize the labels for micro-averaging\n    y_true_bin = label_binarize(y_true, classes=labels)   # shape (N, C)\n    # y_scores already shape (N, C)\n\n    # --------- Micro-averaged ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"Micro-averaged ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Micro-averaged Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(\n        y_true_bin.ravel(), y_scores.ravel()\n    )\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Micro-averaged Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\n\nprint(\"\\n🎯 Evaluation complete.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:06:49.781936Z","iopub.execute_input":"2025-12-01T06:06:49.782619Z","iopub.status.idle":"2025-12-01T06:07:33.642144Z","shell.execute_reply.started":"2025-12-01T06:06:49.782597Z","shell.execute_reply":"2025-12-01T06:07:33.641343Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\ncomplex_cnn_eval.py\n\nLoads your trained model (.keras) and computes:\n- Accuracy\n- Confusion matrix\n- Precision, recall, F1-score (per class)\n- Plots:\n    1) Confusion matrix heatmap\n    2) ROC curve\n    3) Precision-Recall curve\nWorks for BOTH binary and multi-class (micro-averaged for multi-class).\n\"\"\"\n\nimport tensorflow as tf\nfrom tensorflow import keras\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    confusion_matrix,\n    classification_report,\n    accuracy_score,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\n\n# ====================== CONFIG ======================\n# 🔴 CHANGE THESE PATHS ACCORDING TO YOUR KAGGLE DATASETS\nDATA_DIR   = \"/kaggle/working/train/train\"     # same folder structure as training\nMODEL_PATH = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"  # <--- set to your model\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ====================== LOAD DATA (VALIDATION SPLIT) ======================\nval_ds = keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    batch_size=BATCH_SIZE,\n    image_size=IMAGE_SIZE,\n)\n\nclass_names = val_ds.class_names\nnum_classes = len(class_names)\nprint(\"Classes detected:\", num_classes)\nprint(\"Class names example:\", class_names[:10], \"...\")\n\n# Optional prefetch (doesn't break evaluation)\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ====================== LOAD TRAINED MODEL ======================\nprint(\"\\nLoading model from:\", MODEL_PATH)\nmodel = keras.models.load_model(MODEL_PATH)\nmodel.summary()\n\n# ====================== PREDICTIONS & TRUE LABELS ======================\ny_true = []\ny_prob = []\n\nprint(\"\\nRunning predictions on validation set...\")\nfor batch_images, batch_labels in val_ds:\n    y_true.append(batch_labels.numpy())\n    batch_pred = model.predict(batch_images, verbose=0)\n    y_prob.append(batch_pred)\n\ny_true = np.concatenate(y_true, axis=0)   # shape (N,)\ny_prob = np.concatenate(y_prob, axis=0)   # shape (N, num_classes) or (N, 1)\n\n# Convert probabilities to predicted labels\nif num_classes == 2:\n    # Binary: model output shape (N, 1) with sigmoid OR (N, 2) with softmax\n    if y_prob.shape[1] == 1:\n        # Sigmoid\n        y_scores = y_prob.ravel()\n        y_pred = (y_scores >= 0.5).astype(int)\n    else:\n        # Softmax with 2 outputs\n        y_scores = y_prob[:, 1]          # probability of positive class\n        y_pred = np.argmax(y_prob, axis=1)\nelse:\n    # Multi-class: model output shape (N, C) with softmax\n    y_scores = y_prob                    # shape (N, num_classes)\n    y_pred = np.argmax(y_scores, axis=1)\n\n# ====================== METRICS ======================\nlabels = np.arange(num_classes)  # [0, 1, 2, ..., num_classes-1]\n\nacc = accuracy_score(y_true, y_pred)\nprint(f\"\\n✅ Validation Accuracy: {acc * 100:.2f}%\\n\")\n\nprint(\"📊 Classification Report:\")\nprint(classification_report(\n    y_true,\n    y_pred,\n    labels=labels,             # force all class indices\n    target_names=class_names,  # same length as labels\n    zero_division=0            # avoid warnings for empty classes\n))\n\ncm = confusion_matrix(y_true, y_pred, labels=labels)\nprint(\"Confusion Matrix (raw counts):\")\nprint(cm)\n\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\nfig, ax = plt.subplots(figsize=(7, 6))\nim = ax.imshow(cm, interpolation=\"nearest\")\nax.figure.colorbar(im, ax=ax)\n\nax.set_xlabel(\"Predicted label\")\nax.set_ylabel(\"True label\")\nax.set_title(\"Confusion Matrix (121 classes)\")\n\n# Remove tick labels (too many to show nicely)\nax.set_xticks([])\nax.set_yticks([])\n\nplt.tight_layout()\nplt.show()\n\n# For many classes (121), x/y labels will clutter – you can comment next 2 lines if needed\nplt.setp(ax.get_xticklabels(), rotation=90, ha=\"right\", rotation_mode=\"anchor\")\nplt.setp(ax.get_yticklabels(), rotation=0)\n\n# Annotate cells (only makes sense if num_classes is not huge,\n# but we keep it generic)\nthresh = cm.max() / 2.0 if cm.max() > 0 else 0.5\nfor i in range(cm.shape[0]):\n    for j in range(cm.shape[1]):\n        ax.text(\n            j, i, format(cm[i, j], \"d\"),\n            ha=\"center\", va=\"center\",\n            color=\"white\" if cm[i, j] > thresh else \"black\"\n        )\n\nplt.tight_layout()\nplt.show()\n\n# ====================== PLOT 2 & 3: ROC + PR (BINARY or MULTI-CLASS) ======================\nif num_classes == 2:\n    print(\"\\n📈 Plotting binary ROC & PR curves ...\")\n\n    # --------- ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true, y_scores)\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"ROC curve (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(y_true, y_scores)\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"PR curve (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\n\nelse:\n    print(\"\\n📈 Multi-class detected – plotting MICRO-AVERAGED ROC & PR curves ...\")\n\n    # Binarize the labels for micro-averaging\n    y_true_bin = label_binarize(y_true, classes=labels)   # shape (N, C)\n    # y_scores already shape (N, C)\n\n    # --------- Micro-averaged ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"Micro-averaged ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Micro-averaged Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(\n        y_true_bin.ravel(), y_scores.ravel()\n    )\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Micro-averaged Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\n\nprint(\"\\n🎯 Evaluation complete.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:01:13.643584Z","iopub.execute_input":"2025-12-01T06:01:13.644255Z","iopub.status.idle":"2025-12-01T06:01:51.660952Z","shell.execute_reply.started":"2025-12-01T06:01:13.644228Z","shell.execute_reply":"2025-12-01T06:01:51.660096Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\ncomplex_cnn_eval.py\n\nLoads your trained model (.keras) and computes:\n- Accuracy\n- Confusion matrix\n- Precision, recall, F1-score (per class)\n- Plots:\n    1) Confusion matrix heatmap\n    2) ROC curve\n    3) Precision-Recall curve\nWorks for BOTH binary and multi-class (micro-averaged for multi-class).\n\"\"\"\n\nimport tensorflow as tf\nfrom tensorflow import keras\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    confusion_matrix,\n    classification_report,\n    accuracy_score,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\n\n# ====================== CONFIG ======================\n# 🔴 CHANGE THESE PATHS ACCORDING TO YOUR KAGGLE DATASETS\nDATA_DIR   = \"/kaggle/working/train/train\"     # same folder structure as training\nMODEL_PATH = \"/kaggle/input/dadadadad/complex_cnn_model (1).keras\"  # <--- set to your model\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ====================== LOAD DATA (VALIDATION SPLIT) ======================\nval_ds = keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    batch_size=BATCH_SIZE,\n    image_size=IMAGE_SIZE,\n)\n\nclass_names = val_ds.class_names\nnum_classes = len(class_names)\nprint(\"Classes detected:\", num_classes)\nprint(\"Class names example:\", class_names[:10], \"...\")\n\n# Optional prefetch (doesn't break evaluation)\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ====================== LOAD TRAINED MODEL ======================\nprint(\"\\nLoading model from:\", MODEL_PATH)\nmodel = keras.models.load_model(MODEL_PATH)\nmodel.summary()\n\n# ====================== PREDICTIONS & TRUE LABELS ======================\ny_true = []\ny_prob = []\n\nprint(\"\\nRunning predictions on validation set...\")\nfor batch_images, batch_labels in val_ds:\n    y_true.append(batch_labels.numpy())\n    batch_pred = model.predict(batch_images, verbose=0)\n    y_prob.append(batch_pred)\n\ny_true = np.concatenate(y_true, axis=0)   # shape (N,)\ny_prob = np.concatenate(y_prob, axis=0)   # shape (N, num_classes) or (N, 1)\n\n# Convert probabilities to predicted labels\nif num_classes == 2:\n    # Binary: model output shape (N, 1) with sigmoid OR (N, 2) with softmax\n    if y_prob.shape[1] == 1:\n        # Sigmoid\n        y_scores = y_prob.ravel()\n        y_pred = (y_scores >= 0.5).astype(int)\n    else:\n        # Softmax with 2 outputs\n        y_scores = y_prob[:, 1]          # probability of positive class\n        y_pred = np.argmax(y_prob, axis=1)\nelse:\n    # Multi-class: model output shape (N, C) with softmax\n    y_scores = y_prob                    # shape (N, num_classes)\n    y_pred = np.argmax(y_scores, axis=1)\n\n# ====================== METRICS ======================\nlabels = np.arange(num_classes)  # [0, 1, 2, ..., num_classes-1]\n\nacc = accuracy_score(y_true, y_pred)\nprint(f\"\\n✅ Validation Accuracy: {acc * 100:.2f}%\\n\")\n\nprint(\"📊 Classification Report:\")\nprint(classification_report(\n    y_true,\n    y_pred,\n    labels=labels,             # force all class indices\n    target_names=class_names,  # same length as labels\n    zero_division=0            # avoid warnings for empty classes\n))\n\ncm = confusion_matrix(y_true, y_pred, labels=labels)\nprint(\"Confusion Matrix (raw counts):\")\nprint(cm)\n\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\n# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\nfig, ax = plt.subplots(figsize=(7, 6))\nim = ax.imshow(cm, interpolation=\"nearest\")\nax.figure.colorbar(im, ax=ax)\n\nax.set_xlabel(\"Predicted label\")\nax.set_ylabel(\"True label\")\nax.set_title(\"Confusion Matrix (121 classes)\")\n\n# Remove tick labels (too many to show nicely)\nax.set_xticks([])\nax.set_yticks([])\n\nplt.tight_layout()\nplt.show()\n\n# For many classes (121), x/y labels will clutter – you can comment next 2 lines if needed\nplt.setp(ax.get_xticklabels(), rotation=90, ha=\"right\", rotation_mode=\"anchor\")\nplt.setp(ax.get_yticklabels(), rotation=0)\n\n# Annotate cells (only makes sense if num_classes is not huge,\n# but we keep it generic)\nthresh = cm.max() / 2.0 if cm.max() > 0 else 0.5\nfor i in range(cm.shape[0]):\n    for j in range(cm.shape[1]):\n        ax.text(\n            j, i, format(cm[i, j], \"d\"),\n            ha=\"center\", va=\"center\",\n            color=\"white\" if cm[i, j] > thresh else \"black\"\n        )\n\nplt.tight_layout()\nplt.show()\n\n# ====================== PLOT 2 & 3: ROC + PR (BINARY or MULTI-CLASS) ======================\nif num_classes == 2:\n    print(\"\\n📈 Plotting binary ROC & PR curves ...\")\n\n    # --------- ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true, y_scores)\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"ROC curve (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(y_true, y_scores)\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"PR curve (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\n\nelse:\n    print(\"\\n📈 Multi-class detected – plotting MICRO-AVERAGED ROC & PR curves ...\")\n\n    # Binarize the labels for micro-averaging\n    y_true_bin = label_binarize(y_true, classes=labels)   # shape (N, C)\n    # y_scores already shape (N, C)\n\n    # --------- Micro-averaged ROC curve ----------\n    fpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\n    roc_auc = auc(fpr, tpr)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\n    plt.plot([0, 1], [0, 1], linestyle=\"--\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"False Positive Rate\")\n    plt.ylabel(\"True Positive Rate\")\n    plt.title(\"Micro-averaged ROC Curve (Validation)\")\n    plt.legend(loc=\"lower right\")\n    plt.grid(True)\n    plt.show()\n\n    # --------- Micro-averaged Precision-Recall curve ----------\n    precision, recall, _ = precision_recall_curve(\n        y_true_bin.ravel(), y_scores.ravel()\n    )\n    pr_auc = auc(recall, precision)\n\n    plt.figure(figsize=(6, 5))\n    plt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel(\"Recall\")\n    plt.ylabel(\"Precision\")\n    plt.title(\"Micro-averaged Precision-Recall Curve (Validation)\")\n    plt.legend(loc=\"lower left\")\n    plt.grid(True)\n    plt.show()\n\nprint(\"\\n🎯 Evaluation complete.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:03:35.860711Z","iopub.execute_input":"2025-12-01T06:03:35.861002Z","iopub.status.idle":"2025-12-01T06:04:18.254755Z","shell.execute_reply.started":"2025-12-01T06:03:35.860982Z","shell.execute_reply":"2025-12-01T06:04:18.254027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================== PLOT 1: CONFUSION MATRIX HEATMAP ======================\nfig, ax = plt.subplots(figsize=(7, 6))\nim = ax.imshow(cm, interpolation=\"nearest\")\nax.figure.colorbar(im, ax=ax)\n\nax.set_xlabel(\"Predicted label\")\nax.set_ylabel(\"True label\")\nax.set_title(\"Confusion Matrix (121 classes)\")\n\n# Remove tick labels (too many to show nicely)\nax.set_xticks([])\nax.set_yticks([])\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T05:56:09.793536Z","iopub.execute_input":"2025-12-01T05:56:09.794007Z","iopub.status.idle":"2025-12-01T05:56:09.96603Z","shell.execute_reply.started":"2025-12-01T05:56:09.793986Z","shell.execute_reply":"2025-12-01T05:56:09.96513Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# base_cnn.py\n\"\"\"\nBase CNN classifier (from scratch).\nUsage: python base_cnn.py\nChange DATA_DIR at top if needed.\n\"\"\"\nimport os\nimport datetime\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\n# ============== CONFIG ==============\nDATA_DIR = \"/kaggle/working/train/train\"   # <-- change if needed\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nEPOCHS = 25\nSEED = 42\nOUT_DIR = \"./outputs_base_cnn\"\nos.makedirs(OUT_DIR, exist_ok=True)\n# ====================================\n\n# GPU safe mode\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    for g in gpus:\n        tf.config.experimental.set_memory_growth(g, True)\n    print(\"GPU found.\")\nelse:\n    print(\"No GPU — running on CPU.\")\n\nprint(\"Loading dataset...\")\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nclass_names = train_ds.class_names\nNUM_CLASSES = len(class_names)\nprint(\"Detected classes:\", class_names, \"Num classes:\", NUM_CLASSES)\n\nAUTOTUNE = tf.data.AUTOTUNE\ntrain_ds = train_ds.prefetch(AUTOTUNE)\nval_ds = val_ds.prefetch(AUTOTUNE)\n\n# ============== Model ==============\ndef build_base_cnn(input_shape=(224,224,3), n_classes=2):\n    inputs = keras.Input(shape=input_shape)\n    x = layers.Rescaling(1./255)(inputs)\n\n    x = layers.Conv2D(32, 3, padding='same', activation='relu')(x)\n    x = layers.MaxPool2D()(x)\n\n    x = layers.Conv2D(64, 3, padding='same', activation='relu')(x)\n    x = layers.MaxPool2D()(x)\n\n    x = layers.Conv2D(128, 3, padding='same', activation='relu')(x)\n    x = layers.MaxPool2D()(x)\n\n    x = layers.Conv2D(256, 3, padding='same', activation='relu')(x)\n    x = layers.GlobalAveragePooling2D()(x)\n\n    x = layers.Dropout(0.4)(x)\n    x = layers.Dense(128, activation='relu')(x)\n    outputs = layers.Dense(n_classes, activation='softmax')(x)\n\n    model = keras.Model(inputs, outputs, name=\"base_cnn\")\n    return model\n\nmodel = build_base_cnn(input_shape=(*IMAGE_SIZE,3), n_classes=NUM_CLASSES)\nmodel.compile(optimizer=keras.optimizers.Adam(1e-3),\n              loss=\"sparse_categorical_crossentropy\",\n              metrics=[\"accuracy\"])\nmodel.summary()\n\n# ============== Callbacks & Train ==============\nts = datetime.datetime.now().strftime(\"%Y%m%d_%H%M%S\")\nckpt = os.path.join(OUT_DIR, f\"base_cnn_best_{ts}.keras\")\ncallbacks = [\n    keras.callbacks.ModelCheckpoint(ckpt, monitor=\"val_accuracy\", save_best_only=True),\n    keras.callbacks.EarlyStopping(monitor=\"val_loss\", patience=6, restore_best_weights=True)\n]\n\nhistory = model.fit(train_ds, validation_data=val_ds, epochs=EPOCHS, callbacks=callbacks)\n\n# ============== Save ==============\nfinal_path = os.path.join(OUT_DIR, f\"base_cnn_final_{ts}.keras\")\nmodel.save(final_path)\nprint(\"Saved final model to:\", final_path)\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# Optionally print final eval\nloss, acc = model.evaluate(val_ds)\nprint(f\"Validation loss: {loss:.4f}, acc: {acc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T19:07:44.031997Z","iopub.execute_input":"2025-11-30T19:07:44.032358Z","iopub.status.idle":"2025-11-30T19:18:55.754604Z","shell.execute_reply.started":"2025-11-30T19:07:44.032333Z","shell.execute_reply":"2025-11-30T19:18:55.753959Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# hybrid_svm.py\n\"\"\"\nHybrid pipeline:\n1) Extract features using MobileNetV2 (include_top=False + GAP)\n2) Train a sklearn SVM on the extracted features\n3) Evaluate on validation set\n\nUsage: python hybrid_svm.py\n\"\"\"\nimport os\nimport random\nimport numpy as np\nimport joblib\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom sklearn.svm import SVC\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import classification_report, accuracy_score, confusion_matrix\n\n# ============== CONFIG ==============\nDATA_DIR = \"/kaggle/working/train/train\"   # <-- change if needed\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\nOUT_DIR = \"./outputs_hybrid\"\nos.makedirs(OUT_DIR, exist_ok=True)\n# ====================================\n\n# GPU safe mode\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    for g in gpus:\n        tf.config.experimental.set_memory_growth(g, True)\n    print(\"GPU found.\")\nelse:\n    print(\"No GPU — running on CPU.\")\n\n# We'll build lists of filepaths & labels and split into train/val/test\ndef build_file_label_lists(root_dir):\n    filepaths = []\n    labels = []\n    class_names = sorted([d for d in os.listdir(root_dir) if os.path.isdir(os.path.join(root_dir, d))])\n    class_to_idx = {c:i for i,c in enumerate(class_names)}\n    for c in class_names:\n        class_dir = os.path.join(root_dir, c)\n        for fname in os.listdir(class_dir):\n            if fname.lower().endswith(('.jpg','.jpeg','.png','.bmp')):\n                filepaths.append(os.path.join(class_dir, fname))\n                labels.append(class_to_idx[c])\n    return np.array(filepaths), np.array(labels), class_names\n\nfilepaths, labels, class_names = build_file_label_lists(DATA_DIR)\nprint(\"Found\", len(filepaths), \"images across\", len(class_names), \"classes:\", class_names)\n\n# Shuffle\nrng = np.random.RandomState(SEED)\nidxs = np.arange(len(filepaths))\nrng.shuffle(idxs)\nfilepaths = filepaths[idxs]\nlabels = labels[idxs]\n\n# splits\nn = len(filepaths)\ntest_frac = 0.10\nval_frac = 0.15\nn_test = int(n * test_frac)\nn_val = int(n * val_frac)\nn_train = n - n_val - n_test\n\ntrain_fp = filepaths[:n_train]; train_y = labels[:n_train]\nval_fp = filepaths[n_train:n_train+n_val]; val_y = labels[n_train:n_train+n_val]\ntest_fp = filepaths[n_train+n_val:]; test_y = labels[n_train+n_val:]\n\nprint(\"Splits -> train:\", len(train_fp), \"val:\", len(val_fp), \"test:\", len(test_fp))\n\n# Build feature extractor model\nbase = tf.keras.applications.MobileNetV2(include_top=False, weights='imagenet', input_shape=(*IMAGE_SIZE,3))\ninp = keras.Input(shape=(*IMAGE_SIZE,3))\nx = tf.keras.applications.mobilenet_v2.preprocess_input(inp)\nx = base(x, training=False)\nx = layers.GlobalAveragePooling2D()(x)\nfeat_model = keras.Model(inputs=inp, outputs=x, name=\"feat_extractor\")\nfeat_model.trainable = False\nprint(\"Feature extractor ready.\")\n\n# helper to load and batch images -> numpy array\ndef load_images(paths, image_size):\n    imgs = []\n    for p in paths:\n        img = tf.io.read_file(p)\n        img = tf.image.decode_image(img, channels=3, expand_animations=False)\n        img = tf.image.convert_image_dtype(img, tf.float32)\n        img = tf.image.resize(img, image_size)\n        imgs.append(img.numpy())\n    return np.stack(imgs, axis=0)\n\n# Extract features in batches to avoid OOM\ndef extract_features(paths, batch_size=32):\n    feats = []\n    for i in range(0, len(paths), batch_size):\n        batch_paths = paths[i:i+batch_size]\n        batch_imgs = load_images(batch_paths, IMAGE_SIZE)\n        batch_feats = feat_model.predict(batch_imgs, verbose=0)\n        feats.append(batch_feats)\n    return np.vstack(feats)\n\nprint(\"Extracting train features ...\")\nX_train = extract_features(train_fp, batch_size=BATCH_SIZE)\nprint(\"Extracting val features ...\")\nX_val = extract_features(val_fp, batch_size=BATCH_SIZE)\nprint(\"Extracting test features ...\")\nX_test = extract_features(test_fp, batch_size=BATCH_SIZE)\n\nprint(\"Feature shapes:\", X_train.shape, X_val.shape, X_test.shape)\n\n# Standardize\nscaler = StandardScaler()\nX_train_s = scaler.fit_transform(X_train)\nX_val_s = scaler.transform(X_val)\nX_test_s = scaler.transform(X_test)\n\n# Train SVM\nprint(\"Training SVM ...\")\nsvm = SVC(kernel='rbf', probability=True, class_weight='balanced', random_state=SEED)\nsvm.fit(X_train_s, train_y)\n\n# Eval on validation & test\ny_val_pred = svm.predict(X_val_s)\ny_test_pred = svm.predict(X_test_s)\n\nprint(\"Validation acc:\", accuracy_score(val_y, y_val_pred))\nprint(\"Test acc:\", accuracy_score(test_y, y_test_pred))\nprint(\"\\nClassification report (test):\")\nprint(classification_report(test_y, y_test_pred, target_names=class_names))\n\n# Save artifacts\njoblib.dump({'svm': svm, 'scaler': scaler, 'class_names': class_names}, os.path.join(OUT_DIR, \"svm_scaler.joblib\"))\nprint(\"Saved SVM+scaler to:\", os.path.join(OUT_DIR, \"svm_scaler.joblib\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T19:20:24.600733Z","iopub.execute_input":"2025-11-30T19:20:24.601721Z","iopub.status.idle":"2025-11-30T19:23:03.070051Z","shell.execute_reply.started":"2025-11-30T19:20:24.601682Z","shell.execute_reply":"2025-11-30T19:23:03.068925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# complex_cnn_ready.py\n\"\"\"\nAdvanced CNN classifier from scratch.\nVery strong, deep CNN with BatchNorm + Dropout + Scheduler.\nReady-to-run on Kaggle GPU.\n\"\"\"\n\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\n# ====================== CONFIG ======================\nDATA_DIR = \"/kaggle/working/train/train\"     # 🔴 CHANGE THIS\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nEPOCHS = 40\nSEED = 42\n\n# ====================== DATA PIPELINE ======================\ndata_augmentation = keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.1),\n    layers.RandomZoom(0.1),\n    layers.RandomContrast(0.1),\n])\n\n# --------- Load dataset ---------\ntrain_ds = keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    batch_size=BATCH_SIZE,\n    image_size=IMAGE_SIZE,\n)\n\nval_ds = keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    batch_size=BATCH_SIZE,\n    image_size=IMAGE_SIZE,\n)\n\n# --------- Fix class_names issue ---------\nclass_names = train_ds.class_names\nnum_classes = len(class_names)\nprint(\"Classes:\", class_names)\n\n# --------- Prefetch for performance ---------\ntrain_ds = train_ds.prefetch(tf.data.AUTOTUNE)\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ====================== COMPLEX CNN MODEL ======================\ndef conv_block(x, filters):\n    x = layers.Conv2D(filters, 3, padding=\"same\", activation=None,\n                      kernel_regularizer=keras.regularizers.l2(1e-4))(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.ReLU()(x)\n\n    x = layers.Conv2D(filters, 3, padding=\"same\", activation=None,\n                      kernel_regularizer=keras.regularizers.l2(1e-4))(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.ReLU()(x)\n\n    return layers.MaxPooling2D()(x)\n\ndef build_complex_cnn(input_shape=(224,224,3), num_classes=2):\n\n    inputs = keras.Input(shape=input_shape)\n    x = layers.Rescaling(1/255)(inputs)\n\n    # 🌟 Data augmentation inside model\n    x = data_augmentation(x)\n\n    # ------------ Conv Blocks ------------\n    x = conv_block(x, 32)\n    x = conv_block(x, 64)\n    x = conv_block(x, 128)\n    x = conv_block(x, 256)\n    x = conv_block(x, 512)\n\n    # ------------ Classifier ------------\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dense(512, activation=\"relu\")(x)\n    x = layers.Dropout(0.5)(x)\n    x = layers.Dense(128, activation=\"relu\")(x)\n    x = layers.Dropout(0.3)(x)\n\n    if num_classes == 2:\n        outputs = layers.Dense(1, activation=\"sigmoid\")(x)\n    else:\n        outputs = layers.Dense(num_classes, activation=\"softmax\")(x)\n\n    return keras.Model(inputs, outputs)\n\n\nmodel = build_complex_cnn(\n    input_shape=(IMAGE_SIZE[0], IMAGE_SIZE[1], 3),\n    num_classes=num_classes\n)\n\nmodel.summary()\n\n# ====================== COMPILE ======================\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-4),\n    loss=\"binary_crossentropy\" if num_classes == 2 else \"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\n# ====================== CALLBACKS ======================\nlr_scheduler = keras.callbacks.ReduceLROnPlateau(\n    monitor=\"val_loss\",\n    factor=0.5,\n    patience=3,\n    min_lr=1e-6,\n)\n\ncheckpoint = keras.callbacks.ModelCheckpoint(\n    \"complex_cnn_model.keras\",\n    save_best_only=True,\n    monitor=\"val_accuracy\"\n)\n\n# ====================== TRAIN ======================\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=[checkpoint, lr_scheduler]\n)\n\nprint(\"\\n🔥 Training Finished!\")\nprint(\"Saved as: complex_cnn_model.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T19:27:16.778038Z","iopub.execute_input":"2025-11-30T19:27:16.778707Z","iopub.status.idle":"2025-11-30T21:13:33.633832Z","shell.execute_reply.started":"2025-11-30T19:27:16.77868Z","shell.execute_reply":"2025-11-30T21:13:33.632212Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import confusion_matrix, classification_report, precision_recall_curve, roc_curve, auc\nimport seaborn as sns\n\n# ============================\n# 1) Load model\n# ============================\nMODEL_PATH = \"/kaggle/input/sdfghjkl/complex_cnn_model.keras\"   # ya .keras\nmodel = tf.keras.models.load_model(MODEL_PATH)\nprint(\"✅ Model Loaded Successfully\")\n\n# ============================\n# 2) Collect predictions\n# ============================\ny_true = []\ny_pred = []\ny_prob = []   # probabilities\n\nfor images, labels in val_ds:\n    probs = model.predict(images)\n    preds = np.argmax(probs, axis=1)\n\n    y_true.extend(labels.numpy())\n    y_pred.extend(preds)\n    y_prob.extend(probs[:, 1])  # Class-1 ka probability\n\ny_true = np.array(y_true)\ny_pred = np.array(y_pred)\ny_prob = np.array(y_prob)\n\n# ============================\n# 3) Accuracy, Precision, Recall, F1\n# ============================\nprint(\"\\n🔹 Classification Report (Precision, Recall, F1)\")\nprint(classification_report(y_true, y_pred))\n\n# ============================\n# 4) Confusion Matrix Plot\n# ============================\ncm = confusion_matrix(y_true, y_pred)\nplt.figure(figsize=(5,4))\nsns.heatmap(cm, annot=True, fmt='d', cmap=\"Blues\")\nplt.title(\"Confusion Matrix\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.show()\n\n# ============================\n# 5) Accuracy and Loss Plots\n# ============================\n# NOTE: Only if you still have \"history\" variable\ntry:\n    plt.figure()\n    plt.plot(history.history['accuracy'])\n    plt.plot(history.history['val_accuracy'])\n    plt.title(\"Accuracy Curve\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Accuracy\")\n    plt.legend([\"Train\", \"Validation\"])\n    plt.show()\n\n    plt.figure()\n    plt.plot(history.history['loss'])\n    plt.plot(history.history['val_loss'])\n    plt.title(\"Loss Curve\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Loss\")\n    plt.legend([\"Train\", \"Validation\"])\n    plt.show()\nexcept:\n    print(\"⚠️ History not found — skipping accuracy/loss plots\")\n\n\n# ============================\n# 6) Precision-Recall Curve\n# ============================\nprecision, recall, thresholds = precision_recall_curve(y_true, y_prob)\n\nplt.figure()\nplt.plot(recall, precision)\nplt.title(\"Precision–Recall Curve\")\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.grid()\nplt.show()\n\n# ============================\n# 7) ROC Curve + AUC\n# ============================\nfpr, tpr, thresholds = roc_curve(y_true, y_prob)\nauc_score = auc(fpr, tpr)\n\nplt.figure()\nplt.plot(fpr, tpr)\nplt.title(f\"ROC Curve (AUC = {auc_score:.3f})\")\nplt.xlabel(\"False Positive Rate\")\nplt.ylabel(\"True Positive Rate\")\nplt.grid()\nplt.show()\n\nprint(f\"\\n🎯 AUC Score: {auc_score:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T05:03:11.032138Z","iopub.execute_input":"2025-12-01T05:03:11.032752Z","iopub.status.idle":"2025-12-01T05:04:03.329551Z","shell.execute_reply.started":"2025-12-01T05:03:11.032722Z","shell.execute_reply":"2025-12-01T05:04:03.328591Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix, classification_report\nfrom sklearn.metrics import precision_recall_fscore_support\nimport tensorflow as tf\n\n# ======================================================\n# 1) LOAD MODEL\n# ======================================================\nmodel_path = \"/kaggle/input/sdfghjkl/complex_cnn_model.keras\"\nprint(\"Loading model...\")\nmodel = tf.keras.models.load_model(model_path)\nprint(\"Model Loaded!\")\n\n# ======================================================\n# 2) LOAD VALID DATASET\n# ======================================================\nDATA_DIR = \"/kaggle/working/train/train\"\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    shuffle=False,\n    batch_size=BATCH_SIZE,\n    image_size=IMAGE_SIZE\n)\n\nclass_names = val_ds.class_names\nnum_classes = len(class_names)\nprint(\"Classes:\", num_classes)\n\n# ======================================================\n# 3) TRUE LABELS + PREDICTIONS\n# ======================================================\ny_true = np.concatenate([y.numpy() for x, y in val_ds])\ny_prob = model.predict(val_ds, verbose=1)\ny_pred = np.argmax(y_prob, axis=1)\n\n# ======================================================\n# 4) CONFUSION MATRIX\n# ======================================================\ncm = confusion_matrix(y_true, y_pred)\ncm_norm = cm / cm.sum(axis=1, keepdims=True)\n\nplt.figure(figsize=(18, 16))\nsns.heatmap(cm_norm, cmap='Blues')\nplt.title(\"Normalized Confusion Matrix\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.show()\n\n# ======================================================\n# 5) CLASSIFICATION REPORT\n# ======================================================\nprint(\"\\n📌 Classification Report:\")\nprint(classification_report(y_true, y_pred))\n\n# ======================================================\n# 6) PRECISION/RECALL PER-CLASS (first 50)\n# ======================================================\nprec, rec, f1, support = precision_recall_fscore_support(\n    y_true, y_pred, average=None\n)\n\nplt.figure(figsize=(20, 7))\nplt.plot(prec[:50], label=\"Precision (first 50)\")\nplt.plot(rec[:50], label=\"Recall (first 50)\")\nplt.title(\"Precision & Recall (First 50 Classes)\")\nplt.legend()\nplt.show()\n\n# ======================================================\n# 7) TOP MISCLASSIFIED CLASSES\n# ======================================================\ncorrect = np.diag(cm)\nwrong = cm.sum(axis=1) - correct\nmis_idx = np.argsort(wrong)[-10:]\n\nprint(\"\\n🔥 Top 10 Most Misclassified Classes:\")\nfor i in mis_idx:\n    print(f\"Class {i} ({class_names[i]}): wrong={wrong[i]}, correct={correct[i]}\")\n\nprint(\"\\n🎉 DONE — All Evaluation Metrics Generated!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T05:10:34.667151Z","iopub.execute_input":"2025-12-01T05:10:34.667842Z","iopub.status.idle":"2025-12-01T05:10:47.65396Z","shell.execute_reply.started":"2025-12-01T05:10:34.667818Z","shell.execute_reply":"2025-12-01T05:10:47.653153Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# hybrid_cnn_combined.py\n\"\"\"\nHybrid CNN classifier:\n- Combines Base CNN (scratch) + MobileNetV2 pretrained\n- Features from both are concatenated for final classification\n\"\"\"\nimport os\nimport datetime\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\n# ====================== CONFIG ======================\nDATA_DIR = \"/kaggle/working/train/train\"  # <-- change if needed\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nEPOCHS = 30\nSEED = 42\nOUT_DIR = \"./outputs_hybrid_combined\"\nos.makedirs(OUT_DIR, exist_ok=True)\n# ====================================\n\n# GPU safe mode\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    for g in gpus:\n        tf.config.experimental.set_memory_growth(g, True)\n    print(\"GPU found.\")\nelse:\n    print(\"No GPU — running on CPU.\")\n\n# ====================== DATASET ======================\nprint(\"Loading dataset...\")\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    DATA_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE\n)\n\nclass_names = train_ds.class_names\nNUM_CLASSES = len(class_names)\nprint(\"Detected classes:\", class_names, \"Num classes:\", NUM_CLASSES)\n\nAUTOTUNE = tf.data.AUTOTUNE\ntrain_ds = train_ds.prefetch(AUTOTUNE)\nval_ds = val_ds.prefetch(AUTOTUNE)\n\n# ====================== MODEL BUILDERS ======================\ndef build_base_cnn(input_shape=(224,224,3)):\n    inputs = keras.Input(shape=input_shape)\n    x = layers.Rescaling(1./255)(inputs)\n    x = layers.Conv2D(32, 3, padding='same', activation='relu')(x)\n    x = layers.MaxPool2D()(x)\n    x = layers.Conv2D(64, 3, padding='same', activation='relu')(x)\n    x = layers.MaxPool2D()(x)\n    x = layers.Conv2D(128, 3, padding='same', activation='relu')(x)\n    x = layers.MaxPool2D()(x)\n    x = layers.Conv2D(256, 3, padding='same', activation='relu')(x)\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dropout(0.4)(x)\n    x = layers.Dense(128, activation='relu')(x)\n    return keras.Model(inputs, x, name=\"base_cnn_features\")\n\ndef build_mobilenet(input_shape=(224,224,3)):\n    base = tf.keras.applications.MobileNetV2(\n        input_shape=input_shape,\n        include_top=False,\n        weights=\"imagenet\"\n    )\n    base.trainable = False  # freeze initially\n    inputs = keras.Input(shape=input_shape)\n    x = tf.keras.applications.mobilenet_v2.preprocess_input(inputs)\n    x = base(x, training=False)\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dropout(0.25)(x)\n    x = layers.Dense(128, activation='relu')(x)\n    return keras.Model(inputs, x, name=\"mobilenet_features\"), base\n\n# ====================== BUILD HYBRID MODEL ======================\nbase_cnn = build_base_cnn(input_shape=(*IMAGE_SIZE,3))\nmobilenet, base_mobilenet = build_mobilenet(input_shape=(*IMAGE_SIZE,3))\n\n# Combine features\ncombined = layers.concatenate([base_cnn.output, mobilenet.output])\noutputs = layers.Dense(NUM_CLASSES, activation='softmax')(combined)\n\nhybrid_model = keras.Model(inputs=[base_cnn.input, mobilenet.input], outputs=outputs, name=\"hybrid_cnn\")\nhybrid_model.compile(optimizer=keras.optimizers.Adam(1e-4),\n                     loss=\"sparse_categorical_crossentropy\",\n                     metrics=[\"accuracy\"])\n\nhybrid_model.summary()\n\n# ====================== CALLBACKS ======================\nts = datetime.datetime.now().strftime(\"%Y%m%d_%H%M%S\")\nckpt = os.path.join(OUT_DIR, f\"hybrid_best_{ts}.keras\")\ncallbacks = [\n    keras.callbacks.ModelCheckpoint(ckpt, monitor=\"val_accuracy\", save_best_only=True),\n    keras.callbacks.EarlyStopping(monitor=\"val_loss\", patience=6, restore_best_weights=True)\n]\n\n# ====================== TRAIN ======================\n# Note: both inputs are the same images here\nhistory = hybrid_model.fit(\n    [train_ds, train_ds],  # duplicate for both inputs\n    validation_data=([val_ds, val_ds], None),\n    epochs=EPOCHS,\n    callbacks=callbacks\n)\n\n# ====================== FINE-TUNING ======================\nprint(\"\\n🔥 Fine-tuning MobileNet top layers ...\")\nbase_mobilenet.trainable = True\nfor layer in base_mobilenet.layers[:100]:\n    layer.trainable = False\n\nhybrid_model.compile(optimizer=keras.optimizers.Adam(1e-5),\n                     loss=\"sparse_categorical_crossentropy\",\n                     metrics=[\"accuracy\"])\n\nfine_tune = hybrid_model.fit(\n    [train_ds, train_ds],\n    validation_data=([val_ds, val_ds], None),\n    epochs=15,\n    callbacks=[\n        keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),\n        keras.callbacks.ModelCheckpoint(os.path.join(OUT_DIR,f\"hybrid_final_{ts}.keras\"), save_best_only=True)\n    ]\n)\n\n# ====================== SAVE FINAL MODEL ======================\nfinal_path = os.path.join(OUT_DIR, f\"hybrid_final_{ts}.keras\")\nhybrid_model.save(final_path)\nprint(\"Saved final hybrid model to:\", final_path)\n\nloss, acc = hybrid_model.evaluate([val_ds, val_ds])\nprint(f\"Validation loss: {loss:.4f}, acc: {acc:.4f}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID MODEL - CNN + MobileNetV2 Ensemble (No retraining)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score, confusion_matrix, classification_report\nimport pandas as pd\n\n# ===========================================================\n# CONFIG (EDIT PATHS ONLY IF DIFFERENT)\n# ===========================================================\nCNN_MODEL_PATH       = \"/kaggle/working/complex_cnn_model.keras\"         # your CNN model\nMOBILENET_MODEL_PATH = \"/kaggle/working/final_modelllllll.keras\"         # your MobileNetV2 model\nTRAIN_DIR            = \"/kaggle/working/train/train\"     # dataset folder\nTEST_DIR             = \"/kaggle/working/test/test\"       # test folder (for submission)\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ===========================================================\n# LOAD TRAINED MODELS\n# ===========================================================\ncnn_model = load_model(CNN_MODEL_PATH)\nmobilenet_model = load_model(MOBILENET_MODEL_PATH)\n\nprint(\"\\n📌 MODELS LOADED SUCCESSFULLY 🚀\")\n\n\n# ===========================================================\n# VALIDATION SET FOR ENSEMBLE EVALUATION\n# ===========================================================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n)\n\nclass_names = val_ds.class_names\nnum_classes = len(class_names)\n\nprint(\"\\n📌 Classes detected:\", num_classes)\nprint(class_names[:10], \"...\")\n\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n\n# ===========================================================\n# GET PREDICTIONS (p1 = CNN, p2 = MobileNetV2)\n# ===========================================================\nprint(\"\\n🔍 Running predictions ... please wait\\n\")\n\np1 = cnn_model.predict(val_ds)            # CNN output\np2 = mobilenet_model.predict(val_ds)      # MobileNet output\n\n\n# ===========================================================\n# HYBRID ENSEMBLE (Equal Weighted Fusion)\n# ===========================================================\nhybrid = (p1 + p2) / 2               # ensemble brain 🧠🧠\n\ny_true = np.concatenate([y for x,y in val_ds], axis=0)\ny_pred = np.argmax(hybrid, axis=1)\nif num_classes > 2:\n    y_true = np.argmax(y_true, axis=1)\n\nacc = accuracy_score(y_true, y_pred)\n\n# ===========================================================\n# METRICS OUTPUT\n# ===========================================================\nprint(\"\\n✨ HYBRID ENSEMBLE RESULTS ✨\")\nprint(f\"🔥 VALIDATION ACCURACY = {acc:.4f}\")\nprint(\"\\n📌 Confusion Matrix:\\n\", confusion_matrix(y_true, y_pred))\nprint(\"\\n📌 Classification Report:\\n\", classification_report(y_true, y_pred))\n\n\n# ===========================================================\n# ========  TEST ON SINGLE IMAGE instead of CSV  ===============\n# ===========================================================\n\nfrom tensorflow.keras.preprocessing import image\nimport cv2\n\nimg_path = \"/kaggle/input/safasfaf/100000.jpg\"   # << CHANGE FILE NAME HERE\n\n# Load + Preprocess Image\nimg = image.load_img(img_path, target_size=IMAGE_SIZE)\nimg_arr = image.img_to_array(img)\nimg_arr = np.expand_dims(img_arr, axis=0)\n\n# Individual model predictions\np1_test = cnn_model.predict(img_arr)\np2_test = mobilenet_model.predict(img_arr)\n\n# Hybrid fusion output\nhybrid_pred = (p1_test + p2_test) / 2        # ensemble result\npred_class = np.argmax(hybrid_pred)\nconfidence = np.max(hybrid_pred) * 100\n\nprint(\"\\n=================== RESULT ===================\")\nprint(f\"📌 Image tested        : {os.path.basename(img_path)}\")\nprint(f\"🧠 Predicted Class     : {class_names[pred_class]}\")\nprint(f\"🔥 Confidence          : {confidence:.2f}%\")\nprint(\"================================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:17:38.80963Z","iopub.execute_input":"2025-11-30T21:17:38.810272Z","iopub.status.idle":"2025-11-30T21:18:02.71268Z","shell.execute_reply.started":"2025-11-30T21:17:38.810233Z","shell.execute_reply":"2025-11-30T21:18:02.711554Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -rf /kaggle/working/test\n!mkdir -p /kaggle/working/test\n!unzip -q /kaggle/input/datasciencebowl/test.zip -d /kaggle/working/test\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:20:12.851218Z","iopub.execute_input":"2025-11-30T21:20:12.851562Z","iopub.status.idle":"2025-11-30T21:20:23.958135Z","shell.execute_reply.started":"2025-11-30T21:20:12.851535Z","shell.execute_reply":"2025-11-30T21:20:23.957232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID MODEL (CNN + MobileNetV2) WITH AUTO-WEIGHT SEARCH 🔥\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score\nfrom tensorflow.keras.preprocessing import image\n\n# ===========================================================\n# CONFIG\n# ===========================================================\nCNN_MODEL_PATH       = \"/kaggle/working/complex_cnn_model.keras\"\nMOBILENET_MODEL_PATH = \"/kaggle/working/final_modelllllll.keras\"\nTRAIN_DIR            = \"/kaggle/working/train/train\"\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ===========================================================\n# LOAD TRAINED MODELS\n# ===========================================================\ncnn_model = load_model(CNN_MODEL_PATH)\nmobilenet_model = load_model(MOBILENET_MODEL_PATH)\n\nprint(\"\\n📌 Models Loaded Successfully!\")\n\n\n# ===========================================================\n# VALIDATION DATASET\n# ===========================================================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n)\n\nclass_names = val_ds.class_names\nnum_classes = len(class_names)\n\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\ny_true = np.concatenate([y for x,y in val_ds], axis=0)\n\n# ===========================================================\n# GET PREDICTIONS\n# ===========================================================\nprint(\"\\n🔍 Generating model predictions...\")\n\np_cnn = cnn_model.predict(val_ds)\np_mob = mobilenet_model.predict(val_ds)\n\nprint(\"\\n⚙ Running Hybrid Weight Optimization...\\n\")\n\n# ===========================================================\n# AUTO-WEIGHT SEARCH\n# ===========================================================\nbest_acc = 0\nbest_w  = 0\n\nfor w in np.arange(0.0, 1.05, 0.05):\n    hybrid = (w*p_cnn) + ((1-w)*p_mob)\n    pred = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(y_true, pred)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\n\nprint(\"\\n=================================================\")\nprint(f\"🔥 BEST HYBRID FOUND → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Validation Accuracy = {best_acc:.4f}\")\nprint(\"=================================================\\n\")\n\n\n# ===========================================================\n# TEST ON SINGLE IMAGE 🔥\n# ===========================================================\nimg_path = \"/kaggle/input/safasfaf/100000.jpg\"   # ← CHANGE IMAGE\n\nimg = image.load_img(img_path, target_size=IMAGE_SIZE)\nimg_arr = image.img_to_array(img)\nimg_arr = np.expand_dims(img_arr, axis=0)\n\np1 = cnn_model.predict(img_arr)\np2 = mobilenet_model.predict(img_arr)\n\nfinal_pred = (best_w*p1) + ((1-best_w)*p2)\n\ncls = np.argmax(final_pred)\nconf = np.max(final_pred)*100\n\nprint(\"\\n================== FINAL RESULT ==================\")\nprint(f\"📌 Image Tested   : {os.path.basename(img_path)}\")\nprint(f\"🧠 Predicted Class: {class_names[cls]}\")\nprint(f\"🔥 Confidence     : {conf:.2f}%\")\nprint(\"===================================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:25:17.039301Z","iopub.execute_input":"2025-11-30T21:25:17.039753Z","iopub.status.idle":"2025-11-30T21:25:46.628383Z","shell.execute_reply.started":"2025-11-30T21:25:17.039715Z","shell.execute_reply":"2025-11-30T21:25:46.626536Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FIXED HYBRID CNN + MobileNetV2 (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/working/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/working/final_modelllllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/safasfaf/100000.jpg\"   # test image\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x,y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering\n\nprint(f\"🔍 Classes detected = {len(correct_class_order)}\\n\")\n\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\n# CNN class order is unknown → we rebuild ordering\n# ------------------------------------------------------------\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\n# Step 1 — Extract final dense layer weights\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\n\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# Step 2 — Infer CNN class order = class index based on training set\n# Because dataset was used, CNN follows same natural folder index order\n# But to be safe we re-map\n\ncnn_remapped = np.zeros_like(p_cnn)\n\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:,new_idx] = p_cnn[:,old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0,1.05,0.05):\n    hybrid = (w*cnn_remapped) + ((1-w)*p_mob)\n    preds = np.argmax(hybrid,axis=1)\n    acc = accuracy_score(true_labels,preds)\n\n    print(f\"Weight CNN={w:.2f} | Mobilenet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Hybrid Validation Accuracy = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img),axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w*p1) + ((1-best_w)*p2)\ncls = np.argmax(final)\nconf = np.max(final)*100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:27:12.057116Z","iopub.execute_input":"2025-11-30T21:27:12.058007Z","iopub.status.idle":"2025-11-30T21:27:41.844025Z","shell.execute_reply.started":"2025-11-30T21:27:12.057975Z","shell.execute_reply":"2025-11-30T21:27:41.842944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FIXED HYBRID CNN + MobileNetV2 (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/working/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/working/final_modelllllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/dsgsgsg/7370.jpg\"   # test image\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x,y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering\n\nprint(f\"🔍 Classes detected = {len(correct_class_order)}\\n\")\n\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\n# CNN class order is unknown → we rebuild ordering\n# ------------------------------------------------------------\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\n# Step 1 — Extract final dense layer weights\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\n\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# Step 2 — Infer CNN class order = class index based on training set\n# Because dataset was used, CNN follows same natural folder index order\n# But to be safe we re-map\n\ncnn_remapped = np.zeros_like(p_cnn)\n\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:,new_idx] = p_cnn[:,old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0,1.05,0.05):\n    hybrid = (w*cnn_remapped) + ((1-w)*p_mob)\n    preds = np.argmax(hybrid,axis=1)\n    acc = accuracy_score(true_labels,preds)\n\n    print(f\"Weight CNN={w:.2f} | Mobilenet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Hybrid Validation Accuracy = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img),axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w*p1) + ((1-best_w)*p2)\ncls = np.argmax(final)\nconf = np.max(final)*100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:34:01.497044Z","iopub.execute_input":"2025-11-30T21:34:01.497471Z","iopub.status.idle":"2025-11-30T21:34:27.943994Z","shell.execute_reply.started":"2025-11-30T21:34:01.497433Z","shell.execute_reply":"2025-11-30T21:34:27.943239Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FIXED HYBRID CNN + MobileNetV2 (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/working/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/working/final_modelllllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/dsgsdged/3186.jpg\"   # test image\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x,y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering\n\nprint(f\"🔍 Classes detected = {len(correct_class_order)}\\n\")\n\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\n# CNN class order is unknown → we rebuild ordering\n# ------------------------------------------------------------\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\n# Step 1 — Extract final dense layer weights\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\n\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# Step 2 — Infer CNN class order = class index based on training set\n# Because dataset was used, CNN follows same natural folder index order\n# But to be safe we re-map\n\ncnn_remapped = np.zeros_like(p_cnn)\n\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:,new_idx] = p_cnn[:,old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0,1.05,0.05):\n    hybrid = (w*cnn_remapped) + ((1-w)*p_mob)\n    preds = np.argmax(hybrid,axis=1)\n    acc = accuracy_score(true_labels,preds)\n\n    print(f\"Weight CNN={w:.2f} | Mobilenet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Hybrid Validation Accuracy = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img),axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w*p1) + ((1-best_w)*p2)\ncls = np.argmax(final)\nconf = np.max(final)*100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:36:18.821614Z","iopub.execute_input":"2025-11-30T21:36:18.82202Z","iopub.status.idle":"2025-11-30T21:36:44.214523Z","shell.execute_reply.started":"2025-11-30T21:36:18.821991Z","shell.execute_reply":"2025-11-30T21:36:44.213783Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FIXED HYBRID CNN + MobileNetV2 (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/working/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/working/final_modelllllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/asfgbhgf/88485.jpg\"   # test image\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x,y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering\n\nprint(f\"🔍 Classes detected = {len(correct_class_order)}\\n\")\n\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\n# CNN class order is unknown → we rebuild ordering\n# ------------------------------------------------------------\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\n# Step 1 — Extract final dense layer weights\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\n\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# Step 2 — Infer CNN class order = class index based on training set\n# Because dataset was used, CNN follows same natural folder index order\n# But to be safe we re-map\n\ncnn_remapped = np.zeros_like(p_cnn)\n\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:,new_idx] = p_cnn[:,old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0,1.05,0.05):\n    hybrid = (w*cnn_remapped) + ((1-w)*p_mob)\n    preds = np.argmax(hybrid,axis=1)\n    acc = accuracy_score(true_labels,preds)\n\n    print(f\"Weight CNN={w:.2f} | Mobilenet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Hybrid Validation Accuracy = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img),axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w*p1) + ((1-best_w)*p2)\ncls = np.argmax(final)\nconf = np.max(final)*100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:41:14.900699Z","iopub.execute_input":"2025-11-30T21:41:14.901539Z","iopub.status.idle":"2025-11-30T21:41:40.878122Z","shell.execute_reply.started":"2025-11-30T21:41:14.901505Z","shell.execute_reply":"2025-11-30T21:41:40.877327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport matplotlib.pyplot as plt\n\nTRAIN_DIR = \"/kaggle/working/train/train\"   # same as above\n\n# --------- Count images per class ----------\nclass_names = sorted([\n    d for d in os.listdir(TRAIN_DIR)\n    if os.path.isdir(os.path.join(TRAIN_DIR, d))\n])\n\ncounts = []\nfor cls in class_names:\n    cls_folder = os.path.join(TRAIN_DIR, cls)\n    # count only files (ignore subfolders if any)\n    n_images = sum(\n        1 for f in os.listdir(cls_folder)\n        if os.path.isfile(os.path.join(cls_folder, f))\n    )\n    counts.append(n_images)\n\nprint(\"Total classes:\", len(class_names))\nprint(\"Total images:\", sum(counts))\n\n# --------- Plot distribution ----------\nplt.figure(figsize=(18, 8))   # bada graph so it's readable\nplt.bar(range(len(class_names)), counts)\nplt.xticks(range(len(class_names)), class_names, rotation=90)\nplt.xlabel(\"Classes\")\nplt.ylabel(\"Number of images\")\nplt.title(\"Class Distribution in Training Set\")\nplt.tight_layout()\n\n# Save as image\nplt.savefig(\"class_distribution.png\", dpi=300, bbox_inches=\"tight\")\nplt.show()\n\nprint(\"\\n📁 Saved graph as: class_distribution.png\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:44:17.428679Z","iopub.execute_input":"2025-11-30T21:44:17.429102Z","iopub.status.idle":"2025-11-30T21:44:20.55Z","shell.execute_reply.started":"2025-11-30T21:44:17.429074Z","shell.execute_reply":"2025-11-30T21:44:20.549197Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FIXED HYBRID CNN + MobileNetV2 (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/working/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/working/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/asfgbhgf/88485.jpg\"   # test image\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x,y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering\n\nprint(f\"🔍 Classes detected = {len(correct_class_order)}\\n\")\n\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\n# CNN class order is unknown → we rebuild ordering\n# ------------------------------------------------------------\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\n# Step 1 — Extract final dense layer weights\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\n\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# Step 2 — Infer CNN class order = class index based on training set\n# Because dataset was used, CNN follows same natural folder index order\n# But to be safe we re-map\n\ncnn_remapped = np.zeros_like(p_cnn)\n\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:,new_idx] = p_cnn[:,old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0,1.05,0.05):\n    hybrid = (w*cnn_remapped) + ((1-w)*p_mob)\n    preds = np.argmax(hybrid,axis=1)\n    acc = accuracy_score(true_labels,preds)\n\n    print(f\"Weight CNN={w:.2f} | Mobilenet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Hybrid Validation Accuracy = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img),axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w*p1) + ((1-best_w)*p2)\ncls = np.argmax(final)\nconf = np.max(final)*100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:55:16.373133Z","iopub.execute_input":"2025-11-30T21:55:16.37401Z","iopub.status.idle":"2025-11-30T21:55:42.83997Z","shell.execute_reply.started":"2025-11-30T21:55:16.373975Z","shell.execute_reply":"2025-11-30T21:55:42.839301Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FIXED HYBRID CNN + MobileNetV2 (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/working/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/working/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/dsgsgsg/7370.jpg\"   # test image\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x,y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering\n\nprint(f\"🔍 Classes detected = {len(correct_class_order)}\\n\")\n\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\n# CNN class order is unknown → we rebuild ordering\n# ------------------------------------------------------------\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\n# Step 1 — Extract final dense layer weights\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\n\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# Step 2 — Infer CNN class order = class index based on training set\n# Because dataset was used, CNN follows same natural folder index order\n# But to be safe we re-map\n\ncnn_remapped = np.zeros_like(p_cnn)\n\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:,new_idx] = p_cnn[:,old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0,1.05,0.05):\n    hybrid = (w*cnn_remapped) + ((1-w)*p_mob)\n    preds = np.argmax(hybrid,axis=1)\n    acc = accuracy_score(true_labels,preds)\n\n    print(f\"Weight CNN={w:.2f} | Mobilenet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Hybrid Validation Accuracy = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img),axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w*p1) + ((1-best_w)*p2)\ncls = np.argmax(final)\nconf = np.max(final)*100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:55:46.751461Z","iopub.execute_input":"2025-11-30T21:55:46.752098Z","iopub.status.idle":"2025-11-30T21:56:11.881201Z","shell.execute_reply.started":"2025-11-30T21:55:46.752069Z","shell.execute_reply":"2025-11-30T21:56:11.880462Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FIXED HYBRID CNN + MobileNetV2 (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/working/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/working/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x,y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering\n\nprint(f\"🔍 Classes detected = {len(correct_class_order)}\\n\")\n\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\n# CNN class order is unknown → we rebuild ordering\n# ------------------------------------------------------------\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\n# Step 1 — Extract final dense layer weights\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\n\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# Step 2 — Infer CNN class order = class index based on training set\n# Because dataset was used, CNN follows same natural folder index order\n# But to be safe we re-map\n\ncnn_remapped = np.zeros_like(p_cnn)\n\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:,new_idx] = p_cnn[:,old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0,1.05,0.05):\n    hybrid = (w*cnn_remapped) + ((1-w)*p_mob)\n    preds = np.argmax(hybrid,axis=1)\n    acc = accuracy_score(true_labels,preds)\n\n    print(f\"Weight CNN={w:.2f} | Mobilenet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Hybrid Validation Accuracy = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img),axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w*p1) + ((1-best_w)*p2)\ncls = np.argmax(final)\nconf = np.max(final)*100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:57:58.444129Z","iopub.execute_input":"2025-11-30T21:57:58.445096Z","iopub.status.idle":"2025-11-30T21:58:23.751511Z","shell.execute_reply.started":"2025-11-30T21:57:58.445059Z","shell.execute_reply":"2025-11-30T21:58:23.750763Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FIXED HYBRID CNN + MobileNetV2 (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrix\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/sdfghjkl/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224,224)\nBATCH_SIZE = 16\nSEED = 42\n\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x,y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering\n\nprint(f\"🔍 Classes detected = {len(correct_class_order)}\\n\")\n\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# yaha mapping abhi identity hai (same order), lekin future-proof rakh rahe\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | Mobilenet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Hybrid Validation Accuracy (search me best) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\nprint(classification_report(true_labels, preds_hybrid,\n                            target_names=correct_class_order))\n\ncm = confusion_matrix(true_labels, preds_hybrid)\nprint(\"\\n🧮 Confusion Matrix (Hybrid):\\n\")\nprint(cm)\nprint(\"\\n(Confusion matrix rows = true class, cols = predicted class)\\n\")\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T05:33:05.761656Z","iopub.execute_input":"2025-12-01T05:33:05.76233Z","iopub.status.idle":"2025-12-01T05:33:30.194749Z","shell.execute_reply.started":"2025-12-01T05:33:05.762307Z","shell.execute_reply":"2025-12-01T05:33:30.193723Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrix\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/sdfghjkl/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\n\nprint(f\"🔍 Classes detected (total folders) = {len(correct_class_order)}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is effectively identity,\n# but kept for clarity / future-proofing.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---- Safe classification_report with only actually present labels ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\n# unique labels that appear in validation set (e.g., 90 out of 121)\nunique_labels = np.unique(true_labels)\n\n# corresponding class names for those labels\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names\n))\n\n# Confusion matrix with same label order\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T05:39:44.390153Z","iopub.execute_input":"2025-12-01T05:39:44.390874Z","iopub.status.idle":"2025-12-01T05:40:09.701501Z","shell.execute_reply.started":"2025-12-01T05:39:44.390848Z","shell.execute_reply":"2025-12-01T05:40:09.700903Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrix\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/final_modelllllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\n\nprint(f\"🔍 Classes detected (total folders) = {len(correct_class_order)}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is effectively identity,\n# but kept for clarity / future-proofing.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---- Safe classification_report with only actually present labels ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\n# unique labels that appear in validation set (e.g., 90 out of 121)\nunique_labels = np.unique(true_labels)\n\n# corresponding class names for those labels\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names\n))\n\n# Confusion matrix with same label order\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/final_modelllllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False           # IMPORTANT: keep order fixed\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\nnum_classes = len(correct_class_order)\n\nprint(f\"🔍 Classes detected (total folders) = {num_classes}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is identity.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---- Safe classification_report with only actually present labels ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names,\n    zero_division=0\n))\n\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ============================================================\n#  GRAPHS FOR REPORT / PPT\n# ============================================================\n\n# ---------- 1) Bar chart: CNN vs MobileNet vs Hybrid ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\nplt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---------- 2) Full confusion matrix (121 classes) ----------\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (121 classes)\")\nplt.xlabel(\"Predicted label\")\nplt.ylabel(\"True label\")\n# hide ticks (too many)\nplt.xticks([])\nplt.yticks([])\nplt.tight_layout()\nplt.show()\n\n# ---------- 3) Zoomed confusion matrix for top-6 classes ----------\n# pick 6 most frequent true labels\ncounts = np.bincount(true_labels, minlength=num_classes)\ntop_k = 6\ntop_indices = np.argsort(counts)[-top_k:]      # indices of 6 largest supports\n# sort them by frequency descending for nicer plot\ntop_indices = top_indices[np.argsort(counts[top_indices])[::-1]]\n\ncm_small = confusion_matrix(true_labels, preds_hybrid, labels=top_indices)\n\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm_small, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (Top 6 Frequent Classes)\")\n\ntick_labels = [correct_class_order[i] for i in top_indices]\nplt.xticks(np.arange(top_k), tick_labels, rotation=45, ha=\"right\")\nplt.yticks(np.arange(top_k), tick_labels)\n\nthresh = cm_small.max() / 2.0 if cm_small.max() > 0 else 0.5\nfor i in range(cm_small.shape[0]):\n    for j in range(cm_small.shape[1]):\n        plt.text(j, i, format(cm_small[i, j], \"d\"),\n                 ha=\"center\", va=\"center\",\n                 color=\"white\" if cm_small[i, j] > thresh else \"black\")\n\nplt.tight_layout()\nplt.show()\n\n# ---------- 4 & 5) Micro-averaged ROC + PR curves (multi-class) ----------\nlabels_all = np.arange(num_classes)\ny_true_bin = label_binarize(true_labels, classes=labels_all)\ny_scores = hybrid_best  # (N, num_classes)\n\n# ROC\nfpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\nroc_auc = auc(fpr, tpr)\n\nplt.figure(figsize=(6, 5))\nplt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\nplt.plot([0, 1], [0, 1], linestyle=\"--\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"False Positive Rate\")\nplt.ylabel(\"True Positive Rate\")\nplt.title(\"Hybrid Micro-averaged ROC Curve (Validation)\")\nplt.legend(loc=\"lower right\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# PR\nprecision, recall, _ = precision_recall_curve(\n    y_true_bin.ravel(), y_scores.ravel()\n)\npr_auc = auc(recall, precision)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.title(\"Hybrid Micro-averaged Precision-Recall Curve (Validation)\")\nplt.legend(loc=\"lower left\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:13:49.212334Z","iopub.execute_input":"2025-12-01T06:13:49.21319Z","iopub.status.idle":"2025-12-01T06:14:10.62689Z","shell.execute_reply.started":"2025-12-01T06:13:49.213164Z","shell.execute_reply":"2025-12-01T06:14:10.626204Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False           # IMPORTANT: keep order fixed\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\nnum_classes = len(correct_class_order)\n\nprint(f\"🔍 Classes detected (total folders) = {num_classes}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is identity.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---- Safe classification_report with only actually present labels ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names,\n    zero_division=0\n))\n\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ============================================================\n#  GRAPHS FOR REPORT / PPT\n# ============================================================\n\n# ---------- 1) Bar chart: CNN vs MobileNet vs Hybrid ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\nplt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---------- 2) Full confusion matrix (121 classes) ----------\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (121 classes)\")\nplt.xlabel(\"Predicted label\")\nplt.ylabel(\"True label\")\n# hide ticks (too many)\nplt.xticks([])\nplt.yticks([])\nplt.tight_layout()\nplt.show()\n\n# ---------- 3) Zoomed confusion matrix for top-6 classes ----------\n# pick 6 most frequent true labels\ncounts = np.bincount(true_labels, minlength=num_classes)\ntop_k = 6\ntop_indices = np.argsort(counts)[-top_k:]      # indices of 6 largest supports\n# sort them by frequency descending for nicer plot\ntop_indices = top_indices[np.argsort(counts[top_indices])[::-1]]\n\ncm_small = confusion_matrix(true_labels, preds_hybrid, labels=top_indices)\n\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm_small, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (Top 6 Frequent Classes)\")\n\ntick_labels = [correct_class_order[i] for i in top_indices]\nplt.xticks(np.arange(top_k), tick_labels, rotation=45, ha=\"right\")\nplt.yticks(np.arange(top_k), tick_labels)\n\nthresh = cm_small.max() / 2.0 if cm_small.max() > 0 else 0.5\nfor i in range(cm_small.shape[0]):\n    for j in range(cm_small.shape[1]):\n        plt.text(j, i, format(cm_small[i, j], \"d\"),\n                 ha=\"center\", va=\"center\",\n                 color=\"white\" if cm_small[i, j] > thresh else \"black\")\n\nplt.tight_layout()\nplt.show()\n\n# ---------- 4 & 5) Micro-averaged ROC + PR curves (multi-class) ----------\nlabels_all = np.arange(num_classes)\ny_true_bin = label_binarize(true_labels, classes=labels_all)\ny_scores = hybrid_best  # (N, num_classes)\n\n# ROC\nfpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\nroc_auc = auc(fpr, tpr)\n\nplt.figure(figsize=(6, 5))\nplt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\nplt.plot([0, 1], [0, 1], linestyle=\"--\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"False Positive Rate\")\nplt.ylabel(\"True Positive Rate\")\nplt.title(\"Hybrid Micro-averaged ROC Curve (Validation)\")\nplt.legend(loc=\"lower right\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# PR\nprecision, recall, _ = precision_recall_curve(\n    y_true_bin.ravel(), y_scores.ravel()\n)\npr_auc = auc(recall, precision)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.title(\"Hybrid Micro-averaged Precision-Recall Curve (Validation)\")\nplt.legend(loc=\"lower left\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:14:31.144522Z","iopub.execute_input":"2025-12-01T06:14:31.145048Z","iopub.status.idle":"2025-12-01T06:14:52.151509Z","shell.execute_reply.started":"2025-12-01T06:14:31.145026Z","shell.execute_reply":"2025-12-01T06:14:52.150867Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False           # IMPORTANT: keep order fixed\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\nnum_classes = len(correct_class_order)\n\nprint(f\"🔍 Classes detected (total folders) = {num_classes}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is identity.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---- Safe classification_report with only actually present labels ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names,\n    zero_division=0\n))\n\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ============================================================\n#  GRAPHS FOR REPORT / PPT\n# ============================================================\n\n# ---------- 1) Bar chart: CNN vs MobileNet vs Hybrid ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\nplt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---------- 2) Full confusion matrix (121 classes) ----------\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (121 classes)\")\nplt.xlabel(\"Predicted label\")\nplt.ylabel(\"True label\")\n# hide ticks (too many)\nplt.xticks([])\nplt.yticks([])\nplt.tight_layout()\nplt.show()\n\n# ---------- 3) Zoomed confusion matrix for top-6 classes ----------\n# pick 6 most frequent true labels\ncounts = np.bincount(true_labels, minlength=num_classes)\ntop_k = 6\ntop_indices = np.argsort(counts)[-top_k:]      # indices of 6 largest supports\n# sort them by frequency descending for nicer plot\ntop_indices = top_indices[np.argsort(counts[top_indices])[::-1]]\n\ncm_small = confusion_matrix(true_labels, preds_hybrid, labels=top_indices)\n\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm_small, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (Top 6 Frequent Classes)\")\n\ntick_labels = [correct_class_order[i] for i in top_indices]\nplt.xticks(np.arange(top_k), tick_labels, rotation=45, ha=\"right\")\nplt.yticks(np.arange(top_k), tick_labels)\n\nthresh = cm_small.max() / 2.0 if cm_small.max() > 0 else 0.5\nfor i in range(cm_small.shape[0]):\n    for j in range(cm_small.shape[1]):\n        plt.text(j, i, format(cm_small[i, j], \"d\"),\n                 ha=\"center\", va=\"center\",\n                 color=\"white\" if cm_small[i, j] > thresh else \"black\")\n\nplt.tight_layout()\nplt.show()\n\n# ---------- 4 & 5) Micro-averaged ROC + PR curves (multi-class) ----------\nlabels_all = np.arange(num_classes)\ny_true_bin = label_binarize(true_labels, classes=labels_all)\ny_scores = hybrid_best  # (N, num_classes)\n\n# ROC\nfpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\nroc_auc = auc(fpr, tpr)\n\nplt.figure(figsize=(6, 5))\nplt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\nplt.plot([0, 1], [0, 1], linestyle=\"--\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"False Positive Rate\")\nplt.ylabel(\"True Positive Rate\")\nplt.title(\"Hybrid Micro-averaged ROC Curve (Validation)\")\nplt.legend(loc=\"lower right\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# PR\nprecision, recall, _ = precision_recall_curve(\n    y_true_bin.ravel(), y_scores.ravel()\n)\npr_auc = auc(recall, precision)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.title(\"Hybrid Micro-averaged Precision-Recall Curve (Validation)\")\nplt.legend(loc=\"lower left\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:17:32.157161Z","iopub.execute_input":"2025-12-01T06:17:32.15747Z","iopub.status.idle":"2025-12-01T06:17:58.600486Z","shell.execute_reply.started":"2025-12-01T06:17:32.157448Z","shell.execute_reply":"2025-12-01T06:17:58.599899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\nfrom tensorflow.keras.preprocessing import image\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_model (1).keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False           # IMPORTANT: keep order fixed\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\nnum_classes = len(correct_class_order)\n\nprint(f\"🔍 Classes detected (total folders) = {num_classes}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is identity.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---- Safe classification_report with only actually present labels ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names,\n    zero_division=0\n))\n\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ============================================================\n#  GRAPHS FOR REPORT / PPT\n# ============================================================\n\n# ---------- 1) Bar chart: CNN vs MobileNet vs Hybrid ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\nplt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---------- 2) Full confusion matrix (121 classes) ----------\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (121 classes)\")\nplt.xlabel(\"Predicted label\")\nplt.ylabel(\"True label\")\n# hide ticks (too many)\nplt.xticks([])\nplt.yticks([])\nplt.tight_layout()\nplt.show()\n\n# ---------- 3) Zoomed confusion matrix for top-6 classes ----------\n# pick 6 most frequent true labels\ncounts = np.bincount(true_labels, minlength=num_classes)\ntop_k = 6\ntop_indices = np.argsort(counts)[-top_k:]      # indices of 6 largest supports\n# sort them by frequency descending for nicer plot\ntop_indices = top_indices[np.argsort(counts[top_indices])[::-1]]\n\ncm_small = confusion_matrix(true_labels, preds_hybrid, labels=top_indices)\n\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm_small, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (Top 6 Frequent Classes)\")\n\ntick_labels = [correct_class_order[i] for i in top_indices]\nplt.xticks(np.arange(top_k), tick_labels, rotation=45, ha=\"right\")\nplt.yticks(np.arange(top_k), tick_labels)\n\nthresh = cm_small.max() / 2.0 if cm_small.max() > 0 else 0.5\nfor i in range(cm_small.shape[0]):\n    for j in range(cm_small.shape[1]):\n        plt.text(j, i, format(cm_small[i, j], \"d\"),\n                 ha=\"center\", va=\"center\",\n                 color=\"white\" if cm_small[i, j] > thresh else \"black\")\n\nplt.tight_layout()\nplt.show()\n\n# ---------- 4 & 5) Micro-averaged ROC + PR curves (multi-class) ----------\nlabels_all = np.arange(num_classes)\ny_true_bin = label_binarize(true_labels, classes=labels_all)\ny_scores = hybrid_best  # (N, num_classes)\n\n# ROC\nfpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\nroc_auc = auc(fpr, tpr)\n\nplt.figure(figsize=(6, 5))\nplt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\nplt.plot([0, 1], [0, 1], linestyle=\"--\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"False Positive Rate\")\nplt.ylabel(\"True Positive Rate\")\nplt.title(\"Hybrid Micro-averaged ROC Curve (Validation)\")\nplt.legend(loc=\"lower right\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# PR\nprecision, recall, _ = precision_recall_curve(\n    y_true_bin.ravel(), y_scores.ravel()\n)\npr_auc = auc(recall, precision)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.title(\"Hybrid Micro-averaged Precision-Recall Curve (Validation)\")\nplt.legend(loc=\"lower left\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:19:54.416739Z","iopub.execute_input":"2025-12-01T06:19:54.417017Z","iopub.status.idle":"2025-12-01T06:20:14.559272Z","shell.execute_reply.started":"2025-12-01T06:19:54.416999Z","shell.execute_reply":"2025-12-01T06:20:14.558642Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE PROJECT SCRIPT\n#  Custom CNN + MobileNetV2  (121-class plankton)\n#  - Loads both models\n#  - Builds validation set\n#  - Searches best ensemble weight\n#  - Prints metrics\n#  - Plots:\n#       * Bar chart (CNN vs MobileNet vs Hybrid)\n#       * Full confusion matrix (121x121)\n#       * Confusion matrix for top-6 frequent classes\n#       * Micro-averaged ROC + PR curves (Hybrid)\n#  - Tests single image with Hybrid\n# ============================================================\n\nimport os\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nfrom tensorflow.keras.preprocessing import image\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\n\n# ========================= CONFIG ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_model (1).keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # single test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False            # important: keep deterministic order\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names\nnum_classes = len(correct_class_order)\n\nprint(f\"🔍 Classes detected (total folders) = {num_classes}\\n\")\n\n# ===================== MODEL PREDICTIONS ===================\np_cnn = cnn.predict(val_ds)   # shape (N, C)\np_mob = mob.predict(val_ds)   # shape (N, C)\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have the same number of classes\"\n\n# If both trained on same directory order, this mapping is identity,\n# but we keep the loop for clarity / safety.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0.0\nbest_w = 0.0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 FINAL ACCURACY COMPARISON ==================\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNetV2 only accuracy: {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ================== HYBRID CLASSIFICATION REPORT ============\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names,\n    zero_division=0\n))\n\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ============================================================\n#  PLOTS FOR REPORT / PPT\n# ============================================================\n\n# ---------- 1) Bar chart: CNN vs MobileNet vs Hybrid ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\nplt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---------- 2) Full confusion matrix (121 classes) ----------\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (121 classes)\")\nplt.xlabel(\"Predicted label\")\nplt.ylabel(\"True label\")\nplt.xticks([])\nplt.yticks([])\nplt.tight_layout()\nplt.show()\n\n# ---------- 3) Zoomed confusion matrix for top-6 frequent classes ----------\ncounts = np.bincount(true_labels, minlength=num_classes)\ntop_k = 6\ntop_indices = np.argsort(counts)[-top_k:]               # 6 biggest\ntop_indices = top_indices[np.argsort(counts[top_indices])[::-1]]  # sort desc\n\ncm_small = confusion_matrix(true_labels, preds_hybrid, labels=top_indices)\n\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm_small, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (Top 6 Frequent Classes)\")\n\ntick_labels = [correct_class_order[i] for i in top_indices]\nplt.xticks(np.arange(top_k), tick_labels, rotation=45, ha=\"right\")\nplt.yticks(np.arange(top_k), tick_labels)\n\nthresh = cm_small.max() / 2.0 if cm_small.max() > 0 else 0.5\nfor i in range(cm_small.shape[0]):\n    for j in range(cm_small.shape[1]):\n        plt.text(j, i, format(cm_small[i, j], \"d\"),\n                 ha=\"center\", va=\"center\",\n                 color=\"white\" if cm_small[i, j] > thresh else \"black\")\n\nplt.tight_layout()\nplt.show()\n\n# ---------- 4) Micro-averaged ROC curve (Hybrid) ----------\nlabels_all = np.arange(num_classes)\ny_true_bin = label_binarize(true_labels, classes=labels_all)\ny_scores = hybrid_best  # shape (N, C)\n\nfpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\nroc_auc = auc(fpr, tpr)\n\nplt.figure(figsize=(6, 5))\nplt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\nplt.plot([0, 1], [0, 1], linestyle=\"--\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"False Positive Rate\")\nplt.ylabel(\"True Positive Rate\")\nplt.title(\"Hybrid Micro-averaged ROC Curve (Validation)\")\nplt.legend(loc=\"lower right\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# ---------- 5) Micro-averaged Precision–Recall curve (Hybrid) ----------\nprecision, recall, _ = precision_recall_curve(\n    y_true_bin.ravel(), y_scores.ravel()\n)\npr_auc = auc(recall, precision)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.title(\"Hybrid Micro-averaged Precision–Recall Curve (Validation)\")\nplt.legend(loc=\"lower left\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# ======================= SINGLE IMAGE TEST ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg_arr = image.img_to_array(img)\nimg_arr = np.expand_dims(img_arr, axis=0)\n\np1 = cnn.predict(img_arr)\np2 = mob.predict(img_arr)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:23:12.373366Z","iopub.execute_input":"2025-12-01T06:23:12.374177Z","iopub.status.idle":"2025-12-01T06:23:34.161282Z","shell.execute_reply.started":"2025-12-01T06:23:12.374143Z","shell.execute_reply":"2025-12-01T06:23:34.160656Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrix\nfrom tensorflow.keras.preprocessing import image\nimport matplotlib.pyplot as plt   # <-- for barplot\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/sdfghjkl/complex_cnn_model.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\n\nprint(f\"🔍 Classes detected (total folders) = {len(correct_class_order)}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is effectively identity,\n# but kept for clarity / future-proofing.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---------- 📊 BAR PLOT: COMPARISON ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\n\nbars = plt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")  # show value on top\n\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---- Safe classification_report with only actually present labels ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names\n))\n\n# Confusion matrix with same label order\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:28:00.317995Z","iopub.execute_input":"2025-12-01T06:28:00.318281Z","iopub.status.idle":"2025-12-01T06:28:26.278818Z","shell.execute_reply.started":"2025-12-01T06:28:00.318255Z","shell.execute_reply":"2025-12-01T06:28:26.27815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE PROJECT SCRIPT\n#  Custom CNN + MobileNetV2  (121-class plankton)\n# ============================================================\n\nimport os\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nfrom tensorflow.keras.preprocessing import image\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix,\n    roc_curve,\n    auc,\n    precision_recall_curve\n)\nfrom sklearn.preprocessing import label_binarize\n\n# ========================= CONFIG ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/final_modelllllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # single test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False            # important: keep deterministic order\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names\nnum_classes = len(correct_class_order)\n\nprint(f\"🔍 Classes detected (total folders) = {num_classes}\\n\")\n\n# Optional prefetch\nval_ds = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ===================== MODEL PREDICTIONS ===================\np_cnn = cnn.predict(val_ds)   # shape (N, C)\np_mob = mob.predict(val_ds)   # shape (N, C)\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have the same number of classes\"\n\n# If both trained on same directory order, this mapping is identity,\n# but we keep the loop for clarity / safety.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0.0\nbest_w = 0.0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 FINAL ACCURACY COMPARISON ==================\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNetV2 only accuracy: {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ================== HYBRID CLASSIFICATION REPORT ============\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nlabels_all = np.arange(num_classes)\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=labels_all,\n    target_names=correct_class_order,\n    zero_division=0\n))\n\ncm = confusion_matrix(true_labels, preds_hybrid, labels=labels_all)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted]:\\n\")\nprint(cm)\n\n# ============================================================\n#  PLOTS FOR REPORT / PPT\n# ============================================================\n\n# ---------- 1) Bar chart: CNN vs MobileNet vs Hybrid ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\nbars = plt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.4f}\", ha=\"center\")\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---------- 2) Full confusion matrix (121 classes) ----------\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (121 classes)\")\nplt.xlabel(\"Predicted label\")\nplt.ylabel(\"True label\")\nplt.xticks([])\nplt.yticks([])\nplt.tight_layout()\nplt.show()\n\n# ---------- 3) Zoomed confusion matrix for top-6 frequent classes ----------\ncounts = np.bincount(true_labels, minlength=num_classes)\ntop_k = 6\ntop_indices = np.argsort(counts)[-top_k:]               # 6 biggest\ntop_indices = top_indices[np.argsort(counts[top_indices])[::-1]]  # sort desc\n\ncm_small = confusion_matrix(true_labels, preds_hybrid, labels=top_indices)\n\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm_small, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (Top 6 Frequent Classes)\")\n\ntick_labels = [correct_class_order[i] for i in top_indices]\nplt.xticks(np.arange(top_k), tick_labels, rotation=45, ha=\"right\")\nplt.yticks(np.arange(top_k), tick_labels)\n\nthresh = cm_small.max() / 2.0 if cm_small.max() > 0 else 0.5\nfor i in range(cm_small.shape[0]):\n    for j in range(cm_small.shape[1]):\n        plt.text(j, i, format(cm_small[i, j], \"d\"),\n                 ha=\"center\", va=\"center\",\n                 color=\"white\" if cm_small[i, j] > thresh else \"black\")\n\nplt.tight_layout()\nplt.show()\n\n# ---------- 4) Micro-averaged ROC curve (Hybrid) ----------\ny_true_bin = label_binarize(true_labels, classes=labels_all)\ny_scores = hybrid_best  # shape (N, C)\n\nfpr, tpr, _ = roc_curve(y_true_bin.ravel(), y_scores.ravel())\nroc_auc = auc(fpr, tpr)\n\nplt.figure(figsize=(6, 5))\nplt.plot(fpr, tpr, lw=2, label=f\"Micro-avg ROC (AUC = {roc_auc:.3f})\")\nplt.plot([0, 1], [0, 1], linestyle=\"--\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"False Positive Rate\")\nplt.ylabel(\"True Positive Rate\")\nplt.title(\"Hybrid Micro-averaged ROC Curve (Validation)\")\nplt.legend(loc=\"lower right\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# ---------- 5) Micro-averaged Precision–Recall curve (Hybrid) ----------\nprecision, recall, _ = precision_recall_curve(\n    y_true_bin.ravel(), y_scores.ravel()\n)\npr_auc = auc(recall, precision)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.title(\"Hybrid Micro-averaged Precision–Recall Curve (Validation)\")\nplt.legend(loc=\"lower left\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# ======================= SINGLE IMAGE TEST ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg_arr = image.img_to_array(img)\nimg_arr = np.expand_dims(img_arr, axis=0)\n\np1 = cnn.predict(img_arr)\np2 = mob.predict(img_arr)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:26:49.318544Z","iopub.execute_input":"2025-12-01T06:26:49.319255Z","iopub.status.idle":"2025-12-01T06:27:10.525663Z","shell.execute_reply.started":"2025-12-01T06:26:49.319231Z","shell.execute_reply":"2025-12-01T06:27:10.525058Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrix\nfrom tensorflow.keras.preprocessing import image\nimport matplotlib.pyplot as plt   # <-- for barplot\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\n\nprint(f\"🔍 Classes detected (total folders) = {len(correct_class_order)}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is effectively identity,\n# but kept for clarity / future-proofing.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---------- 📊 BAR PLOT: COMPARISON ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\n\nbars = plt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")  # show value on top\n\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---- Safe classification_report with only actually present labels ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names\n))\n\n# Confusion matrix with same label order\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted, same order as above classes]:\\n\")\nprint(cm)\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:29:14.65107Z","iopub.execute_input":"2025-12-01T06:29:14.651366Z","iopub.status.idle":"2025-12-01T06:29:33.931593Z","shell.execute_reply.started":"2025-12-01T06:29:14.651343Z","shell.execute_reply":"2025-12-01T06:29:33.931022Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix,\n    precision_recall_curve,\n    auc\n)\nfrom sklearn.preprocessing import label_binarize\nfrom tensorflow.keras.preprocessing import image\nimport matplotlib.pyplot as plt   # for plots\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\nnum_classes = len(correct_class_order)\n\nprint(f\"🔍 Classes detected (total folders) = {num_classes}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is effectively identity,\n# but kept for clarity / future-proofing.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---------- 📊 BAR PLOT: COMPARISON ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\n\nbars = plt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")  # show value on top\n\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---- Hybrid classification report ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names,\n    zero_division=0\n))\n\n# ================= CONFUSION MATRICES =======================\n\n# 1) Full confusion matrix (all classes)\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted]:\\n\")\nprint(cm)\n\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (All Classes)\")\nplt.xlabel(\"Predicted label\")\nplt.ylabel(\"True label\")\nplt.xticks([])\nplt.yticks([])\nplt.tight_layout()\nplt.show()\n\n# 2) Confusion matrix for top-6 frequent classes\ncounts = np.bincount(true_labels, minlength=num_classes)\ntop_k = 6\ntop_indices = np.argsort(counts)[-top_k:]               # 6 biggest supports\ntop_indices = top_indices[np.argsort(counts[top_indices])[::-1]]  # sort desc\n\ncm_small = confusion_matrix(true_labels, preds_hybrid, labels=top_indices)\n\nplt.figure(figsize=(7, 6))\nim = plt.imshow(cm_small, interpolation=\"nearest\")\nplt.colorbar(im)\nplt.title(\"Hybrid Confusion Matrix (Top 6 Frequent Classes)\")\n\ntick_labels = [correct_class_order[i] for i in top_indices]\nplt.xticks(np.arange(top_k), tick_labels, rotation=45, ha=\"right\")\nplt.yticks(np.arange(top_k), tick_labels)\n\nthresh = cm_small.max() / 2.0 if cm_small.max() > 0 else 0.5\nfor i in range(cm_small.shape[0]):\n    for j in range(cm_small.shape[1]):\n        plt.text(j, i, format(cm_small[i, j], \"d\"),\n                 ha=\"center\", va=\"center\",\n                 color=\"white\" if cm_small[i, j] > thresh else \"black\")\n\nplt.tight_layout()\nplt.show()\n\n# ================= PRECISION–RECALL CURVE ===================\n\nprint(\"\\n📈 Plotting micro-averaged Precision–Recall curve for Hybrid...\\n\")\n\nlabels_all = np.arange(num_classes)\ny_true_bin = label_binarize(true_labels, classes=labels_all)\ny_scores = hybrid_best  # (N, num_classes)\n\nprecision, recall, _ = precision_recall_curve(\n    y_true_bin.ravel(), y_scores.ravel()\n)\npr_auc = auc(recall, precision)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.title(\"Hybrid Micro-averaged Precision–Recall Curve (Validation)\")\nplt.legend(loc=\"lower left\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:37:44.296977Z","iopub.execute_input":"2025-12-01T06:37:44.297858Z","iopub.status.idle":"2025-12-01T06:38:05.206267Z","shell.execute_reply.started":"2025-12-01T06:37:44.297825Z","shell.execute_reply":"2025-12-01T06:38:05.205599Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  HYBRID ENSEMBLE: Custom CNN + MobileNet (with class alignment)\n# ============================================================\n\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix,\n    precision_recall_curve,\n    auc\n)\nfrom sklearn.preprocessing import label_binarize\nfrom tensorflow.keras.preprocessing import image\nimport matplotlib.pyplot as plt   # <-- for plots\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\n\n# ======================= LOAD MODELS =======================\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\nprint(\"\\n📌 Models Loaded\\n\")\n\n# ======================= VALIDATION SET ====================\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\ntrue_labels = np.concatenate([y for x, y in val_ds])\ncorrect_class_order = val_ds.class_names  # TRUE ordering from directory\nnum_classes = len(correct_class_order)\n\nprint(f\"🔍 Classes detected (total folders) = {num_classes}\\n\")\n\n# ===================== GET MODEL PREDICTIONS ================\np_cnn = cnn.predict(val_ds)       # CNN output\np_mob = mob.predict(val_ds)       # MobileNet output\n\nprint(\"⚙ Fixing CNN → MobileNet class mapping...\")\n\ncnn_classes = p_cnn.shape[1]\nmob_classes = p_mob.shape[1]\nassert cnn_classes == mob_classes, \"❌ Models must have same class count.\"\n\n# If both models were trained on same folder order, this is effectively identity,\n# but kept for clarity / future-proofing.\ncnn_remapped = np.zeros_like(p_cnn)\nfor old_idx in range(len(correct_class_order)):\n    new_idx = correct_class_order.index(correct_class_order[old_idx])\n    cnn_remapped[:, new_idx] = p_cnn[:, old_idx]\n\nprint(\"✅ Class alignment completed.\\n\")\n\n# ======================= HYBRID SEARCH ======================\nbest_acc = 0\nbest_w = 0\n\nprint(\"🔎 Running weighted hybrid optimization...\\n\")\n\nfor w in np.arange(0, 1.05, 0.05):\n    hybrid = (w * cnn_remapped) + ((1 - w) * p_mob)\n    preds = np.argmax(hybrid, axis=1)\n    acc = accuracy_score(true_labels, preds)\n\n    print(f\"Weight CNN={w:.2f} | MobileNet={1-w:.2f} → ACC={acc:.4f}\")\n\n    if acc > best_acc:\n        best_acc = acc\n        best_w = w\n\nprint(\"\\n====================================================\")\nprint(f\"🔥 FINAL BEST HYBRID WEIGHT → CNN={best_w:.2f}  MobileNet={1-best_w:.2f}\")\nprint(f\"✨ Best Hybrid Validation Accuracy (search) = {best_acc:.4f}\")\nprint(\"====================================================\\n\")\n\n# ============ 📊 TOTAL HYBRID RESULT ON VALIDATION ==========\nprint(\"\\n📊 Final comparison on validation set (CNN vs MobileNet vs Hybrid)\\n\")\n\n# 1) CNN-only accuracy\npreds_cnn = np.argmax(cnn_remapped, axis=1)\nacc_cnn = accuracy_score(true_labels, preds_cnn)\n\n# 2) MobileNet-only accuracy\npreds_mob = np.argmax(p_mob, axis=1)\nacc_mob = accuracy_score(true_labels, preds_mob)\n\n# 3) Hybrid accuracy using BEST weight\nhybrid_best = (best_w * cnn_remapped) + ((1 - best_w) * p_mob)\npreds_hybrid = np.argmax(hybrid_best, axis=1)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNet only accuracy  : {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid accuracy (final)  : {acc_hybrid:.4f}\")\n\n# ---------- 📊 BAR PLOT: COMPARISON ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid\"]\naccs = [acc_cnn, acc_mob, acc_hybrid]\n\nbars = plt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")  # show value on top\n\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ---- Hybrid classification report ----\nprint(\"\\n📋 Classification report (Hybrid, best weight):\\n\")\n\nunique_labels = np.unique(true_labels)\nused_class_names = [correct_class_order[i] for i in unique_labels]\n\nprint(classification_report(\n    true_labels,\n    preds_hybrid,\n    labels=unique_labels,\n    target_names=used_class_names,\n    zero_division=0\n))\n# ================= CONFUSION MATRIX (HYBRID, NORMALIZED) ===================\n\n# sirf wahi labels use karo jo validation mein aaye hain\nunique_labels = np.unique(true_labels)\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\n\nprint(\"\\n🧮 Confusion Matrix (Hybrid) [rows=true, cols=predicted]:\\n\")\nprint(cm)\n\n# row-wise normalize (har row ka sum 1)\ncm_norm = cm.astype(\"float\")\nrow_sums = cm_norm.sum(axis=1, keepdims=True)\nrow_sums[row_sums == 0] = 1.0        # jinke samples 0 hain unko 0 hi rehne do\ncm_norm = cm_norm / row_sums\n\nn_classes_used = len(unique_labels)\n\nplt.figure(figsize=(6, 5))\nim = plt.imshow(cm_norm, interpolation=\"nearest\", vmin=0.0, vmax=1.0)\nplt.colorbar(im)\nplt.title(\"Normalized CM (Hybrid)\")\nplt.xlabel(\"Predicted label index\")\nplt.ylabel(\"True label index\")\n\n# yahan ab 0..n_classes_used-1 tak ticks rakho\nstep = max(1, n_classes_used // 10)   # approx 10 ticks\nplt.xticks(np.arange(0, n_classes_used, step))\nplt.yticks(np.arange(0, n_classes_used, step))\n\nplt.tight_layout()\nplt.show()\n\n\n\n# (agar chaho to yahan sirf top-6 ka normalized CM bhi bana sakte ho)\n\n# ================= PRECISION–RECALL CURVE ===================\n\nprint(\"\\n📈 Plotting micro-averaged Precision–Recall curve for Hybrid...\\n\")\n\nlabels_all = np.arange(num_classes)\ny_true_bin = label_binarize(true_labels, classes=labels_all)\ny_scores = hybrid_best  # (N, num_classes)\n\nprecision, recall, _ = precision_recall_curve(\n    y_true_bin.ravel(), y_scores.ravel()\n)\npr_auc = auc(recall, precision)\n\nplt.figure(figsize=(6, 5))\nplt.plot(recall, precision, lw=2, label=f\"Micro-avg PR (AUC = {pr_auc:.3f})\")\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel(\"Recall\")\nplt.ylabel(\"Precision\")\nplt.title(\"Hybrid Micro-averaged Precision–Recall Curve (Validation)\")\nplt.legend(loc=\"lower left\")\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\n# ======================= TEST SINGLE IMAGE ==================\nprint(\"\\n🖼 Testing Single Image on Hybrid Model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nimg = np.expand_dims(image.img_to_array(img), axis=0)\n\np1 = cnn.predict(img)\np2 = mob.predict(img)\n\nfinal = (best_w * p1) + ((1 - best_w) * p2)\ncls = np.argmax(final)\nconf = np.max(final) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {correct_class_order[cls]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:47:14.611043Z","iopub.execute_input":"2025-12-01T06:47:14.611816Z","iopub.status.idle":"2025-12-01T06:47:35.06284Z","shell.execute_reply.started":"2025-12-01T06:47:14.611793Z","shell.execute_reply":"2025-12-01T06:47:35.062003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n#  FEATURE-LEVEL HYBRID ENSEMBLE:\n#  Custom CNN + MobileNetV2 with feature fusion\n#\n#  Steps:\n#   1) Load trained CNN + MobileNet models\n#   2) Build feature-extractor models from second-last layer\n#   3) Concatenate features and train a small fusion head\n#   4) Compare CNN vs MobileNet vs Hybrid on validation\n# ============================================================\n\nimport os\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model, Model\nfrom tensorflow.keras import layers, Input, optimizers\nfrom tensorflow.keras.preprocessing import image\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    confusion_matrix\n)\n\n# ========================= PATHS ===========================\nCNN_PATH       = \"/kaggle/input/dadadadad/base_cnn_best_20251130_190746.keras\"\nMOBILENET_PATH = \"/kaggle/input/dadadadad/best_modellllll.keras\"\nTRAIN_DIR      = \"/kaggle/working/train/train\"\nIMG_PATH       = \"/kaggle/input/zxcvbnm/22613.jpg\"   # test image\n\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 16\nSEED = 42\nEPOCHS_FUSION = 20   # start small; increase if needed\n\n# ========================= DATASET =========================\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=True\n)\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    TRAIN_DIR,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\nclass_names = train_ds.class_names\nnum_classes = len(class_names)\nprint(\"\\n📚 Classes:\", class_names)\nprint(\"Total classes:\", num_classes)\n\ntrain_ds = train_ds.prefetch(tf.data.AUTOTUNE)\nval_ds   = val_ds.prefetch(tf.data.AUTOTUNE)\n\n# ======================= LOAD MODELS =======================\nprint(\"\\n📌 Loading base models...\\n\")\ncnn = load_model(CNN_PATH)\nmob = load_model(MOBILENET_PATH)\n\nprint(\"\\n=========== CNN SUMMARY ===========\")\ncnn.summary()\nprint(\"\\n====== MobileNetV2 SUMMARY ========\")\nmob.summary()\n\n# =================== FEATURE EXTRACTORS ====================\n# Assumption:\n# - Last layer = Dense(num_classes, softmax)\n# - Second-last layer (layers[-2]) is a good feature layer\n# If needed, replace layers[-2] with cnn.get_layer(\"<name>\").output\n\nprint(\"\\n🔧 Building feature extractor models...\")\n\ncnn_feat = Model(\n    inputs=cnn.input,\n    outputs=cnn.layers[-2].output,\n    name=\"cnn_feature_model\"\n)\n\nmob_feat = Model(\n    inputs=mob.input,\n    outputs=mob.layers[-2].output,\n    name=\"mob_feature_model\"\n)\n\nprint(\"CNN feature shape :\", cnn_feat.output_shape)\nprint(\"MOB feature shape :\", mob_feat.output_shape)\n\n# Freeze base extractors initially (only fusion head will train)\ncnn_feat.trainable = False\nmob_feat.trainable = False\n\n# ================= HYBRID FEATURE-LEVEL MODEL ==============\nprint(\"\\n⚙ Building feature-level hybrid model...\")\n\ninp = Input(shape=(IMAGE_SIZE[0], IMAGE_SIZE[1], 3), name=\"image_input\")\n\n# Pass same input through both feature extractors\nf_cnn = cnn_feat(inp)   # shape: (None, D1)\nf_mob = mob_feat(inp)   # shape: (None, D2)\n\n# Concatenate feature vectors\nfused = layers.Concatenate(name=\"feature_concat\")([f_cnn, f_mob])\n\n# Small fusion head on top of fused features\nx = layers.Dense(256, activation=\"relu\", name=\"fusion_dense\")(fused)\nx = layers.Dropout(0.5, name=\"fusion_dropout\")(x)\nout = layers.Dense(num_classes, activation=\"softmax\", name=\"fusion_output\")(x)\n\nhybrid_feature_model = Model(inputs=inp, outputs=out, name=\"HybridFeatureModel\")\n\nprint(\"\\n=========== HYBRID FEATURE MODEL SUMMARY ===========\")\nhybrid_feature_model.summary()\n\nhybrid_feature_model.compile(\n    optimizer=optimizers.Adam(1e-3),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\n# ======================= TRAIN FUSION HEAD =================\nprint(\"\\n🚀 Training fusion head (base features frozen)...\\n\")\n\nhistory = hybrid_feature_model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS_FUSION\n)\n\n# ================ COLLECT TRUE LABELS FOR VAL ==============\ntrue_labels_list = []\nfor X_batch, y_batch in val_ds:\n    true_labels_list.append(y_batch.numpy())\ntrue_labels = np.concatenate(true_labels_list, axis=0)\n\n# ================= PREDICTIONS: CNN / MOB / HYBRID =========\nprint(\"\\n📤 Getting predictions on validation set...\\n\")\n\n# Predict with original full models\np_cnn_val = cnn.predict(val_ds, verbose=1)\np_mob_val = mob.predict(val_ds, verbose=1)\n\n# Predict with hybrid feature-level model\np_hybrid_val = hybrid_feature_model.predict(val_ds, verbose=1)\n\npreds_cnn    = np.argmax(p_cnn_val, axis=1)\npreds_mob    = np.argmax(p_mob_val, axis=1)\npreds_hybrid = np.argmax(p_hybrid_val, axis=1)\n\n# ====================== ACCURACY COMPARE ===================\nacc_cnn    = accuracy_score(true_labels, preds_cnn)\nacc_mob    = accuracy_score(true_labels, preds_mob)\nacc_hybrid = accuracy_score(true_labels, preds_hybrid)\n\nprint(\"\\n📊 Validation Accuracy Comparison:\")\nprint(f\"🧱 CNN only accuracy        : {acc_cnn:.4f}\")\nprint(f\"📱 MobileNetV2 only accuracy: {acc_mob:.4f}\")\nprint(f\"⚡ Hybrid FEATURE accuracy   : {acc_hybrid:.4f}\")\n\n# ---------- 📊 BAR PLOT: COMPARISON ----------\nplt.figure(figsize=(5, 4))\nmodels = [\"CNN\", \"MobileNetV2\", \"Hybrid-Feature\"]\naccs   = [acc_cnn, acc_mob, acc_hybrid]\n\nbars = plt.bar(models, accs)\nplt.ylim(0, 1.0)\nfor i, v in enumerate(accs):\n    plt.text(i, v + 0.01, f\"{v:.2f}\", ha=\"center\")\nplt.ylabel(\"Validation Accuracy\")\nplt.title(\"Model Comparison (Validation)\")\nplt.tight_layout()\nplt.show()\n\n# ===================== CLASS REPORT ========================\n\n# Only keep classes that actually appear in true_labels / preds\nunique_labels = np.unique(true_labels)\nused_class_names = [class_names[i] for i in unique_labels]\n\nprint(\"\\n📋 Classification report (Hybrid FEATURE, present classes only):\\n\")\nprint(\n    classification_report(\n        true_labels,\n        preds_hybrid,\n        labels=unique_labels,\n        target_names=used_class_names,\n        zero_division=0\n    )\n)\n\n# ================= CONFUSION MATRIX (HYBRID) ===============\ncm = confusion_matrix(true_labels, preds_hybrid, labels=unique_labels)\nprint(\"\\n🧮 Confusion Matrix (Hybrid FEATURE) [rows=true, cols=predicted]:\\n\")\nprint(cm)\n\n# Normalized confusion matrix (row-wise)\ncm_norm = cm.astype(\"float\")\nrow_sums = cm_norm.sum(axis=1, keepdims=True)\nrow_sums[row_sums == 0] = 1.0\ncm_norm = cm_norm / row_sums\n\nplt.figure(figsize=(6, 5))\nim = plt.imshow(cm_norm, interpolation=\"nearest\", vmin=0.0, vmax=1.0)\nplt.colorbar(im)\nplt.title(\"Normalized CM (Hybrid FEATURE)\")\nplt.xlabel(\"Predicted label index\")\nplt.ylabel(\"True label index\")\n\n# Agar bohot classes ho to sparse ticks rakho\nmax_idx = len(unique_labels)\nstep = max(1, max_idx // 10)\nplt.xticks(np.arange(0, max_idx, step))\nplt.yticks(np.arange(0, max_idx, step))\n\nplt.tight_layout()\nplt.show()\n\n# ================== SINGLE IMAGE PREDICTION =================\nprint(\"\\n🖼 Testing single image on Hybrid FEATURE model...\\n\")\n\nimg = image.load_img(IMG_PATH, target_size=IMAGE_SIZE)\nx = image.img_to_array(img)\nx = np.expand_dims(x, axis=0)\n\nprobs = hybrid_feature_model.predict(x)\ncls_idx = np.argmax(probs[0])\nconf = np.max(probs[0]) * 100\n\nprint(\"=============== RESULT ==================\")\nprint(f\"📌 Image        : {os.path.basename(IMG_PATH)}\")\nprint(f\"🧠 Predicted    : {class_names[cls_idx]}\")\nprint(f\"🔥 Confidence   : {conf:.2f}%\")\nprint(\"==========================================\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-01T06:55:09.667438Z","iopub.execute_input":"2025-12-01T06:55:09.667756Z","iopub.status.idle":"2025-12-01T07:13:25.982774Z","shell.execute_reply.started":"2025-12-01T06:55:09.667735Z","shell.execute_reply":"2025-12-01T07:13:25.982138Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}