{"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"},{"sourceId":287023933,"sourceType":"kernelVersion"}],"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-12-27T23:14:23.781529Z","iopub.execute_input":"2025-12-27T23:14:23.781810Z","iopub.status.idle":"2025-12-27T23:14:36.666487Z","shell.execute_reply.started":"2025-12-27T23:14:23.781779Z","shell.execute_reply":"2025-12-27T23:14:36.665539Z"}},"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-27T23:14:36.667450Z","iopub.execute_input":"2025-12-27T23:14:36.668075Z","iopub.status.idle":"2025-12-27T23:14:37.219789Z","shell.execute_reply.started":"2025-12-27T23:14:36.668038Z","shell.execute_reply":"2025-12-27T23:14:37.218834Z"}},"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-12-27T23:14:37.221635Z","iopub.execute_input":"2025-12-27T23:14:37.221868Z","iopub.status.idle":"2025-12-27T23:14:37.246644Z","shell.execute_reply.started":"2025-12-27T23:14:37.221849Z","shell.execute_reply":"2025-12-27T23:14:37.245866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"os.makedirs(save_dir, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:14:37.247607Z","iopub.execute_input":"2025-12-27T23:14:37.247848Z","iopub.status.idle":"2025-12-27T23:14:37.261211Z","shell.execute_reply.started":"2025-12-27T23:14:37.247820Z","shell.execute_reply":"2025-12-27T23:14:37.260511Z"}},"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-27T23:14:37.261936Z","iopub.execute_input":"2025-12-27T23:14:37.262235Z","iopub.status.idle":"2025-12-27T23:14:37.296677Z","shell.execute_reply.started":"2025-12-27T23:14:37.262205Z","shell.execute_reply":"2025-12-27T23:14:37.296038Z"}},"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-12-27T23:14:43.584406Z","iopub.execute_input":"2025-12-27T23:14:43.584727Z","iopub.status.idle":"2025-12-27T23:14:43.590140Z","shell.execute_reply.started":"2025-12-27T23:14:43.584697Z","shell.execute_reply":"2025-12-27T23:14:43.589310Z"}},"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-27T23:14:44.188901Z","iopub.execute_input":"2025-12-27T23:14:44.189212Z","iopub.status.idle":"2025-12-27T23:21:21.790564Z","shell.execute_reply.started":"2025-12-27T23:14:44.189188Z","shell.execute_reply":"2025-12-27T23:21:21.789658Z"}},"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-12-27T23:21:21.791657Z","iopub.execute_input":"2025-12-27T23:21:21.791882Z","iopub.status.idle":"2025-12-27T23:21:21.795915Z","shell.execute_reply.started":"2025-12-27T23:21:21.791862Z","shell.execute_reply":"2025-12-27T23:21:21.795168Z"}},"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-27T23:21:21.797321Z","iopub.execute_input":"2025-12-27T23:21:21.797570Z","iopub.status.idle":"2025-12-27T23:21:22.050558Z","shell.execute_reply.started":"2025-12-27T23:21:21.797531Z","shell.execute_reply":"2025-12-27T23:21:22.049598Z"}},"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-12-27T23:21:22.051964Z","iopub.execute_input":"2025-12-27T23:21:22.052315Z","iopub.status.idle":"2025-12-27T23:21:22.058240Z","shell.execute_reply.started":"2025-12-27T23:21:22.052287Z","shell.execute_reply":"2025-12-27T23:21:22.057287Z"}},"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-27T23:21:22.059227Z","iopub.execute_input":"2025-12-27T23:21:22.059590Z","iopub.status.idle":"2025-12-27T23:21:24.097767Z","shell.execute_reply.started":"2025-12-27T23:21:22.059558Z","shell.execute_reply":"2025-12-27T23:21:24.096206Z"}},"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-12-27T23:21:24.099048Z","iopub.execute_input":"2025-12-27T23:21:24.099517Z","iopub.status.idle":"2025-12-27T23:21:24.105654Z","shell.execute_reply.started":"2025-12-27T23:21:24.099462Z","shell.execute_reply":"2025-12-27T23:21:24.104702Z"}},"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-27T23:21:24.106670Z","iopub.execute_input":"2025-12-27T23:21:24.106932Z","iopub.status.idle":"2025-12-27T23:21:24.144692Z","shell.execute_reply.started":"2025-12-27T23:21:24.106912Z","shell.execute_reply":"2025-12-27T23:21:24.143931Z"}},"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-12-27T23:21:24.147049Z","iopub.execute_input":"2025-12-27T23:21:24.147334Z","iopub.status.idle":"2025-12-27T23:21:24.153485Z","shell.execute_reply.started":"2025-12-27T23:21:24.147311Z","shell.execute_reply":"2025-12-27T23:21:24.152782Z"}},"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-27T23:21:24.154839Z","iopub.execute_input":"2025-12-27T23:21:24.155114Z","iopub.status.idle":"2025-12-27T23:21:31.773270Z","shell.execute_reply.started":"2025-12-27T23:21:24.155055Z","shell.execute_reply":"2025-12-27T23:21:31.772359Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base1.trainable = True\nbase2.trainable = True","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:21:31.774365Z","iopub.execute_input":"2025-12-27T23:21:31.774611Z","iopub.status.idle":"2025-12-27T23:21:31.778179Z","shell.execute_reply.started":"2025-12-27T23:21:31.774588Z","shell.execute_reply":"2025-12-27T23:21:31.777368Z"}},"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-27T23:21:31.779044Z","iopub.execute_input":"2025-12-27T23:21:31.779374Z","iopub.status.idle":"2025-12-27T23:21:31.805246Z","shell.execute_reply.started":"2025-12-27T23:21:31.779341Z","shell.execute_reply":"2025-12-27T23:21:31.804525Z"}},"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-27T23:21:31.805987Z","iopub.execute_input":"2025-12-27T23:21:31.806216Z","iopub.status.idle":"2025-12-27T23:21:31.846533Z","shell.execute_reply.started":"2025-12-27T23:21:31.806195Z","shell.execute_reply":"2025-12-27T23:21:31.845911Z"}},"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-27T23:21:31.847356Z","iopub.execute_input":"2025-12-27T23:21:31.847645Z","iopub.status.idle":"2025-12-27T23:21:31.893785Z","shell.execute_reply.started":"2025-12-27T23:21:31.847615Z","shell.execute_reply":"2025-12-27T23:21:31.892991Z"}},"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-27T23:21:31.894694Z","iopub.execute_input":"2025-12-27T23:21:31.894910Z","iopub.status.idle":"2025-12-27T23:21:31.908865Z","shell.execute_reply.started":"2025-12-27T23:21:31.894890Z","shell.execute_reply":"2025-12-27T23:21:31.908263Z"}},"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-27T23:21:31.909598Z","iopub.execute_input":"2025-12-27T23:21:31.909891Z","iopub.status.idle":"2025-12-27T23:21:31.915177Z","shell.execute_reply.started":"2025-12-27T23:21:31.909866Z","shell.execute_reply":"2025-12-27T23:21:31.914435Z"}},"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-27T23:21:31.915925Z","iopub.execute_input":"2025-12-27T23:21:31.916244Z","iopub.status.idle":"2025-12-27T23:21:31.928416Z","shell.execute_reply.started":"2025-12-27T23:21:31.916222Z","shell.execute_reply":"2025-12-27T23:21:31.927516Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# لحفظ أفضل Multibranch CNN\nfrom tensorflow.keras.callbacks import ModelCheckpoint\n\ncheckpoint_multibranch = ModelCheckpoint(\n    \"best_multibranch_model.keras\",  # اسم الملف\n    monitor=\"val_accuracy\",          # المراقبة على دقة validation\n    verbose=1,                       # طباعة التقدم\n    save_best_only=True,             # حفظ أفضل موديل فقط\n    mode=\"max\"                       # لأننا نريد أعلى قيمة\n)\n\n\n# تدريب الموديل مع حفظ أفضل نسخة\nmultibranch_history = multibranch_model_1.fit(\n    train_ds,\n    epochs=50,\n    batch_size=batch_size,\n    validation_data=val_ds,\n    class_weight=class_weights,\n    callbacks=[lr_callback,checkpoint_multibranch],\n    verbose=1)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:21:31.929326Z","iopub.execute_input":"2025-12-27T23:21:31.929600Z","iopub.status.idle":"2025-12-27T23:32:42.792218Z","shell.execute_reply.started":"2025-12-27T23:21:31.929578Z","shell.execute_reply":"2025-12-27T23:32:42.791467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"multibranch_model_1.save(\"multibranch_model_1.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:32:42.793246Z","iopub.execute_input":"2025-12-27T23:32:42.793571Z","iopub.status.idle":"2025-12-27T23:32:44.056569Z","shell.execute_reply.started":"2025-12-27T23:32:42.793535Z","shell.execute_reply":"2025-12-27T23:32:44.055603Z"}},"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-27T23:32:44.058008Z","iopub.execute_input":"2025-12-27T23:32:44.058340Z","iopub.status.idle":"2025-12-27T23:33:05.935301Z","shell.execute_reply.started":"2025-12-27T23:32:44.058316Z","shell.execute_reply":"2025-12-27T23:33:05.934440Z"}},"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-12-27T23:33:05.936365Z","iopub.execute_input":"2025-12-27T23:33:05.936691Z","iopub.status.idle":"2025-12-27T23:33:05.942343Z","shell.execute_reply.started":"2025-12-27T23:33:05.936655Z","shell.execute_reply":"2025-12-27T23:33:05.941549Z"}},"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-27T23:33:05.943172Z","iopub.execute_input":"2025-12-27T23:33:05.943390Z","iopub.status.idle":"2025-12-27T23:33:06.020238Z","shell.execute_reply.started":"2025-12-27T23:33:05.943370Z","shell.execute_reply":"2025-12-27T23:33:06.019544Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnn_model.compile(\n    optimizer=tf.keras.optimizers.AdamW(learning_rate=1e-4),\n    loss=tf.keras.losses.SparseCategoricalCrossentropy(),\n    metrics=[\"accuracy\"]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:33:06.020949Z","iopub.execute_input":"2025-12-27T23:33:06.021208Z","iopub.status.idle":"2025-12-27T23:33:06.029975Z","shell.execute_reply.started":"2025-12-27T23:33:06.021187Z","shell.execute_reply":"2025-12-27T23:33:06.029162Z"}},"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-27T23:33:06.034190Z","iopub.execute_input":"2025-12-27T23:33:06.034423Z","iopub.status.idle":"2025-12-27T23:34:42.878215Z","shell.execute_reply.started":"2025-12-27T23:33:06.034403Z","shell.execute_reply":"2025-12-27T23:34:42.877507Z"}},"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-27T23:34:42.880417Z","iopub.execute_input":"2025-12-27T23:34:42.880650Z","iopub.status.idle":"2025-12-27T23:34:44.735930Z","shell.execute_reply.started":"2025-12-27T23:34:42.880629Z","shell.execute_reply":"2025-12-27T23:34:44.734989Z"}},"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-27T23:34:44.736921Z","iopub.execute_input":"2025-12-27T23:34:44.737204Z","iopub.status.idle":"2025-12-27T23:34:45.794058Z","shell.execute_reply.started":"2025-12-27T23:34:44.737181Z","shell.execute_reply":"2025-12-27T23:34:45.793419Z"}},"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-12-27T23:34:45.794948Z","iopub.execute_input":"2025-12-27T23:34:45.795259Z","iopub.status.idle":"2025-12-27T23:34:46.295232Z","shell.execute_reply.started":"2025-12-27T23:34:45.795235Z","shell.execute_reply":"2025-12-27T23:34:46.294397Z"}},"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-27T23:34:46.296246Z","iopub.execute_input":"2025-12-27T23:34:46.296610Z","iopub.status.idle":"2025-12-27T23:35:23.739452Z","shell.execute_reply.started":"2025-12-27T23:34:46.296575Z","shell.execute_reply":"2025-12-27T23:35:23.738739Z"}},"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)\ntrue_labels = np.concatenate([y for x, y in test_ds], axis=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:35:23.740341Z","iopub.execute_input":"2025-12-27T23:35:23.740648Z","iopub.status.idle":"2025-12-27T23:35:23.751624Z","shell.execute_reply.started":"2025-12-27T23:35:23.740614Z","shell.execute_reply":"2025-12-27T23:35:23.750651Z"}},"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-27T23:35:23.752515Z","iopub.execute_input":"2025-12-27T23:35:23.752834Z","iopub.status.idle":"2025-12-27T23:35:23.759292Z","shell.execute_reply.started":"2025-12-27T23:35:23.752803Z","shell.execute_reply":"2025-12-27T23:35:23.758387Z"}},"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-27T23:35:23.760211Z","iopub.execute_input":"2025-12-27T23:35:23.760435Z","iopub.status.idle":"2025-12-27T23:35:24.163283Z","shell.execute_reply.started":"2025-12-27T23:35:23.760416Z","shell.execute_reply":"2025-12-27T23:35:24.162399Z"}},"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-12-27T23:35:24.163971Z","iopub.execute_input":"2025-12-27T23:35:24.164271Z","iopub.status.idle":"2025-12-27T23:35:28.754463Z","shell.execute_reply.started":"2025-12-27T23:35:24.164239Z","shell.execute_reply":"2025-12-27T23:35:28.753744Z"}},"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-27T23:35:28.755272Z","iopub.execute_input":"2025-12-27T23:35:28.755507Z","iopub.status.idle":"2025-12-27T23:35:28.758973Z","shell.execute_reply.started":"2025-12-27T23:35:28.755476Z","shell.execute_reply":"2025-12-27T23:35:28.758169Z"}},"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-27T23:35:28.760625Z","iopub.execute_input":"2025-12-27T23:35:28.760951Z","iopub.status.idle":"2025-12-27T23:35:28.782495Z","shell.execute_reply.started":"2025-12-27T23:35:28.760928Z","shell.execute_reply":"2025-12-27T23:35:28.781621Z"}},"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=5, 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=5, 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=5, 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-27T23:35:28.783452Z","iopub.execute_input":"2025-12-27T23:35:28.783799Z","iopub.status.idle":"2025-12-27T23:35:29.311522Z","shell.execute_reply.started":"2025-12-27T23:35:28.783765Z","shell.execute_reply":"2025-12-27T23:35:29.310581Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report\n\n# افترضنا إن ensemble_class_labels و true_labels موجودين عندك بالفعل\n# لو عندك أسماء الـ classes\nclass_labels = [\"Class1\", \"Class2\", \"Class3\", \"Class4\", \"Class5\"]  # عدّل حسب أسماء الفئات عندك\n\nreport = classification_report(true_labels, ensemble_class_labels, target_names=class_labels)\nprint(report)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:35:29.312359Z","iopub.execute_input":"2025-12-27T23:35:29.312623Z","iopub.status.idle":"2025-12-27T23:35:29.325233Z","shell.execute_reply.started":"2025-12-27T23:35:29.312589Z","shell.execute_reply":"2025-12-27T23:35:29.324348Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report, confusion_matrix\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# أسماء الفئات (عدّل حسب بياناتك)\nclass_labels = [\"Class1\", \"Class2\", \"Class3\", \"Class4\", \"Class5\"]\n\n# --- Multibranch Model ---\nmultibranch_cm = confusion_matrix(true_labels, multibranch_class_labels)\nprint(\"Multibranch Classification Report:\\n\")\nprint(classification_report(true_labels, multibranch_class_labels, target_names=class_labels))\n\nplt.figure(figsize=(8,6))\nsns.heatmap(multibranch_cm, annot=True, fmt='d', xticklabels=class_labels, yticklabels=class_labels, cmap=\"Blues\")\nplt.title(\"Multibranch Confusion Matrix\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"True\")\nplt.show()\n\n# --- CNN Model ---\ncnn_cm = confusion_matrix(true_labels, cnn_class_labels)\nprint(\"CNN Classification Report:\\n\")\nprint(classification_report(true_labels, cnn_class_labels, target_names=class_labels))\n\nplt.figure(figsize=(8,6))\nsns.heatmap(cnn_cm, annot=True, fmt='d', xticklabels=class_labels, yticklabels=class_labels, cmap=\"Greens\")\nplt.title(\"CNN Confusion Matrix\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"True\")\nplt.show()\n\n# --- Ensemble Model ---\nensemble_cm = confusion_matrix(true_labels, ensemble_class_labels)\nprint(\"Ensemble Classification Report:\\n\")\nprint(classification_report(true_labels, ensemble_class_labels, target_names=class_labels))\n\nplt.figure(figsize=(8,6))\nsns.heatmap(ensemble_cm, annot=True, fmt='d', xticklabels=class_labels, yticklabels=class_labels, cmap=\"Oranges\")\nplt.title(\"Ensemble Confusion Matrix\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"True\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:35:29.325985Z","iopub.execute_input":"2025-12-27T23:35:29.326247Z","iopub.status.idle":"2025-12-27T23:35:30.087600Z","shell.execute_reply.started":"2025-12-27T23:35:29.326223Z","shell.execute_reply":"2025-12-27T23:35:30.086708Z"}},"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-12-27T23:35:30.088676Z","iopub.execute_input":"2025-12-27T23:35:30.089005Z","iopub.status.idle":"2025-12-27T23:35:30.092820Z","shell.execute_reply.started":"2025-12-27T23:35:30.088972Z","shell.execute_reply":"2025-12-27T23:35:30.091995Z"}},"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-27T23:35:30.093653Z","iopub.execute_input":"2025-12-27T23:35:30.093959Z","iopub.status.idle":"2025-12-27T23:35:30.109421Z","shell.execute_reply.started":"2025-12-27T23:35:30.093926Z","shell.execute_reply":"2025-12-27T23:35:30.108633Z"}},"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-27T23:35:30.110195Z","iopub.execute_input":"2025-12-27T23:35:30.110506Z","iopub.status.idle":"2025-12-27T23:35:30.128148Z","shell.execute_reply.started":"2025-12-27T23:35:30.110471Z","shell.execute_reply":"2025-12-27T23:35:30.127343Z"}},"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-27T23:35:30.129176Z","iopub.execute_input":"2025-12-27T23:35:30.129471Z","iopub.status.idle":"2025-12-27T23:35:30.143075Z","shell.execute_reply.started":"2025-12-27T23:35:30.129440Z","shell.execute_reply":"2025-12-27T23:35:30.142201Z"}},"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-27T23:35:30.143987Z","iopub.execute_input":"2025-12-27T23:35:30.144317Z","iopub.status.idle":"2025-12-27T23:35:55.965785Z","shell.execute_reply.started":"2025-12-27T23:35:30.144283Z","shell.execute_reply":"2025-12-27T23:35:55.964665Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, Model\n\n# افترض إن الموديلات جاهزة ومحمّلة\nmultibranch_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\")\n\n# نعمل inputs جديدة\ninputs = tf.keras.Input(shape=(224, 224, 3))\n\n# نعمل forward pass لكل موديل\npred1 = multibranch_model(inputs)\npred2 = cnn_model(inputs)\n\n# ندمج النتائج بالـ weights\nensemble_output = layers.Lambda(lambda x: weight_multibranch*x[0] + weight_cnn*x[1])([pred1, pred2])\n\n# نختار الـ class النهائي\nensemble_class = layers.Lambda(lambda x: tf.argmax(x, axis=1))(ensemble_output)\n\n# نبني الموديل\nensemble_model = Model(inputs=inputs, outputs=ensemble_class)\n\n# نحفظ الموديل\nensemble_model.save(\"ensemble_model.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:36:11.151231Z","iopub.execute_input":"2025-12-27T23:36:11.151544Z","iopub.status.idle":"2025-12-27T23:36:18.463785Z","shell.execute_reply.started":"2025-12-27T23:36:11.151519Z","shell.execute_reply":"2025-12-27T23:36:18.463069Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.layers import Input, Lambda, Concatenate\nfrom tensorflow.keras.models import Model\n\n# تحميل الموديلات الأصلية\nmultibranch_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\")\n\n# تثبيت أوزان ensemble\nweight_multibranch = 0.7\nweight_cnn = 0.3\n\n# إنشاء مدخل واحد للموديل\ninputs = Input(shape=(224, 224, 3))\n\n# إخراج الموديلات\npred1 = multibranch_model(inputs)\npred2 = cnn_model(inputs)\n\n# دمج المخرجات بالوزن\nensemble_output = Lambda(lambda x: weight_multibranch*x[0] + weight_cnn*x[1])([pred1, pred2])\n\n# إنشاء موديل جديد\nensemble_model = Model(inputs, ensemble_output)\nensemble_model.save(\"ensemble_model_for_tflite.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:36:18.464898Z","iopub.execute_input":"2025-12-27T23:36:18.465187Z","iopub.status.idle":"2025-12-27T23:36:25.756753Z","shell.execute_reply.started":"2025-12-27T23:36:18.465164Z","shell.execute_reply":"2025-12-27T23:36:25.755797Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\nclass WeightedEnsemble(tf.keras.layers.Layer):\n    def __init__(self, weight1=0.7, weight2=0.3, **kwargs):\n        super(WeightedEnsemble, self).__init__(**kwargs)\n        self.weight1 = weight1\n        self.weight2 = weight2\n\n    def call(self, inputs):\n        return self.weight1*inputs[0] + self.weight2*inputs[1]\n\n    def get_config(self):\n        config = super(WeightedEnsemble, self).get_config()\n        config.update({\"weight1\": self.weight1, \"weight2\": self.weight2})\n        return config\n\n# استخدام الـ Custom Layer\ninputs = tf.keras.Input(shape=(224, 224, 3))\npred1 = multibranch_model(inputs)\npred2 = cnn_model(inputs)\nensemble_output = WeightedEnsemble()( [pred1, pred2] )\nensemble_model = tf.keras.Model(inputs, ensemble_output)\n\n# حفظ الموديل\nensemble_model.save(\"ensemble_model_for_tflite.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:36:25.758217Z","iopub.execute_input":"2025-12-27T23:36:25.758467Z","iopub.status.idle":"2025-12-27T23:36:28.514274Z","shell.execute_reply.started":"2025-12-27T23:36:25.758445Z","shell.execute_reply":"2025-12-27T23:36:28.513250Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\n# تعريف الـ Custom Layer\nclass WeightedEnsemble(tf.keras.layers.Layer):\n    def __init__(self, weight1=0.7, weight2=0.3, **kwargs):\n        super(WeightedEnsemble, self).__init__(**kwargs)\n        self.weight1 = weight1\n        self.weight2 = weight2\n\n    def call(self, inputs):\n        return self.weight1*inputs[0] + self.weight2*inputs[1]\n\n    def get_config(self):\n        config = super(WeightedEnsemble, self).get_config()\n        config.update({\"weight1\": self.weight1, \"weight2\": self.weight2})\n        return config\n\n# إعادة تحميل الموديلات الفرعية\nmultibranch_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\")\n\n# بناء موديل الـ Ensemble\ninputs = tf.keras.Input(shape=(224, 224, 3))\npred1 = multibranch_model(inputs)\npred2 = cnn_model(inputs)\nensemble_output = WeightedEnsemble()( [pred1, pred2] )\nensemble_model = tf.keras.Model(inputs, ensemble_output)\n\n# حفظ الموديل بصيغة .keras\nensemble_model.save(\"ensemble_model_for_tflite.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:36:28.515886Z","iopub.execute_input":"2025-12-27T23:36:28.516245Z","iopub.status.idle":"2025-12-27T23:36:36.117435Z","shell.execute_reply.started":"2025-12-27T23:36:28.516212Z","shell.execute_reply":"2025-12-27T23:36:36.116347Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# إنشاء Converter\nconverter = tf.lite.TFLiteConverter.from_keras_model(ensemble_model)\n\n# اختياري: تفعيل تحسينات الحجم والأداء\nconverter.optimizations = [tf.lite.Optimize.DEFAULT]\n\n# تحويل الموديل\ntflite_model = converter.convert()\n\n# حفظ الموديل النهائي\nwith open(\"ensemble_model.tflite\", \"wb\") as f:\n    f.write(tflite_model)\n\nprint(\"تم حفظ الموديل بصيغة TFLite بنجاح ✅\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:36:36.118521Z","iopub.execute_input":"2025-12-27T23:36:36.118852Z","iopub.status.idle":"2025-12-27T23:37:48.015953Z","shell.execute_reply.started":"2025-12-27T23:36:36.118816Z","shell.execute_reply":"2025-12-27T23:37:48.015169Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport shutil\nimport pandas as pd\n\n# Paths\ntrain_images_dir = \"/kaggle/input/aptos2019-blindness-detection/train_images\"\ncsv_path = \"/kaggle/input/aptos2019-blindness-detection/train.csv\"\nsample_dir = \"/kaggle/working/Sample\"\n\n# Read labels CSV\ndf = pd.read_csv(csv_path)\n\n# Create Sample directory\nos.makedirs(sample_dir, exist_ok=True)\n\n# Create class folders (0 to 4)\nfor cls in range(5):\n    os.makedirs(os.path.join(sample_dir, str(cls)), exist_ok=True)\n\n# For each class, copy 10 images\nfor cls in range(5):\n    class_images = df[df[\"diagnosis\"] == cls][\"id_code\"].values[:10]\n\n    for img_id in class_images:\n        src = os.path.join(train_images_dir, img_id + \".png\")\n        dst = os.path.join(sample_dir, str(cls), img_id + \".png\")\n\n        if os.path.exists(src):\n            shutil.copy(src, dst)\n\nprint(\"✅ Sample folder created successfully!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:37:48.016837Z","iopub.execute_input":"2025-12-27T23:37:48.017125Z","iopub.status.idle":"2025-12-27T23:37:48.242950Z","shell.execute_reply.started":"2025-12-27T23:37:48.017076Z","shell.execute_reply":"2025-12-27T23:37:48.241936Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import shutil\nimport os\n\nsample_dir = \"/kaggle/working/Sample\"\nzip_path = \"/kaggle/working/Sample.zip\"\n\n# Remove zip if exists\nif os.path.exists(zip_path):\n    os.remove(zip_path)\n\n# Create zip\nshutil.make_archive(\n    base_name=zip_path.replace(\".zip\", \"\"),\n    format=\"zip\",\n    root_dir=sample_dir\n)\n\nprint(\"✅ Sample.zip created successfully!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:37:48.243893Z","iopub.execute_input":"2025-12-27T23:37:48.244157Z","iopub.status.idle":"2025-12-27T23:37:53.458212Z","shell.execute_reply.started":"2025-12-27T23:37:48.244134Z","shell.execute_reply":"2025-12-27T23:37:53.457355Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nfrom tqdm import tqdm\n\n# Image size\nimg_size = 224\n\n# Paths\ninput_root = \"/kaggle/working/Sample\"\noutput_root = \"/kaggle/working/Sample_Preprocessed\"\n\nos.makedirs(output_root, exist_ok=True)\n\ndef apply_clahe_and_save(image_path, save_dir):\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    # Resize\n    image = cv2.resize(image, (img_size, img_size))\n\n    # BGR → LAB\n    lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)\n    l, a, b = cv2.split(lab)\n\n    # CLAHE on L channel\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    cl = clahe.apply(l)\n\n    # Merge + LAB → RGB\n    merged_lab = cv2.merge((cl, a, b))\n    final_image = cv2.cvtColor(merged_lab, cv2.COLOR_LAB2RGB)\n\n    # Save\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\n\n\n# Loop over classes\nfor cls in range(5):\n    class_input_dir = os.path.join(input_root, str(cls))\n    class_output_dir = os.path.join(output_root, str(cls))\n    os.makedirs(class_output_dir, exist_ok=True)\n\n    images = os.listdir(class_input_dir)\n\n    for img_name in tqdm(images, desc=f\"Processing class {cls}\"):\n        img_path = os.path.join(class_input_dir, img_name)\n        apply_clahe_and_save(img_path, class_output_dir)\n\nprint(\"✅ Preprocessing completed successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:37:53.460542Z","iopub.execute_input":"2025-12-27T23:37:53.460837Z","iopub.status.idle":"2025-12-27T23:37:59.652862Z","shell.execute_reply.started":"2025-12-27T23:37:53.460813Z","shell.execute_reply":"2025-12-27T23:37:59.652182Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import shutil\nimport os\n\nsample_dir = \"/kaggle/working/Sample_Preprocessed\"\nzip_path = \"/kaggle/working/Sample_Preprocessed.zip\"\n\n# Remove zip if exists\nif os.path.exists(zip_path):\n    os.remove(zip_path)\n\n# Create zip\nshutil.make_archive(\n    base_name=zip_path.replace(\".zip\", \"\"),\n    format=\"zip\",\n    root_dir=sample_dir\n)\n\nprint(\"✅ Sample.zip created successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:37:59.653818Z","iopub.execute_input":"2025-12-27T23:37:59.654044Z","iopub.status.idle":"2025-12-27T23:37:59.817807Z","shell.execute_reply.started":"2025-12-27T23:37:59.654023Z","shell.execute_reply":"2025-12-27T23:37:59.817073Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from keras import config\nconfig.enable_unsafe_deserialization()\n\nimport tensorflow as tf\n\nmodel_path = \"/kaggle/working/multibranch_model_1.h5\"\nmodel = tf.keras.models.load_model(model_path, compile=False)\n\nprint(\"Model loaded successfully!\")\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:43:18.316779Z","iopub.execute_input":"2025-12-27T23:43:18.317123Z","iopub.status.idle":"2025-12-27T23:43:23.785678Z","shell.execute_reply.started":"2025-12-27T23:43:18.317066Z","shell.execute_reply":"2025-12-27T23:43:23.784953Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nimport cv2\nimport os\nimport numpy as np\nfrom tqdm import tqdm\n\nimg_size = 224\nbatch_size = 32\n\nsample_dir = \"/kaggle/working/Sample_Preprocessed\"\ndef apply_clahe_to_image(image_path):\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    if image is None:\n        raise ValueError(f\"Unable to read image: {image_path}\")\n    \n    image = cv2.resize(image, (img_size, img_size))\n    \n    lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)\n    l, a, b = cv2.split(lab)\n    \n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    cl = clahe.apply(l)\n    \n    merged_lab = cv2.merge((cl, a, b))\n    final_image = cv2.cvtColor(merged_lab, cv2.COLOR_LAB2RGB)\n    \n    return final_image / 255.0\nimage_paths = []\nlabels = []\n\nfor class_label in os.listdir(sample_dir):\n    class_path = os.path.join(sample_dir, class_label)\n    if os.path.isdir(class_path):\n        for img_name in os.listdir(class_path):\n            img_path = os.path.join(class_path, img_name)\n            image_paths.append(img_path)\n            labels.append(int(class_label))\n\nprint(f\"Total images: {len(image_paths)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:47:26.083158Z","iopub.execute_input":"2025-12-27T23:47:26.083499Z","iopub.status.idle":"2025-12-27T23:47:26.092161Z","shell.execute_reply.started":"2025-12-27T23:47:26.083474Z","shell.execute_reply":"2025-12-27T23:47:26.091424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_preprocessed_image(image_path, label):\n    image = apply_clahe_to_image(image_path)\n    return image, label\n\n# استخدم tf.data.Dataset\ndataset = tf.data.Dataset.from_tensor_slices((image_paths, labels))\n\ndef tf_preprocess(path, label):\n    image = tf.py_function(func=lambda p: apply_clahe_to_image(p.numpy().decode()), \n                           inp=[path], \n                           Tout=tf.float32)\n    image.set_shape([img_size, img_size, 3])\n    return image, label\n\ndataset = dataset.shuffle(len(image_paths))\ndataset = dataset.map(tf_preprocess, num_parallel_calls=tf.data.AUTOTUNE)\ndataset = dataset.batch(batch_size).prefetch(tf.data.AUTOTUNE)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:47:35.328366Z","iopub.execute_input":"2025-12-27T23:47:35.328702Z","iopub.status.idle":"2025-12-27T23:47:35.393370Z","shell.execute_reply.started":"2025-12-27T23:47:35.328672Z","shell.execute_reply":"2025-12-27T23:47:35.392447Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"preds = model.predict(dataset)\npred_classes = np.argmax(preds, axis=1)\n\nprint(\"Predictions done!\")\nprint(pred_classes[:10])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:47:44.945727Z","iopub.execute_input":"2025-12-27T23:47:44.946048Z","iopub.status.idle":"2025-12-27T23:48:19.871031Z","shell.execute_reply.started":"2025-12-27T23:47:44.946021Z","shell.execute_reply":"2025-12-27T23:48:19.870327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport math\nimport cv2\n\n# عدد الصور في كل صف\ncols = 5\n\n# لو dataset كبير — نقدر نعرض أول 50 صورة فقط مثلاً\nnum_images_to_show = len(image_paths)  # ممكن تغيّر الرقم\nrows = math.ceil(num_images_to_show / cols)\n\nplt.figure(figsize=(20, 4*rows))\n\nfor i, img_path in enumerate(image_paths[:num_images_to_show]):\n    img = cv2.imread(img_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    pred_label = pred_classes[i]\n    \n    plt.subplot(rows, cols, i+1)\n    plt.imshow(img)\n    plt.title(f\"Predicted: {pred_label}\")\n    plt.axis('off')\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-27T23:49:24.581446Z","iopub.execute_input":"2025-12-27T23:49:24.581771Z","iopub.status.idle":"2025-12-27T23:49:32.483294Z","shell.execute_reply.started":"2025-12-27T23:49:24.581745Z","shell.execute_reply":"2025-12-27T23:49:32.481679Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\n# فعل unsafe deserialization لو فيه Lambda layers\nfrom keras import config\nconfig.enable_unsafe_deserialization()\n\nh5_model_path = \"/kaggle/working/multibranch_model_1.h5\"\nmodel = tf.keras.models.load_model(h5_model_path, compile=False)\n\nprint(\"Model loaded successfully!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-28T00:01:00.234750Z","iopub.execute_input":"2025-12-28T00:01:00.235124Z","iopub.status.idle":"2025-12-28T00:01:05.110563Z","shell.execute_reply.started":"2025-12-28T00:01:00.235067Z","shell.execute_reply":"2025-12-28T00:01:05.109817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom keras import config\nconfig.enable_unsafe_deserialization()\n\nh5_model_path = \"/kaggle/working/multibranch_model_1.h5\"\nmodel = tf.keras.models.load_model(h5_model_path, compile=False)\n\ntflite_model_path = \"/kaggle/working/multibranch_model_1.tflite\"\n\nconverter = tf.lite.TFLiteConverter.from_keras_model(model)\nconverter.optimizations = [tf.lite.Optimize.DEFAULT]\ntflite_model = converter.convert()\n\nwith open(tflite_model_path, \"wb\") as f:\n    f.write(tflite_model)\n\nprint(f\"TFLite model saved at: {tflite_model_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-28T00:01:50.372288Z","iopub.execute_input":"2025-12-28T00:01:50.372615Z","iopub.status.idle":"2025-12-28T00:03:00.597419Z","shell.execute_reply.started":"2025-12-28T00:01:50.372591Z","shell.execute_reply":"2025-12-28T00:03:00.596472Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"interpreter = tf.lite.Interpreter(model_path=tflite_model_path)\ninterpreter.allocate_tensors()\n\ninput_details = interpreter.get_input_details()\noutput_details = interpreter.get_output_details()\nprint(\"TFLite interpreter ready!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-28T00:03:31.249763Z","iopub.execute_input":"2025-12-28T00:03:31.250077Z","iopub.status.idle":"2025-12-28T00:03:31.392438Z","shell.execute_reply.started":"2025-12-28T00:03:31.250052Z","shell.execute_reply":"2025-12-28T00:03:31.391407Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nimport cv2\nimport os\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\n\nimg_size = 224\nsample_dir = \"/kaggle/working/Sample_Preprocessed\"\ntflite_model_path = \"/kaggle/working/multibranch_model_1.tflite\"\ninterpreter = tf.lite.Interpreter(model_path=tflite_model_path)\ninterpreter.allocate_tensors()\n\ninput_details = interpreter.get_input_details()\noutput_details = interpreter.get_output_details()\ndef preprocess_image(image_path):\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    if image is None:\n        raise ValueError(f\"Unable to read image: {image_path}\")\n    \n    image = cv2.resize(image, (img_size, img_size))\n    \n    lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)\n    l, a, b = cv2.split(lab)\n    \n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    cl = clahe.apply(l)\n    \n    merged_lab = cv2.merge((cl, a, b))\n    final_image = cv2.cvtColor(merged_lab, cv2.COLOR_LAB2RGB)\n    \n    # normalize\n    return final_image.astype(np.float32) / 255.0\nimage_paths = []\nlabels = []\n\nfor class_label in os.listdir(sample_dir):\n    class_path = os.path.join(sample_dir, class_label)\n    if os.path.isdir(class_path):\n        for img_name in os.listdir(class_path):\n            img_path = os.path.join(class_path, img_name)\n            image_paths.append(img_path)\n            labels.append(int(class_label))\n\nprint(f\"Total images: {len(image_paths)}\")\npred_classes = []\n\nfor img_path in tqdm(image_paths, desc=\"Predicting\"):\n    img = preprocess_image(img_path)\n    img = np.expand_dims(img, axis=0)  # إضافة batch dimension\n    \n    interpreter.set_tensor(input_details[0]['index'], img)\n    interpreter.invoke()\n    \n    pred = interpreter.get_tensor(output_details[0]['index'])\n    pred_class = np.argmax(pred, axis=1)[0]\n    pred_classes.append(pred_class)\ndf_preds = pd.DataFrame({\n    \"image_path\": image_paths,\n    \"true_label\": labels,\n    \"predicted_class\": pred_classes\n})\n\ncsv_save_path = \"/kaggle/working/sample_tflite_predictions.csv\"\ndf_preds.to_csv(csv_save_path, index=False)\nprint(f\"Predictions saved to: {csv_save_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-28T00:04:46.922450Z","iopub.execute_input":"2025-12-28T00:04:46.922796Z","iopub.status.idle":"2025-12-28T00:04:59.965541Z","shell.execute_reply.started":"2025-12-28T00:04:46.922766Z","shell.execute_reply":"2025-12-28T00:04:59.964711Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndf_preds = pd.DataFrame({\n    \"image_path\": image_paths,\n    \"predicted_class\": pred_classes\n})\n\ndf_preds.to_csv(\"/kaggle/working/sample_predictions.csv\", index=False)\nprint(\"Saved predictions to sample_predictions.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-28T00:07:22.581447Z","iopub.execute_input":"2025-12-28T00:07:22.581788Z","iopub.status.idle":"2025-12-28T00:07:22.588443Z","shell.execute_reply.started":"2025-12-28T00:07:22.581763Z","shell.execute_reply":"2025-12-28T00:07:22.587686Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom keras import config\nimport matplotlib.pyplot as plt\nimport math\nfrom tqdm import tqdm\n\n# === تمكين unsafe deserialization لو فيه Lambda layers ===\nconfig.enable_unsafe_deserialization()\n\n# === تحميل الموديل ===\nmodel_path = \"/kaggle/working/multibranch_model_1.h5\"\nmodel = tf.keras.models.load_model(model_path, compile=False)\nprint(\"Model loaded successfully!\")\n\n# === إعداد المتغيرات ===\nimg_size = 224\nbatch_size = 32\nsample_dir = \"/kaggle/working/Sample_Preprocessed\"\n\n# === دالة preprocessing (CLAHE + normalization) ===\ndef apply_clahe_to_image(image_path):\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    if image is None:\n        raise ValueError(f\"Unable to read image: {image_path}\")\n    \n    image = cv2.resize(image, (img_size, img_size))\n    \n    lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)\n    l, a, b = cv2.split(lab)\n    \n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    cl = clahe.apply(l)\n    \n    merged_lab = cv2.merge((cl, a, b))\n    final_image = cv2.cvtColor(merged_lab, cv2.COLOR_LAB2RGB)\n    \n    return final_image / 255.0\n\n# === جمع كل الصور والكلاسات ===\nimage_paths = []\nlabels = []\n\nfor class_label in os.listdir(sample_dir):\n    class_path = os.path.join(sample_dir, class_label)\n    if os.path.isdir(class_path):\n        for img_name in os.listdir(class_path):\n            img_path = os.path.join(class_path, img_name)\n            image_paths.append(img_path)\n            labels.append(int(class_label))\n\nprint(f\"Total images: {len(image_paths)}\")\n\n# === إنشاء dataset مع tf.data ===\ndataset = tf.data.Dataset.from_tensor_slices((image_paths, labels))\n\ndef tf_preprocess(path, label):\n    image = tf.py_function(func=lambda p: apply_clahe_to_image(p.numpy().decode()), \n                           inp=[path], \n                           Tout=tf.float32)\n    image.set_shape([img_size, img_size, 3])\n    return image, label\n\ndataset = dataset.map(tf_preprocess, num_parallel_calls=tf.data.AUTOTUNE)\ndataset = dataset.batch(batch_size).prefetch(tf.data.AUTOTUNE)\n\n# === عمل Predictions ===\npreds = model.predict(dataset)\npred_classes = np.argmax(preds, axis=1)\nprint(\"Predictions done!\")\n\n# === حفظ النتائج في CSV ===\ndf_preds = pd.DataFrame({\n    \"image_path\": image_paths,\n    \"true_label\": labels,\n    \"predicted_class\": pred_classes\n})\n\ncsv_save_path = \"/kaggle/working/sample_predictions1.csv\"\ndf_preds.to_csv(csv_save_path, index=False)\nprint(f\"Predictions saved to CSV: {csv_save_path}\")\n\n# === عرض الصور مع التنبؤات ===\ncols = 5\nnum_images_to_show = len(image_paths)  # يمكن تقليل العدد لو الصور كثيرة\nrows = math.ceil(num_images_to_show / cols)\n\nplt.figure(figsize=(20, 4*rows))\n\nfor i, img_path in enumerate(image_paths[:num_images_to_show]):\n    img = cv2.imread(img_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    pred_label = pred_classes[i]\n    true_label = labels[i]\n    \n    plt.subplot(rows, cols, i+1)\n    plt.imshow(img)\n    plt.title(f\"True: {true_label} | Pred: {pred_label}\")\n    plt.axis('off')\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-28T00:13:36.761028Z","iopub.execute_input":"2025-12-28T00:13:36.761375Z","iopub.status.idle":"2025-12-28T00:14:14.854661Z","shell.execute_reply.started":"2025-12-28T00:13:36.761351Z","shell.execute_reply":"2025-12-28T00:14:14.853140Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\n# تحويل القوائم إلى numpy arrays\ntrue_labels = np.array(labels)\npred_labels = np.array(pred_classes)\n\n# حساب عدد الصور الصحيحة\ncorrect = np.sum(true_labels == pred_labels)\ntotal = len(true_labels)\n\naccuracy = (correct / total) * 100\nprint(f\"Accuracy: {accuracy:.2f}% ({correct}/{total})\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-28T00:13:20.833804Z","iopub.execute_input":"2025-12-28T00:13:20.834162Z","iopub.status.idle":"2025-12-28T00:13:20.839751Z","shell.execute_reply.started":"2025-12-28T00:13:20.834129Z","shell.execute_reply":"2025-12-28T00:13:20.838918Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}