{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":4104,"databundleVersionId":46661,"sourceType":"competition"},{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"}],"dockerImageVersionId":30840,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## IMPORTING LIBRARIES","metadata":{}},{"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":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-18T23:31:24.886040Z","iopub.execute_input":"2025-11-18T23:31:24.886229Z","iopub.status.idle":"2025-11-18T23:31:39.548461Z","shell.execute_reply.started":"2025-11-18T23:31:24.886210Z","shell.execute_reply":"2025-11-18T23:31:39.547766Z"}},"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-11-18T23:31:49.444568Z","iopub.execute_input":"2025-11-18T23:31:49.444914Z","iopub.status.idle":"2025-11-18T23:31:50.241999Z","shell.execute_reply.started":"2025-11-18T23:31:49.444886Z","shell.execute_reply":"2025-11-18T23:31:50.241134Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## LOADING DATA","metadata":{}},{"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-11-18T23:31:54.478291Z","iopub.execute_input":"2025-11-18T23:31:54.478619Z","iopub.status.idle":"2025-11-18T23:31:54.482739Z","shell.execute_reply.started":"2025-11-18T23:31:54.478596Z","shell.execute_reply":"2025-11-18T23:31:54.481774Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"os.makedirs(save_dir, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-18T23:31:58.698352Z","iopub.execute_input":"2025-11-18T23:31:58.698706Z","iopub.status.idle":"2025-11-18T23:31:58.703126Z","shell.execute_reply.started":"2025-11-18T23:31:58.698654Z","shell.execute_reply":"2025-11-18T23:31:58.701850Z"}},"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-11-18T23:32:01.058292Z","iopub.execute_input":"2025-11-18T23:32:01.058595Z","iopub.status.idle":"2025-11-18T23:32:01.095081Z","shell.execute_reply.started":"2025-11-18T23:32:01.058572Z","shell.execute_reply":"2025-11-18T23:32:01.094408Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## CLACHE PREPROCESSING","metadata":{}},{"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-11-18T23:32:03.758786Z","iopub.execute_input":"2025-11-18T23:32:03.759077Z","iopub.status.idle":"2025-11-18T23:32:03.764721Z","shell.execute_reply.started":"2025-11-18T23:32:03.759056Z","shell.execute_reply":"2025-11-18T23:32:03.763591Z"}},"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-11-18T23:32:08.378428Z","iopub.execute_input":"2025-11-18T23:32:08.378762Z","iopub.status.idle":"2025-11-18T23:38:52.507211Z","shell.execute_reply.started":"2025-11-18T23:32:08.378734Z","shell.execute_reply":"2025-11-18T23:38:52.506336Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## LOADING PREPROCESSED DATA INTO TF DATASET","metadata":{}},{"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-11-18T23:38:55.138296Z","iopub.execute_input":"2025-11-18T23:38:55.138592Z","iopub.status.idle":"2025-11-18T23:38:55.142977Z","shell.execute_reply.started":"2025-11-18T23:38:55.138569Z","shell.execute_reply":"2025-11-18T23:38:55.142242Z"}},"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-11-18T23:38:57.618600Z","iopub.execute_input":"2025-11-18T23:38:57.618917Z","iopub.status.idle":"2025-11-18T23:38:57.870395Z","shell.execute_reply.started":"2025-11-18T23:38:57.618894Z","shell.execute_reply":"2025-11-18T23:38:57.869708Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## BEFORE AND AFTER CLACHE PREPROCESS","metadata":{}},{"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-11-18T23:39:02.238641Z","iopub.execute_input":"2025-11-18T23:39:02.238956Z","iopub.status.idle":"2025-11-18T23:39:02.245009Z","shell.execute_reply.started":"2025-11-18T23:39:02.238935Z","shell.execute_reply":"2025-11-18T23:39:02.244160Z"}},"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-11-18T23:39:06.278797Z","iopub.execute_input":"2025-11-18T23:39:06.279118Z","iopub.status.idle":"2025-11-18T23:39:08.455639Z","shell.execute_reply.started":"2025-11-18T23:39:06.279091Z","shell.execute_reply":"2025-11-18T23:39:08.454270Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## SPLITTING AND PREFETCHING DATASET","metadata":{}},{"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-11-18T23:39:18.316731Z","iopub.execute_input":"2025-11-18T23:39:18.317028Z","iopub.status.idle":"2025-11-18T23:39:18.321617Z","shell.execute_reply.started":"2025-11-18T23:39:18.317007Z","shell.execute_reply":"2025-11-18T23:39:18.320771Z"}},"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-11-18T23:39:21.199582Z","iopub.execute_input":"2025-11-18T23:39:21.200049Z","iopub.status.idle":"2025-11-18T23:39:21.226972Z","shell.execute_reply.started":"2025-11-18T23:39:21.200016Z","shell.execute_reply":"2025-11-18T23:39:21.225930Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## MULTIBRANCH CNN WITH TRANSFER LEARNING","metadata":{}},{"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-11-18T23:39:23.954608Z","iopub.execute_input":"2025-11-18T23:39:23.954925Z","iopub.status.idle":"2025-11-18T23:39:23.965255Z","shell.execute_reply.started":"2025-11-18T23:39:23.954902Z","shell.execute_reply":"2025-11-18T23:39:23.964349Z"}},"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-11-18T23:39:25.934455Z","iopub.execute_input":"2025-11-18T23:39:25.934785Z","iopub.status.idle":"2025-11-18T23:39:38.611610Z","shell.execute_reply.started":"2025-11-18T23:39:25.934759Z","shell.execute_reply":"2025-11-18T23:39:38.610913Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base1.trainable = True\nbase2.trainable = True","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-18T23:39:41.094925Z","iopub.execute_input":"2025-11-18T23:39:41.095241Z","iopub.status.idle":"2025-11-18T23:39:41.099186Z","shell.execute_reply.started":"2025-11-18T23:39:41.095218Z","shell.execute_reply":"2025-11-18T23:39:41.098150Z"}},"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-11-18T23:39:43.134326Z","iopub.execute_input":"2025-11-18T23:39:43.134785Z","iopub.status.idle":"2025-11-18T23:39:43.151335Z","shell.execute_reply.started":"2025-11-18T23:39:43.134749Z","shell.execute_reply":"2025-11-18T23:39:43.150412Z"}},"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-11-18T23:39:45.194231Z","iopub.execute_input":"2025-11-18T23:39:45.194520Z","iopub.status.idle":"2025-11-18T23:39:45.228238Z","shell.execute_reply.started":"2025-11-18T23:39:45.194500Z","shell.execute_reply":"2025-11-18T23:39:45.227331Z"}},"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-11-18T23:39:46.914197Z","iopub.execute_input":"2025-11-18T23:39:46.914519Z","iopub.status.idle":"2025-11-18T23:39:46.963438Z","shell.execute_reply.started":"2025-11-18T23:39:46.914493Z","shell.execute_reply":"2025-11-18T23:39:46.962743Z"}},"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-11-18T23:39:53.495803Z","iopub.execute_input":"2025-11-18T23:39:53.496129Z","iopub.status.idle":"2025-11-18T23:39:53.511835Z","shell.execute_reply.started":"2025-11-18T23:39:53.496101Z","shell.execute_reply":"2025-11-18T23:39:53.510911Z"}},"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-11-18T23:39:56.274886Z","iopub.execute_input":"2025-11-18T23:39:56.275206Z","iopub.status.idle":"2025-11-18T23:39:56.280839Z","shell.execute_reply.started":"2025-11-18T23:39:56.275183Z","shell.execute_reply":"2025-11-18T23:39:56.280032Z"}},"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-11-18T23:39:58.654378Z","iopub.execute_input":"2025-11-18T23:39:58.654739Z","iopub.status.idle":"2025-11-18T23:39:58.658949Z","shell.execute_reply.started":"2025-11-18T23:39:58.654708Z","shell.execute_reply":"2025-11-18T23:39:58.658198Z"}},"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-11-18T23:40:01.179358Z","iopub.execute_input":"2025-11-18T23:40:01.179649Z","iopub.status.idle":"2025-11-18T23:40:01.183384Z","shell.execute_reply.started":"2025-11-18T23:40:01.179627Z","shell.execute_reply":"2025-11-18T23:40:01.182520Z"}},"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-11-18T23:40:02.394402Z","iopub.execute_input":"2025-11-18T23:40:02.394733Z","iopub.status.idle":"2025-11-18T23:45:51.534586Z","shell.execute_reply.started":"2025-11-18T23:40:02.394705Z","shell.execute_reply":"2025-11-18T23:45:51.533762Z"}},"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-11-18T23:46:07.356162Z","iopub.execute_input":"2025-11-18T23:46:07.356478Z","iopub.status.idle":"2025-11-18T23:46:11.446463Z","shell.execute_reply.started":"2025-11-18T23:46:07.356456Z","shell.execute_reply":"2025-11-18T23:46:11.445448Z"}},"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-11-18T23:46:13.535550Z","iopub.execute_input":"2025-11-18T23:46:13.535901Z","iopub.status.idle":"2025-11-18T23:46:35.665034Z","shell.execute_reply.started":"2025-11-18T23:46:13.535874Z","shell.execute_reply":"2025-11-18T23:46:35.664285Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2D CNN","metadata":{}},{"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-11-18T23:46:48.979254Z","iopub.execute_input":"2025-11-18T23:46:48.979555Z","iopub.status.idle":"2025-11-18T23:46:48.985655Z","shell.execute_reply.started":"2025-11-18T23:46:48.979532Z","shell.execute_reply":"2025-11-18T23:46:48.984661Z"}},"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-11-18T23:47:27.194246Z","iopub.execute_input":"2025-11-18T23:47:27.194589Z","iopub.status.idle":"2025-11-18T23:47:27.262541Z","shell.execute_reply.started":"2025-11-18T23:47:27.194562Z","shell.execute_reply":"2025-11-18T23:47:27.261851Z"}},"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-11-18T23:47:31.974189Z","iopub.execute_input":"2025-11-18T23:47:31.974491Z","iopub.status.idle":"2025-11-18T23:47:31.982601Z","shell.execute_reply.started":"2025-11-18T23:47:31.974468Z","shell.execute_reply":"2025-11-18T23:47:31.981928Z"}},"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-11-18T23:47:34.834350Z","iopub.execute_input":"2025-11-18T23:47:34.834656Z","iopub.status.idle":"2025-11-18T23:49:11.815746Z","shell.execute_reply.started":"2025-11-18T23:47:34.834633Z","shell.execute_reply":"2025-11-18T23:49:11.815028Z"}},"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-11-18T23:49:22.038793Z","iopub.execute_input":"2025-11-18T23:49:22.039090Z","iopub.status.idle":"2025-11-18T23:49:24.166348Z","shell.execute_reply.started":"2025-11-18T23:49:22.039069Z","shell.execute_reply":"2025-11-18T23:49:24.165608Z"}},"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-11-18T23:49:27.882343Z","iopub.execute_input":"2025-11-18T23:49:27.882653Z","iopub.status.idle":"2025-11-18T23:49:28.971985Z","shell.execute_reply.started":"2025-11-18T23:49:27.882630Z","shell.execute_reply":"2025-11-18T23:49:28.971268Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## VISUALIZATION OF MODEL TRAININGS","metadata":{}},{"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-11-18T23:49:33.377741Z","iopub.execute_input":"2025-11-18T23:49:33.378078Z","iopub.status.idle":"2025-11-18T23:49:33.900009Z","shell.execute_reply.started":"2025-11-18T23:49:33.378052Z","shell.execute_reply":"2025-11-18T23:49:33.899038Z"}},"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-11-18T23:49:39.618145Z","iopub.execute_input":"2025-11-18T23:49:39.618505Z","iopub.status.idle":"2025-11-18T23:50:16.667885Z","shell.execute_reply.started":"2025-11-18T23:49:39.618478Z","shell.execute_reply":"2025-11-18T23:50:16.667140Z"}},"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-11-18T23:50:20.158158Z","iopub.execute_input":"2025-11-18T23:50:20.158448Z","iopub.status.idle":"2025-11-18T23:50:20.169607Z","shell.execute_reply.started":"2025-11-18T23:50:20.158425Z","shell.execute_reply":"2025-11-18T23:50:20.168858Z"}},"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-11-18T23:50:22.243626Z","iopub.execute_input":"2025-11-18T23:50:22.243967Z","iopub.status.idle":"2025-11-18T23:50:22.249864Z","shell.execute_reply.started":"2025-11-18T23:50:22.243941Z","shell.execute_reply":"2025-11-18T23:50:22.249059Z"}},"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-11-18T23:50:26.218330Z","iopub.execute_input":"2025-11-18T23:50:26.218661Z","iopub.status.idle":"2025-11-18T23:50:26.619479Z","shell.execute_reply.started":"2025-11-18T23:50:26.218633Z","shell.execute_reply":"2025-11-18T23:50:26.618504Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ENSEMBLE MODEL","metadata":{}},{"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-11-18T23:50:31.398500Z","iopub.execute_input":"2025-11-18T23:50:31.398815Z","iopub.status.idle":"2025-11-18T23:50:36.131688Z","shell.execute_reply.started":"2025-11-18T23:50:31.398790Z","shell.execute_reply":"2025-11-18T23:50:36.130759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"weight_multibranch = 0.7\nweight_cnn = 0.3","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-18T23:50:39.393249Z","iopub.execute_input":"2025-11-18T23:50:39.393599Z","iopub.status.idle":"2025-11-18T23:50:39.397312Z","shell.execute_reply.started":"2025-11-18T23:50:39.393570Z","shell.execute_reply":"2025-11-18T23:50:39.396518Z"}},"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-11-18T23:50:41.923340Z","iopub.execute_input":"2025-11-18T23:50:41.923780Z","iopub.status.idle":"2025-11-18T23:50:41.936766Z","shell.execute_reply.started":"2025-11-18T23:50:41.923743Z","shell.execute_reply":"2025-11-18T23:50:41.936001Z"}},"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-11-18T23:50:46.298163Z","iopub.execute_input":"2025-11-18T23:50:46.298470Z","iopub.status.idle":"2025-11-18T23:50:46.823358Z","shell.execute_reply.started":"2025-11-18T23:50:46.298446Z","shell.execute_reply":"2025-11-18T23:50:46.822526Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## PREDICTING WITH USER INPUTS","metadata":{}},{"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-11-18T23:50:51.658896Z","iopub.execute_input":"2025-11-18T23:50:51.659202Z","iopub.status.idle":"2025-11-18T23:50:51.662902Z","shell.execute_reply.started":"2025-11-18T23:50:51.659179Z","shell.execute_reply":"2025-11-18T23:50:51.662082Z"}},"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-11-18T23:52:08.919621Z","iopub.execute_input":"2025-11-18T23:52:08.920007Z","iopub.status.idle":"2025-11-18T23:52:08.925891Z","shell.execute_reply.started":"2025-11-18T23:52:08.919981Z","shell.execute_reply":"2025-11-18T23:52:08.924952Z"}},"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-11-18T23:52:11.658609Z","iopub.execute_input":"2025-11-18T23:52:11.659002Z","iopub.status.idle":"2025-11-18T23:52:11.664715Z","shell.execute_reply.started":"2025-11-18T23:52:11.658974Z","shell.execute_reply":"2025-11-18T23:52:11.663592Z"}},"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-11-18T23:52:14.284005Z","iopub.execute_input":"2025-11-18T23:52:14.284320Z","iopub.status.idle":"2025-11-18T23:52:14.291702Z","shell.execute_reply.started":"2025-11-18T23:52:14.284293Z","shell.execute_reply":"2025-11-18T23:52:14.290832Z"}},"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-11-18T23:52:18.028319Z","iopub.execute_input":"2025-11-18T23:52:18.028613Z","iopub.status.idle":"2025-11-18T23:52:18.034181Z","shell.execute_reply.started":"2025-11-18T23:52:18.028589Z","shell.execute_reply":"2025-11-18T23:52:18.033108Z"}},"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-11-18T23:52:21.778345Z","iopub.execute_input":"2025-11-18T23:52:21.778651Z","iopub.status.idle":"2025-11-18T23:52:48.322541Z","shell.execute_reply.started":"2025-11-18T23:52:21.778627Z","shell.execute_reply":"2025-11-18T23:52:48.321529Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}