{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":4104,"databundleVersionId":46661,"sourceType":"competition"},{"sourceId":7251,"sourceType":"datasetVersion","datasetId":2798},{"sourceId":7866129,"sourceType":"datasetVersion","datasetId":4614938},{"sourceId":7869237,"sourceType":"datasetVersion","datasetId":4617269}],"dockerImageVersionId":30673,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**Pre traitement et l'importation des bibliothèques nécessaires**","metadata":{}},{"cell_type":"code","source":"# copy the weights and configurations for the pre-trained models \n!mkdir ~/.keras\n!mkdir ~/.keras/models7\n!cp ../input/keras-pretrained-models/*notop* ~/.keras/models/\n!cp ../input/keras-pretrained-models/imagenet_class_index.json ~/.keras/models/","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:40:26.071279Z","iopub.execute_input":"2025-05-13T21:40:26.071694Z","iopub.status.idle":"2025-05-13T21:40:36.150763Z","shell.execute_reply.started":"2025-05-13T21:40:26.071668Z","shell.execute_reply":"2025-05-13T21:40:36.149436Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom glob import glob\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom sklearn.metrics import cohen_kappa_score, confusion_matrix, accuracy_score, ConfusionMatrixDisplay","metadata":{"execution":{"iopub.status.busy":"2025-05-13T21:40:36.152836Z","iopub.execute_input":"2025-05-13T21:40:36.153129Z","iopub.status.idle":"2025-05-13T21:40:36.158554Z","shell.execute_reply.started":"2025-05-13T21:40:36.153105Z","shell.execute_reply":"2025-05-13T21:40:36.157587Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"thetrainpath= glob('/kaggle/input/diabetic-retinopathy-train-unzipped/train/*.jpeg')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:40:36.159425Z","iopub.execute_input":"2025-05-13T21:40:36.159665Z","iopub.status.idle":"2025-05-13T21:40:37.313821Z","shell.execute_reply.started":"2025-05-13T21:40:36.159645Z","shell.execute_reply":"2025-05-13T21:40:37.313104Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"file_lbl=\"/kaggle/input/diabetic-retinopathy-detection/trainLabels.csv.zip\"\ndf_train=pd.read_csv(file_lbl,sep=',')\ndf_train","metadata":{"execution":{"iopub.status.busy":"2025-05-13T21:40:37.316118Z","iopub.execute_input":"2025-05-13T21:40:37.316715Z","iopub.status.idle":"2025-05-13T21:40:37.348611Z","shell.execute_reply.started":"2025-05-13T21:40:37.316682Z","shell.execute_reply":"2025-05-13T21:40:37.347722Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Dossier contenant les images d'entraînement\ntrain_images_path = '/kaggle/input/diabetic-retinopathy-train-unzipped/train/'\n\n# Obtenir la liste des fichiers image avec extension .jpeg\nimage_files = glob(os.path.join(train_images_path, '*.jpeg'))\n\n# Chargement du fichier CSV contenant les étiquettes\nlabels_path = os.path.join('/kaggle/input/diabetic-retinopathy-train-unzipped', file_lbl)\nlabels_df = pd.read_csv(labels_path)\n\n# Ajout de l'identifiant du patient et du chemin complet de l'image\nlabels_df['PatientId'] = labels_df['image'].apply(lambda name: name.split('_')[0])\nlabels_df['image_path'] = labels_df['image'].apply(lambda name: os.path.join(train_images_path, f'{name}.jpeg'))\n\n# Vérification de l'existence du fichier image\nlabels_df['is_present'] = labels_df['image_path'].apply(os.path.exists)\nprint(f\"{labels_df['is_present'].sum()} images disponibles sur {labels_df.shape[0]} attendues.\")\n\n# Encodage de l'œil (gauche = 1, droite = 0)\nlabels_df['eye_side'] = labels_df['image'].apply(lambda name: 1 if name.endswith('left') else 0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:40:37.349629Z","iopub.execute_input":"2025-05-13T21:40:37.349864Z","iopub.status.idle":"2025-05-13T21:41:39.454196Z","shell.execute_reply.started":"2025-05-13T21:40:37.349845Z","shell.execute_reply":"2025-05-13T21:41:39.453266Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.utils import to_categorical\n\n# Conversion de la colonne 'level' en vecteurs one-hot\nlabels_df['level_encoded'] = labels_df['level'].apply(\n    lambda label: to_categorical(label, num_classes=labels_df['level'].max() + 1)\n)\n\n# Suppression des lignes contenant des valeurs manquantes\nlabels_df.dropna(inplace=True)\n\n# Filtrage pour ne garder que les images disponibles\nlabels_df = labels_df[labels_df['is_present']]\n\n# Affichage de quelques exemples\nlabels_df.sample(3)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:39.455313Z","iopub.execute_input":"2025-05-13T21:41:39.455568Z","iopub.status.idle":"2025-05-13T21:41:41.257604Z","shell.execute_reply.started":"2025-05-13T21:41:39.455547Z","shell.execute_reply":"2025-05-13T21:41:41.256814Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Visualiser la distribution des variables 'level' et 'eye' avec un pairplot\nsns.pairplot(labels_df[['level', 'eye_side']], hue='eye_side', palette='coolwarm')\nplt.show()\n\n# Violinplot pour la distribution des niveaux selon l'œil\nsns.violinplot(x='eye_side', y='level', data=labels_df, palette='muted')\nplt.title('Répartition des niveaux en fonction de l\\'œil')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:41.258899Z","iopub.execute_input":"2025-05-13T21:41:41.259609Z","iopub.status.idle":"2025-05-13T21:41:42.603628Z","shell.execute_reply.started":"2025-05-13T21:41:41.259573Z","shell.execute_reply":"2025-05-13T21:41:42.602721Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils import resample\nimport matplotlib.pyplot as plt\n\n# Préparation des données à partir du jeu de données 'labels_df'\n# Extraction des identifiants patients uniques avec leurs niveaux de gravité\nunique_patients_df = labels_df[['PatientId', 'level']].drop_duplicates()\n\n# Séparation stratifiée des patients pour garantir une répartition équilibrée des niveaux\ntrain_patients, val_patients = train_test_split(\n    unique_patients_df['PatientId'],\n    test_size=0.25,\n    stratify=unique_patients_df['level'],\n    random_state=2018\n)\n\n# Filtrage des données complètes selon les identifiants sélectionnés\ntrain_df = labels_df[labels_df['PatientId'].isin(train_patients)]\nval_df = labels_df[labels_df['PatientId'].isin(val_patients)]\n\nprint(f\"Nombre d'observations - Entraînement: {train_df.shape[0]}, Validation: {val_df.shape[0]}\")\n\n# Équilibrage de l'ensemble d'entraînement par sur-échantillonnage (oversampling)\n# Calcul du nombre cible d'échantillons pour chaque groupe (level, eye)\ntarget_count = train_df.groupby(['level', 'eye_side']).size().max()\n\n# Application de la stratégie de sur-échantillonnage\nbalanced_train_df = (\n    train_df.groupby(['level', 'eye_side'], group_keys=False)\n    .apply(lambda group: resample(group, replace=True, n_samples=target_count, random_state=2018))\n    .reset_index(drop=True)\n)\n\nprint(f\"Taille après équilibrage de l'ensemble d'entraînement : {balanced_train_df.shape[0]}\")\n\n# Visualisation de la distribution des classes dans l'ensemble équilibré\nfig, axes = plt.subplots(1, 2, figsize=(12, 5))\nbalanced_train_df['level'].value_counts().sort_index().plot(kind='bar', ax=axes[0], title=\"Répartition par niveau\")\nbalanced_train_df['eye_side'].value_counts().sort_index().plot(kind='bar', ax=axes[1], title=\"Répartition par œil\")\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:42.604682Z","iopub.execute_input":"2025-05-13T21:41:42.604986Z","iopub.status.idle":"2025-05-13T21:41:42.985549Z","shell.execute_reply.started":"2025-05-13T21:41:42.604958Z","shell.execute_reply":"2025-05-13T21:41:42.984600Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array\nfrom tensorflow.keras.utils import to_categorical\n\n\n# 1 Initialisation des ImageDataGenerators (avec et sans augmentation)\ntrain_datagen = ImageDataGenerator(\n    rescale           = 1. / 255,\n    rotation_range    = 40,\n    width_shift_range = 0.20,\n    height_shift_range= 0.20,\n    shear_range       = 0.20,\n    zoom_range        = 0.20,\n    horizontal_flip   = True,\n    fill_mode         = \"nearest\"\n)\n\nvalid_datagen = ImageDataGenerator(rescale = 1. / 255)\n\n# 2 Fonction utilitaire pour générer les batches à partir d'un DataFrame\ndef generate_from_dataframe(df,\n                             datagen,\n                             batch_size  : int = 32,\n                             target_size : tuple = (224, 224),\n                             num_classes : int = None,\n                             shuffle     : bool = True):\n    \"\"\"\n    Génère des batches (images, labels) en appliquant des transformations \n    via ImageDataGenerator.\n\n    Paramètres\n    ----------\n    df          : DataFrame avec au minimum les colonnes 'image_path' et 'level'\n    datagen     : un ImageDataGenerator configuré\n    batch_size  : nombre d'images par batch\n    target_size : taille (H, W) des images après redimensionnement\n    num_classes : si précisé, les labels seront encodés en one-hot\n    shuffle     : réorganiser le DataFrame au début de chaque époque\n    \"\"\"\n    while True:\n        # Réorganiser les lignes du DataFrame si nécessaire\n        if shuffle:\n            df = df.sample(frac=1, random_state=None).reset_index(drop=True)\n\n        # Parcourir les données par batchs\n        for start in range(0, len(df), batch_size):\n            end   = start + batch_size\n            batch = df.iloc[start:end]\n\n            # ----- Chargement des images -----\n            imgs, lbls = [], []\n            for _, row in batch.iterrows():\n                # Chargement et redimensionnement de l'image\n                img = load_img(row['image_path'], target_size=target_size)   # <-- colonne adaptée\n                imgs.append(img_to_array(img))\n                lbls.append(row['level'])                                    # <-- toujours 'level'\n\n            # Conversion en tableaux NumPy\n            X = np.array(imgs, dtype=np.float32)\n            y = np.array(lbls, dtype=np.int64)\n\n            # Encodage des labels en one-hot si nécessaire\n            if num_classes is not None:\n                y = to_categorical(y, num_classes=num_classes)\n\n            # ImageDataGenerator.flow est un générateur infini ; on \"déballe\" un batch à la fois\n            for aug_X, aug_y in datagen.flow(X, y, batch_size=batch_size, shuffle=False):\n                yield aug_X, aug_y\n                break   # Sortir après un seul batch pour que la boucle externe contrôle l'ordre\n\n# 3 Détection automatique du nombre de classes et création des générateurs\n# Identification du nombre de classes à partir du DataFrame d'entraînement\nnum_classes = balanced_train_df['level'].nunique()\n\n# Création des générateurs d'entraînement et de validation\ntrain_generator = generate_from_dataframe(\n    balanced_train_df,\n    train_datagen,\n    batch_size   = 32,\n    target_size  = (224, 224),\n    num_classes  = num_classes\n)\n\nvalid_generator = generate_from_dataframe(\n    val_df,\n    valid_datagen,\n    batch_size   = 32,\n    target_size  = (224, 224),\n    num_classes  = num_classes,\n    shuffle      = False  # Pas de réorganisation des données de validation entre les époques\n)\n\n# 4 Vérification rapide : récupérer un batch\nX_b, y_b = next(train_generator)\nprint(f\"Shape des images : {X_b.shape}\")   # -> (32, 224, 224, 3)\nprint(f\"Shape des labels : {y_b.shape}\")   # -> (32, 5) si num_classes == 5\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:42.986819Z","iopub.execute_input":"2025-05-13T21:41:42.987102Z","iopub.status.idle":"2025-05-13T21:41:45.708179Z","shell.execute_reply.started":"2025-05-13T21:41:42.987073Z","shell.execute_reply":"2025-05-13T21:41:45.707358Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\ndef visaugmtdimgs(generator, batch_size=8):\n    \"\"\"\n    Visualise un batch d'images augmentées avec leurs labels.\n\n    Paramètres :\n    - generator : générateur d'images augmentées\n    - batch_size : nombre d'images à afficher\n    \"\"\"\n    # Obtenir un batch d'images et de labels\n    t_x, t_y = next(generator)\n\n    # Vérifier la taille du batch pour s'assurer qu'il est assez grand\n    actual_batch_size = min(batch_size, t_x.shape[0])\n\n    # Créer une figure avec des sous-graphiques\n    fig, axes = plt.subplots(2, 4, figsize=(18, 9))\n    axes = axes.flatten()\n\n    for i in range(actual_batch_size):\n        # Dé-normaliser les images pour la visualisation\n        img = np.clip(t_x[i] * 255, 0, 255).astype(np.uint8)\n\n        # Identification du niveau et de l'œil\n        level = np.argmax(t_y[i])  # Récupération du niveau de gravité\n        eye_side = \"left\" if level % 2 == 0 else \"right\"  # Adaptation simplifiée\n\n        # Affichage de l'image\n        axes[i].imshow(img)\n        axes[i].axis('off')\n        axes[i].set_title(f\"Niveau : {level}\")\n\n    # Supprimer les sous-graphiques inutilisés s'il y en a\n    for j in range(actual_batch_size, len(axes)):\n        axes[j].axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\n# Exemple d'utilisation : visualiser les images augmentées du générateur d'entraînement\nvisaugmtdimgs(train_generator, batch_size=8)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:45.712234Z","iopub.execute_input":"2025-05-13T21:41:45.712586Z","iopub.status.idle":"2025-05-13T21:41:49.621404Z","shell.execute_reply.started":"2025-05-13T21:41:45.712544Z","shell.execute_reply":"2025-05-13T21:41:49.620023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\ndef plot_valid_img(validation_gen, batch_size=8):\n    \"\"\"\n    Visualise un batch d'images de validation avec leurs informations associées.\n\n    Paramètres :\n    - valid_gen : générateur d'images de validation\n    - batch_size : nombre d'images à afficher\n    \"\"\"\n    # Récupérer un batch complet\n    v_x, v_y = next(validation_gen)\n\n    # Ajuster le batch_size si le batch est plus petit\n    actual_batch_size = min(batch_size, len(v_x))\n\n    # Configuration de la figure avec des sous-graphiques\n    fig, axes = plt.subplots(2, 4, figsize=(20, 10))\n    axes = axes.flatten()\n\n    for i in range(actual_batch_size):\n        # Image\n        img = np.clip(v_x[i] * 255, 0, 255).astype(np.uint8)\n\n        # Label (one-hot -> index)\n        level = np.argmax(v_y[i])\n\n        # Affichage\n        axes[i].imshow(img)\n        axes[i].axis('off')\n        axes[i].set_title(f\"Niveau : {level}\")\n\n    # Cacher les axes inutilisés si le batch est incomplet\n    for j in range(actual_batch_size, len(axes)):\n        axes[j].axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\n# Visualiser un batch de validation\nplot_valid_img(valid_generator, batch_size=8)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:49.622660Z","iopub.execute_input":"2025-05-13T21:41:49.622977Z","iopub.status.idle":"2025-05-13T21:41:52.512406Z","shell.execute_reply.started":"2025-05-13T21:41:49.622950Z","shell.execute_reply":"2025-05-13T21:41:52.511189Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport tensorflow as tf\nfrom tensorflow.keras.applications import InceptionV3\nfrom tensorflow.keras.layers import (\n    Input, Conv2D, Dropout, multiply, GlobalAveragePooling2D,\n    Lambda, Dense, BatchNormalization\n)\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.metrics import TopKCategoricalAccuracy\n\n\n# 1. Initialisation des hyperparamètres\ninput_shape = (224, 224, 3)\nnum_classes = balanced_train_df['level'].nunique()\ndropout_rate = 0.5\nattention_filters = [64, 16, 8]\n\n# 2. Définition de l'Input Layer\nimage_input = Input(shape=input_shape, name=\"Input_Image\")\n\n# 3. Modèle pré-entraîné : InceptionV3 (couche de base)\nbase_model = InceptionV3(\n    input_shape=input_shape,\n    include_top=False,\n    weights='imagenet'\n)\nbase_model.trainable = False\n\n# Extraction des caractéristiques de la base pré-entraînée\nbase_features = base_model(image_input)\nbase_features = BatchNormalization(name=\"Base_BatchNorm\")(base_features)\n\n# 4. Mécanisme d'Attention\nattention_layer = base_features\n\nfor filters in attention_filters:\n    attention_layer = Conv2D(filters, kernel_size=(1, 1), padding='same', activation='relu')(Dropout(dropout_rate)(attention_layer))\n\n# Couche finale d'attention avec sigmoid pour la mise à l'échelle des activations\nattention_layer = Conv2D(1, kernel_size=(1, 1), padding='valid', activation='sigmoid', name=\"Attention_Mask\")(attention_layer)\n\n# Redimensionnement du masque pour qu'il corresponde aux canaux de `base_features`\nup_conv = Conv2D(\n    filters=base_features.shape[-1],\n    kernel_size=(1, 1),\n    padding='same',\n    activation='linear',\n    use_bias=False,\n    name=\"Expand_Mask\"\n)\n\n# Création du poids constant (1) pour la couche `up_conv`\ninitial_weights = np.ones((1, 1, 1, base_features.shape[-1]))\n\n# Assigner manuellement les poids à `up_conv`\nup_conv.build(input_shape=attention_layer.shape)\nup_conv.set_weights([initial_weights])\nup_conv.trainable = False\n\n# Appliquer la couche de redimensionnement\nattention_layer = up_conv(attention_layer)\n\n# 5. Application du masque d'attention\nmasked_features = multiply([attention_layer, base_features], name=\"Masked_Features\")\n\n# 6. Global Average Pooling & Rescaling\ngap_features = GlobalAveragePooling2D(name=\"GAP_Features\")(masked_features)\ngap_mask = GlobalAveragePooling2D(name=\"GAP_Mask\")(attention_layer)\n\n# Recalage pour compenser les pixels masqués\nrescaled_gap = Lambda(lambda x: x[0] / (x[1] + 1e-6), name=\"RescaleGAP\")([gap_features, gap_mask])\n\n# 7. Couches Fully Connected\nfc_layer = Dropout(0.25, name=\"Dropout1\")(rescaled_gap)\nfc_layer = Dense(128, activation='relu', name=\"FC_Layer\")(fc_layer)\nfc_layer = Dropout(0.25, name=\"Dropout2\")(fc_layer)\n\n# 8. Couche de sortie\noutput_layer = Dense(num_classes, activation='softmax', name=\"Output_Layer\")(fc_layer)\n\n# 9. Modèle final\nretina_model = Model(inputs=image_input, outputs=output_layer, name=\"Retina_Attention_Model\")\n\n\n\n# Compilation of the model\nretina_model.compile(\n    optimizer='adam',\n    loss='categorical_crossentropy',\n    metrics=[\n        'categorical_accuracy', \n        TopKCategoricalAccuracy(k=2, name='top_2_accuracy')\n    ]\n)\n\n\n# Résumé du modèle\nretina_model.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:52.514089Z","iopub.execute_input":"2025-05-13T21:41:52.514931Z","iopub.status.idle":"2025-05-13T21:41:54.160586Z","shell.execute_reply.started":"2025-05-13T21:41:52.514903Z","shell.execute_reply":"2025-05-13T21:41:54.159713Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau\n\nweight_path = \"{}_weights.best.weights.h5\".format('retina')\n\ncheckpoint = ModelCheckpoint(weight_path, monitor='val_loss', verbose=1, \n                             save_best_only=True, mode='min', save_weights_only=True)\n\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.8, patience=3, verbose=1, mode='auto', min_lr=0.0001)\nearly = EarlyStopping(monitor=\"val_loss\", mode=\"min\", patience=6)\n\ncallbacks_list = [checkpoint, early, reduceLROnPlat]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.161683Z","iopub.execute_input":"2025-05-13T21:41:54.161936Z","iopub.status.idle":"2025-05-13T21:41:54.670757Z","shell.execute_reply.started":"2025-05-13T21:41:54.161915Z","shell.execute_reply":"2025-05-13T21:41:54.669486Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -rf ~/.keras # clean up before starting training","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.671616Z","iopub.status.idle":"2025-05-13T21:41:54.672026Z","shell.execute_reply.started":"2025-05-13T21:41:54.671814Z","shell.execute_reply":"2025-05-13T21:41:54.671832Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"batch_size = 32\n\nretina_model.fit(\n    train_generator, \n    steps_per_epoch=balanced_train_df.shape[0] // batch_size,\n    validation_data=valid_generator, \n    validation_steps=val_df.shape[0] // batch_size,\n    epochs=25, \n    callbacks=callbacks_list\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.673503Z","iopub.status.idle":"2025-05-13T21:41:54.673899Z","shell.execute_reply.started":"2025-05-13T21:41:54.673693Z","shell.execute_reply":"2025-05-13T21:41:54.673711Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nprint(tf.__version__)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# load the best version of the model\nretina_model.load_weights(weight_path)\nretina_model.save('full_retina_model.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.675243Z","iopub.status.idle":"2025-05-13T21:41:54.675648Z","shell.execute_reply.started":"2025-05-13T21:41:54.675438Z","shell.execute_reply":"2025-05-13T21:41:54.675456Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"##### create one fixed dataset for evaluating\nfrom tqdm import tqdm_notebook\n# fresh valid gen\nvalid_gen = flow_from_dataframe(valid_datagen, val_df, \n                             path_col = 'path',\n                            y_col = 'level_encoded') \nvbatch_count = (val_df.shape[0]//batch_size-1)\nout_size = vbatch_count*batch_size\ntest_X = np.zeros((out_size,)+t_x.shape[1:], dtype = np.float32)\ntest_Y = np.zeros((out_size,)+t_y.shape[1:], dtype = np.float32)\nfor i, (c_x, c_y) in zip(tqdm_notebook(range(vbatch_count)), \n                         valid_gen):\n    j = i*batch_size\n    test_X[j:(j+c_x.shape[0])] = c_x\n    test_Y[j:(j+c_x.shape[0])] = c_y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.676799Z","iopub.status.idle":"2025-05-13T21:41:54.677204Z","shell.execute_reply.started":"2025-05-13T21:41:54.676997Z","shell.execute_reply":"2025-05-13T21:41:54.677015Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"show attention","metadata":{}},{"cell_type":"code","source":"# get the attention layer since it is the only one with a single output dim\nfor attn_layer in retina_model.layers:\n    c_shape = attn_layer.get_output_shape_at(0)\n    if len(c_shape)==4:\n        if c_shape[-1]==1:\n            print(attn_layer)\n            break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.678485Z","iopub.status.idle":"2025-05-13T21:41:54.678875Z","shell.execute_reply.started":"2025-05-13T21:41:54.678669Z","shell.execute_reply":"2025-05-13T21:41:54.678687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import keras.backend as K\nrand_idx = np.random.choice(range(len(test_X)), size = 6)\nattn_func = K.function(inputs = [retina_model.get_input_at(0), K.learning_phase()],\n           outputs = [attn_layer.get_output_at(0)]\n          )\nfig, m_axs = plt.subplots(len(rand_idx), 2, figsize = (8, 4*len(rand_idx)))\n[c_ax.axis('off') for c_ax in m_axs.flatten()]\nfor c_idx, (img_ax, attn_ax) in zip(rand_idx, m_axs):\n    cur_img = test_X[c_idx:(c_idx+1)]\n    attn_img = attn_func([cur_img, 0])[0]\n    img_ax.imshow(np.clip(cur_img[0,:,:,:]*127+127, 0, 255).astype(np.uint8))\n    attn_ax.imshow(attn_img[0, :, :, 0]/attn_img[0, :, :, 0].max(), cmap = 'viridis', \n                   vmin = 0, vmax = 1, \n                   interpolation = 'lanczos')\n    real_cat = np.argmax(test_Y[c_idx, :])\n    img_ax.set_title('Eye Image\\nCat:%2d' % (real_cat))\n    pred_cat = retina_model.predict(cur_img)\n    attn_ax.set_title('Attention Map\\nPred:%2.2f%%' % (100*pred_cat[0,real_cat]))\nfig.savefig('attention_map.png', dpi = 300)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.680435Z","iopub.status.idle":"2025-05-13T21:41:54.680839Z","shell.execute_reply.started":"2025-05-13T21:41:54.680632Z","shell.execute_reply":"2025-05-13T21:41:54.680649Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"eval the results ","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score, classification_report\npred_Y = retina_model.predict(test_X, batch_size = 32, verbose = True)\npred_Y_cat = np.argmax(pred_Y, -1)\ntest_Y_cat = np.argmax(test_Y, -1)\nprint('Accuracy on Test Data: %2.2f%%' % (accuracy_score(test_Y_cat, pred_Y_cat)))\nprint(classification_report(test_Y_cat, pred_Y_cat))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.681726Z","iopub.status.idle":"2025-05-13T21:41:54.682145Z","shell.execute_reply.started":"2025-05-13T21:41:54.681916Z","shell.execute_reply":"2025-05-13T21:41:54.681934Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nfrom sklearn.metrics import confusion_matrix\nsns.heatmap(confusion_matrix(test_Y_cat, pred_Y_cat), \n            annot=True, fmt=\"d\", cbar = False, cmap = plt.cm.Blues, vmax = test_X.shape[0]//16)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.683107Z","iopub.status.idle":"2025-05-13T21:41:54.683530Z","shell.execute_reply.started":"2025-05-13T21:41:54.683313Z","shell.execute_reply":"2025-05-13T21:41:54.683331Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"roc curve sain vs malade","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import roc_curve, roc_auc_score\nsick_vec = test_Y_cat>0\nsick_score = np.sum(pred_Y[:,1:],1)\nfpr, tpr, _ = roc_curve(sick_vec, sick_score)\nfig, ax1 = plt.subplots(1,1, figsize = (6, 6), dpi = 150)\nax1.plot(fpr, tpr, 'b.-', label = 'Model Prediction (AUC: %2.2f)' % roc_auc_score(sick_vec, sick_score))\nax1.plot(fpr, fpr, 'g-', label = 'Random Guessing')\nax1.legend()\nax1.set_xlabel('False Positive Rate')\nax1.set_ylabel('True Positive Rate');","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.684996Z","iopub.status.idle":"2025-05-13T21:41:54.685431Z","shell.execute_reply.started":"2025-05-13T21:41:54.685187Z","shell.execute_reply":"2025-05-13T21:41:54.685203Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, m_axs = plt.subplots(2, 4, figsize = (32, 20))\nfor (idx, c_ax) in enumerate(m_axs.flatten()):\n    c_ax.imshow(np.clip(test_X[idx]*127+127,0 , 255).astype(np.uint8), cmap = 'bone')\n    c_ax.set_title('Actual Severity: {}\\n{}'.format(test_Y_cat[idx], \n                                                           '\\n'.join(['Predicted %02d (%04.1f%%): %s' % (k, 100*v, '*'*int(10*v)) for k, v in sorted(enumerate(pred_Y[idx]), key = lambda x: -1*x[1])])), loc='left')\n    c_ax.axis('off')\nfig.savefig('trained_img_predictions.png', dpi = 300)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-13T21:41:54.686725Z","iopub.status.idle":"2025-05-13T21:41:54.687143Z","shell.execute_reply.started":"2025-05-13T21:41:54.686923Z","shell.execute_reply":"2025-05-13T21:41:54.686940Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"paths_train= glob('/kaggle/input/diabetic-retinopathy-train-unzipped/train/*.jpeg')\nimage=cv2.imread(paths_train[0])\nplt.imshow(image)","metadata":{"execution":{"iopub.status.busy":"2025-05-13T21:41:54.688284Z","iopub.status.idle":"2025-05-13T21:41:54.688697Z","shell.execute_reply.started":"2025-05-13T21:41:54.688487Z","shell.execute_reply":"2025-05-13T21:41:54.688504Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"paths_test = glob('/kaggle/input/diabetic-retinopathy-test-unzipped/test/*.jpeg')\nimage=cv2.imread(paths_test[0])\nplt.imshow(image)","metadata":{"execution":{"iopub.status.busy":"2025-05-13T21:41:54.689965Z","iopub.status.idle":"2025-05-13T21:41:54.690397Z","shell.execute_reply.started":"2025-05-13T21:41:54.690156Z","shell.execute_reply":"2025-05-13T21:41:54.690173Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"file_sub=\"/kaggle/input/diabetic-retinopathy-detection/sampleSubmission.csv.zip\"\ndf_submission=pd.read_csv(file_sub,sep=',')\ndf_submission.loc[0, 'level']=1\ndf_submission","metadata":{"execution":{"iopub.status.busy":"2025-05-13T21:41:54.691564Z","iopub.status.idle":"2025-05-13T21:41:54.691967Z","shell.execute_reply.started":"2025-05-13T21:41:54.691758Z","shell.execute_reply":"2025-05-13T21:41:54.691775Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_submission.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2025-05-13T21:41:54.692967Z","iopub.status.idle":"2025-05-13T21:41:54.693396Z","shell.execute_reply.started":"2025-05-13T21:41:54.693156Z","shell.execute_reply":"2025-05-13T21:41:54.693173Z"},"trusted":true},"outputs":[],"execution_count":null}]}