{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":14774,"databundleVersionId":875431}],"dockerImageVersionId":31287,"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},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport glob\nimport time\nimport random\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report, confusion_matrix, roc_auc_score, roc_curve, precision_score, recall_score, f1_score\nfrom tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout, Conv2D, BatchNormalization, Activation, Multiply, Add, Input, Layer\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping\nimport warnings\n\nwarnings.filterwarnings('ignore')\n\n\nSEED = 42\nIMG_SIZE = 300 \nBATCH_SIZE = 8   \nEPOCHS = 30      \nLEARNING_RATE = 1e-4\nDATA_DIR = '/kaggle/input/aptos2019-blindness-detection'\nTRAIN_IMG_DIR = os.path.join(DATA_DIR, 'train_images')\nCSV_PATH = os.path.join(DATA_DIR, 'train.csv')\n\ndef seed_everything(seed=42):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    tf.random.set_seed(seed)\n\nseed_everything(SEED)\nprint(f\"✅ البيئة جاهزة. TensorFlow: {tf.__version__}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ------------------------------------------------------------------------------\n# DATA GENERATORS DEFINITION\n# ------------------------------------------------------------------------------\n\ndef get_data_generators():\n    df = pd.read_csv(CSV_PATH)\n    \n    \n    df['binary_target'] = df['diagnosis'].apply(lambda x: 1 if x > 0 else 0)\n    df['file_path'] = df['id_code'].apply(lambda x: os.path.join(TRAIN_IMG_DIR, f\"{x}.png\"))\n    \n    # Stratified Split\n    train_df, val_df = train_test_split(df, test_size=0.2, random_state=SEED, stratify=df['binary_target'])\n    \n    class DataGenerator(tf.keras.utils.Sequence):\n        def __init__(self, df, batch_size=BATCH_SIZE, augment=False):\n            self.df = df\n            self.batch_size = batch_size\n            self.augment = augment\n            self.indices = np.arange(len(self.df))\n            \n            self.aug_layers = tf.keras.Sequential([\n                tf.keras.layers.RandomFlip(\"horizontal\"),\n                tf.keras.layers.RandomRotation(0.1),\n                tf.keras.layers.RandomZoom(0.1)\n            ]) if augment else None\n\n        def __len__(self):\n            return int(np.ceil(len(self.df) / self.batch_size))\n\n        def __getitem__(self, index):\n            indices = self.indices[index*self.batch_size:(index+1)*self.batch_size]\n            batch_df = self.df.iloc[indices]\n            X = np.empty((len(batch_df), IMG_SIZE, IMG_SIZE, 3), dtype=np.float32)\n            y = np.empty((len(batch_df)), dtype=np.float32)\n            \n            for i, (_, row) in enumerate(batch_df.iterrows()):\n                # استدعاء دالة الـ preprocess التي وضعتِها في الخلية السابقة\n                img = preprocess_pipeline(row['file_path']) \n                X[i,] = img\n                y[i] = row['binary_target']\n                \n            if self.augment: X = self.aug_layers(X)\n            return X, y\n\n        def on_epoch_end(self):\n            np.random.shuffle(self.indices)\n\n    train_gen = DataGenerator(train_df, batch_size=BATCH_SIZE, augment=True)\n    val_gen = DataGenerator(val_df, batch_size=BATCH_SIZE, augment=False)\n    \n    return train_gen, val_gen, train_df, val_df\n\nprint(\"✅ تم تعريف وظائف البيانات بنجاح!\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def crop_image_from_gray(img, tol=7):\n    if img.ndim == 2:\n        mask = img > tol\n        return img[np.ix_(mask.any(1), mask.any(0))]\n    elif img.ndim == 3:\n        gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n        mask = gray_img > tol\n        if (img[:,:,0][np.ix_(mask.any(1), mask.any(0))].shape[0] == 0): \n            return img\n        else:\n            img1 = img[:,:,0][np.ix_(mask.any(1), mask.any(0))]\n            img2 = img[:,:,1][np.ix_(mask.any(1), mask.any(0))]\n            img3 = img[:,:,2][np.ix_(mask.any(1), mask.any(0))]\n            img = np.stack([img1, img2, img3], axis=-1)\n        return img\n\ndef preprocess_pipeline(image_path, sigmaX=10):\n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = crop_image_from_gray(img)\n    img = cv2.resize(img, (IMG_SIZE, IMG_SIZE))\n    # تطبيق Ben Graham's preprocessing\n    img = cv2.addWeighted(img, 4, cv2.GaussianBlur(img, (0, 0), sigmaX), -4, 128)\n    return img.astype('float32') / 255.0","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CBAMBlock(Layer):\n    def __init__(self, reduction_ratio=16, **kwargs):\n        super(CBAMBlock, self).__init__(**kwargs)\n        self.reduction_ratio = reduction_ratio\n\n    def build(self, input_shape):\n        channel_dims = input_shape[-1]\n        self.shared_dense_one = Dense(channel_dims // self.reduction_ratio, activation='relu')\n        self.shared_dense_two = Dense(channel_dims)\n        self.conv2d_spatial = Conv2D(1, (7, 7), padding='same', activation='sigmoid')\n        super(CBAMBlock, self).build(input_shape)\n\n    def call(self, inputs):\n        # Channel Attention\n        avg_pool = tf.reduce_mean(inputs, axis=[1, 2], keepdims=True)\n        max_pool = tf.reduce_max(inputs, axis=[1, 2], keepdims=True)\n        avg_out = self.shared_dense_two(self.shared_dense_one(avg_pool))\n        max_out = self.shared_dense_two(self.shared_dense_one(max_pool))\n        channel_attn = tf.nn.sigmoid(avg_out + max_out)\n        refined = inputs * channel_attn\n        # Spatial Attention\n        spatial_attn = self.conv2d_spatial(tf.concat([tf.reduce_mean(refined, axis=-1, keepdims=True), \n                                                      tf.reduce_max(refined, axis=-1, keepdims=True)], axis=-1))\n        return refined * spatial_attn\n\n# بناء الموديل النهائي\nbase_model = EfficientNetB3(weights='imagenet', include_top=False, input_shape=(IMG_SIZE, IMG_SIZE, 3))\nx = base_model.output\nx = CBAMBlock()(x)\nx = GlobalAveragePooling2D()(x)\nx = BatchNormalization()(x)\nx = Dense(256, activation='relu')(x)\nx = Dropout(0.5)(x)\noutputs = Dense(1, activation='sigmoid')(x)\n\nmodel = Model(inputs=base_model.input, outputs=outputs)\nmodel.compile(optimizer=Adam(learning_rate=LEARNING_RATE), loss='binary_crossentropy', metrics=['accuracy'])\nprint(\"✅ تم بناء الموديل بنجاح.\")\n\nmodel.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_gen, val_gen, train_df, val_df = get_data_generators()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ------------------------------------------------------------------------------\n# PHASE 4: DATA PREPARATION & TRAINING\n# ------------------------------------------------------------------------------\n\n# 1. استدعاء المولدات \nprint(\"⌛ جاري تحميل وتجهيز البيانات...\")\ntrain_gen, val_gen, train_df, val_df = get_data_generators()\n\n# 2. تعريف الـ Callbacks للحصول على أفضل رسم بياني ومنع الـ Overfitting\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\n\ncallbacks = [\n    # التوقف التلقائي إذا بدأ الموديل يحفظ الصور (يمنع الفجوة الكبيرة بين الخطين)\n    EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True, verbose=1),\n    \n    # تقليل سرعة التعلم إذا ثبتت الدقة لضمان استقرار الخط البرتقالي\n    ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, min_lr=1e-7, verbose=1),\n    \n]\n\n# 3. (Training)\n\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=30, \n    callbacks=callbacks,\n    verbose=1\n)\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ------------------------------------------------------------------------------\n# رسم النتائج النهائية (الدقة والخسارة)\n# ------------------------------------------------------------------------------\nimport matplotlib.pyplot as plt\n\nplt.figure(figsize=(14, 5))\n\n# 1. رسم منحنى الدقة (Accuracy)\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='Training Accuracy', color='#1f77b4', linewidth=2, marker='o')\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy', color='#ff7f0e', linewidth=2, marker='x')\nplt.title('Model Accuracy Progress', fontsize=14)\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.grid(True, linestyle='--', alpha=0.6)\nplt.legend()\n\n# 2. رسم منحنى الخسارة (Loss)\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Training Loss', color='#1f77b4', linewidth=2, marker='o')\nplt.plot(history.history['val_loss'], label='Validation Loss', color='#ff7f0e', linewidth=2, marker='x')\nplt.title('Model Loss Progress', fontsize=14)\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.grid(True, linestyle='--', alpha=0.6)\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix, classification_report\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport numpy as np\n\ny_true = []\ny_pred = []\n\nfor i in range(len(val_gen)):\n    X_batch, y_batch = val_gen[i]\n    \n    preds = model.predict(X_batch, verbose=0)\n    \n    # y_batch مباشرة\n    y_true.extend(y_batch)\n    \n    # predictions → class\n    y_pred.extend(np.argmax(preds, axis=1))\n\ny_true = np.array(y_true)\ny_pred = np.array(y_pred)\n\n# Confusion Matrix\ncm = confusion_matrix(y_true, y_pred)\n\nclass_names = ['Normal', 'Mild', 'Moderate', 'Severe', 'Proliferative']\n\nplt.figure(figsize=(8,7))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n            xticklabels=class_names,\n            yticklabels=class_names)\n\nplt.title('Confusion Matrix (5 Classes)')\nplt.ylabel('Actual')\nplt.xlabel('Predicted')\nplt.show()\n\n# Report\nprint(classification_report(y_true, y_pred, target_names=class_names))\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import FileLink\n\n\nFileLink(r'Diabetic_Retinopathy_Model_98.keras')","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}