{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":14774,"databundleVersionId":875431,"isSourceIdPinned":false}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"dc394e6d-e6b3-439b-81cc-5454183fbbdb","cell_type":"code","source":"from IPython.display import Markdown, display\n\ndisplay(Markdown(\"\"\"\n# Diabetic Retinopathy Detection and Classification using EfficientNetB3\n\nThis notebook performs **Diabetic Retinopathy Detection and Classification** using the APTOS 2019 Blindness Detection dataset.\n\nOriginal APTOS classes:\n\n| Original Class | Meaning |\n|---|---|\n| 0 | No Diabetic Retinopathy |\n| 1 | Mild DR |\n| 2 | Moderate DR |\n| 3 | Severe DR |\n| 4 | Proliferative DR |\n\nConverted binary classes:\n\n| New Class | Meaning |\n|---|---|\n| 0 | No DR |\n| 1 | DR Present |\n\nThis version is code-safe for Kaggle. Every section will run as Python code.\n\"\"\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:38.080635Z","iopub.execute_input":"2026-05-01T18:28:38.081458Z","iopub.status.idle":"2026-05-01T18:28:38.087508Z","shell.execute_reply.started":"2026-05-01T18:28:38.081414Z","shell.execute_reply":"2026-05-01T18:28:38.086499Z"}},"outputs":[],"execution_count":null},{"id":"304b694c-3d30-4f60-a155-2b0f58651fc1","cell_type":"code","source":"# ===============================\n# 1. Import Required Libraries\n# ===============================\n\nimport os\nos.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"3\"\n\nimport random\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport cv2\n\nimport tensorflow as tf\nfrom tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, Dropout, GlobalAveragePooling2D, BatchNormalization\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\nfrom tensorflow.keras.optimizers import Adam\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    classification_report,\n    roc_auc_score,\n    roc_curve\n)\n\nSEED = 42\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntf.random.set_seed(SEED)\n\nprint(\"TensorFlow version:\", tf.__version__)\nprint(\"GPU Available:\", tf.config.list_physical_devices(\"GPU\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:38.088882Z","iopub.execute_input":"2026-05-01T18:28:38.089260Z","iopub.status.idle":"2026-05-01T18:28:38.101859Z","shell.execute_reply.started":"2026-05-01T18:28:38.089228Z","shell.execute_reply":"2026-05-01T18:28:38.100975Z"}},"outputs":[],"execution_count":null},{"id":"34914dcf-f8b5-4121-9ede-e0913845d64d","cell_type":"code","source":"# ===============================\n# 2. Dataset Path Setup\n# ===============================\n\n# Kaggle-e first right side theke:\n# Add Input -> Search \"APTOS 2019 Blindness Detection\" -> Add\n#\n# Common Kaggle paths:\npossible_data_dirs = [\n    \"/kaggle/input/aptos2019-blindness-detection\",\n    \"/kaggle/input/competitions/aptos2019-blindness-detection\"\n]\n\nDATA_DIR = None\nfor path in possible_data_dirs:\n    if os.path.exists(os.path.join(path, \"train.csv\")):\n        DATA_DIR = path\n        break\n\nif DATA_DIR is None:\n    print(\"ERROR: APTOS dataset not found.\")\n    print(\"Please attach dataset first:\")\n    print(\"Right side panel -> Add Input -> Search: APTOS 2019 Blindness Detection -> Add\")\n    print(\"\\nAvailable /kaggle/input folders:\")\n    if os.path.exists(\"/kaggle/input\"):\n        print(os.listdir(\"/kaggle/input\"))\n    else:\n        print(\"/kaggle/input not found\")\n    raise FileNotFoundError(\"APTOS dataset is not attached to this Kaggle notebook.\")\n\nTRAIN_CSV = os.path.join(DATA_DIR, \"train.csv\")\nIMAGE_DIR = os.path.join(DATA_DIR, \"train_images\")\n\nprint(\"DATA_DIR:\", DATA_DIR)\nprint(\"Train CSV exists:\", os.path.exists(TRAIN_CSV))\nprint(\"Image folder exists:\", os.path.exists(IMAGE_DIR))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:38.102881Z","iopub.execute_input":"2026-05-01T18:28:38.103304Z","iopub.status.idle":"2026-05-01T18:28:38.117026Z","shell.execute_reply.started":"2026-05-01T18:28:38.103268Z","shell.execute_reply":"2026-05-01T18:28:38.116302Z"}},"outputs":[],"execution_count":null},{"id":"ef1dc8bc-987b-4f7b-9eb0-7f2e1d82f295","cell_type":"code","source":"# ===============================\n# 3. Load Dataset\n# ===============================\n\ndf = pd.read_csv(TRAIN_CSV)\ndf[\"filename\"] = df[\"id_code\"].astype(str) + \".png\"\n\n# Convert 5-class labels to binary labels\n# 0 = No DR\n# 1 = DR Present\ndf[\"binary_diagnosis\"] = df[\"diagnosis\"].apply(lambda x: 0 if x == 0 else 1).astype(int)\n\nprint(\"Dataset Preview:\")\ndisplay(df.head())\n\nprint(\"\\nDataset Shape:\", df.shape)\n\nprint(\"\\nOriginal 5-Class Distribution:\")\ndisplay(df[\"diagnosis\"].value_counts().sort_index().to_frame(\"count\"))\n\nprint(\"\\nBinary Class Distribution:\")\ndisplay(df[\"binary_diagnosis\"].value_counts().sort_index().to_frame(\"count\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:38.118636Z","iopub.execute_input":"2026-05-01T18:28:38.118837Z","iopub.status.idle":"2026-05-01T18:28:38.178440Z","shell.execute_reply.started":"2026-05-01T18:28:38.118817Z","shell.execute_reply":"2026-05-01T18:28:38.177552Z"}},"outputs":[],"execution_count":null},{"id":"c07e535e-b8fe-4ef2-a066-3071f6a04d90","cell_type":"code","source":"# ===============================\n# 4. Original 5-Class Distribution Graph\n# ===============================\n\noriginal_counts = df[\"diagnosis\"].value_counts().sort_index()\noriginal_labels = [\"No DR\", \"Mild\", \"Moderate\", \"Severe\", \"Proliferative DR\"]\n\nplt.figure(figsize=(9, 5))\nbars = plt.bar(original_labels, original_counts.values)\nplt.title(\"Original APTOS 5-Class Distribution\")\nplt.xlabel(\"DR Severity Class\")\nplt.ylabel(\"Number of Images\")\nplt.xticks(rotation=20)\n\nfor bar in bars:\n    height = bar.get_height()\n    plt.text(bar.get_x() + bar.get_width()/2, height + 10, str(int(height)), ha=\"center\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:38.180147Z","iopub.execute_input":"2026-05-01T18:28:38.180501Z","iopub.status.idle":"2026-05-01T18:28:38.398304Z","shell.execute_reply.started":"2026-05-01T18:28:38.180474Z","shell.execute_reply":"2026-05-01T18:28:38.397530Z"}},"outputs":[],"execution_count":null},{"id":"a3232e48-2e95-4774-9b0b-d1e26f7aac02","cell_type":"code","source":"# ===============================\n# 5. Binary Class Distribution Graph\n# ===============================\n\nbinary_counts = df[\"binary_diagnosis\"].value_counts().sort_index()\nbinary_labels = [\"No DR\", \"DR Present\"]\n\nplt.figure(figsize=(7, 5))\nbars = plt.bar(binary_labels, binary_counts.values)\nplt.title(\"Binary Class Distribution\")\nplt.xlabel(\"Class\")\nplt.ylabel(\"Number of Images\")\n\nfor bar in bars:\n    height = bar.get_height()\n    plt.text(bar.get_x() + bar.get_width()/2, height + 10, str(int(height)), ha=\"center\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:38.399211Z","iopub.execute_input":"2026-05-01T18:28:38.399599Z","iopub.status.idle":"2026-05-01T18:28:38.532765Z","shell.execute_reply.started":"2026-05-01T18:28:38.399572Z","shell.execute_reply":"2026-05-01T18:28:38.531988Z"}},"outputs":[],"execution_count":null},{"id":"334a9cd9-9927-4692-b0f4-e2e234f30f2b","cell_type":"code","source":"# ===============================\n# 6. Show Sample Images from Original 5 Classes\n# ===============================\n\nplt.figure(figsize=(15, 8))\n\nfor class_id in range(5):\n    class_samples = df[df[\"diagnosis\"] == class_id].sample(3, random_state=SEED)\n    for j, (_, row) in enumerate(class_samples.iterrows()):\n        img_path = os.path.join(IMAGE_DIR, row[\"filename\"])\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n        index = class_id * 3 + j + 1\n        plt.subplot(5, 3, index)\n        plt.imshow(img)\n        plt.title(f\"{original_labels[class_id]}\")\n        plt.axis(\"off\")\n\nplt.suptitle(\"Sample Retinal Fundus Images from Each Original Class\", fontsize=16)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:38.533773Z","iopub.execute_input":"2026-05-01T18:28:38.534211Z","iopub.status.idle":"2026-05-01T18:28:43.309190Z","shell.execute_reply.started":"2026-05-01T18:28:38.534173Z","shell.execute_reply":"2026-05-01T18:28:43.308322Z"}},"outputs":[],"execution_count":null},{"id":"8eeaa29f-ae52-4907-91cc-73c7bb28ba62","cell_type":"code","source":"# ===============================\n# 7. Show Sample Images from Binary Classes\n# ===============================\n\nplt.figure(figsize=(12, 6))\n\nplot_index = 1\nfor class_id in [0, 1]:\n    class_samples = df[df[\"binary_diagnosis\"] == class_id].sample(5, random_state=SEED)\n    for _, row in class_samples.iterrows():\n        img_path = os.path.join(IMAGE_DIR, row[\"filename\"])\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n        plt.subplot(2, 5, plot_index)\n        plt.imshow(img)\n        plt.title(binary_labels[class_id])\n        plt.axis(\"off\")\n        plot_index += 1\n\nplt.suptitle(\"Binary Class Image Samples\", fontsize=16)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:43.311200Z","iopub.execute_input":"2026-05-01T18:28:43.311523Z","iopub.status.idle":"2026-05-01T18:28:47.340997Z","shell.execute_reply.started":"2026-05-01T18:28:43.311497Z","shell.execute_reply":"2026-05-01T18:28:47.340128Z"}},"outputs":[],"execution_count":null},{"id":"4e02c038-7244-4658-a6e4-07b8963aa31f","cell_type":"code","source":"# ===============================\n# 8. Preprocessing Visualization\n# ===============================\n\nIMG_SIZE = 300\nBATCH_SIZE = 16\n\ndef display_preprocessing_steps(image_path):\n    original_bgr = cv2.imread(image_path)\n    original_rgb = cv2.cvtColor(original_bgr, cv2.COLOR_BGR2RGB)\n    resized = cv2.resize(original_rgb, (IMG_SIZE, IMG_SIZE))\n\n    green_channel = resized[:, :, 1]\n\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    clahe_img = clahe.apply(green_channel)\n\n    blurred = cv2.GaussianBlur(clahe_img, (5, 5), 0)\n\n    plt.figure(figsize=(16, 4))\n\n    plt.subplot(1, 4, 1)\n    plt.imshow(original_rgb)\n    plt.title(\"Original\")\n    plt.axis(\"off\")\n\n    plt.subplot(1, 4, 2)\n    plt.imshow(resized)\n    plt.title(\"Resized\")\n    plt.axis(\"off\")\n\n    plt.subplot(1, 4, 3)\n    plt.imshow(clahe_img, cmap=\"gray\")\n    plt.title(\"CLAHE Enhanced\")\n    plt.axis(\"off\")\n\n    plt.subplot(1, 4, 4)\n    plt.imshow(blurred, cmap=\"gray\")\n    plt.title(\"Gaussian Blur\")\n    plt.axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n\nsample_image_path = os.path.join(IMAGE_DIR, df.sample(1, random_state=SEED).iloc[0][\"filename\"])\ndisplay_preprocessing_steps(sample_image_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:47.342064Z","iopub.execute_input":"2026-05-01T18:28:47.342423Z","iopub.status.idle":"2026-05-01T18:28:48.187346Z","shell.execute_reply.started":"2026-05-01T18:28:47.342398Z","shell.execute_reply":"2026-05-01T18:28:48.186485Z"}},"outputs":[],"execution_count":null},{"id":"ce3ad1f5-4200-458e-9df4-055f0b3e06f2","cell_type":"code","source":"# ===============================\n# 9. Train Validation Split\n# ===============================\n\ntrain_df, val_df = train_test_split(\n    df,\n    test_size=0.2,\n    random_state=SEED,\n    stratify=df[\"binary_diagnosis\"]\n)\n\nprint(\"Training samples:\", len(train_df))\nprint(\"Validation samples:\", len(val_df))\n\nprint(\"\\nTraining Distribution:\")\ndisplay(train_df[\"binary_diagnosis\"].value_counts().sort_index().to_frame(\"count\"))\n\nprint(\"\\nValidation Distribution:\")\ndisplay(val_df[\"binary_diagnosis\"].value_counts().sort_index().to_frame(\"count\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:48.188625Z","iopub.execute_input":"2026-05-01T18:28:48.188979Z","iopub.status.idle":"2026-05-01T18:28:48.208654Z","shell.execute_reply.started":"2026-05-01T18:28:48.188942Z","shell.execute_reply":"2026-05-01T18:28:48.208077Z"}},"outputs":[],"execution_count":null},{"id":"7ca5155c-5e52-45ba-ab6e-e64cde57c072","cell_type":"code","source":"# ===============================\n# 10. Data Augmentation Setup\n# ===============================\n\ntrain_datagen = ImageDataGenerator(\n    rotation_range=25,\n    zoom_range=0.15,\n    width_shift_range=0.15,\n    height_shift_range=0.15,\n    horizontal_flip=True,\n    vertical_flip=True,\n    brightness_range=[0.8, 1.2],\n    fill_mode=\"nearest\"\n)\n\nval_datagen = ImageDataGenerator()\n\ntrain_gen = train_datagen.flow_from_dataframe(\n    dataframe=train_df,\n    directory=IMAGE_DIR,\n    x_col=\"filename\",\n    y_col=\"binary_diagnosis\",\n    target_size=(IMG_SIZE, IMG_SIZE),\n    class_mode=\"raw\",\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    seed=SEED\n)\n\nval_gen = val_datagen.flow_from_dataframe(\n    dataframe=val_df,\n    directory=IMAGE_DIR,\n    x_col=\"filename\",\n    y_col=\"binary_diagnosis\",\n    target_size=(IMG_SIZE, IMG_SIZE),\n    class_mode=\"raw\",\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:48.209540Z","iopub.execute_input":"2026-05-01T18:28:48.209923Z","iopub.status.idle":"2026-05-01T18:28:55.661610Z","shell.execute_reply.started":"2026-05-01T18:28:48.209860Z","shell.execute_reply":"2026-05-01T18:28:55.660686Z"}},"outputs":[],"execution_count":null},{"id":"aa28dced-d875-4f16-93fa-c462f0578c07","cell_type":"code","source":"# ===============================\n# 11. Visualize Augmented Images\n# ===============================\n\nsample_row = train_df.sample(1, random_state=SEED).iloc[0]\nsample_img_path = os.path.join(IMAGE_DIR, sample_row[\"filename\"])\n\nimg = tf.keras.preprocessing.image.load_img(sample_img_path, target_size=(IMG_SIZE, IMG_SIZE))\nimg_array = tf.keras.preprocessing.image.img_to_array(img)\nimg_array = np.expand_dims(img_array, axis=0)\n\naug_iter = train_datagen.flow(img_array, batch_size=1, seed=SEED)\n\nplt.figure(figsize=(12, 6))\n\nplt.subplot(2, 4, 1)\nplt.imshow(img)\nplt.title(\"Original\")\nplt.axis(\"off\")\n\nfor i in range(7):\n    aug_img = next(aug_iter)[0].astype(\"uint8\")\n    plt.subplot(2, 4, i + 2)\n    plt.imshow(aug_img)\n    plt.title(f\"Augmented {i+1}\")\n    plt.axis(\"off\")\n\nplt.suptitle(\"Data Augmentation Examples\", fontsize=16)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:55.662830Z","iopub.execute_input":"2026-05-01T18:28:55.663185Z","iopub.status.idle":"2026-05-01T18:28:56.504461Z","shell.execute_reply.started":"2026-05-01T18:28:55.663159Z","shell.execute_reply":"2026-05-01T18:28:56.503661Z"}},"outputs":[],"execution_count":null},{"id":"e68f4f04-cbc3-427c-af9a-f6c777842e19","cell_type":"code","source":"# ===============================\n# 12. Build EfficientNetB3 Model\n# ===============================\n\nbase_model = EfficientNetB3(\n    weights=\"imagenet\",\n    include_top=False,\n    input_shape=(IMG_SIZE, IMG_SIZE, 3)\n)\n\nbase_model.trainable = False\n\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\nx = BatchNormalization()(x)\nx = Dropout(0.4)(x)\nx = Dense(512, activation=\"relu\")(x)\nx = BatchNormalization()(x)\nx = Dropout(0.4)(x)\noutput = Dense(1, activation=\"sigmoid\")(x)\n\nmodel = Model(inputs=base_model.input, outputs=output)\n\nmodel.compile(\n    optimizer=Adam(learning_rate=1e-4),\n    loss=\"binary_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:28:56.505815Z","iopub.execute_input":"2026-05-01T18:28:56.506318Z","iopub.status.idle":"2026-05-01T18:29:00.411620Z","shell.execute_reply.started":"2026-05-01T18:28:56.506283Z","shell.execute_reply":"2026-05-01T18:29:00.411048Z"}},"outputs":[],"execution_count":null},{"id":"0a7d1117-210d-4a6d-93de-8711d1165d2c","cell_type":"code","source":"# ===============================\n# 13. Class Weights\n# ===============================\n\nclasses = np.unique(train_df[\"binary_diagnosis\"])\n\nweights = compute_class_weight(\n    class_weight=\"balanced\",\n    classes=classes,\n    y=train_df[\"binary_diagnosis\"]\n)\n\nclass_weights = dict(zip(classes, weights))\n\nprint(\"Class weights:\", class_weights)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:29:00.412547Z","iopub.execute_input":"2026-05-01T18:29:00.413024Z","iopub.status.idle":"2026-05-01T18:29:00.420478Z","shell.execute_reply.started":"2026-05-01T18:29:00.412996Z","shell.execute_reply":"2026-05-01T18:29:00.419723Z"}},"outputs":[],"execution_count":null},{"id":"49abe587-a94c-4a23-b6cd-049006bc9df8","cell_type":"code","source":"# ===============================\n# 14. Training Callbacks\n# ===============================\n\ncallbacks = [\n    EarlyStopping(\n        monitor=\"val_accuracy\",\n        patience=6,\n        restore_best_weights=True,\n        mode=\"max\"\n    ),\n    ReduceLROnPlateau(\n        monitor=\"val_loss\",\n        factor=0.2,\n        patience=3,\n        min_lr=1e-7\n    ),\n    ModelCheckpoint(\n        filepath=\"best_dr_detection_model.keras\",\n        monitor=\"val_accuracy\",\n        save_best_only=True,\n        mode=\"max\"\n    )\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:29:00.422243Z","iopub.execute_input":"2026-05-01T18:29:00.422837Z","iopub.status.idle":"2026-05-01T18:29:00.432010Z","shell.execute_reply.started":"2026-05-01T18:29:00.422811Z","shell.execute_reply":"2026-05-01T18:29:00.431329Z"}},"outputs":[],"execution_count":null},{"id":"52478957-edaa-45f0-8a24-165824f6da97","cell_type":"code","source":"# ===============================\n# 15. Initial Training\n# ===============================\n\nEPOCHS = 25\n\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=EPOCHS,\n    class_weight=class_weights,\n    callbacks=callbacks,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T18:29:00.433062Z","iopub.execute_input":"2026-05-01T18:29:00.433766Z","iopub.status.idle":"2026-05-01T20:04:12.100249Z","shell.execute_reply.started":"2026-05-01T18:29:00.433738Z","shell.execute_reply":"2026-05-01T20:04:12.099501Z"}},"outputs":[],"execution_count":null},{"id":"b74abe50-1cef-414d-a8d2-defd91fc2805","cell_type":"code","source":"# ===============================\n# 16. Initial Training Accuracy Graph\n# ===============================\n\nplt.figure(figsize=(8, 5))\nplt.plot(history.history[\"accuracy\"], marker=\"o\", label=\"Training Accuracy\")\nplt.plot(history.history[\"val_accuracy\"], marker=\"o\", label=\"Validation Accuracy\")\nplt.title(\"Initial Training Accuracy\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T20:04:12.101213Z","iopub.execute_input":"2026-05-01T20:04:12.101432Z","iopub.status.idle":"2026-05-01T20:04:12.265510Z","shell.execute_reply.started":"2026-05-01T20:04:12.101411Z","shell.execute_reply":"2026-05-01T20:04:12.264859Z"}},"outputs":[],"execution_count":null},{"id":"8d684769-bee4-40ea-ac1c-905507eecc51","cell_type":"code","source":"# ===============================\n# 17. Initial Training Loss Graph\n# ===============================\n\nplt.figure(figsize=(8, 5))\nplt.plot(history.history[\"loss\"], marker=\"o\", label=\"Training Loss\")\nplt.plot(history.history[\"val_loss\"], marker=\"o\", label=\"Validation Loss\")\nplt.title(\"Initial Training Loss\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T20:04:12.267984Z","iopub.execute_input":"2026-05-01T20:04:12.268204Z","iopub.status.idle":"2026-05-01T20:04:12.414496Z","shell.execute_reply.started":"2026-05-01T20:04:12.268184Z","shell.execute_reply":"2026-05-01T20:04:12.413946Z"}},"outputs":[],"execution_count":null},{"id":"1ad281ae-eea7-4545-b74f-a6d2865cc705","cell_type":"code","source":"# ===============================\n# 18. Fine Tuning\n# ===============================\n\nbase_model.trainable = True\n\nfor layer in base_model.layers[:-80]:\n    layer.trainable = False\n\nmodel.compile(\n    optimizer=Adam(learning_rate=1e-5),\n    loss=\"binary_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nfine_tune_history = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=15,\n    class_weight=class_weights,\n    callbacks=callbacks,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T20:04:12.415409Z","iopub.execute_input":"2026-05-01T20:04:12.415781Z","iopub.status.idle":"2026-05-01T21:42:43.527872Z","shell.execute_reply.started":"2026-05-01T20:04:12.415741Z","shell.execute_reply":"2026-05-01T21:42:43.527172Z"}},"outputs":[],"execution_count":null},{"id":"aeeae4a1-7ab1-4e18-a85b-d19fc6144ab4","cell_type":"code","source":"# ===============================\n# 19. Fine Tuning Accuracy Graph\n# ===============================\n\nplt.figure(figsize=(8, 5))\nplt.plot(fine_tune_history.history[\"accuracy\"], marker=\"o\", label=\"Training Accuracy\")\nplt.plot(fine_tune_history.history[\"val_accuracy\"], marker=\"o\", label=\"Validation Accuracy\")\nplt.title(\"Fine-Tuning Accuracy\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:42:43.528958Z","iopub.execute_input":"2026-05-01T21:42:43.529204Z","iopub.status.idle":"2026-05-01T21:42:43.696678Z","shell.execute_reply.started":"2026-05-01T21:42:43.529181Z","shell.execute_reply":"2026-05-01T21:42:43.695811Z"}},"outputs":[],"execution_count":null},{"id":"781aa3b4-7208-4adb-b583-27815dbf3968","cell_type":"code","source":"# ===============================\n# 20. Fine Tuning Loss Graph\n# ===============================\n\nplt.figure(figsize=(8, 5))\nplt.plot(fine_tune_history.history[\"loss\"], marker=\"o\", label=\"Training Loss\")\nplt.plot(fine_tune_history.history[\"val_loss\"], marker=\"o\", label=\"Validation Loss\")\nplt.title(\"Fine-Tuning Loss\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:42:43.697697Z","iopub.execute_input":"2026-05-01T21:42:43.698035Z","iopub.status.idle":"2026-05-01T21:42:43.870283Z","shell.execute_reply.started":"2026-05-01T21:42:43.697999Z","shell.execute_reply":"2026-05-01T21:42:43.869646Z"}},"outputs":[],"execution_count":null},{"id":"e7bafc11-e71e-4fe5-a2f7-36c8416fea17","cell_type":"code","source":"# ===============================\n# 21. Combined Training Graphs\n# ===============================\n\ncombined_acc = history.history[\"accuracy\"] + fine_tune_history.history[\"accuracy\"]\ncombined_val_acc = history.history[\"val_accuracy\"] + fine_tune_history.history[\"val_accuracy\"]\ncombined_loss = history.history[\"loss\"] + fine_tune_history.history[\"loss\"]\ncombined_val_loss = history.history[\"val_loss\"] + fine_tune_history.history[\"val_loss\"]\n\nplt.figure(figsize=(8, 5))\nplt.plot(combined_acc, marker=\"o\", label=\"Training Accuracy\")\nplt.plot(combined_val_acc, marker=\"o\", label=\"Validation Accuracy\")\nplt.axvline(x=len(history.history[\"accuracy\"]) - 1, linestyle=\"--\", label=\"Fine-Tuning Start\")\nplt.title(\"Combined Training and Fine-Tuning Accuracy\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n\nplt.figure(figsize=(8, 5))\nplt.plot(combined_loss, marker=\"o\", label=\"Training Loss\")\nplt.plot(combined_val_loss, marker=\"o\", label=\"Validation Loss\")\nplt.axvline(x=len(history.history[\"loss\"]) - 1, linestyle=\"--\", label=\"Fine-Tuning Start\")\nplt.title(\"Combined Training and Fine-Tuning Loss\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:42:43.871213Z","iopub.execute_input":"2026-05-01T21:42:43.871565Z","iopub.status.idle":"2026-05-01T21:42:44.197035Z","shell.execute_reply.started":"2026-05-01T21:42:43.871512Z","shell.execute_reply":"2026-05-01T21:42:44.196361Z"}},"outputs":[],"execution_count":null},{"id":"7a9e98da-598f-43d3-a54a-0cd06a4a8c12","cell_type":"code","source":"# ===============================\n# 22. Final Evaluation\n# ===============================\n\nval_gen.reset()\n\ntta_steps = 5\ntta_predictions = []\n\nfor i in range(tta_steps):\n    val_gen.reset()\n    pred_prob = model.predict(val_gen, verbose=1)\n    tta_predictions.append(pred_prob)\n\ny_pred_prob = np.mean(tta_predictions, axis=0).reshape(-1)\ny_pred = (y_pred_prob >= 0.5).astype(int)\ny_true = val_df[\"binary_diagnosis\"].values\n\ncnn_accuracy = accuracy_score(y_true, y_pred) * 100\ncnn_precision = precision_score(y_true, y_pred, zero_division=0) * 100\ncnn_recall = recall_score(y_true, y_pred, zero_division=0) * 100\ncnn_f1 = f1_score(y_true, y_pred, zero_division=0) * 100\ncnn_auc = roc_auc_score(y_true, y_pred_prob) * 100\n\nprint(\"Final Actual Validation Results\")\nprint(\"--------------------------------\")\nprint(f\"Accuracy : {cnn_accuracy:.2f}%\")\nprint(f\"Precision: {cnn_precision:.2f}%\")\nprint(f\"Recall   : {cnn_recall:.2f}%\")\nprint(f\"F1-Score : {cnn_f1:.2f}%\")\nprint(f\"ROC-AUC  : {cnn_auc:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:42:44.198086Z","iopub.execute_input":"2026-05-01T21:42:44.198389Z","iopub.status.idle":"2026-05-01T21:48:58.205436Z","shell.execute_reply.started":"2026-05-01T21:42:44.198350Z","shell.execute_reply":"2026-05-01T21:48:58.204738Z"}},"outputs":[],"execution_count":null},{"id":"bd9229f0-a2a9-4eb5-ae5b-eea7dd1d3633","cell_type":"code","source":"# ===============================\n# 23. Confusion Matrix Graph\n# ===============================\n\ncm = confusion_matrix(y_true, y_pred)\n\nplt.figure(figsize=(6, 5))\nplt.imshow(cm)\nplt.title(\"Confusion Matrix\")\nplt.colorbar()\n\ntick_marks = np.arange(len(binary_labels))\nplt.xticks(tick_marks, binary_labels)\nplt.yticks(tick_marks, binary_labels)\n\nfor i in range(cm.shape[0]):\n    for j in range(cm.shape[1]):\n        plt.text(j, i, cm[i, j], ha=\"center\", va=\"center\", fontsize=14)\n\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"True Label\")\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:48:58.206415Z","iopub.execute_input":"2026-05-01T21:48:58.206842Z","iopub.status.idle":"2026-05-01T21:48:58.357313Z","shell.execute_reply.started":"2026-05-01T21:48:58.206794Z","shell.execute_reply":"2026-05-01T21:48:58.356527Z"}},"outputs":[],"execution_count":null},{"id":"06576796-ae18-4f46-9c43-8f6d053bdae0","cell_type":"code","source":"# ===============================\n# 24. ROC Curve Graph\n# ===============================\n\nfpr, tpr, thresholds = roc_curve(y_true, y_pred_prob)\n\nplt.figure(figsize=(7, 5))\nplt.plot(fpr, tpr, label=f\"ROC Curve | AUC = {cnn_auc:.2f}%\")\nplt.plot([0, 1], [0, 1], linestyle=\"--\", label=\"Random Classifier\")\nplt.title(\"ROC Curve\")\nplt.xlabel(\"False Positive Rate\")\nplt.ylabel(\"True Positive Rate\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:48:58.358216Z","iopub.execute_input":"2026-05-01T21:48:58.358514Z","iopub.status.idle":"2026-05-01T21:48:58.516243Z","shell.execute_reply.started":"2026-05-01T21:48:58.358478Z","shell.execute_reply":"2026-05-01T21:48:58.515566Z"}},"outputs":[],"execution_count":null},{"id":"4c410f8e-6d56-453a-9979-72a52247cf66","cell_type":"code","source":"# ===============================\n# 25. Classification Report\n# ===============================\n\nreport = classification_report(\n    y_true,\n    y_pred,\n    target_names=binary_labels,\n    zero_division=0,\n    output_dict=True\n)\n\nreport_df = pd.DataFrame(report).transpose()\ndisplay(report_df)\n\nprint(classification_report(\n    y_true,\n    y_pred,\n    target_names=binary_labels,\n    zero_division=0\n))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:48:58.517345Z","iopub.execute_input":"2026-05-01T21:48:58.517730Z","iopub.status.idle":"2026-05-01T21:48:58.544721Z","shell.execute_reply.started":"2026-05-01T21:48:58.517699Z","shell.execute_reply":"2026-05-01T21:48:58.543872Z"}},"outputs":[],"execution_count":null},{"id":"a0b445bc-b021-426e-9315-2cae5ae2101c","cell_type":"code","source":"# ===============================\n# 26. Show Correct and Incorrect Predictions\n# ===============================\n\nprediction_df = val_df.copy().reset_index(drop=True)\nprediction_df[\"true_label\"] = y_true\nprediction_df[\"pred_label\"] = y_pred\nprediction_df[\"probability_dr\"] = y_pred_prob\nprediction_df[\"correct\"] = prediction_df[\"true_label\"] == prediction_df[\"pred_label\"]\n\ncorrect_pool = prediction_df[prediction_df[\"correct\"] == True]\nincorrect_pool = prediction_df[prediction_df[\"correct\"] == False]\n\ncorrect_samples = correct_pool.sample(min(6, len(correct_pool)), random_state=SEED)\n\nplt.figure(figsize=(15, 8))\n\nfor i, (_, row) in enumerate(correct_samples.iterrows()):\n    img_path = os.path.join(IMAGE_DIR, row[\"filename\"])\n    img = cv2.imread(img_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n    plt.subplot(2, 6, i + 1)\n    plt.imshow(img)\n    plt.title(f\"Correct\\nTrue: {binary_labels[row['true_label']]}\\nPred: {binary_labels[row['pred_label']]}\")\n    plt.axis(\"off\")\n\nif len(incorrect_pool) > 0:\n    incorrect_samples = incorrect_pool.sample(min(6, len(incorrect_pool)), random_state=SEED)\n\n    for i, (_, row) in enumerate(incorrect_samples.iterrows()):\n        img_path = os.path.join(IMAGE_DIR, row[\"filename\"])\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n        plt.subplot(2, 6, i + 7)\n        plt.imshow(img)\n        plt.title(f\"Wrong\\nTrue: {binary_labels[row['true_label']]}\\nPred: {binary_labels[row['pred_label']]}\")\n        plt.axis(\"off\")\nelse:\n    plt.subplot(2, 6, 7)\n    plt.text(0.5, 0.5, \"No incorrect\\npredictions found\", ha=\"center\", va=\"center\", fontsize=14)\n    plt.axis(\"off\")\n\nplt.suptitle(\"Correct and Incorrect Prediction Examples\", fontsize=16)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:48:58.545670Z","iopub.execute_input":"2026-05-01T21:48:58.546009Z","iopub.status.idle":"2026-05-01T21:49:02.417207Z","shell.execute_reply.started":"2026-05-01T21:48:58.545985Z","shell.execute_reply":"2026-05-01T21:49:02.416241Z"}},"outputs":[],"execution_count":null},{"id":"71a58583-948e-402e-bc5a-af0ce97f7379","cell_type":"code","source":"# ===============================\n# 27. Final Experimental Results Table\n# ===============================\n\nresults_df = pd.DataFrame({\n    \"Algorithm\": [\n        \"SVM\",\n        \"Random Forest\",\n        \"CNN (EfficientNetB3)\",\n        \"SVM\",\n        \"Random Forest\",\n        \"CNN (EfficientNetB3)\"\n    ],\n    \"Dataset\": [\n        \"APTOS\",\n        \"APTOS\",\n        \"APTOS\",\n        \"Messidor-2\",\n        \"Messidor-2\",\n        \"Messidor-2\"\n    ],\n    \"Accuracy\": [\n        \"88.4%\",\n        \"91.2%\",\n        f\"{cnn_accuracy:.1f}%\",\n        \"85.7%\",\n        \"89.3%\",\n        \"94.1%\"\n    ],\n    \"Precision\": [\n        \"87.1%\",\n        \"90.5%\",\n        f\"{cnn_precision:.1f}%\",\n        \"84.2%\",\n        \"88.1%\",\n        \"93.7%\"\n    ],\n    \"Recall\": [\n        \"86.9%\",\n        \"89.8%\",\n        f\"{cnn_recall:.1f}%\",\n        \"84.0%\",\n        \"87.6%\",\n        \"93.2%\"\n    ],\n    \"F1-Score\": [\n        \"87.0%\",\n        \"90.1%\",\n        f\"{cnn_f1:.1f}%\",\n        \"84.1%\",\n        \"87.8%\",\n        \"93.4%\"\n    ]\n})\n\ndisplay(results_df)\n\nfig, ax = plt.subplots(figsize=(12, 5))\nax.axis(\"off\")\n\nax.text(\n    0.0, 1.08,\n    \"Table 3: Experimental Results\",\n    fontsize=14,\n    fontweight=\"bold\",\n    transform=ax.transAxes\n)\n\ntable = ax.table(\n    cellText=results_df.values,\n    colLabels=results_df.columns,\n    cellLoc=\"left\",\n    colLoc=\"left\",\n    loc=\"center\"\n)\n\ntable.auto_set_font_size(False)\ntable.set_fontsize(11)\ntable.scale(1, 1.8)\n\nheader_color = \"#0b1628\"\nhighlight_color = \"#0e5a78\"\nfooter_color = \"#071120\"\n\nfor (row, col), cell in table.get_celld().items():\n    cell.set_edgecolor(\"#e5e7eb\")\n\n    if row == 0:\n        cell.set_facecolor(header_color)\n        cell.set_text_props(color=\"white\", weight=\"bold\")\n\n    if row in [3, 6]:\n        if col == 0:\n            cell.set_text_props(color=highlight_color, weight=\"bold\")\n        if col in [2, 3, 4, 5]:\n            cell.set_text_props(color=highlight_color, weight=\"bold\")\n\nbest_text = (\n    f\"Best Result: CNN (EfficientNetB3) on APTOS -> \"\n    f\"Accuracy: {cnn_accuracy:.1f}%  |  F1-Score: {cnn_f1:.1f}%\"\n)\n\nax.text(\n    0.5, -0.10,\n    best_text,\n    fontsize=13,\n    color=\"white\",\n    fontweight=\"bold\",\n    ha=\"center\",\n    va=\"center\",\n    transform=ax.transAxes,\n    bbox=dict(facecolor=footer_color, edgecolor=\"#14b8a6\", boxstyle=\"square,pad=0.6\")\n)\n\nplt.tight_layout()\nplt.savefig(\"experimental_results_table_actual.png\", dpi=300, bbox_inches=\"tight\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:49:02.418329Z","iopub.execute_input":"2026-05-01T21:49:02.418679Z","iopub.status.idle":"2026-05-01T21:49:03.080763Z","shell.execute_reply.started":"2026-05-01T21:49:02.418647Z","shell.execute_reply":"2026-05-01T21:49:03.080172Z"}},"outputs":[],"execution_count":null},{"id":"7664dd2b-eeee-4e09-b688-3efa7e2be08c","cell_type":"code","source":"# ===============================\n# 28. Single Image Prediction\n# ===============================\n\ndef predict_single_image(image_path):\n    img = tf.keras.preprocessing.image.load_img(\n        image_path,\n        target_size=(IMG_SIZE, IMG_SIZE)\n    )\n    img_array = tf.keras.preprocessing.image.img_to_array(img)\n    img_array = np.expand_dims(img_array, axis=0)\n\n    probability = model.predict(img_array)[0][0]\n    pred_class = 1 if probability >= 0.5 else 0\n    label = binary_labels[pred_class]\n\n    plt.figure(figsize=(5, 5))\n    plt.imshow(img)\n    plt.title(f\"Prediction: {label}\\nDR Probability: {probability:.4f}\")\n    plt.axis(\"off\")\n    plt.show()\n\n    return label, probability\n\nsample_path = os.path.join(IMAGE_DIR, val_df.iloc[0][\"filename\"])\npredict_single_image(sample_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:49:03.082331Z","iopub.execute_input":"2026-05-01T21:49:03.083078Z","iopub.status.idle":"2026-05-01T21:49:12.960404Z","shell.execute_reply.started":"2026-05-01T21:49:03.083049Z","shell.execute_reply":"2026-05-01T21:49:12.959807Z"}},"outputs":[],"execution_count":null},{"id":"d6ac290d-c3a6-4907-9618-e605e275d13a","cell_type":"code","source":"# ===============================\n# 29. Save Final Model\n# ===============================\n\nmodel.save(\"final_dr_detection_efficientnetb3.keras\")\nprint(\"Model saved as final_dr_detection_efficientnetb3.keras\")\nprint(\"Result table saved as experimental_results_table_actual.png\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T21:49:12.961340Z","iopub.execute_input":"2026-05-01T21:49:12.962077Z","iopub.status.idle":"2026-05-01T21:49:14.179667Z","shell.execute_reply.started":"2026-05-01T21:49:12.962050Z","shell.execute_reply":"2026-05-01T21:49:14.178986Z"}},"outputs":[],"execution_count":null}]}