{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.13","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":4104},{"sourceType":"datasetVersion","sourceId":7866129},{"sourceType":"datasetVersion","sourceId":7869237},{"sourceType":"kernelVersion","sourceId":345873090}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":6454.836682,"end_time":"2026-08-30T16:14:13.868142","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2026-08-30T14:26:39.03146","version":"2.5.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"ff4fd2c9","cell_type":"markdown","source":"# Importation des librairies","metadata":{"papermill":{"duration":0.004566,"end_time":"2026-08-30T14:26:42.468127","exception":false,"start_time":"2026-08-30T14:26:42.463561","status":"completed"},"tags":[]}},{"id":"743faaf3","cell_type":"markdown","source":"Ce notebook ne réentraîne rien, il prend le modèle utilisé dans le premier notebook et prédit sur les images de test","metadata":{"papermill":{"duration":0.00384,"end_time":"2026-08-30T14:26:42.476147","exception":false,"start_time":"2026-08-30T14:26:42.472307","status":"completed"},"tags":[]}},{"id":"4b1e29c1","cell_type":"code","source":"import os\nimport glob\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\n\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras.applications.efficientnet import preprocess_input","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:23.858039Z","iopub.execute_input":"2026-08-30T17:46:23.858292Z","iopub.status.idle":"2026-08-30T17:46:38.377033Z","shell.execute_reply.started":"2026-08-30T17:46:23.858270Z","shell.execute_reply":"2026-08-30T17:46:38.376353Z"},"papermill":{"duration":17.926013,"end_time":"2026-08-30T14:27:00.406081","exception":false,"start_time":"2026-08-30T14:26:42.480068","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"0ccabb58","cell_type":"markdown","source":"## Fichiers du notebook 1","metadata":{"papermill":{"duration":0.003868,"end_time":"2026-08-30T14:27:00.414266","exception":false,"start_time":"2026-08-30T14:27:00.410398","status":"completed"},"tags":[]}},{"id":"18e43d5d","cell_type":"code","source":"CSV_DIR = \"/kaggle/input/competitions/diabetic-retinopathy-detection\"\nTEST_DIR = \"/kaggle/input/datasets/josephrynkiewicz/diabetic-retinopathy-test-unzipped/test\"\n\n# Paramètres identiques au notebook d'apprentissage\nIMG_SIZE = 512\nBATCH_SIZE = 8\nNUM_CLASSES = 5\nAUTOTUNE = tf.data.AUTOTUNE","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:40.547312Z","iopub.execute_input":"2026-08-30T17:46:40.548337Z","iopub.status.idle":"2026-08-30T17:46:40.553479Z","shell.execute_reply.started":"2026-08-30T17:46:40.548308Z","shell.execute_reply":"2026-08-30T17:46:40.552692Z"},"papermill":{"duration":0.010794,"end_time":"2026-08-30T14:27:00.429167","exception":false,"start_time":"2026-08-30T14:27:00.418373","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"bb23defd","cell_type":"code","source":"MODEL_DIR = \"/kaggle/input/notebooks/aminesellami/train\"\n\nMODEL_PATH = os.path.join(MODEL_DIR, \"final_model.keras\")\nTH_PATH = os.path.join(MODEL_DIR, \"thresholds.npy\")\nAL_PATH = os.path.join(MODEL_DIR, \"alpha.npy\")\n\nIMG_SIZE = 512\nBATCH_SIZE = 8\nNUM_CLASSES = 5\n\nAUTOTUNE = tf.data.AUTOTUNE\n\nprint(\"Model :\", MODEL_PATH)\nprint(\"Seuils :\", TH_PATH)\nprint(\"Alpha :\", AL_PATH)\n\nprint(\"Modèle présent :\", os.path.exists(MODEL_PATH))\nprint(\"Seuils présents :\", os.path.exists(TH_PATH))\nprint(\"Alpha présent :\", os.path.exists(AL_PATH))","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:41.837280Z","iopub.execute_input":"2026-08-30T17:46:41.837960Z","iopub.status.idle":"2026-08-30T17:46:41.848424Z","shell.execute_reply.started":"2026-08-30T17:46:41.837922Z","shell.execute_reply":"2026-08-30T17:46:41.847769Z"},"papermill":{"duration":0.017049,"end_time":"2026-08-30T14:27:00.450162","exception":false,"start_time":"2026-08-30T14:27:00.433113","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"4c1c9a3d","cell_type":"markdown","source":"## Paramètres","metadata":{"papermill":{"duration":0.003757,"end_time":"2026-08-30T14:27:00.458057","exception":false,"start_time":"2026-08-30T14:27:00.4543","status":"completed"},"tags":[]}},{"id":"8607d5ca","cell_type":"code","source":"thresholds = np.load(TH_PATH)\nalpha = float(np.load(AL_PATH))\n\nprint(\"Thresholds :\", thresholds)\nprint(\"Alpha :\", alpha)","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:44.581223Z","iopub.execute_input":"2026-08-30T17:46:44.581624Z","iopub.status.idle":"2026-08-30T17:46:44.591315Z","shell.execute_reply.started":"2026-08-30T17:46:44.581596Z","shell.execute_reply":"2026-08-30T17:46:44.590520Z"},"papermill":{"duration":0.017608,"end_time":"2026-08-30T14:27:00.48063","exception":false,"start_time":"2026-08-30T14:27:00.463022","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"566d4b1e","cell_type":"markdown","source":"# Prétraitement\n\nLes fonctions sont ici reprises du premier notebook 1. Le traitement est identique à celui de l'entraînement. ","metadata":{"papermill":{"duration":0.004051,"end_time":"2026-08-30T14:27:00.488911","exception":false,"start_time":"2026-08-30T14:27:00.48486","status":"completed"},"tags":[]}},{"id":"c24bf60e","cell_type":"code","source":"#suppression des bords noirs\ndef crop_dark_border(image, threshold=12):\n    gray = tf.image.rgb_to_grayscale(image)[..., 0]\n    mask = gray > threshold\n    rows = tf.reduce_any(mask, axis=1)\n    cols = tf.reduce_any(mask, axis=0)\n\n    def crop():\n        y = tf.where(rows)[:, 0]\n        x = tf.where(cols)[:, 0]\n        return image[\n            tf.reduce_min(y):tf.reduce_max(y)+1,\n            tf.reduce_min(x):tf.reduce_max(x)+1,:]\n\n    return tf.cond(tf.reduce_any(mask), crop, lambda: image)","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:45.896428Z","iopub.execute_input":"2026-08-30T17:46:45.896930Z","iopub.status.idle":"2026-08-30T17:46:45.902331Z","shell.execute_reply.started":"2026-08-30T17:46:45.896904Z","shell.execute_reply":"2026-08-30T17:46:45.901319Z"},"papermill":{"duration":0.012742,"end_time":"2026-08-30T14:27:00.505775","exception":false,"start_time":"2026-08-30T14:27:00.493033","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"7fe8f17d","cell_type":"code","source":"#lecture d'une image\ndef load_image(path):\n    image = tf.io.read_file(path)\n    image = tf.io.decode_jpeg(image, channels=3, ratio=2)\n    image = tf.cast(image, tf.float32)\n    image = crop_dark_border(image)\n    image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))\n\n    return image","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:46.545235Z","iopub.execute_input":"2026-08-30T17:46:46.546017Z","iopub.status.idle":"2026-08-30T17:46:46.550005Z","shell.execute_reply.started":"2026-08-30T17:46:46.545988Z","shell.execute_reply":"2026-08-30T17:46:46.549231Z"},"papermill":{"duration":0.011182,"end_time":"2026-08-30T14:27:00.521038","exception":false,"start_time":"2026-08-30T14:27:00.509856","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"0cee773f","cell_type":"markdown","source":"## Modèle\n\nOn reconstruit exactement la même architecture que dans le premier notebook en prenant les poids sauvegardés","metadata":{"papermill":{"duration":0.004028,"end_time":"2026-08-30T14:27:00.529112","exception":false,"start_time":"2026-08-30T14:27:00.525084","status":"completed"},"tags":[]}},{"id":"e06df4c0","cell_type":"code","source":"from tensorflow.keras.models import load_model\n\nmodel = load_model(\n    MODEL_PATH,\n    compile=False\n)\n\nprint(\"Model loaded.\")","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:47.194222Z","iopub.execute_input":"2026-08-30T17:46:47.194613Z","iopub.status.idle":"2026-08-30T17:46:54.109712Z","shell.execute_reply.started":"2026-08-30T17:46:47.194585Z","shell.execute_reply":"2026-08-30T17:46:54.109093Z"},"papermill":{"duration":0.01212,"end_time":"2026-08-30T14:27:00.545374","exception":false,"start_time":"2026-08-30T14:27:00.533254","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"9ac39dfd","cell_type":"code","source":"thresholds = np.load(TH_PATH)\nALPHA = float(np.load(AL_PATH)[0])\n\nprint(\"ALPHA\", ALPHA)\nprint(\"Seuils\", np.round(thresholds, 3))","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:54.111136Z","iopub.execute_input":"2026-08-30T17:46:54.111449Z","iopub.status.idle":"2026-08-30T17:46:54.117892Z","shell.execute_reply.started":"2026-08-30T17:46:54.111426Z","shell.execute_reply":"2026-08-30T17:46:54.117094Z"},"papermill":{"duration":6.791279,"end_time":"2026-08-30T14:27:07.34065","exception":false,"start_time":"2026-08-30T14:27:00.549371","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"c65482c3","cell_type":"code","source":"# Vérification\ndummy = tf.zeros((1, IMG_SIZE, IMG_SIZE, 3))\ndummy_pred = model.predict(dummy, verbose=0)\n\nprint(\"Prédiction test :\", dummy_pred)","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:46:54.118761Z","iopub.execute_input":"2026-08-30T17:46:54.119392Z","iopub.status.idle":"2026-08-30T17:47:09.498124Z","shell.execute_reply.started":"2026-08-30T17:46:54.119366Z","shell.execute_reply":"2026-08-30T17:47:09.497335Z"},"papermill":{"duration":12.274566,"end_time":"2026-08-30T14:27:19.621589","exception":false,"start_time":"2026-08-30T14:27:07.347023","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"dd61f23d","cell_type":"markdown","source":"## TTA","metadata":{"papermill":{"duration":0.006061,"end_time":"2026-08-30T14:27:19.634094","exception":false,"start_time":"2026-08-30T14:27:19.628033","status":"completed"},"tags":[]}},{"id":"0d038546","cell_type":"code","source":"# TTA : on prédit chaque image en 4 versions (original + flips) puis on fait la moyenne\ndef predict_tta(model, paths):\n    preds = []\n    for fh in [False, True]:\n        for fv in [False, True]:\n            def load_tta(p):\n                img = load_image(p)\n                if fh: img = tf.image.flip_left_right(img)\n                if fv: img = tf.image.flip_up_down(img)\n                return img\n            ds = (tf.data.Dataset.from_tensor_slices(paths)\n                  .map(load_tta, num_parallel_calls=AUTOTUNE)\n                  .batch(BATCH_SIZE).prefetch(AUTOTUNE))\n            preds.append(model.predict(ds, verbose=1))\n    return np.mean(preds, axis=0)","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:47:09.499421Z","iopub.execute_input":"2026-08-30T17:47:09.499619Z","iopub.status.idle":"2026-08-30T17:47:09.505245Z","shell.execute_reply.started":"2026-08-30T17:47:09.499599Z","shell.execute_reply":"2026-08-30T17:47:09.504404Z"},"papermill":{"duration":0.014011,"end_time":"2026-08-30T14:27:19.654084","exception":false,"start_time":"2026-08-30T14:27:19.640073","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"017f8b1f","cell_type":"markdown","source":"## Fonction de prédiction","metadata":{"papermill":{"duration":0.005503,"end_time":"2026-08-30T14:27:19.6652","exception":false,"start_time":"2026-08-30T14:27:19.659697","status":"completed"},"tags":[]}},{"id":"e02d846f","cell_type":"code","source":"#fct de prédiction\ndef predict_labels(probabilities, thresholds):\n    thresholds = np.asarray(thresholds)\n    predictions = (probabilities > thresholds).sum(axis=1) - 1\n    return np.clip(predictions, 0, NUM_CLASSES - 1)","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:47:12.402986Z","iopub.execute_input":"2026-08-30T17:47:12.403494Z","iopub.status.idle":"2026-08-30T17:47:12.407667Z","shell.execute_reply.started":"2026-08-30T17:47:12.403467Z","shell.execute_reply":"2026-08-30T17:47:12.406909Z"},"papermill":{"duration":0.012596,"end_time":"2026-08-30T14:27:19.683255","exception":false,"start_time":"2026-08-30T14:27:19.670659","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"7e8a2e06","cell_type":"markdown","source":"## Blending","metadata":{"papermill":{"duration":0.00535,"end_time":"2026-08-30T14:27:19.694121","exception":false,"start_time":"2026-08-30T14:27:19.688771","status":"completed"},"tags":[]}},{"id":"24e6e587","cell_type":"code","source":"#regroupement des probas des yeux gauche et droit\ndef patient_mean(df, proba):\n    d = df.reset_index(drop=True).copy()\n    d[\"patient\"] = d[\"image\"].str.split(\"_\").str[0]\n    cols = [f\"p{k}\" for k in range(NUM_CLASSES)]\n    for k in range(NUM_CLASSES):\n        d[f\"p{k}\"] = proba[:, k]\n    return d[cols].values, d.groupby(\"patient\")[cols].transform(\"mean\").values","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:47:14.474398Z","iopub.execute_input":"2026-08-30T17:47:14.474825Z","iopub.status.idle":"2026-08-30T17:47:14.480347Z","shell.execute_reply.started":"2026-08-30T17:47:14.474796Z","shell.execute_reply":"2026-08-30T17:47:14.479424Z"},"papermill":{"duration":0.013067,"end_time":"2026-08-30T14:27:19.712754","exception":false,"start_time":"2026-08-30T14:27:19.699687","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"fa57aacf","cell_type":"markdown","source":"## Prédiction sur le test","metadata":{"papermill":{"duration":0.005326,"end_time":"2026-08-30T14:27:19.723639","exception":false,"start_time":"2026-08-30T14:27:19.718313","status":"completed"},"tags":[]}},{"id":"0078eec4","cell_type":"code","source":"#fichier de soumission\ntest_df = pd.read_csv(f\"{CSV_DIR}/sampleSubmission.csv.zip\")\ntest_df[\"path\"] = TEST_DIR + \"/\" + test_df[\"image\"] + \".jpeg\"\n\nprint(\"Images de test :\", len(test_df))\n#print(\"Chemins valides :\", test_df[\"path\"].apply(os.path.exists).mean(), \"(doit valoir 1.0)\")","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:50:07.887705Z","iopub.execute_input":"2026-08-30T17:50:07.888130Z","iopub.status.idle":"2026-08-30T17:50:07.937079Z","shell.execute_reply.started":"2026-08-30T17:50:07.888103Z","shell.execute_reply":"2026-08-30T17:50:07.936382Z"},"papermill":{"duration":115.705658,"end_time":"2026-08-30T14:29:15.434896","exception":false,"start_time":"2026-08-30T14:27:19.729238","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e0d48dca","cell_type":"code","source":"#prédictions avec TTA\ntest_predictions = predict_tta(model, test_df[\"path\"].values)","metadata":{"execution":{"iopub.status.busy":"2026-08-30T17:50:41.620946Z","iopub.execute_input":"2026-08-30T17:50:41.621580Z","execution_failed":"2026-08-30T17:56:11.646Z"},"papermill":{"duration":6285.918951,"end_time":"2026-08-30T16:14:01.359985","exception":false,"start_time":"2026-08-30T14:29:15.441034","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"07d12562","cell_type":"code","source":"#vérification\nprint(\"Shape :\", test_predictions.shape)\nprint(\"Moyennes des probabilités :\",np.round(test_predictions.mean(axis=0), 4))\nprint(\"Min :\",np.round(test_predictions.min(axis=0), 4))\nprint(\"Max :\",np.round(test_predictions.max(axis=0), 4))","metadata":{"execution":{"iopub.execute_input":"2026-08-30T16:14:03.905339Z","iopub.status.busy":"2026-08-30T16:14:03.904422Z","iopub.status.idle":"2026-08-30T16:14:03.921822Z","shell.execute_reply":"2026-08-30T16:14:03.921Z"},"papermill":{"duration":1.347444,"end_time":"2026-08-30T16:14:03.923592","exception":false,"start_time":"2026-08-30T16:14:02.576148","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"b3e6c21c","cell_type":"code","source":"#blending + création du fichier\np_self, p_moy = patient_mean(test_df, test_predictions)\nproba_blend = ALPHA * p_self + (1 - ALPHA) * p_moy\n\nsubmission = pd.DataFrame({\n    \"image\": test_df[\"image\"],\n    \"level\": predict_labels(proba_blend, thresholds)\n})\nsubmission.to_csv(\"submission.csv\", index=False)\n\nprint(\"Lignes :\", len(submission), \"(attendu 53576)\")\nprint(submission[\"level\"].value_counts(normalize=True).round(3).sort_index())\nsubmission.head()","metadata":{"execution":{"iopub.execute_input":"2026-08-30T16:14:06.508534Z","iopub.status.busy":"2026-08-30T16:14:06.50779Z","iopub.status.idle":"2026-08-30T16:14:06.755806Z","shell.execute_reply":"2026-08-30T16:14:06.754753Z"},"papermill":{"duration":1.529874,"end_time":"2026-08-30T16:14:06.757841","exception":false,"start_time":"2026-08-30T16:14:05.227967","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"39c7eb4b","cell_type":"code","source":"","metadata":{"papermill":{"duration":1.27824,"end_time":"2026-08-30T16:14:09.352806","exception":false,"start_time":"2026-08-30T16:14:08.074566","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}