{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:13:11.025519Z","iopub.execute_input":"2026-02-13T19:13:11.026413Z","iopub.status.idle":"2026-02-13T19:13:14.826559Z","shell.execute_reply.started":"2026-02-13T19:13:11.026375Z","shell.execute_reply":"2026-02-13T19:13:14.825908Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    print(os.path.join(dirname))\n    for filename in filenames:\n        print(\"   →\", filename)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:13:14.827701Z","iopub.execute_input":"2026-02-13T19:13:14.827946Z","iopub.status.idle":"2026-02-13T19:13:14.898523Z","shell.execute_reply.started":"2026-02-13T19:13:14.827928Z","shell.execute_reply":"2026-02-13T19:13:14.897907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ntrain_df = pd.read_csv(\"/kaggle/input/aptos2019-blindness-detection/train.csv\")\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:13:14.899342Z","iopub.execute_input":"2026-02-13T19:13:14.900141Z","iopub.status.idle":"2026-02-13T19:13:14.912697Z","shell.execute_reply.started":"2026-02-13T19:13:14.900119Z","shell.execute_reply":"2026-02-13T19:13:14.912044Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport matplotlib.pyplot as plt\n\nimg_name = train_df['id_code'][0] + \".png\"\nimg_path = \"/kaggle/input/aptos2019-blindness-detection/train_images/\" + img_name\n\nimg = cv2.imread(img_path)\nimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\nplt.imshow(img)\nplt.axis('off')\nplt.title(\"Example Image\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:13:14.913536Z","iopub.execute_input":"2026-02-13T19:13:14.913898Z","iopub.status.idle":"2026-02-13T19:13:16.398520Z","shell.execute_reply.started":"2026-02-13T19:13:14.913877Z","shell.execute_reply":"2026-02-13T19:13:16.397831Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nfrom tqdm import tqdm # لإظهار شريط التحميل\n\n# 1. تعريف دالة المعالجة الاحترافية\ndef preprocess_retina_image(image, img_size=380):\n    # تحويل لرمادي لإيجاد القناع\n    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n    # عمل قناع لعزل السواد (أي بكسل قيمته أكبر من 10 يعتبر جزء من العين)\n    mask = gray > 10\n    # قص الأجزاء السوداء الزائدة\n    if np.any(mask):\n        image = image[np.ix_(mask.any(1), mask.any(0))]\n    # تغيير الحجم لـ 380 (المثالي لـ EfficientNetB4)\n    image = cv2.resize(image, (img_size, img_size))\n    # تقنية تحسين التباين (Ben Graham's Processing) لإبراز العروق\n    image = cv2.addWeighted(image, 4, cv2.GaussianBlur(image, (0,0), img_size/10), -4, 128)\n    return image\n\n# 2. تطبيق المعالجة على مصفوفة الصور X كاملة\nprint(\"جاري معالجة وتحسين الصور... قد يستغرق ذلك دقيقة\")\nX_processed = []\nfor img in tqdm(X):\n    # نستخدم الدالة التي عرفناها فوق\n    processed_img = preprocess_retina_image(img)\n    X_processed.append(processed_img)\n\n# تحويل القائمة إلى مصفوفة numpy وتحديث X الأصلية\nX = np.array(X_processed)\n\nprint(\"تم تحسين جميع الصور بنجاح. الأبعاد الجديدة:\", X.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:13:16.400445Z","iopub.execute_input":"2026-02-13T19:13:16.400749Z","iopub.status.idle":"2026-02-13T19:25:56.731348Z","shell.execute_reply.started":"2026-02-13T19:13:16.400730Z","shell.execute_reply":"2026-02-13T19:25:56.730643Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"X shape:\", X.shape)\nprint(\"y shape:\", y.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:32:08.355039Z","iopub.execute_input":"2026-02-13T19:32:08.355325Z","iopub.status.idle":"2026-02-13T19:32:08.360042Z","shell.execute_reply.started":"2026-02-13T19:32:08.355281Z","shell.execute_reply":"2026-02-13T19:32:08.359206Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nX_train, X_val, y_train, y_val = train_test_split(\n    X, y, test_size=0.2, random_state=42, stratify=y\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:32:08.361508Z","iopub.execute_input":"2026-02-13T19:32:08.361730Z","iopub.status.idle":"2026-02-13T19:32:08.490790Z","shell.execute_reply.started":"2026-02-13T19:32:08.361715Z","shell.execute_reply":"2026-02-13T19:32:08.490165Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. تعريف طبقة توليد البيانات (Data Augmentation)\ndata_augmentation = tf.keras.Sequential([\n    layers.RandomFlip(\"horizontal_and_vertical\"),\n    layers.RandomRotation(0.2), \n    layers.RandomZoom(0.2),      \n    layers.RandomContrast(0.2),  \n])\n\n# 2. تعريف المدخلات\ninputs = layers.Input(shape=(224, 224, 3))\n\n# 3. استدعاء طبقة الـ Augmentation هنا (أول خطوة بعد المدخلات)\nx = data_augmentation(inputs)\n\n# 4. إضافة طبقة المعالجة المدمجة\nx = Lambda(preprocess_input)(x)\n\n# 5. تحميل الموديل الأساسي (Fine-tuning)\nbase_model = EfficientNetB4(weights='imagenet', include_top=False)\nbase_model.trainable = True\nfor layer in base_model.layers[:-60]: # تجميد أغلب الطبقات وفتح آخر 60\n    layer.trainable = False\n\n# 6. ربط الطبقات ببعضها\nx = base_model(x)\nx = GlobalAveragePooling2D()(x)\nx = BatchNormalization()(x)\nx = Dropout(0.5)(x) # رفعنا الـ Dropout قليلاً لزيادة الدقة\noutput = Dense(5, activation='softmax')(x)\n\n# 7. إنشاء الموديل النهائي\nmodel = Model(inputs, output)\n\n# 8. الـ Compile (تأكد من استخدام Learning Rate صغير)\nmodel.compile(\n    optimizer=Adam(learning_rate=1e-5),\n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nprint(\"تم دمج طبقة الـ Data Augmentation بنجاح!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:32:08.491532Z","iopub.execute_input":"2026-02-13T19:32:08.491747Z","iopub.status.idle":"2026-02-13T19:32:10.632973Z","shell.execute_reply.started":"2026-02-13T19:32:08.491721Z","shell.execute_reply":"2026-02-13T19:32:10.632322Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.utils import class_weight\nimport numpy as np\n\n# حساب الأوزان بناءً على توزيع الصور في بياناتك\nweights = class_weight.compute_class_weight(\n    class_weight='balanced',\n    classes=np.unique(y_train),\n    y=y_train\n)\nclass_weights_dict = dict(enumerate(weights))\n\nprint(\"تم حساب أوزان الأصناف لموازنة البيانات:\")\nprint(class_weights_dict)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:32:10.633707Z","iopub.execute_input":"2026-02-13T19:32:10.633976Z","iopub.status.idle":"2026-02-13T19:32:10.641082Z","shell.execute_reply.started":"2026-02-13T19:32:10.633950Z","shell.execute_reply":"2026-02-13T19:32:10.640115Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. فتح الموديل بالكامل للتدريب\nbase_model.trainable = True \n\n# 2. استخدام Learning Rate صغير جداً (للحفاظ على استقرار الموديل)\n# لاحظ غيرناه إلى 1e-5 ليكون أقوى قليلاً من الـ 1e-7 الذي توقف عنده الموديل\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5), \n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nprint(\"تم فتح جميع الطبقات للتدريب العميق.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:32:10.641900Z","iopub.execute_input":"2026-02-13T19:32:10.642162Z","iopub.status.idle":"2026-02-13T19:32:10.658391Z","shell.execute_reply.started":"2026-02-13T19:32:10.642138Z","shell.execute_reply":"2026-02-13T19:32:10.657783Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\n\n# 1. مراقب لإيقاف التدريب إذا توقفت الدقة عن التحسن (صبر لمدة 15 دورة)\nearly_stop = EarlyStopping(monitor='val_accuracy', patience=15, restore_best_weights=True, verbose=1)\n\n# 2. مراقب لتقليل سرعة التعلم إذا ثبتت الدقة (للدخول في التفاصيل الدقيقة جداً)\nreduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=5, min_lr=1e-8, verbose=1)\n\n# 3. حفظ أفضل نسخة من الموديل تلقائياً\ncheckpoint = ModelCheckpoint('best_model_retinopathy.h5', monitor='val_accuracy', save_best_only=True, verbose=1)\n\nprint(\"تم تجهيز المراقبين للوصول للدقة القصوى.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T20:00:57.781309Z","iopub.execute_input":"2026-02-13T20:00:57.782158Z","iopub.status.idle":"2026-02-13T20:00:57.787497Z","shell.execute_reply.started":"2026-02-13T20:00:57.782112Z","shell.execute_reply":"2026-02-13T20:00:57.786753Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ملاحظة: تأكد أن X_train و y_train تم تقسيمهم بعد عملية الـ Crop والتحسين التي قمت بها\nprint(\"بدء مرحلة الـ Fine-Tuning العميق...\")\n\nhistory_final = model.fit(\n    X_train, y_train,\n    validation_data=(X_val, y_val),\n    epochs=50,             # عدد دورات كافٍ للوصول للـ 98%\n    batch_size=8,          # حجم صغير للدقة العالية وتجنب امتلاء الذاكرة\n    callbacks=[early_stop, reduce_lr, checkpoint],\n    class_weight=class_weights_dict  # الأوزان التي حسبتها في صورتك الأخيرة\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T20:01:36.458634Z","iopub.execute_input":"2026-02-13T20:01:36.458936Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. تعريف طبقة توليد البيانات (Data Augmentation)\ndata_augmentation = tf.keras.Sequential([\n    layers.RandomFlip(\"horizontal_and_vertical\"),\n    layers.RandomRotation(0.2), \n    layers.RandomZoom(0.2),      \n    layers.RandomContrast(0.2),  \n])\n\n# 2. تعريف المدخلات\ninputs = layers.Input(shape=(224, 224, 3))\n\n# 3. استدعاء طبقة الـ Augmentation هنا (أول خطوة بعد المدخلات)\nx = data_augmentation(inputs)\n\n# 4. إضافة طبقة المعالجة المدمجة\nx = Lambda(preprocess_input)(x)\n\n# 5. تحميل الموديل الأساسي (Fine-tuning)\nbase_model = EfficientNetB4(weights='imagenet', include_top=False)\nbase_model.trainable = True\nfor layer in base_model.layers[:-60]: # تجميد أغلب الطبقات وفتح آخر 60\n    layer.trainable = False\n\n# 6. ربط الطبقات ببعضها\nx = base_model(x)\nx = GlobalAveragePooling2D()(x)\nx = BatchNormalization()(x)\nx = Dropout(0.5)(x) # رفعنا الـ Dropout قليلاً لزيادة الدقة\noutput = Dense(5, activation='softmax')(x)\n\n# 7. إنشاء الموديل النهائي\nmodel = Model(inputs, output)\n\n# 8. الـ Compile (تأكد من استخدام Learning Rate صغير)\nmodel.compile(\n    optimizer=Adam(learning_rate=1e-5),\n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nprint(\"تم دمج طبقة الـ Data Augmentation بنجاح!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:32:10.659056Z","iopub.execute_input":"2026-02-13T19:32:10.659282Z","iopub.status.idle":"2026-02-13T19:32:13.871354Z","shell.execute_reply.started":"2026-02-13T19:32:10.659265Z","shell.execute_reply":"2026-02-13T19:32:13.870656Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = model.fit(\n    X_train, y_train,\n    validation_data=(X_val, y_val),\n    epochs=50,\n    batch_size=16,\n    callbacks=[early_stop, reduce_lr],\n    class_weight=class_weights_dict # ضروري جداً لموازنة الـ 5 أصناف\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T19:32:13.872067Z","iopub.execute_input":"2026-02-13T19:32:13.872397Z","iopub.status.idle":"2026-02-13T19:47:42.315890Z","shell.execute_reply.started":"2026-02-13T19:32:13.872380Z","shell.execute_reply":"2026-02-13T19:47:42.315249Z"}},"outputs":[],"execution_count":null}]}