{"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":14774,"databundleVersionId":875431,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport cv2\nimport os\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping\nfrom tqdm import tqdm\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:27:05.145908Z","iopub.execute_input":"2025-12-03T17:27:05.146421Z","iopub.status.idle":"2025-12-03T17:27:21.63624Z","shell.execute_reply.started":"2025-12-03T17:27:05.146397Z","shell.execute_reply":"2025-12-03T17:27:21.635649Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Num GPUs Available:\", len(tf.config.experimental.list_physical_devices('GPU')))\ntf.config.experimental.set_memory_growth(tf.config.experimental.list_physical_devices('GPU')[0], True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:27:22.207195Z","iopub.execute_input":"2025-12-03T17:27:22.207455Z","iopub.status.idle":"2025-12-03T17:27:22.411464Z","shell.execute_reply.started":"2025-12-03T17:27:22.207429Z","shell.execute_reply":"2025-12-03T17:27:22.41077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"img_size = 224\nbatch_size = 32\ncsv_path = \"/kaggle/input/aptos2019-blindness-detection/train.csv\"\nimg_dir = \"/kaggle/input/aptos2019-blindness-detection/train_images\"\nsave_dir = \"/kaggle/working/preprocessed_images\" #to save preprocessed images","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:27:25.284265Z","iopub.execute_input":"2025-12-03T17:27:25.284916Z","iopub.status.idle":"2025-12-03T17:27:25.288592Z","shell.execute_reply.started":"2025-12-03T17:27:25.28488Z","shell.execute_reply":"2025-12-03T17:27:25.287743Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"os.makedirs(save_dir, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:27:28.67936Z","iopub.execute_input":"2025-12-03T17:27:28.679667Z","iopub.status.idle":"2025-12-03T17:27:28.683872Z","shell.execute_reply.started":"2025-12-03T17:27:28.679639Z","shell.execute_reply":"2025-12-03T17:27:28.683148Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(csv_path)\ndf[\"id_code\"] = df[\"id_code\"].apply(lambda x: os.path.join(img_dir, x + \".png\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:27:34.410701Z","iopub.execute_input":"2025-12-03T17:27:34.411391Z","iopub.status.idle":"2025-12-03T17:27:34.440555Z","shell.execute_reply.started":"2025-12-03T17:27:34.411362Z","shell.execute_reply":"2025-12-03T17:27:34.439934Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def apply_clahe_and_save(image_path, save_dir):\n    # Read and resize the image\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    if image is None:\n        raise ValueError(f\"Unable to read image at path: {image_path}\")\n    image = cv2.resize(image, (img_size, img_size))\n    \n    # Convert to LAB color space\n    lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)\n    l, a, b = cv2.split(lab)\n    \n    # Apply CLAHE to the L channel\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    cl = clahe.apply(l)\n    \n    # Merge the LAB channels and convert back to RGB\n    merged_lab = cv2.merge((cl, a, b))\n    final_image = cv2.cvtColor(merged_lab, cv2.COLOR_LAB2RGB)\n    \n    # Save the preprocessed image\n    save_path = os.path.join(save_dir, os.path.basename(image_path))\n    cv2.imwrite(save_path, cv2.cvtColor(final_image, cv2.COLOR_RGB2BGR))\n    \n    return final_image / 255.0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:27:39.891011Z","iopub.execute_input":"2025-12-03T17:27:39.891699Z","iopub.status.idle":"2025-12-03T17:27:39.89685Z","shell.execute_reply.started":"2025-12-03T17:27:39.891676Z","shell.execute_reply":"2025-12-03T17:27:39.896076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Preprocessing images and saving to disk...\")\nfor image_path in tqdm(df[\"id_code\"], desc=\"Processing Images\"):  # Add tqdm here\n    apply_clahe_and_save(image_path, save_dir)\nprint(\"Preprocessing complete!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:27:44.896136Z","iopub.execute_input":"2025-12-03T17:27:44.896677Z","iopub.status.idle":"2025-12-03T17:34:14.895161Z","shell.execute_reply.started":"2025-12-03T17:27:44.896656Z","shell.execute_reply":"2025-12-03T17:34:14.894396Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_preprocessed_image(image_path, label):\n    image = tf.io.read_file(image_path)\n    \n    image = tf.image.decode_png(image, channels=3)\n    image = tf.image.resize(image, [img_size, img_size])\n    image = tf.cast(image, tf.float32) / 255.0\n    \n    return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:34:37.565173Z","iopub.execute_input":"2025-12-03T17:34:37.565843Z","iopub.status.idle":"2025-12-03T17:34:37.56975Z","shell.execute_reply.started":"2025-12-03T17:34:37.565821Z","shell.execute_reply":"2025-12-03T17:34:37.56896Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_paths = [os.path.join(save_dir, os.path.basename(path)) for path in df[\"id_code\"]]\nlabels = df[\"diagnosis\"].values\ndataset = tf.data.Dataset.from_tensor_slices((image_paths, labels))\ndataset = dataset.shuffle(len(df)).map(load_preprocessed_image, num_parallel_calls=tf.data.AUTOTUNE)\ndataset = dataset.batch(batch_size).prefetch(tf.data.AUTOTUNE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:34:56.192558Z","iopub.execute_input":"2025-12-03T17:34:56.192936Z","iopub.status.idle":"2025-12-03T17:34:56.409741Z","shell.execute_reply.started":"2025-12-03T17:34:56.192915Z","shell.execute_reply":"2025-12-03T17:34:56.408903Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def visualize_clahe_effect(df_sample):\n    fig, axes = plt.subplots(len(df_sample), 2, figsize=(10, 5 * len(df_sample)))\n    \n    for i, row in enumerate(df_sample.itertuples()):\n        img_path = row.id_code\n        original = cv2.imread(img_path, cv2.IMREAD_COLOR)\n        original = cv2.resize(original, (img_size, img_size))\n        original = cv2.cvtColor(original, cv2.COLOR_BGR2RGB)\n        \n        processed_path = os.path.join(save_dir, os.path.basename(img_path))\n        processed = cv2.imread(processed_path, cv2.IMREAD_COLOR)\n        processed = cv2.cvtColor(processed, cv2.COLOR_BGR2RGB)\n        \n        axes[i, 0].imshow(original)\n        axes[i, 0].set_title(f\"Original - {row.diagnosis}\")\n        axes[i, 0].axis(\"off\")\n\n        axes[i, 1].imshow(processed)\n        axes[i, 1].set_title(f\"CLAHE Processed - {row.diagnosis}\")\n        axes[i, 1].axis(\"off\")\n    \n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:35:00.613457Z","iopub.execute_input":"2025-12-03T17:35:00.613771Z","iopub.status.idle":"2025-12-03T17:35:00.61945Z","shell.execute_reply.started":"2025-12-03T17:35:00.613747Z","shell.execute_reply":"2025-12-03T17:35:00.618833Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_sample = df.sample(5)\nvisualize_clahe_effect(df_sample)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:35:07.991857Z","iopub.execute_input":"2025-12-03T17:35:07.992128Z","iopub.status.idle":"2025-12-03T17:35:09.740255Z","shell.execute_reply.started":"2025-12-03T17:35:07.992107Z","shell.execute_reply":"2025-12-03T17:35:09.739206Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def dataset_split(ds, train=0.7, val=0.15, test=0.15):\n    ds_size = len(ds)\n    train_size = int(ds_size * train)\n    val_size = int(ds_size * val)\n    \n    train_ds = ds.take(train_size)\n    val_ds = ds.skip(train_size).take(val_size)\n    test_ds = ds.skip(train_size + val_size)\n    \n    return train_ds, val_ds, test_ds","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:35:16.907434Z","iopub.execute_input":"2025-12-03T17:35:16.907723Z","iopub.status.idle":"2025-12-03T17:35:16.912207Z","shell.execute_reply.started":"2025-12-03T17:35:16.907701Z","shell.execute_reply":"2025-12-03T17:35:16.911588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds, val_ds, test_ds = dataset_split(dataset)\ntrain_ds = train_ds.cache().shuffle(1000).prefetch(tf.data.AUTOTUNE)\nval_ds = val_ds.cache().prefetch(tf.data.AUTOTUNE)\ntest_ds = test_ds.cache().prefetch(tf.data.AUTOTUNE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:35:21.6481Z","iopub.execute_input":"2025-12-03T17:35:21.648371Z","iopub.status.idle":"2025-12-03T17:35:21.671162Z","shell.execute_reply.started":"2025-12-03T17:35:21.64835Z","shell.execute_reply":"2025-12-03T17:35:21.670556Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.applications import DenseNet201,ResNet50\nfrom sklearn.utils.class_weight import compute_class_weight","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:35:24.929138Z","iopub.execute_input":"2025-12-03T17:35:24.92963Z","iopub.status.idle":"2025-12-03T17:35:24.93465Z","shell.execute_reply.started":"2025-12-03T17:35:24.929595Z","shell.execute_reply":"2025-12-03T17:35:24.934101Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base1 = DenseNet201(weights=\"imagenet\", include_top=False, input_shape=(img_size, img_size, 3))\nbase2 = ResNet50(weights=\"imagenet\", include_top=False, input_shape=(img_size, img_size, 3))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:35:29.720707Z","iopub.execute_input":"2025-12-03T17:35:29.721297Z","iopub.status.idle":"2025-12-03T17:35:43.883338Z","shell.execute_reply.started":"2025-12-03T17:35:29.721276Z","shell.execute_reply":"2025-12-03T17:35:43.882556Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base1.trainable = True\nbase2.trainable = True","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:02.697255Z","iopub.execute_input":"2025-12-03T17:37:02.697798Z","iopub.status.idle":"2025-12-03T17:37:02.7011Z","shell.execute_reply.started":"2025-12-03T17:37:02.697767Z","shell.execute_reply":"2025-12-03T17:37:02.700355Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for layer in base1.layers[:-10]:  # Unfreeze the last 10 layers of EfficientNetB0\n    layer.trainable = False\nfor layer in base2.layers[:-10]:  # Unfreeze the last 10 layers of ResNet50\n    layer.trainable = False","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:08.080511Z","iopub.execute_input":"2025-12-03T17:37:08.081093Z","iopub.status.idle":"2025-12-03T17:37:08.093696Z","shell.execute_reply.started":"2025-12-03T17:37:08.081069Z","shell.execute_reply":"2025-12-03T17:37:08.09274Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"inputs = tf.keras.Input(shape=(img_size, img_size, 3))\nx1 = layers.GlobalAveragePooling2D()(base1(inputs))\nx2 = layers.GlobalAveragePooling2D()(base2(inputs))\nmerged = layers.Concatenate()([x1, x2])\nx = layers.Dense(256, activation=\"relu\")(merged)\nx = layers.Dropout(0.5)(x)\noutputs = layers.Dense(5, activation=\"softmax\")(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:19.779154Z","iopub.execute_input":"2025-12-03T17:37:19.779675Z","iopub.status.idle":"2025-12-03T17:37:19.811584Z","shell.execute_reply.started":"2025-12-03T17:37:19.779647Z","shell.execute_reply":"2025-12-03T17:37:19.811047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_model_1 = Model(inputs, outputs)\nmultibranch_model_1.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:26.81551Z","iopub.execute_input":"2025-12-03T17:37:26.816034Z","iopub.status.idle":"2025-12-03T17:37:26.856312Z","shell.execute_reply.started":"2025-12-03T17:37:26.81601Z","shell.execute_reply":"2025-12-03T17:37:26.855789Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_model_1.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),\n    loss=tf.keras.losses.SparseCategoricalCrossentropy(),\n    metrics=[\"accuracy\"]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:31.751513Z","iopub.execute_input":"2025-12-03T17:37:31.752089Z","iopub.status.idle":"2025-12-03T17:37:31.767315Z","shell.execute_reply.started":"2025-12-03T17:37:31.752064Z","shell.execute_reply":"2025-12-03T17:37:31.766577Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_weights = compute_class_weight(\"balanced\", classes=np.unique(labels), y=labels)\nclass_weights = dict(enumerate(class_weights))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:36.397058Z","iopub.execute_input":"2025-12-03T17:37:36.397319Z","iopub.status.idle":"2025-12-03T17:37:36.402806Z","shell.execute_reply.started":"2025-12-03T17:37:36.397299Z","shell.execute_reply":"2025-12-03T17:37:36.40208Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def lr_scheduler(epoch, lr):\n    if epoch > 0 and epoch % 10 == 0:  # Reduce LR every 10 epochs\n        return lr * 0.1\n    return lr\n\nlr_callback = tf.keras.callbacks.LearningRateScheduler(lr_scheduler)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:40.505357Z","iopub.execute_input":"2025-12-03T17:37:40.506093Z","iopub.status.idle":"2025-12-03T17:37:40.510427Z","shell.execute_reply.started":"2025-12-03T17:37:40.506067Z","shell.execute_reply":"2025-12-03T17:37:40.509855Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"early_stopping = tf.keras.callbacks.EarlyStopping(\n    monitor=\"val_loss\",\n    patience=5,  # Stop after 5 epochs without improvement\n    restore_best_weights=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:44.693582Z","iopub.execute_input":"2025-12-03T17:37:44.693906Z","iopub.status.idle":"2025-12-03T17:37:44.697659Z","shell.execute_reply.started":"2025-12-03T17:37:44.693884Z","shell.execute_reply":"2025-12-03T17:37:44.697015Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_history = multibranch_model_1.fit(\n    train_ds,\n    epochs=50,\n    batch_size=batch_size,\n    verbose=1,\n    class_weight=class_weights,\n    validation_data=val_ds,\n    callbacks=[lr_callback,early_stopping]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:37:55.956151Z","iopub.execute_input":"2025-12-03T17:37:55.957001Z","iopub.status.idle":"2025-12-03T17:46:51.370494Z","shell.execute_reply.started":"2025-12-03T17:37:55.956967Z","shell.execute_reply":"2025-12-03T17:46:51.369667Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_model_1.save(\"multibranch_model_1.keras\")\nmultibranch_model_1.save(\"multibranch_model_1.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:46:55.758117Z","iopub.execute_input":"2025-12-03T17:46:55.758356Z","iopub.status.idle":"2025-12-03T17:46:59.935738Z","shell.execute_reply.started":"2025-12-03T17:46:55.758339Z","shell.execute_reply":"2025-12-03T17:46:59.934895Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_loss, test_acc = multibranch_model_1.evaluate(test_ds)\nprint(f\"Test Accuracy: {test_acc * 100:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:47:42.219297Z","iopub.execute_input":"2025-12-03T17:47:42.219572Z","iopub.status.idle":"2025-12-03T17:48:07.062363Z","shell.execute_reply.started":"2025-12-03T17:47:42.21955Z","shell.execute_reply":"2025-12-03T17:48:07.061657Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_2d_cnn(input_shape=(224, 224, 3), num_classes=5):\n    inputs = tf.keras.Input(shape=input_shape)\n    \n    # Convolutional layers\n    x = layers.Conv2D(32, (3, 3), activation=\"relu\", padding=\"same\")(inputs)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Conv2D(64, (3, 3), activation=\"relu\", padding=\"same\")(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Conv2D(128, (3, 3), activation=\"relu\", padding=\"same\")(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    \n    # Fully connected layers\n    x = layers.Flatten()(x)\n    x = layers.Dense(256, activation=\"relu\")(x)\n    x = layers.Dropout(0.5)(x)\n    \n    # Output layer\n    outputs = layers.Dense(num_classes, activation=\"softmax\")(x)\n    \n    # Build the model\n    cnn_model = Model(inputs, outputs)\n    return cnn_model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:51:09.196845Z","iopub.execute_input":"2025-12-03T17:51:09.197147Z","iopub.status.idle":"2025-12-03T17:51:09.203392Z","shell.execute_reply.started":"2025-12-03T17:51:09.197126Z","shell.execute_reply":"2025-12-03T17:51:09.202437Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnn_model = build_2d_cnn(input_shape=(img_size, img_size, 3), num_classes=5)\ncnn_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:51:15.259861Z","iopub.execute_input":"2025-12-03T17:51:15.261265Z","iopub.status.idle":"2025-12-03T17:51:15.32425Z","shell.execute_reply.started":"2025-12-03T17:51:15.261229Z","shell.execute_reply":"2025-12-03T17:51:15.32365Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnn_model.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),\n    loss=tf.keras.losses.SparseCategoricalCrossentropy(),\n    metrics=[\"accuracy\"]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:51:20.820654Z","iopub.execute_input":"2025-12-03T17:51:20.820938Z","iopub.status.idle":"2025-12-03T17:51:20.829513Z","shell.execute_reply.started":"2025-12-03T17:51:20.820919Z","shell.execute_reply":"2025-12-03T17:51:20.828906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnn_history = cnn_model.fit(\n    train_ds,\n    epochs=40,\n    batch_size=batch_size,\n    verbose=1,\n    class_weight=class_weights,\n    validation_data=val_ds,\n    callbacks=[lr_callback]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:51:26.094687Z","iopub.execute_input":"2025-12-03T17:51:26.094959Z","iopub.status.idle":"2025-12-03T17:53:02.302327Z","shell.execute_reply.started":"2025-12-03T17:51:26.094939Z","shell.execute_reply":"2025-12-03T17:53:02.301769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnn_model.save(\"cnn_model_1.keras\")\ncnn_model.save(\"cnn_model_1.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:53:11.08914Z","iopub.execute_input":"2025-12-03T17:53:11.089413Z","iopub.status.idle":"2025-12-03T17:53:12.954259Z","shell.execute_reply.started":"2025-12-03T17:53:11.089393Z","shell.execute_reply":"2025-12-03T17:53:12.953635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_loss, test_acc = cnn_model.evaluate(test_ds)\nprint(f\"Test Accuracy: {test_acc * 100:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:53:16.890867Z","iopub.execute_input":"2025-12-03T17:53:16.891417Z","iopub.status.idle":"2025-12-03T17:53:17.900813Z","shell.execute_reply.started":"2025-12-03T17:53:16.891393Z","shell.execute_reply":"2025-12-03T17:53:17.90023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# ✅ Convert only if still a History object\nif hasattr(multibranch_history, 'history'):\n    multibranch_history = multibranch_history.history\n\nif hasattr(cnn_history, 'history'):\n    cnn_history = cnn_history.history\n\n# ✅ Plot Accuracy and Loss Comparison\nfig, axes = plt.subplots(1, 2, figsize=(16, 6))\n\n# --- Accuracy ---\naxes[0].plot(multibranch_history['accuracy'], label='Multi-Branch CNN Training Accuracy')\naxes[0].plot(multibranch_history['val_accuracy'], label='Multi-Branch CNN Validation Accuracy')\naxes[0].plot(cnn_history['accuracy'], label='2D CNN Training Accuracy')\naxes[0].plot(cnn_history['val_accuracy'], label='2D CNN Validation Accuracy')\naxes[0].set_title('Accuracy Comparison')\naxes[0].set_xlabel('Epoch')\naxes[0].set_ylabel('Accuracy')\naxes[0].legend()\naxes[0].grid(True)\n\n# --- Loss ---\naxes[1].plot(multibranch_history['loss'], label='Multi-Branch CNN Training Loss')\naxes[1].plot(multibranch_history['val_loss'], label='Multi-Branch CNN Validation Loss')\naxes[1].plot(cnn_history['loss'], label='2D CNN Training Loss')\naxes[1].plot(cnn_history['val_loss'], label='2D CNN Validation Loss')\naxes[1].set_title('Loss Comparison')\naxes[1].set_xlabel('Epoch')\naxes[1].set_ylabel('Loss')\naxes[1].legend()\naxes[1].grid(True)\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:53:29.092809Z","iopub.execute_input":"2025-12-03T17:53:29.093366Z","iopub.status.idle":"2025-12-03T17:53:29.554754Z","shell.execute_reply.started":"2025-12-03T17:53:29.093343Z","shell.execute_reply":"2025-12-03T17:53:29.5541Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_predictions = multibranch_model_1.predict(test_ds)\ncnn_predictions = cnn_model.predict(test_ds)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:53:36.251875Z","iopub.execute_input":"2025-12-03T17:53:36.252395Z","iopub.status.idle":"2025-12-03T17:54:22.95569Z","shell.execute_reply.started":"2025-12-03T17:53:36.252374Z","shell.execute_reply":"2025-12-03T17:54:22.954816Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_class_labels = np.argmax(multibranch_predictions, axis=1) \ncnn_class_labels = np.argmax(cnn_predictions, axis=1)\n\ntrue_labels = np.concatenate([y for x, y in test_ds], axis=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:54:29.345715Z","iopub.execute_input":"2025-12-03T17:54:29.346342Z","iopub.status.idle":"2025-12-03T17:54:29.35753Z","shell.execute_reply.started":"2025-12-03T17:54:29.346318Z","shell.execute_reply":"2025-12-03T17:54:29.35695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_cm = confusion_matrix(true_labels, multibranch_class_labels)\ncnn_cm = confusion_matrix(true_labels, cnn_class_labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:54:33.168189Z","iopub.execute_input":"2025-12-03T17:54:33.168454Z","iopub.status.idle":"2025-12-03T17:54:33.175217Z","shell.execute_reply.started":"2025-12-03T17:54:33.168435Z","shell.execute_reply":"2025-12-03T17:54:33.174554Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(16, 6))\n\nplt.subplot(1, 2, 1)\nsns.heatmap(multibranch_cm, annot=True, fmt='d', cmap='Blues', cbar=False)\nplt.title('Multi-Branch CNN Confusion Matrix')\nplt.xlabel('Predicted Labels')\nplt.ylabel('True Labels')\n\nplt.subplot(1, 2, 2)\nsns.heatmap(cnn_cm, annot=True, fmt='d', cmap='Greens', cbar=False)\nplt.title('2D CNN Confusion Matrix')\nplt.xlabel('Predicted Labels')\nplt.ylabel('True Labels')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:54:36.879142Z","iopub.execute_input":"2025-12-03T17:54:36.879773Z","iopub.status.idle":"2025-12-03T17:54:37.20429Z","shell.execute_reply.started":"2025-12-03T17:54:36.879748Z","shell.execute_reply":"2025-12-03T17:54:37.203633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_model=tf.keras.models.load_model(\"/kaggle/working/multibranch_model_1.h5\")\ncnn_model = tf.keras.models.load_model(\"/kaggle/working/cnn_model_1.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:54:41.834724Z","iopub.execute_input":"2025-12-03T17:54:41.83501Z","iopub.status.idle":"2025-12-03T17:54:45.597447Z","shell.execute_reply.started":"2025-12-03T17:54:41.834989Z","shell.execute_reply":"2025-12-03T17:54:45.596907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"weight_multibranch = 0.7\nweight_cnn = 0.3","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:54:52.05296Z","iopub.execute_input":"2025-12-03T17:54:52.053663Z","iopub.status.idle":"2025-12-03T17:54:52.057017Z","shell.execute_reply.started":"2025-12-03T17:54:52.053637Z","shell.execute_reply":"2025-12-03T17:54:52.056128Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ensemble_predictions = (weight_multibranch * multibranch_predictions) + (weight_cnn * cnn_predictions)\nensemble_class_labels = np.argmax(ensemble_predictions, axis=1)\ntrue_labels = np.concatenate([y for x, y in test_ds], axis=0)\nensemble_accuracy = np.mean(ensemble_class_labels == true_labels)\nprint(f\"Ensemble Accuracy: {ensemble_accuracy * 100:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:54:54.713743Z","iopub.execute_input":"2025-12-03T17:54:54.714533Z","iopub.status.idle":"2025-12-03T17:54:54.726661Z","shell.execute_reply.started":"2025-12-03T17:54:54.714504Z","shell.execute_reply":"2025-12-03T17:54:54.725971Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(12, 6))\n\nplt.subplot(1, 3, 1)\nplt.hist(multibranch_class_labels, bins=6, range=(0, 5), alpha=0.7, color='blue')\nplt.title('Multi-Branch CNN Predictions')\nplt.xlabel('Class')\nplt.ylabel('Frequency')\n\nplt.subplot(1, 3, 2)\nplt.hist(cnn_class_labels, bins=6, range=(0, 5), alpha=0.7, color='green')\nplt.title('2D CNN Predictions')\nplt.xlabel('Class')\nplt.ylabel('Frequency')\n\n# Ensemble predictions\nplt.subplot(1, 3, 3)\nplt.hist(ensemble_class_labels, bins=6, range=(0, 5), alpha=0.7, color='red')\nplt.title('Ensemble Predictions')\nplt.xlabel('Class')\nplt.ylabel('Frequency')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:54:58.178166Z","iopub.execute_input":"2025-12-03T17:54:58.178757Z","iopub.status.idle":"2025-12-03T17:54:58.620701Z","shell.execute_reply.started":"2025-12-03T17:54:58.178716Z","shell.execute_reply":"2025-12-03T17:54:58.620008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nimport tensorflow as tf\nimport matplotlib.pyplot as plt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:55:05.583834Z","iopub.execute_input":"2025-12-03T17:55:05.584541Z","iopub.status.idle":"2025-12-03T17:55:05.588075Z","shell.execute_reply.started":"2025-12-03T17:55:05.584519Z","shell.execute_reply":"2025-12-03T17:55:05.587246Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_image(image_path, img_size=224):\n    \"\"\"\n    Preprocesses the image using CLAHE and resizes it.\n    \"\"\"\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    if image is None:\n        raise ValueError(f\"Unable to read image at path: {image_path}\")\n\n    image = cv2.resize(image, (img_size, img_size))\n    lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)\n    l, a, b = cv2.split(lab)\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    l_clahe = clahe.apply(l)\n\n    merged_lab = cv2.merge((l_clahe, a, b))\n    final_image = cv2.cvtColor(merged_lab, cv2.COLOR_LAB2RGB)\n\n    final_image = final_image / 255.0  # Normalize to [0, 1]\n    return final_image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:55:08.457213Z","iopub.execute_input":"2025-12-03T17:55:08.458029Z","iopub.status.idle":"2025-12-03T17:55:08.463365Z","shell.execute_reply.started":"2025-12-03T17:55:08.458003Z","shell.execute_reply":"2025-12-03T17:55:08.462634Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def saliency_map(model, img_array):\n    \"\"\"\n    Generates a Saliency Map for a given model and image.\n    \"\"\"\n    img_tensor = tf.convert_to_tensor(img_array)\n    with tf.GradientTape() as tape:\n        tape.watch(img_tensor)\n        predictions = model(img_tensor)\n        top_pred = tf.argmax(predictions[0])\n        loss = predictions[:, top_pred]\n\n    grads = tape.gradient(loss, img_tensor)[0]\n    saliency = tf.reduce_max(tf.abs(grads), axis=-1).numpy()\n    return saliency","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:55:11.944213Z","iopub.execute_input":"2025-12-03T17:55:11.944724Z","iopub.status.idle":"2025-12-03T17:55:11.9491Z","shell.execute_reply.started":"2025-12-03T17:55:11.9447Z","shell.execute_reply":"2025-12-03T17:55:11.948405Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_with_explanations(image_path, multibranch_model, cnn_model, img_size=224, weight_multibranch=0.7, weight_cnn=0.3):\n    \"\"\"\n    Predicts the class probabilities for an image using multi-branch CNN, 2D CNN, and ensemble.\n    Also generates Saliency Map explanations for each model.\n\n    Args:\n        image_path (str): Path to the input image.\n        multibranch_model (tf.keras.Model): Trained multi-branch CNN model.\n        cnn_model (tf.keras.Model): Trained 2D CNN model.\n        img_size (int): Size of the input image (default: 224).\n        weight_multibranch (float): Weight for multi-branch CNN in the ensemble (default: 0.7).\n        weight_cnn (float): Weight for 2D CNN in the ensemble (default: 0.3).\n\n    Returns:\n        dict: A dictionary containing the predictions, confidence percentages, and Saliency Maps.\n    \"\"\"\n    # Preprocess the image\n    preprocessed_image = preprocess_image(image_path, img_size)\n    img_array = np.expand_dims(preprocessed_image, axis=0)  # Add batch dimension\n\n    # Get predictions from multi-branch CNN\n    multibranch_probs = multibranch_model.predict(img_array, verbose=0)[0]\n    multibranch_class = np.argmax(multibranch_probs)\n    multibranch_confidence = float(multibranch_probs[multibranch_class])\n\n    # Get predictions from 2D CNN\n    cnn_probs = cnn_model.predict(img_array, verbose=0)[0]\n    cnn_class = np.argmax(cnn_probs)\n    cnn_confidence = float(cnn_probs[cnn_class])\n\n    # Combine predictions using weighted average for ensemble\n    ensemble_probs = (weight_multibranch * multibranch_probs) + (weight_cnn * cnn_probs)\n    ensemble_class = np.argmax(ensemble_probs)\n    ensemble_confidence = float(ensemble_probs[ensemble_class])\n\n    # Generate Saliency Map for multi-branch CNN\n    multibranch_saliency = saliency_map(multibranch_model, img_array)\n\n    # Generate Saliency Map for 2D CNN\n    cnn_saliency = saliency_map(cnn_model, img_array)\n\n    # Return results as a dictionary\n    results = {\n        \"multi_branch_cnn\": {\n            \"class\": int(multibranch_class),\n            \"confidence\": multibranch_confidence,\n            \"probabilities\": [float(prob) for prob in multibranch_probs],\n            \"saliency_map\": multibranch_saliency,\n        },\n        \"2d_cnn\": {\n            \"class\": int(cnn_class),\n            \"confidence\": cnn_confidence,\n            \"probabilities\": [float(prob) for prob in cnn_probs],\n            \"saliency_map\": cnn_saliency,\n        },\n        \"ensemble\": {\n            \"class\": int(ensemble_class),\n            \"confidence\": ensemble_confidence,\n            \"probabilities\": [float(prob) for prob in ensemble_probs],\n        },\n    }\n    return results","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:55:16.799535Z","iopub.execute_input":"2025-12-03T17:55:16.799832Z","iopub.status.idle":"2025-12-03T17:55:16.807441Z","shell.execute_reply.started":"2025-12-03T17:55:16.79981Z","shell.execute_reply":"2025-12-03T17:55:16.806759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def visualize_results(results, original_image):\n    \"\"\"\n    Visualizes the predictions and Saliency Map explanations.\n\n    Args:\n        results (dict): Dictionary containing predictions and Saliency Maps.\n        original_image (np.array): Original input image.\n    \"\"\"\n    plt.figure(figsize=(18, 12))\n\n    # Display original image\n    plt.subplot(2, 2, 1)\n    plt.imshow(original_image)\n    plt.title(\"Original Image\")\n    plt.axis(\"off\")\n\n    # Display Saliency Map for multi-branch CNN\n    plt.subplot(2, 2, 2)\n    plt.imshow(results[\"multi_branch_cnn\"][\"saliency_map\"], cmap=\"hot\")\n    plt.title(\"Saliency Map (Multi-Branch CNN)\")\n    plt.axis(\"off\")\n\n    # Display Saliency Map for 2D CNN\n    plt.subplot(2, 2, 4)\n    plt.imshow(results[\"2d_cnn\"][\"saliency_map\"], cmap=\"hot\")\n    plt.title(\"Saliency Map (2D CNN)\")\n    plt.axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:57:20.156798Z","iopub.execute_input":"2025-12-03T17:57:20.157518Z","iopub.status.idle":"2025-12-03T17:57:20.162895Z","shell.execute_reply.started":"2025-12-03T17:57:20.157492Z","shell.execute_reply":"2025-12-03T17:57:20.162081Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Example usage\nimage_path = \"/kaggle/input/aptos2019-blindness-detection/test_images/006efc72b638.png\"\nresults = predict_with_explanations(image_path, multibranch_model_1, cnn_model)\n\n# Print predictions\nprint(\"Multi-Branch CNN Predictions:\")\nprint(f\"Class: {results['multi_branch_cnn']['class']}\")\nprint(f\"Confidence: {results['multi_branch_cnn']['confidence'] * 100:.2f}%\")\nprint(f\"Probabilities: {results['multi_branch_cnn']['probabilities']}\")\n\nprint(\"\\n2D CNN Predictions:\")\nprint(f\"Class: {results['2d_cnn']['class']}\")\nprint(f\"Confidence: {results['2d_cnn']['confidence'] * 100:.2f}%\")\nprint(f\"Probabilities: {results['2d_cnn']['probabilities']}\")\n\nprint(\"\\nEnsemble Predictions:\")\nprint(f\"Class: {results['ensemble']['class']}\")\nprint(f\"Confidence: {results['ensemble']['confidence'] * 100:.2f}%\")\nprint(f\"Probabilities: {results['ensemble']['probabilities']}\")\n\n# Visualize results\noriginal_image = cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB)\nvisualize_results(results, original_image)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T17:57:26.118466Z","iopub.execute_input":"2025-12-03T17:57:26.119038Z","iopub.status.idle":"2025-12-03T17:57:28.703233Z","shell.execute_reply.started":"2025-12-03T17:57:26.119014Z","shell.execute_reply":"2025-12-03T17:57:28.70245Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}