{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","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":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\n\nbase_path = \"/kaggle/input/competitions/rsna-pneumonia-detection-challenge\"\n\nlabels = pd.read_csv(\n    f\"{base_path}/stage_2_train_labels.csv\"\n)\n\nprint(\"Shape :\", labels.shape)\nlabels.head()\n\n\n\ndf = labels.groupby(\"patientId\")[\"Target\"].max().reset_index()\n\ndf[\"path\"] = df[\"patientId\"].apply(\n    lambda x: f\"{base_path}/stage_2_train_images/{x}.dcm\"\n)\n\nprint(\"Shape du dataframe :\", df.shape)\n\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-22T02:09:39.423394Z","iopub.execute_input":"2026-07-22T02:09:39.423804Z","iopub.status.idle":"2026-07-22T02:09:40.473253Z","shell.execute_reply.started":"2026-07-22T02:09:39.423769Z","shell.execute_reply":"2026-07-22T02:09:40.472574Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nbase_path=\"/kaggle/input/datasets/paultimothymooney/chest-xray-pneumonia/chest_xray\"\n\nprint(os.listdir(base_path))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T01:34:40.647904Z","iopub.execute_input":"2026-07-21T01:34:40.648203Z","iopub.status.idle":"2026-07-21T01:34:40.664810Z","shell.execute_reply.started":"2026-07-21T01:34:40.648131Z","shell.execute_reply":"2026-07-21T01:34:40.663999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\ntest_normal=os.listdir(base_path+\"/test/NORMAL\")\ntest_pneu=os.listdir(base_path+\"/test/PNEUMONIA\")\n\nprint(\"NORMAL :\",len(test_normal))\nprint(\"PNEUMONIA :\",len(test_pneu))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T01:35:39.083759Z","iopub.execute_input":"2026-07-21T01:35:39.084595Z","iopub.status.idle":"2026-07-21T01:35:39.124264Z","shell.execute_reply.started":"2026-07-21T01:35:39.084561Z","shell.execute_reply":"2026-07-21T01:35:39.123343Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\n\nbase_path=\"/kaggle/input/datasets/paultimothymooney/chest-xray-pneumonia/chest_xray/test\"\n\ndata = []\n\n# NORMAL = 0\nnormal_dir = os.path.join(base_path, \"NORMAL\")\nfor img in os.listdir(normal_dir):\n    data.append({\n        \"image_path\": os.path.join(normal_dir, img),\n        \"label\": 0\n    })\n\n# PNEUMONIA = 1\npneumonia_dir = os.path.join(base_path, \"PNEUMONIA\")\nfor img in os.listdir(pneumonia_dir):\n    data.append({\n        \"image_path\": os.path.join(pneumonia_dir, img),\n        \"label\": 1\n    })\n\ntest_df = pd.DataFrame(data)\n\nprint(test_df.head())\nprint(test_df.label.value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T02:13:48.311246Z","iopub.execute_input":"2026-07-21T02:13:48.312393Z","iopub.status.idle":"2026-07-21T02:13:48.336052Z","shell.execute_reply.started":"2026-07-21T02:13:48.312330Z","shell.execute_reply":"2026-07-21T02:13:48.335063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nimport cv2\nimport numpy as np\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport torch\n\nimport torch.nn as nn\nimport torch.optim as optim\n\nfrom torchvision import models\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    roc_auc_score,\n    confusion_matrix\n)\n\n \n \n\ndef read_image(path, image_size=224, use_clahe=True):\n    \"\"\"\n    Lecture des images PNG/JPG/JPEG du dataset Kermany\n    avec le même prétraitement que RSNA.\n    \"\"\"\n\n    image = cv2.imread(path)\n\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n    image = cv2.resize(image, (image_size, image_size))\n\n    if use_clahe:\n\n        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n\n        clahe = cv2.createCLAHE(\n            clipLimit=2.0,\n            tileGridSize=(8,8)\n        )\n\n        gray = clahe.apply(gray)\n\n        image = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)\n\n    return image\n\nclass KermanyDataset(Dataset):\n\n    def __init__(self,\n                 dataframe,\n                 augmentation=None,\n                 use_clahe=True):\n\n        self.dataframe = dataframe.reset_index(drop=True)\n\n        self.augmentation = augmentation\n\n        self.use_clahe = use_clahe\n\n        self.normalize = transforms.Compose([\n\n            transforms.ToTensor(),\n\n            transforms.Normalize(\n                mean=[0.485,0.456,0.406],\n                std=[0.229,0.224,0.225]\n            )\n\n        ])\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n\n        path = self.dataframe.loc[idx,\"image_path\"]\n\n        label = self.dataframe.loc[idx,\"label\"]\n\n        image = read_image(\n            path,\n            image_size=224,\n            use_clahe=self.use_clahe\n        )\n\n        if self.augmentation:\n\n            image = self.augmentation(image=image)[\"image\"]\n\n        image = self.normalize(image)\n\n        # modèle Optuna : BCEWithLogitsLoss\n        label = torch.tensor(label,dtype=torch.float32)\n\n        return image,label\n\n\n#test_dataset = KermanyDataset(    dataframe=test_df,    augmentation=None,    use_clahe=True)\n\n\ntest_dataset = KermanyDataset(\n    dataframe=test_df,\n    augmentation=None,\n    use_clahe=False\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=2\n)\n\ndropout_rate = 0.3350745495831822\ndense_units = 512\n   \n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = models.densenet121(weights=None)\n\nnum_features = model.classifier.in_features\n\nmodel.classifier = nn.Sequential(\n    nn.Dropout(dropout_rate),\n    nn.Linear(num_features, dense_units),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(dense_units, 1)\n)\n\nmodel.load_state_dict(\n    torch.load(\n        \"//kaggle/input/models/souhailmdaouer/best-densenet121-optunav3/pytorch/default/1/best_densenet121_optunaV3.pth\",\n        map_location=device\n    )\n)\n\nmodel = model.to(device)\nmodel.eval()\n\n\n \nall_labels = []\nall_preds = []\nall_probs = []\n\nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = model(images)\n\n        # Cas binaire (1 sortie)\n        probs = torch.sigmoid(outputs).squeeze()\n\n        preds = (probs >= 0.2).long()\n\n        all_labels.extend(labels.cpu().numpy())\n        all_preds.extend(preds.cpu().numpy())\n        all_probs.extend(probs.cpu().numpy())\n\naccuracy = accuracy_score(all_labels, all_preds)\nprecision = precision_score(all_labels, all_preds)\nrecall = recall_score(all_labels, all_preds)\nf1 = f1_score(all_labels, all_preds)\nauc = roc_auc_score(all_labels, all_probs)\ncm = confusion_matrix(all_labels, all_preds)\n\nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\nprint(\"\\nConfusion Matrix\")\nprint(cm)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T02:22:41.961821Z","iopub.execute_input":"2026-07-21T02:22:41.962383Z","iopub.status.idle":"2026-07-21T02:22:47.606387Z","shell.execute_reply.started":"2026-07-21T02:22:41.962346Z","shell.execute_reply":"2026-07-21T02:22:47.605241Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nplt.hist(all_probs, bins=50)\nplt.xlabel(\"Probabilité\")\nplt.ylabel(\"Nombre d'images\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T02:18:36.857713Z","iopub.execute_input":"2026-07-21T02:18:36.858695Z","iopub.status.idle":"2026-07-21T02:18:37.171917Z","shell.execute_reply.started":"2026-07-21T02:18:36.858653Z","shell.execute_reply":"2026-07-21T02:18:37.170928Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for th in [0.2,0.3,0.4,0.5]:\n\n    preds = (np.array(all_probs)>=th).astype(int)\n\n    print(th)\n\n    print(\"Accuracy :\",accuracy_score(all_labels,preds))\n\n    print(\"Recall :\",recall_score(all_labels,preds))\n\n    print(\"Precision :\",precision_score(all_labels,preds))\n\n    print(\"F1 :\",f1_score(all_labels,preds))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T02:19:36.806995Z","iopub.execute_input":"2026-07-21T02:19:36.807909Z","iopub.status.idle":"2026-07-21T02:19:36.857197Z","shell.execute_reply.started":"2026-07-21T02:19:36.807861Z","shell.execute_reply":"2026-07-21T02:19:36.856368Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"####### rendre les éléments aléatoires identiques #################\nimport os\nimport random\nimport numpy as np\nimport torch\n\nseed = 42\n\nos.environ[\"PYTHONHASHSEED\"] = str(seed)\n\nrandom.seed(seed)\nnp.random.seed(seed)\n\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed(seed)\ntorch.cuda.manual_seed_all(seed)\n\ntorch.backends.cudnn.deterministic = True\ntorch.backends.cudnn.benchmark = False\n\n# Depuis PyTorch 1.8+\ntorch.use_deterministic_algorithms(True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-22T02:10:01.898401Z","iopub.execute_input":"2026-07-22T02:10:01.899145Z","iopub.status.idle":"2026-07-22T02:10:08.774740Z","shell.execute_reply.started":"2026-07-22T02:10:01.899114Z","shell.execute_reply":"2026-07-22T02:10:08.773894Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# #########################\" Prétraitement des données ########################\nimport os\nimport cv2\nimport pydicom\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport albumentations as A\n\n\n# Chemin dataset Kaggle\nbase_path = \"/kaggle/input/competitions/rsna-pneumonia-detection-challenge\"\n\n\n\ndef apply_clahe(image):\n    \"\"\"\n    Applique CLAHE pour améliorer le contraste local.\n    Entrée : image grayscale uint8\n    Sortie : image RGB uint8\n    \"\"\"\n    if len(image.shape) == 3:\n        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n    else:\n        gray = image\n\n    clahe = cv2.createCLAHE(\n        clipLimit=2.0,\n        tileGridSize=(8, 8)\n    )\n\n    enhanced = clahe.apply(gray)\n    enhanced_rgb = cv2.cvtColor(enhanced, cv2.COLOR_GRAY2RGB)\n\n    return enhanced_rgb\n\n\ndef read_dicom_image(path, image_size=224, use_clahe=True):\n    \"\"\"\n    Lit une image DICOM RSNA et applique le prétraitement.\n    \"\"\"\n    dcm = pydicom.dcmread(path)\n    image = dcm.pixel_array.astype(np.float32)\n\n    # Normalisation entre 0 et 255\n    image = image - image.min()\n    image = image / image.max()\n    image = (image * 255).astype(np.uint8)\n\n    # CLAHE ou simple conversion RGB\n    if use_clahe:\n        image = apply_clahe(image)\n    else:\n        image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)\n\n    # Redimensionnement\n    image = cv2.resize(image, (image_size, image_size))\n\n    return image\n\n\ntrain_augmentation = A.Compose([\n    A.Rotate(limit=10, p=0.5),\n    A.HorizontalFlip(p=0.5),\n    A.RandomScale(scale_limit=0.10, p=0.4),\n    A.Resize(224, 224),\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-22T02:10:11.788042Z","iopub.execute_input":"2026-07-22T02:10:11.788439Z","iopub.status.idle":"2026-07-22T02:10:14.558695Z","shell.execute_reply.started":"2026-07-22T02:10:11.788417Z","shell.execute_reply":"2026-07-22T02:10:14.558093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\nimport pydicom\nimport matplotlib.pyplot as plt\n\nsample_path = df.iloc[9][\"path\"]\n\noriginal = read_dicom_image(sample_path, use_clahe=False)\nclahe_img = read_dicom_image(sample_path, use_clahe=True)\n\naugmented = train_augmentation(image=clahe_img)[\"image\"]\n\nplt.figure(figsize=(12, 4))\n\nplt.subplot(1, 3, 1)\nplt.imshow(original)\nplt.title(\"Originale\")\nplt.axis(\"off\")\n\nplt.subplot(1, 3, 2)\nplt.imshow(clahe_img)\nplt.title(\"Avec CLAHE\")\nplt.axis(\"off\")\n\nplt.subplot(1, 3, 3)\nplt.imshow(augmented)\nplt.title(\"CLAHE + Augmentation\")\nplt.axis(\"off\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-22T02:11:36.630476Z","iopub.execute_input":"2026-07-22T02:11:36.631124Z","iopub.status.idle":"2026-07-22T02:11:37.022062Z","shell.execute_reply.started":"2026-07-22T02:11:36.631092Z","shell.execute_reply":"2026-07-22T02:11:37.021306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"###########\" visuliser les bounding box\"\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport pydicom\nimport pandas as pd\n\nlabels = pd.read_csv(\n    \"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv\"\n)\n\nlabels.head()\n\n#positive_patient = labels[labels[\"Target\"] == 1]\n#\npositive_patients = labels[\n    labels[\"Target\"] == 1\n][\"patientId\"].unique()\n\nprint(len(positive_patients))\n\npatient_id = positive_patients[10]\n\n\nimage_path = f\"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_train_images/{patient_id}.dcm\"\n\ndcm = pydicom.dcmread(image_path)\n\nimage = dcm.pixel_array\n\n\nfig, ax = plt.subplots(figsize=(8,8))\n\nax.imshow(image, cmap=\"gray\")\n\npatient_boxes = labels[\n    labels[\"patientId\"] == patient_id\n]\n\nfor _, row in patient_boxes.iterrows():\n\n    if row[\"Target\"] == 1:\n\n        rect = patches.Rectangle(\n\n            (row[\"x\"], row[\"y\"]),\n\n            row[\"width\"],\n\n            row[\"height\"],\n\n            linewidth=2,\n\n            edgecolor=\"red\",\n\n            facecolor=\"none\"\n\n        )\n\n        ax.add_patch(rect)\n\nplt.title(f\"Patient : {patient_id}\")\n\nplt.axis(\"off\")\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-07T16:03:58.346080Z","iopub.execute_input":"2026-07-07T16:03:58.346791Z","iopub.status.idle":"2026-07-07T16:03:58.801150Z","shell.execute_reply.started":"2026-07-07T16:03:58.346725Z","shell.execute_reply":"2026-07-07T16:03:58.800275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntrain_df, temp_df = train_test_split(\n    df,\n    test_size=0.30,\n    stratify=df[\"Target\"],\n    random_state=42\n)\n\nval_df, test_df = train_test_split(\n    temp_df,\n    test_size=0.50,\n    stratify=temp_df[\"Target\"],\n    random_state=42\n)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T16:50:25.968694Z","iopub.execute_input":"2026-07-21T16:50:25.969352Z","iopub.status.idle":"2026-07-21T16:50:26.011844Z","shell.execute_reply.started":"2026-07-21T16:50:25.969321Z","shell.execute_reply":"2026-07-21T16:50:26.010844Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\n\nclass RSNADataset(Dataset):\n    def __init__(self, dataframe, augmentation=None, use_clahe=True):\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.augmentation = augmentation\n        self.use_clahe = use_clahe\n\n        self.normalize = transforms.Compose([\n            transforms.ToTensor(),\n            transforms.Normalize(\n                mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225]\n            )\n        ])\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        path = self.dataframe.loc[idx, \"path\"]\n        label = self.dataframe.loc[idx, \"Target\"]\n\n        image = read_dicom_image(\n            path,\n            image_size=224,\n            use_clahe=self.use_clahe\n        )\n\n        if self.augmentation:\n            image = self.augmentation(image=image)[\"image\"]\n\n        image = self.normalize(image)\n        label = torch.tensor(label, dtype=torch.long)\n\n        return image, label\n\n\ntrain_dataset = RSNADataset(train_df, augmentation=train_augmentation, use_clahe=True)\nval_dataset = RSNADataset(val_df, augmentation=None, use_clahe=True)\ntest_dataset = RSNADataset(test_df, augmentation=None, use_clahe=True)\n\ng = torch.Generator()\ng.manual_seed(seed)\n\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=2, generator=g)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=2, generator=g)\ntest_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=2, generator=g)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T16:50:35.900058Z","iopub.execute_input":"2026-07-21T16:50:35.900558Z","iopub.status.idle":"2026-07-21T16:50:35.913128Z","shell.execute_reply.started":"2026-07-21T16:50:35.900532Z","shell.execute_reply":"2026-07-21T16:50:35.912330Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"####################  HOG+SVM  ########################\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\n\nfrom skimage.feature import hog\nfrom sklearn.svm import LinearSVC\nfrom sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report\n\ntrain_sample = train_df.groupby(\"Target\", group_keys=False).apply(\n    lambda x: x.sample(min(len(x), 1500), random_state=42)\n)\n\nval_sample = val_df.groupby(\"Target\", group_keys=False).apply(\n    lambda x: x.sample(min(len(x), 500), random_state=42)\n)\n\nprint(train_sample[\"Target\"].value_counts())\nprint(val_sample[\"Target\"].value_counts())\n\ndef extract_hog_features(dataframe):\n    features = []\n    labels = []\n\n    for _, row in tqdm(dataframe.iterrows(), total=len(dataframe)):\n        path = row[\"path\"]\n        label = row[\"Target\"]\n\n        image = read_dicom_image(path, image_size=224, use_clahe=True)\n\n        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n\n        hog_feature = hog(\n            gray,\n            orientations=9,\n            pixels_per_cell=(16, 16),\n            cells_per_block=(2, 2),\n            block_norm=\"L2-Hys\",\n            feature_vector=True\n        )\n\n        features.append(hog_feature)\n        labels.append(label)\n\n    return np.array(features), np.array(labels)\n\n\nX_train_hog, y_train_hog = extract_hog_features(train_sample)\nX_val_hog, y_val_hog = extract_hog_features(val_sample)\n\nprint(X_train_hog.shape)\nprint(y_train_hog.shape)\nprint(X_val_hog.shape)\nprint(y_val_hog.shape)\n\nsvm_model = LinearSVC(\n    class_weight=\"balanced\",\n    max_iter=5000,\n    random_state=42\n)\n\nsvm_model.fit(X_train_hog, y_train_hog)\n\ny_pred = svm_model.predict(X_val_hog)\n\nprint(\"Accuracy :\", accuracy_score(y_val_hog, y_pred))\nprint(\"Precision :\", precision_score(y_val_hog, y_pred))\nprint(\"Recall :\", recall_score(y_val_hog, y_pred))\nprint(\"F1-score :\", f1_score(y_val_hog, y_pred))\n\n\nprint(\"\\nMatrice de confusion :\")\nprint(confusion_matrix(y_val_hog, y_pred))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-24T15:32:11.986220Z","iopub.execute_input":"2026-06-24T15:32:11.987284Z","iopub.status.idle":"2026-06-24T15:35:37.194333Z","shell.execute_reply.started":"2026-06-24T15:32:11.987243Z","shell.execute_reply":"2026-06-24T15:35:37.193454Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"##################################### Resnet50   ###########\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\n\nfrom torchvision import models\nfrom sklearn.utils.class_weight import compute_class_weight\nimport numpy as np\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n#print(device)\n\nlearning_rate = 1e-4\nbatch_size = 32\nnum_epochs = 20\nweight_decay = 1e-4\ndropout_rate = 0.3\n\n\nmodel = models.resnet50(\n    weights=models.ResNet50_Weights.IMAGENET1K_V2\n)\n\nfor param in model.parameters():\n    param.requires_grad = False\n\nmodel.fc = nn.Sequential(\n    nn.Linear(2048, 512),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(512, 2)\n)\n\nmodel = model.to(device)\n\n# print(model.fc)\n\nclass_weights = compute_class_weight(\n    class_weight='balanced',\n    classes=np.unique(train_df[\"Target\"]),\n    y=train_df[\"Target\"]\n)\n\nclass_weights = torch.tensor(\n    class_weights,\n    dtype=torch.float\n).to(device)\n\nprint(class_weights)\n\ncriterion = nn.CrossEntropyLoss(\n    weight=class_weights\n)\n\noptimizer = optim.Adam(\n    model.fc.parameters(),\n    lr=learning_rate,\n    weight_decay=weight_decay\n)\n\nclass EarlyStopping:\n\n    def __init__(self, patience=5, min_delta=0):\n\n        self.patience = patience\n        self.min_delta = min_delta\n\n        self.counter = 0\n        self.best_loss = None\n        self.early_stop = False\n\n    def __call__(self, val_loss):\n\n        if self.best_loss is None:\n            self.best_loss = val_loss\n\n        elif val_loss > self.best_loss - self.min_delta:\n\n            self.counter += 1\n\n            if self.counter >= self.patience:\n                self.early_stop = True\n\n        else:\n            self.best_loss = val_loss\n            self.counter = 0\n\nearly_stopping = EarlyStopping(patience=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-08T16:14:48.043512Z","iopub.execute_input":"2026-07-08T16:14:48.043910Z","iopub.status.idle":"2026-07-08T16:14:49.307626Z","shell.execute_reply.started":"2026-07-08T16:14:48.043881Z","shell.execute_reply":"2026-07-08T16:14:49.306765Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"######   entrainement avec les courbes d'apprentissage (loss/accuracy par époque) pour ResNet50 \nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nimport torch\n\nbest_val_loss = float(\"inf\")\n\ntrain_losses = []\nval_losses = []\n\ntrain_accuracies = []\nval_accuracies = []\n\nfor epoch in range(num_epochs):\n\n    # ==================================================\n    # Entraînement\n    # ==================================================\n    model.train()\n\n    running_loss = 0.0\n    correct = 0\n    total = 0\n\n    for images, labels in tqdm(train_loader):\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n\n        optimizer.step()\n\n        running_loss += loss.item()\n\n        _, predicted = torch.max(outputs, 1)\n\n        total += labels.size(0)\n\n        correct += (predicted == labels).sum().item()\n\n    train_loss = running_loss / len(train_loader)\n\n    train_accuracy = correct / total\n\n    # ==================================================\n    # Validation\n    # ==================================================\n    model.eval()\n\n    running_val_loss = 0.0\n\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n\n        for images, labels in val_loader:\n\n            images = images.to(device)\n            labels = labels.to(device)\n\n            outputs = model(images)\n\n            loss = criterion(outputs, labels)\n\n            running_val_loss += loss.item()\n\n            _, predicted = torch.max(outputs, 1)\n\n            total += labels.size(0)\n\n            correct += (predicted == labels).sum().item()\n\n    val_loss = running_val_loss / len(val_loader)\n\n    val_accuracy = correct / total\n\n    # ==================================================\n    # Sauvegarde du meilleur modèle\n    # ==================================================\n    if val_loss < best_val_loss:\n\n        best_val_loss = val_loss\n\n        torch.save(\n            model.state_dict(),\n            \"/kaggle/working/best_resnet50.pth\"\n        )\n\n        print(\"Best model saved\")\n\n    # ==================================================\n    # Historique\n    # ==================================================\n    train_losses.append(train_loss)\n    val_losses.append(val_loss)\n\n    train_accuracies.append(train_accuracy)\n    val_accuracies.append(val_accuracy)\n\n    print(\n        f\"Epoch {epoch+1}/{num_epochs} | \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Val Loss: {val_loss:.4f} | \"\n        f\"Train Acc: {train_accuracy:.4f} | \"\n        f\"Val Acc: {val_accuracy:.4f}\"\n    )\n\n    # ==================================================\n    # Early Stopping\n    # ==================================================\n    early_stopping(val_loss)\n\n    if early_stopping.early_stop:\n        print(\"Early stopping\")\n        break\n\n\n# ==================================================\n# Sauvegarde de l'historique\n# ==================================================\n\nhistory = pd.DataFrame({\n\n    \"Epoch\": range(1, len(train_losses)+1),\n\n    \"Train Loss\": train_losses,\n\n    \"Validation Loss\": val_losses,\n\n    \"Train Accuracy\": train_accuracies,\n\n    \"Validation Accuracy\": val_accuracies\n\n})\n\nhistory.to_csv(\n    \"/kaggle/working/resnet50_history.csv\",\n    index=False\n)\n\nprint(\"Historique sauvegardé.\")\n\n# ==================================================\n# Courbe des pertes\n# ==================================================\n\nplt.figure(figsize=(8,5))\n\nplt.plot(history[\"Epoch\"],\n         history[\"Train Loss\"],\n         marker='o',\n         label=\"Train Loss\")\n\nplt.plot(history[\"Epoch\"],\n         history[\"Validation Loss\"],\n         marker='s',\n         label=\"Validation Loss\")\n\nplt.xlabel(\"Époque\")\nplt.ylabel(\"Loss\")\nplt.title(\"Courbe d'apprentissage - ResNet50\")\nplt.legend()\nplt.grid(True)\n\nplt.savefig(\n    \"/kaggle/working/resnet50_loss_curve.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()\n\n\n# ==================================================\n# Courbe Accuracy\n# ==================================================\n\nplt.figure(figsize=(8,5))\n\nplt.plot(history[\"Epoch\"],\n         history[\"Train Accuracy\"],\n         marker='o',\n         label=\"Train Accuracy\")\n\nplt.plot(history[\"Epoch\"],\n         history[\"Validation Accuracy\"],\n         marker='s',\n         label=\"Validation Accuracy\")\n\nplt.xlabel(\"Époque\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Accuracy par époque - ResNet50\")\nplt.legend()\nplt.grid(True)\n\nplt.savefig(\n    \"/kaggle/working/resnet50_accuracy_curve.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-08T16:14:59.175135Z","iopub.execute_input":"2026-07-08T16:14:59.175848Z","iopub.status.idle":"2026-07-08T17:27:03.887248Z","shell.execute_reply.started":"2026-07-08T16:14:59.175818Z","shell.execute_reply":"2026-07-08T17:27:03.886204Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"######   Validation Resnet50\n\nmodel.load_state_dict(\n    torch.load(\"/kaggle/working/best_resnet50.pth\")\n)\n\nmodel.eval()\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    roc_auc_score\n)\n\nall_preds = []\nall_labels = []\nall_probs = []\n\nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = model(images)\n\n        preds = torch.argmax(outputs, dim=1)\n        probs = torch.softmax(outputs, dim=1)[:, 1]\n\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n        all_probs.extend(probs.cpu().numpy())\n\n# ==========================\n# Calcul des métriques\n# ==========================\n\naccuracy = accuracy_score(all_labels, all_preds)\nprecision = precision_score(all_labels, all_preds)\nrecall = recall_score(all_labels, all_preds)\nf1 = f1_score(all_labels, all_preds)\nauc = roc_auc_score(all_labels, all_probs)\ncm = confusion_matrix(all_labels, all_preds)\n\nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\n\nprint(\"\\nMatrice de confusion :\")\nprint(cm)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-08T17:34:06.811813Z","iopub.execute_input":"2026-07-08T17:34:06.812254Z","iopub.status.idle":"2026-07-08T17:34:46.778427Z","shell.execute_reply.started":"2026-07-08T17:34:06.812218Z","shell.execute_reply":"2026-07-08T17:34:46.777487Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"resnet50_results = {\n    \"model\": \"ResNet50 Transfer Learning\",\n    \"accuracy\": accuracy,\n    \"precision\": precision,\n    \"recall\": recall,\n    \"f1_score\": f1,\n    \"auc\": auc\n}\n\nresnet50_results_df = pd.DataFrame([resnet50_results])\n\nresnet50_results_df.to_csv(\n    \"/kaggle/working/resnet50_results.csv\",\n    index=False\n)\n\nresnet50_results_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-25T03:31:53.199999Z","iopub.execute_input":"2026-06-25T03:31:53.200509Z","iopub.status.idle":"2026-06-25T03:31:53.239371Z","shell.execute_reply.started":"2026-06-25T03:31:53.200470Z","shell.execute_reply":"2026-06-25T03:31:53.238594Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"######################\"Densenet121####################\"\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torchvision import models\nfrom sklearn.utils.class_weight import compute_class_weight\nimport numpy as np\nfrom tqdm import tqdm\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n# print(device)\n\nlearning_rate = 1e-4\nnum_epochs = 20\nweight_decay = 1e-4\ndropout_rate = 0.3\n\n\ndensenet = models.densenet121(\n    weights=models.DenseNet121_Weights.IMAGENET1K_V1\n)\n\nfor param in densenet.parameters():\n    param.requires_grad = False\n\nnum_features = densenet.classifier.in_features\n\ndensenet.classifier = nn.Sequential(\n    nn.Linear(num_features, 512),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(512, 2)\n)\n\n# Fine tuning\nfor param in densenet.features.denseblock4.parameters():\n    param.requires_grad = True\n\nfor param in densenet.features.norm5.parameters():\n    param.requires_grad = True\n\ndensenet = densenet.to(device)\n\n#print(densenet.classifier)\n\nclass_weights = compute_class_weight(\n    class_weight=\"balanced\",\n    classes=np.unique(train_df[\"Target\"]),\n    y=train_df[\"Target\"]\n)\n\nclass_weights = torch.tensor(\n    class_weights,\n    dtype=torch.float\n).to(device)\n\ncriterion = nn.CrossEntropyLoss(\n    weight=class_weights\n)\n\noptimizer = optim.Adam(\n    filter(lambda p: p.requires_grad,\n           densenet.parameters()),\n    lr=learning_rate,\n    weight_decay=weight_decay\n)\n\n\nclass EarlyStopping:\n\n    def __init__(self, patience=5, min_delta=0.001):\n\n        self.patience = patience\n        self.min_delta = min_delta\n\n        self.counter = 0\n        self.best_loss = None\n\n        self.early_stop = False\n\n    def __call__(self, val_loss):\n\n        if self.best_loss is None:\n            self.best_loss = val_loss\n\n        elif val_loss > self.best_loss - self.min_delta:\n\n            self.counter += 1\n\n            if self.counter >= self.patience:\n                self.early_stop = True\n\n        else:\n\n            self.best_loss = val_loss\n            self.counter = 0\n\n\nearly_stopping = EarlyStopping(\n    patience=5,\n    min_delta=0.001\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-08T17:46:22.050713Z","iopub.execute_input":"2026-07-08T17:46:22.051004Z","iopub.status.idle":"2026-07-08T17:46:22.299021Z","shell.execute_reply.started":"2026-07-08T17:46:22.050975Z","shell.execute_reply":"2026-07-08T17:46:22.298426Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"######   entrainement avec les courbes\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom tqdm import tqdm\nimport torch\n\nbest_val_loss = float(\"inf\")\n\ntrain_losses_densenet = []\nval_losses_densenet = []\n\ntrain_accuracies_densenet = []\nval_accuracies_densenet = []\n\nfor epoch in range(num_epochs):\n\n    # ======================================================\n    # Entraînement\n    # ======================================================\n    densenet.train()\n\n    running_loss = 0.0\n    correct = 0\n    total = 0\n\n    for images, labels in tqdm(train_loader):\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = densenet(images)\n\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n\n        optimizer.step()\n\n        running_loss += loss.item()\n\n        _, predicted = torch.max(outputs, 1)\n\n        total += labels.size(0)\n\n        correct += (predicted == labels).sum().item()\n\n    train_loss = running_loss / len(train_loader)\n\n    train_accuracy = correct / total\n\n    # ======================================================\n    # Validation\n    # ======================================================\n    densenet.eval()\n\n    running_val_loss = 0.0\n\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n\n        for images, labels in val_loader:\n\n            images = images.to(device)\n            labels = labels.to(device)\n\n            outputs = densenet(images)\n\n            loss = criterion(outputs, labels)\n\n            running_val_loss += loss.item()\n\n            _, predicted = torch.max(outputs, 1)\n\n            total += labels.size(0)\n\n            correct += (predicted == labels).sum().item()\n\n    val_loss = running_val_loss / len(val_loader)\n\n    val_accuracy = correct / total\n\n    # ======================================================\n    # Sauvegarde du meilleur modèle\n    # ======================================================\n    if val_loss < best_val_loss:\n\n        best_val_loss = val_loss\n\n        torch.save(\n            densenet.state_dict(),\n            \"/kaggle/working/best_densenet121.pth\"\n        )\n\n        print(\"Best DenseNet121 model saved\")\n\n    # ======================================================\n    # Historique\n    # ======================================================\n    train_losses_densenet.append(train_loss)\n    val_losses_densenet.append(val_loss)\n\n    train_accuracies_densenet.append(train_accuracy)\n    val_accuracies_densenet.append(val_accuracy)\n\n    print(\n        f\"Epoch {epoch+1}/{num_epochs} | \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Val Loss: {val_loss:.4f} | \"\n        f\"Train Acc: {train_accuracy:.4f} | \"\n        f\"Val Acc: {val_accuracy:.4f}\"\n    )\n\n    # ======================================================\n    # Early Stopping\n    # ======================================================\n    early_stopping(val_loss)\n\n    if early_stopping.early_stop:\n        print(\"Early stopping\")\n        break\n\n\n# ======================================================\n# Sauvegarde de l'historique\n# ======================================================\n\nhistory_densenet = pd.DataFrame({\n\n    \"Epoch\": range(1, len(train_losses_densenet)+1),\n\n    \"Train Loss\": train_losses_densenet,\n\n    \"Validation Loss\": val_losses_densenet,\n\n    \"Train Accuracy\": train_accuracies_densenet,\n\n    \"Validation Accuracy\": val_accuracies_densenet\n\n})\n\nhistory_densenet.to_csv(\n    \"/kaggle/working/densenet121_history.csv\",\n    index=False\n)\n\nprint(\"Historique DenseNet121 sauvegardé.\")\n\n\n# ======================================================\n# Courbe des pertes\n# ======================================================\n\nplt.figure(figsize=(8,5))\n\nplt.plot(\n    history_densenet[\"Epoch\"],\n    history_densenet[\"Train Loss\"],\n    marker='o',\n    label=\"Train Loss\"\n)\n\nplt.plot(\n    history_densenet[\"Epoch\"],\n    history_densenet[\"Validation Loss\"],\n    marker='s',\n    label=\"Validation Loss\"\n)\n\nplt.xticks(np.arange(2, len(history_densenet)+1, 2))\n\nplt.xlabel(\"Époque\")\nplt.ylabel(\"Loss\")\nplt.title(\"Courbe d'apprentissage - DenseNet121\")\n\nplt.grid(True)\nplt.legend()\n\nplt.savefig(\n    \"/kaggle/working/densenet121_loss_curve.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()\n\n\n# ======================================================\n# Courbe Accuracy\n# ======================================================\n\nplt.figure(figsize=(8,5))\n\nplt.plot(\n    history_densenet[\"Epoch\"],\n    history_densenet[\"Train Accuracy\"],\n    marker='o',\n    label=\"Train Accuracy\"\n)\n\nplt.plot(\n    history_densenet[\"Epoch\"],\n    history_densenet[\"Validation Accuracy\"],\n    marker='s',\n    label=\"Validation Accuracy\"\n)\n\nplt.xticks(np.arange(2, len(history_densenet)+1, 2))\n\nplt.xlabel(\"Époque\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Accuracy par époque - DenseNet121\")\n\nplt.grid(True)\nplt.legend()\n\nplt.savefig(\n    \"/kaggle/working/densenet121_accuracy_curve.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-08T17:46:36.888355Z","iopub.execute_input":"2026-07-08T17:46:36.889081Z","iopub.status.idle":"2026-07-08T18:15:14.985415Z","shell.execute_reply.started":"2026-07-08T17:46:36.889050Z","shell.execute_reply":"2026-07-08T18:15:14.984505Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"######   ######   Validation Densenet121\n\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    classification_report,\n    roc_auc_score\n)\n\nimport torch\nimport numpy as np\n\nall_labels = []\nall_preds = []\nall_probs = []\n\n\ndensenet.eval()\n\nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = densenet(images)\n\n        probs = torch.softmax(outputs, dim=1)[:, 1]\n        preds = torch.argmax(outputs, dim=1)\n\n        all_labels.extend(labels.cpu().numpy())\n        all_preds.extend(preds.cpu().numpy())\n        all_probs.extend(probs.cpu().numpy())\n\naccuracy = accuracy_score(all_labels, all_preds)\nprecision = precision_score(all_labels, all_preds)\nrecall = recall_score(all_labels, all_preds)\nf1 = f1_score(all_labels, all_preds)\nauc = roc_auc_score(all_labels, all_probs)\ncm = confusion_matrix(all_labels, all_preds)\n\nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\n\nprint(\"\\nMatrice de confusion :\")\nprint(cm)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-08T18:18:27.783060Z","iopub.execute_input":"2026-07-08T18:18:27.783536Z","iopub.status.idle":"2026-07-08T18:19:08.069534Z","shell.execute_reply.started":"2026-07-08T18:18:27.783500Z","shell.execute_reply":"2026-07-08T18:19:08.068498Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n##segmentation +densenet\nAccuracy : 0.769922558081439\nPrecision : 0.49323843416370106\nRecall : 0.7682926829268293\nF1-score : 0.6007802340702211\nAUC : 0.8516241095247866\n\nMatrice de confusion :\n[[2389  712]\n [ 209  693]]\n\n\n### densenet + optuma\nAccuracy : 0.8028978266300275\nPrecision : 0.545308740978348\nRecall : 0.753880266075388\nF1-score : 0.6328524895300139\nAUC : 0.8749995531088964\n\nMatrice de confusion :\n[[2534  567]\n [ 222  680]]\n\n\n\n######## densenet121 \nAccuracy : 0.7641768673494879\nPrecision : 0.48541666666666666\nRecall : 0.7749445676274944\nF1-score : 0.5969257045260461\nAUC : 0.8524994798187553\n\nMatrice de confusion :\n[[2360  741]\n [ 203  699]]\n\n\n\n###### resnet50\n\nAccuracy : 0.7374469148138896\nPrecision : 0.4533500313087038\nRecall : 0.802660753880266\nF1-score : 0.5794317727090836\nAUC : 0.8356273743324342\n\nMatrice de confusion :\n[[2228  873]\n [ 178  724]]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#################\" Telecharger les poids de U-net pour la segmentation #################\n\nfrom tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Conv2DTranspose, concatenate\nfrom tensorflow.keras.models import Model\n\ndef build_unet(input_size=(256, 256, 1)):\n    inputs = Input(input_size)\n\n    c1 = Conv2D(32, 3, activation='relu', padding='same')(inputs)\n    c1 = Conv2D(32, 3, activation='relu', padding='same')(c1)\n    p1 = MaxPooling2D((2, 2))(c1)\n\n    c2 = Conv2D(64, 3, activation='relu', padding='same')(p1)\n    c2 = Conv2D(64, 3, activation='relu', padding='same')(c2)\n    p2 = MaxPooling2D((2, 2))(c2)\n\n    c3 = Conv2D(128, 3, activation='relu', padding='same')(p2)\n    c3 = Conv2D(128, 3, activation='relu', padding='same')(c3)\n    p3 = MaxPooling2D((2, 2))(c3)\n\n    c4 = Conv2D(256, 3, activation='relu', padding='same')(p3)\n    c4 = Conv2D(256, 3, activation='relu', padding='same')(c4)\n    p4 = MaxPooling2D((2, 2))(c4)\n\n    c5 = Conv2D(512, 3, activation='relu', padding='same')(p4)\n    c5 = Conv2D(512, 3, activation='relu', padding='same')(c5)\n\n    u6 = Conv2DTranspose(256, (2, 2), strides=(2, 2), padding='same')(c5)\n    u6 = concatenate([u6, c4])\n    c6 = Conv2D(256, 3, activation='relu', padding='same')(u6)\n\n    \n    c6 = Conv2D(256, 3, activation='relu', padding='same')(c6)\n\n    u7 = Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c6)\n    u7 = concatenate([u7, c3])\n    c7 = Conv2D(128, 3, activation='relu', padding='same')(u7)\n    c7 = Conv2D(128, 3, activation='relu', padding='same')(c7)\n\n    u8 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c7)\n    u8 = concatenate([u8, c2])\n    c8 = Conv2D(64, 3, activation='relu', padding='same')(u8)\n    c8 = Conv2D(64, 3, activation='relu', padding='same')(c8)\n\n    u9 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c8)\n    u9 = concatenate([u9, c1])\n    c9 = Conv2D(32, 3, activation='relu', padding='same')(u9)\n    c9 = Conv2D(32, 3, activation='relu', padding='same')(c9)\n\n    outputs = Conv2D(1, (1, 1), activation='sigmoid')(c9)\n\n    model = Model(inputs=[inputs], outputs=[outputs])\n    return model\n\nunet_path = \"/kaggle/input/notebooks/nikhilpandey360/lung-segmentation-from-chest-x-ray-dataset/cxr_reg_weights.best.hdf5\"\n\nunet_model = build_unet(input_size=(256, 256, 1))\nunet_model.load_weights(unet_path)\n\nprint(\"Poids U-Net chargés avec succès\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-22T01:20:24.981419Z","iopub.execute_input":"2026-07-22T01:20:24.981805Z","iopub.status.idle":"2026-07-22T01:20:45.339045Z","shell.execute_reply.started":"2026-07-22T01:20:24.981775Z","shell.execute_reply":"2026-07-22T01:20:45.338333Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\ntotal_params = unet_model.count_params()\n\nprint(f\"Nombre total de paramètres : {total_params:,}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-22T01:21:01.175579Z","iopub.execute_input":"2026-07-22T01:21:01.176542Z","iopub.status.idle":"2026-07-22T01:21:01.181162Z","shell.execute_reply.started":"2026-07-22T01:21:01.176508Z","shell.execute_reply":"2026-07-22T01:21:01.180361Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"########### visualiser la segmentatioon\nimport cv2\nimport pydicom\nimport numpy as np\nimport matplotlib.pyplot as plt\n\ndef read_dicom_gray(path):\n    dcm = pydicom.dcmread(path)\n    image = dcm.pixel_array.astype(np.float32)\n\n    image = image - image.min()\n    image = image / image.max()\n    image = (image * 255).astype(np.uint8)\n\n    return image\n\n\ndef segment_lungs_unet_keras(image, model, input_size=256, threshold=0.5):\n    h, w = image.shape\n\n    img = cv2.resize(image, (input_size, input_size))\n    img = img.astype(np.float32) / 255.0\n    img = np.expand_dims(img, axis=-1)\n    img = np.expand_dims(img, axis=0)\n\n    pred = model.predict(img, verbose=0)[0, :, :, 0]\n\n    mask = (pred > threshold).astype(np.uint8) * 255\n    mask = cv2.resize(mask, (w, h))\n\n    segmented = cv2.bitwise_and(image, image, mask=mask)\n\n    return segmented, mask\n\n\nsample_path = train_df.iloc[0][\"path\"]\n\nimg = read_dicom_gray(sample_path)\nsegmented, mask = segment_lungs_unet_keras(img, unet_model)\n\nplt.figure(figsize=(12, 4))\n\nplt.subplot(1, 3, 1)\nplt.imshow(img, cmap=\"gray\")\nplt.title(\"Image originale\")\nplt.axis(\"off\")\n\nplt.subplot(1, 3, 2)\nplt.imshow(mask, cmap=\"gray\")\nplt.title(\"Masque U-Net\")\nplt.axis(\"off\")\n\nplt.subplot(1, 3, 3)\nplt.imshow(segmented, cmap=\"gray\")\nplt.title(\"Image segmentée U-Net\")\nplt.axis(\"off\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-07T15:51:27.715237Z","iopub.execute_input":"2026-07-07T15:51:27.716118Z","iopub.status.idle":"2026-07-07T15:51:32.416582Z","shell.execute_reply.started":"2026-07-07T15:51:27.716089Z","shell.execute_reply":"2026-07-07T15:51:32.415800Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"########### visualiser la segmentatioon avec clache\n\nimport cv2\nimport pydicom\nimport numpy as np\nimport matplotlib.pyplot as plt\n\ndef read_dicom_gray(path):\n    dcm = pydicom.dcmread(path)\n    image = dcm.pixel_array.astype(np.float32)\n\n    image = image - image.min()\n    image = image / image.max()\n    image = (image * 255).astype(np.uint8)\n\n    return image\n\ndef apply_clahe_gray(image):\n\n    clahe = cv2.createCLAHE(\n        clipLimit=2.0,\n        tileGridSize=(8,8)\n    )\n\n    image_clahe = clahe.apply(image)\n\n    return image_clahe\n\ndef segment_lungs_unet_keras(image, model, input_size=256, threshold=0.5):\n    h, w = image.shape\n\n    img = cv2.resize(image, (input_size, input_size))\n    img = img.astype(np.float32) / 255.0\n    img = np.expand_dims(img, axis=-1)\n    img = np.expand_dims(img, axis=0)\n\n    pred = model.predict(img, verbose=0)[0, :, :, 0]\n\n    mask = (pred > threshold).astype(np.uint8) * 255\n    mask = cv2.resize(mask, (w, h))\n\n    segmented = cv2.bitwise_and(image, image, mask=mask)\n\n    return segmented, mask\n\n\nsample_path = train_df.iloc[0][\"path\"]\n\nimg = read_dicom_gray(sample_path)\n\nimg_clahe = apply_clahe_gray(img)\n\nsegmented, mask = segment_lungs_unet_keras(\n    img_clahe,\n    unet_model\n)\n\nplt.figure(figsize=(16,4))\n\nplt.subplot(1,4,1)\nplt.imshow(img, cmap=\"gray\")\nplt.title(\"Originale\")\nplt.axis(\"off\")\n\nplt.subplot(1,4,2)\nplt.imshow(img_clahe, cmap=\"gray\")\nplt.title(\"CLAHE\")\nplt.axis(\"off\")\n\nplt.subplot(1,4,3)\nplt.imshow(mask, cmap=\"gray\")\nplt.title(\"Masque U-Net\")\nplt.axis(\"off\")\n\nplt.subplot(1,4,4)\nplt.imshow(segmented, cmap=\"gray\")\nplt.title(\"Segmentée\")\nplt.axis(\"off\")\n\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-11T15:45:51.368137Z","iopub.execute_input":"2026-07-11T15:45:51.368608Z","iopub.status.idle":"2026-07-11T15:45:52.231280Z","shell.execute_reply.started":"2026-07-11T15:45:51.368576Z","shell.execute_reply":"2026-07-11T15:45:52.229781Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"##########\" visualiser les masques générés par U-net\"\nimport os\nimport cv2\nimport pydicom\nimport matplotlib.pyplot as plt\n\nidx = 24  # choisir une image\n\ndicom_path = df.iloc[idx][\"path\"]\nseg_path = df.iloc[idx][\"segmented_path\"]\n\n# Image originale\ndcm = pydicom.dcmread(dicom_path)\noriginal = dcm.pixel_array\n\n# Image segmentée\nsegmented = cv2.imread(seg_path, cv2.IMREAD_GRAYSCALE)\n\nplt.figure(figsize=(10,5))\n\nplt.subplot(1,2,1)\nplt.imshow(original, cmap=\"gray\")\nplt.title(\"Image originale\")\nplt.axis(\"off\")\n\nplt.subplot(1,2,2)\nplt.imshow(segmented, cmap=\"gray\")\nplt.title(\"Image segmentée\")\nplt.axis(\"off\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T19:40:16.080360Z","iopub.execute_input":"2026-07-12T19:40:16.081134Z","iopub.status.idle":"2026-07-12T19:40:16.397378Z","shell.execute_reply.started":"2026-07-12T19:40:16.081104Z","shell.execute_reply":"2026-07-12T19:40:16.396470Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"############ segmenter tous les images \n\nimport os\nimport cv2\nimport pydicom\nimport numpy as np\nimport pandas as pd\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom tqdm import tqdm\n\nimport albumentations as A\n\n\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(device)\n\n\nprint(train_df.shape, val_df.shape, test_df.shape)\n\ndef apply_clahe_gray(image):\n    clahe = cv2.createCLAHE(\n        clipLimit=2.0,\n        tileGridSize=(8, 8)\n    )\n    return clahe.apply(image)\n\n\ndef read_dicom_gray(path):\n    dcm = pydicom.dcmread(path)\n    image = dcm.pixel_array.astype(np.float32)\n\n    image = image - image.min()\n    image = image / image.max()\n    image = (image * 255).astype(np.uint8)\n\n    return image\n\n\ndef read_dicom_image(path, image_size=224, use_clahe=True):\n    dcm = pydicom.dcmread(path)\n    image = dcm.pixel_array.astype(np.float32)\n\n    image = image - image.min()\n    image = image / image.max()\n    image = (image * 255).astype(np.uint8)\n\n    if use_clahe:\n        image = apply_clahe_gray(image)\n\n    image = cv2.resize(image, (image_size, image_size))\n    image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)\n\n    return image\n\n\ndef segment_lungs_unet_keras(image, model, input_size=256, threshold=0.5):\n    h, w = image.shape\n\n    img = cv2.resize(image, (input_size, input_size))\n    img = img.astype(np.float32) / 255.0\n    img = np.expand_dims(img, axis=-1)\n    img = np.expand_dims(img, axis=0)\n\n    pred = model.predict(img, verbose=0)[0, :, :, 0]\n\n    mask = (pred > threshold).astype(np.uint8) * 255\n    mask = cv2.resize(mask, (w, h))\n\n    segmented = cv2.bitwise_and(image, image, mask=mask)\n\n    return segmented, mask\n\n\n\ndef save_segmented_dataset(df, save_dir):\n\n    for _, row in tqdm(df.iterrows(), total=len(df)):\n\n        dicom_path = row[\"path\"]\n\n        image = read_dicom_gray(dicom_path)\n\n        # CLAHE\n        image = apply_clahe_gray(image)\n\n        # U-Net\n        segmented, mask = segment_lungs_unet_keras(\n            image,\n            unet_model,\n            input_size=256,\n            threshold=0.5\n        )\n\n        segmented = cv2.resize(segmented, (224,224))\n\n        filename = os.path.basename(dicom_path)\n        filename = filename.replace(\".dcm\", \".png\")\n\n        cv2.imwrite(\n            os.path.join(save_dir, filename),\n            segmented\n        )\n\ntrain_augmentation = A.Compose([\n    A.Rotate(limit=10, p=0.5),\n    A.HorizontalFlip(p=0.5),\n    A.RandomScale(scale_limit=0.10, p=0.4),\n    A.Resize(224, 224),\n])\n\n\nsave_dir = \"/kaggle/working/segmented_images\"\nos.makedirs(save_dir, exist_ok=True)\n\nsave_segmented_dataset(df, save_dir)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T16:43:34.641048Z","iopub.execute_input":"2026-07-12T16:43:34.641716Z","iopub.status.idle":"2026-07-12T16:43:37.521717Z","shell.execute_reply.started":"2026-07-12T16:43:34.641687Z","shell.execute_reply":"2026-07-12T16:43:37.521027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nsave_dir = \"/kaggle/input/datasets/souhailmdaouer/segmented-images\"\n\n\ndf[\"segmented_path\"] = df[\"path\"].apply(\n    lambda x: os.path.join(\n        save_dir,\n        os.path.basename(x).replace(\".dcm\",\".png\")\n    )\n)\n\ntrain_df[\"segmented_path\"] = train_df[\"path\"].apply(\n    lambda x: os.path.join(save_dir, os.path.basename(x).replace(\".dcm\", \".png\"))\n)\n\nval_df[\"segmented_path\"] = val_df[\"path\"].apply(\n    lambda x: os.path.join(save_dir, os.path.basename(x).replace(\".dcm\", \".png\"))\n)\n\ntest_df[\"segmented_path\"] = test_df[\"path\"].apply(\n    lambda x: os.path.join(save_dir, os.path.basename(x).replace(\".dcm\", \".png\"))\n)\n\nclass EarlyStopping:\n\n    def __init__(self, patience=5, min_delta=0):\n\n        self.patience = patience\n        self.min_delta = min_delta\n\n        self.counter = 0\n        self.best_loss = None\n        self.early_stop = False\n\n    def __call__(self, val_loss):\n\n        if self.best_loss is None:\n            self.best_loss = val_loss\n\n        elif val_loss > self.best_loss - self.min_delta:\n\n            self.counter += 1\n\n            if self.counter >= self.patience:\n                self.early_stop = True\n\n        else:\n            self.best_loss = val_loss\n            self.counter = 0\n\nearly_stopping = EarlyStopping(patience=5)\n\n\n\nclass RSNASegmentedDataset(Dataset):\n\n    def __init__(self, dataframe, augmentation=None):\n\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.augmentation = augmentation\n\n        self.normalize = transforms.Compose([\n            transforms.ToTensor(),\n            transforms.Normalize(\n                mean=[0.485,0.456,0.406],\n                std=[0.229,0.224,0.225]\n            )\n        ])\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n\n        path = self.dataframe.loc[idx,\"segmented_path\"]\n        label = self.dataframe.loc[idx,\"Target\"]\n\n        image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)\n\n        image = cv2.cvtColor(\n            image,\n            cv2.COLOR_GRAY2RGB\n        )\n\n        if self.augmentation:\n            image = self.augmentation(image=image)[\"image\"]\n\n        image = self.normalize(image)\n\n        return image, torch.tensor(label,dtype=torch.long)\n\n\n\n\ntrain_seg_dataset = RSNASegmentedDataset(\n    train_df,\n    augmentation=train_augmentation)\n\nval_seg_dataset = RSNASegmentedDataset(\n    val_df,\n    augmentation=None)\n\ntest_seg_dataset = RSNASegmentedDataset(\n    test_df,\n    augmentation=None)\n\ng = torch.Generator()\ng.manual_seed(seed)\n\n\ntrain_seg_loader = DataLoader(\n    train_seg_dataset,\n    batch_size=32,\n    shuffle=True,\n    generator=g,\n    num_workers=0)\n\nval_seg_loader = DataLoader(\n    val_seg_dataset,\n    batch_size=32,\n    shuffle=False,\n    generator=g,\n    num_workers=0)\n\ntest_seg_loader = DataLoader(\n    test_seg_dataset,\n    batch_size=32,\n    shuffle=False,\n    generator=g,\n    num_workers=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T18:26:20.551883Z","iopub.execute_input":"2026-07-12T18:26:20.552487Z","iopub.status.idle":"2026-07-12T18:26:20.568493Z","shell.execute_reply.started":"2026-07-12T18:26:20.552418Z","shell.execute_reply":"2026-07-12T18:26:20.567713Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"####### entrainer densenet +segmentation \n\nimport os\nimport random\n\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\n\nfrom torchvision import models, transforms\nfrom torch.utils.data import Dataset, DataLoader\n\nfrom tqdm import tqdm\n\n    \n\nbatch_size = 32\nlearning_rate = 0.0006545776317255478\ndropout_rate = 0.3350745495831822\ndense_units = 512\nweight_decay = 1.7703130607090214e-06\npositive_class_weight = 2.6448777190609833\nnum_epochs = 20\n\n\ndensenet_seg = models.densenet121(\n    weights=models.DenseNet121_Weights.IMAGENET1K_V1\n)\n\n# Geler tout le réseau\nfor param in densenet_seg.parameters():\n    param.requires_grad = False\n\n# Dégeler les 100 derniers paramètres des features\nfor param in list(densenet_seg.features.parameters())[-100:]:\n    param.requires_grad = True\n\nnum_features = densenet_seg.classifier.in_features\n\ndensenet_seg.classifier = nn.Sequential(\n\n    nn.Dropout(dropout_rate),\n\n    nn.Linear(num_features, dense_units),\n\n    nn.ReLU(),\n\n    nn.Dropout(dropout_rate),\n\n    nn.Linear(dense_units, 1)\n\n)\n\ndensenet_seg = densenet_seg.to(device)\n\ncriterion = nn.BCEWithLogitsLoss(\n\n    pos_weight=torch.tensor(\n        [positive_class_weight],\n        device=device\n    )\n\n)\n\noptimizer = optim.AdamW(\n\n    filter(\n        lambda p: p.requires_grad,\n        densenet_seg.parameters()\n    ),\n\n    lr=learning_rate,\n\n    weight_decay=weight_decay\n\n)\n\n\n\n\n\nbest_val_loss = float(\"inf\")\n\ntrain_losses_seg = []\nval_losses_seg = []\n\ntrain_accuracies_seg = []\nval_accuracies_seg = []\n\nfor epoch in range(num_epochs):\n\n    # ============================================\n    # Entraînement\n    # ============================================\n\n    densenet_seg.train()\n\n    running_loss = 0.0\n\n    correct = 0\n    total = 0\n\n    for images, labels in tqdm(train_seg_loader):\n\n        images = images.to(device)\n        labels = labels.to(device).unsqueeze(1).float()\n\n        optimizer.zero_grad()\n\n        outputs = densenet_seg(images)\n\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n\n        optimizer.step()\n\n        running_loss += loss.item()\n\n        preds = (torch.sigmoid(outputs) >= 0.5).float()\n\n        correct += (preds == labels).sum().item()\n\n        total += labels.size(0)\n\n    train_loss = running_loss / len(train_seg_loader)\n\n    train_acc = correct / total\n\n    # ============================================\n    # Validation\n    # ============================================\n\n    densenet_seg.eval()\n\n    running_val_loss = 0.0\n\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n\n        for images, labels in val_seg_loader:\n\n            images = images.to(device)\n            labels = labels.to(device).unsqueeze(1).float()\n\n            outputs = densenet_seg(images)\n\n            loss = criterion(outputs, labels)\n\n            running_val_loss += loss.item()\n\n            preds = (torch.sigmoid(outputs) >= 0.5).float()\n\n            correct += (preds == labels).sum().item()\n\n            total += labels.size(0)\n\n    val_loss = running_val_loss / len(val_seg_loader)\n\n    val_acc = correct / total\n\n    # ============================================\n    # Sauvegarde du meilleur modèle\n    # ============================================\n\n    if val_loss < best_val_loss:\n\n        best_val_loss = val_loss\n\n        torch.save(\n            densenet_seg.state_dict(),\n            \"/kaggle/working/best_densenet121_segmented.pth\"\n        )\n\n        print(\"Best DenseNet121 segmented model saved\")\n\n    # ============================================\n    # Historique\n    # ============================================\n\n    train_losses_seg.append(train_loss)\n    val_losses_seg.append(val_loss)\n\n    train_accuracies_seg.append(train_acc)\n    val_accuracies_seg.append(val_acc)\n\n    print(\n        f\"Epoch {epoch+1}/{num_epochs} | \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Val Loss: {val_loss:.4f} | \"\n        f\"Train Acc: {train_acc:.4f} | \"\n        f\"Val Acc: {val_acc:.4f}\"\n    )\n\n    # ============================================\n    # Early Stopping\n    # ============================================\n\n    early_stopping(val_loss)\n\n    if early_stopping.early_stop:\n\n        print(\"Early stopping\")\n\n        break\n\n\n\nhistory_seg = pd.DataFrame({\n    \"Epoch\": range(1, len(train_losses_seg)+1),\n    \"Train Loss\": train_losses_seg,\n    \"Validation Loss\": val_losses_seg,\n    \"Train Accuracy\": train_accuracies_seg,\n    \"Validation Accuracy\": val_accuracies_seg\n})\n\n\n\nplt.figure(figsize=(8,5))\n\nplt.plot(\n    history_seg[\"Epoch\"],\n    history_seg[\"Train Loss\"],\n    marker='o',\n    label=\"Train Loss\"\n)\n\nplt.plot(\n    history_seg[\"Epoch\"],\n    history_seg[\"Validation Loss\"],\n    marker='s',\n    label=\"Validation Loss\"\n)\n\nplt.xticks(np.arange(2, len(history_seg)+1, 2))\n\nplt.xlabel(\"Époque\")\nplt.ylabel(\"Loss\")\nplt.title(\"Courbe d'apprentissage - DenseNet121 + Segmentation\")\n\nplt.grid(True)\nplt.legend()\n\nplt.savefig(\n    \"/kaggle/working/densenet121_segmented_loss_curve.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()\n\n\n# ======================================================\n# Courbe Accuracy\n# ======================================================\n\nplt.figure(figsize=(8,5))\n\nplt.plot(\n    history_seg[\"Epoch\"],\n    history_seg[\"Train Accuracy\"],\n    marker='o',\n    label=\"Train Accuracy\"\n)\n\nplt.plot(\n    history_seg[\"Epoch\"],\n    history_seg[\"Validation Accuracy\"],\n    marker='s',\n    label=\"Validation Accuracy\"\n)\n\nplt.xticks(np.arange(2, len(history_seg)+1, 2))\n\nplt.xlabel(\"Époque\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Accuracy par époque - DenseNet121 + Segmentation\")\n\nplt.grid(True)\nplt.legend()\n\nplt.savefig(\n    \"/kaggle/working/densenet121_segmented_accuracy_curve.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T18:26:45.340039Z","iopub.execute_input":"2026-07-12T18:26:45.341008Z","iopub.status.idle":"2026-07-12T18:57:38.024885Z","shell.execute_reply.started":"2026-07-12T18:26:45.340968Z","shell.execute_reply":"2026-07-12T18:57:38.024195Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    roc_auc_score,\n)\n\nimport torch\nimport numpy as np\n\nall_labels = []\nall_preds = []\nall_probs = []\n\ndensenet_seg.load_state_dict(\n    torch.load(\n        \"/kaggle/working/best_densenet121_segmented.pth\",\n        map_location=device\n    )\n)\n\ndensenet_seg.eval()\n\nwith torch.no_grad():\n\n    for images, labels in test_seg_loader:\n\n        images = images.to(device)\n        labels = labels.to(device).unsqueeze(1).float()\n\n        outputs = densenet_seg(images)\n\n        # Probabilité de la classe Pneumonie\n        probs = torch.sigmoid(outputs)\n\n        # Prédiction binaire\n        preds = (probs >= 0.5).float()\n\n        all_labels.extend(labels.cpu().numpy().ravel())\n        all_preds.extend(preds.cpu().numpy().ravel())\n        all_probs.extend(probs.cpu().numpy().ravel())\n\naccuracy = accuracy_score(all_labels, all_preds)\nprecision = precision_score(all_labels, all_preds)\nrecall = recall_score(all_labels, all_preds)\nf1 = f1_score(all_labels, all_preds)\nauc = roc_auc_score(all_labels, all_probs)\ncm = confusion_matrix(all_labels, all_preds)\n\nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\n\nprint(\"\\nMatrice de confusion :\")\nprint(cm)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T18:59:58.311605Z","iopub.execute_input":"2026-07-12T18:59:58.312274Z","iopub.status.idle":"2026-07-12T19:00:29.523933Z","shell.execute_reply.started":"2026-07-12T18:59:58.312242Z","shell.execute_reply":"2026-07-12T19:00:29.523225Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"Accuracy : 0.769922558081439\nPrecision : 0.49323843416370106\nRecall : 0.7682926829268293\nF1-score : 0.6007802340702211\nAUC : 0.8516241095247866\n\nMatrice de confusion :\n[[2389  712]\n [ 209  693]]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\n\nclass RSNADataset(Dataset):\n    def __init__(self, dataframe, augmentation=None, use_clahe=True):\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.augmentation = augmentation\n        self.use_clahe = use_clahe\n\n        self.normalize = transforms.Compose([\n            transforms.ToTensor(),\n            transforms.Normalize(\n                mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225]\n            )\n        ])\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        path = self.dataframe.loc[idx, \"path\"]\n        label = self.dataframe.loc[idx, \"Target\"]\n\n        image = read_dicom_image(\n            path,\n            image_size=224,\n            use_clahe=self.use_clahe\n        )\n\n        if self.augmentation:\n            image = self.augmentation(image=image)[\"image\"]\n\n        image = self.normalize(image)\n        label = torch.tensor(label, dtype=torch.float32)\n       # label = torch.tensor(label, dtype=torch.long)\n\n        return image, label\n\nbatch_size = 32\n\ntrain_dataset = RSNADataset(train_df, augmentation=train_augmentation, use_clahe=True)\nval_dataset = RSNADataset(val_df, augmentation=None, use_clahe=True)\ntest_dataset = RSNADataset(test_df, augmentation=None, use_clahe=True)\n\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=2)\ntest_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=2)\n\nprint(len(train_loader), len(val_loader), len(test_loader))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T02:37:29.536739Z","iopub.execute_input":"2026-07-04T02:37:29.537590Z","iopub.status.idle":"2026-07-04T02:37:29.548902Z","shell.execute_reply.started":"2026-07-04T02:37:29.537555Z","shell.execute_reply":"2026-07-04T02:37:29.547780Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install optuna -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-27T23:43:39.302023Z","iopub.execute_input":"2026-06-27T23:43:39.302665Z","iopub.status.idle":"2026-06-27T23:43:43.385907Z","shell.execute_reply.started":"2026-06-27T23:43:39.302635Z","shell.execute_reply":"2026-06-27T23:43:43.384642Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"##################### Algoritme OPTUMA     ####################\"\"\nimport optuna\nfrom sklearn.metrics import roc_auc_score, recall_score, precision_score, f1_score\n\nclass RSNADataset(Dataset):\n    def __init__(self, dataframe, augmentation=None, use_clahe=True):\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.augmentation = augmentation\n        self.use_clahe = use_clahe\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        row = self.dataframe.iloc[idx]\n\n        image = read_dicom_image(\n            row[\"path\"],\n            image_size=224,\n            use_clahe=self.use_clahe\n        )\n\n        label = int(row[\"Target\"])   # correction ici\n\n        if self.augmentation:\n            augmented = self.augmentation(image=image)\n            image = augmented[\"image\"]\n\n        image = image.astype(np.float32) / 255.0\n        image = np.transpose(image, (2, 0, 1))\n\n        image = torch.tensor(image, dtype=torch.float32)\n        label = torch.tensor(label, dtype=torch.float32)\n\n        return image, label\n\nval_dataset = RSNADataset(\n    val_df,\n    augmentation=A.Compose([A.Resize(224, 224)]),\n    use_clahe=True\n)\n\ntest_dataset = RSNADataset(\n    test_df,\n    augmentation=A.Compose([A.Resize(224, 224)]),\n    use_clahe=True\n)\ndef create_densenet121(trial):\n    model = models.densenet121(weights=models.DenseNet121_Weights.IMAGENET1K_V1)\n\n    unfreeze_layers = trial.suggest_categorical(\n        \"unfreeze_layers\",\n        [0, 20, 50, 100]\n    )\n\n    for param in model.features.parameters():\n        param.requires_grad = False\n\n    if unfreeze_layers > 0:\n        for param in list(model.features.parameters())[-unfreeze_layers:]:\n            param.requires_grad = True\n\n    dropout = trial.suggest_float(\"dropout\", 0.2, 0.6)\n    dense_units = trial.suggest_categorical(\"dense_units\", [128, 256, 512])\n\n    in_features = model.classifier.in_features\n\n    model.classifier = nn.Sequential(\n        nn.Dropout(dropout),\n        nn.Linear(in_features, dense_units),\n        nn.ReLU(),\n        nn.Dropout(dropout),\n        nn.Linear(dense_units, 1)\n    )\n\n    return model.to(device)\n\n\ndef train_one_epoch(model, loader, criterion, optimizer):\n    model.train()\n    running_loss = 0.0\n\n    for images, labels in tqdm(loader, leave=False):\n        images = images.to(device)\n        labels = labels.to(device).unsqueeze(1)\n\n        optimizer.zero_grad()\n        outputs = model(images)\n\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n\n    return running_loss / len(loader)\n\ndef evaluate(model, loader, criterion):\n    model.eval()\n\n    running_loss = 0.0\n    all_labels = []\n    all_probs = []\n\n    with torch.no_grad():\n        for images, labels in loader:\n            images = images.to(device)\n            labels = labels.to(device).unsqueeze(1)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            probs = torch.sigmoid(outputs)\n\n            running_loss += loss.item()\n\n            all_labels.extend(labels.cpu().numpy().ravel())\n            all_probs.extend(probs.cpu().numpy().ravel())\n\n    all_labels = np.array(all_labels)\n    all_probs = np.array(all_probs)\n    all_preds = (all_probs >= 0.5).astype(int)\n\n    val_loss = running_loss / len(loader)\n    val_auc = roc_auc_score(all_labels, all_probs)\n    val_recall = recall_score(all_labels, all_preds)\n    val_precision = precision_score(all_labels, all_preds, zero_division=0)\n    val_f1 = f1_score(all_labels, all_preds)\n\n    return val_loss, val_auc, val_recall, val_precision, val_f1\n\ndef objective(trial):\n\n    batch_size = trial.suggest_categorical(\n        \"batch_size\",\n        [16, 32]\n    )\n\n    train_dataset = RSNADataset(\n        train_df,\n        augmentation=train_augmentation,\n        use_clahe=True\n    )\n\n    train_loader = DataLoader(\n        train_dataset,\n        batch_size=batch_size,\n        shuffle=True,\n        num_workers=2,\n        pin_memory=True\n    )\n\n    val_loader = DataLoader(\n        val_dataset,\n        batch_size=batch_size,\n        shuffle=False,\n        num_workers=2,\n        pin_memory=True\n    )\n\n    model = create_densenet121(trial)\n\n    learning_rate = trial.suggest_float(\n        \"learning_rate\",\n        1e-5,\n        1e-3,\n        log=True\n    )\n\n    weight_decay = trial.suggest_float(\n        \"weight_decay\",\n        1e-6,\n        1e-3,\n        log=True\n    )\n\n    optimizer_name = trial.suggest_categorical(\n        \"optimizer\",\n        [\"Adam\", \"AdamW\"]\n    )\n\n    if optimizer_name == \"Adam\":\n        optimizer = optim.Adam(\n            filter(lambda p: p.requires_grad, model.parameters()),\n            lr=learning_rate,\n            weight_decay=weight_decay\n        )\n    else:\n        optimizer = optim.AdamW(\n            filter(lambda p: p.requires_grad, model.parameters()),\n            lr=learning_rate,\n            weight_decay=weight_decay\n        )\n\n    positive_class_weight = trial.suggest_float(\n        \"positive_class_weight\",\n        1.0,\n        6.0\n    )\n\n    criterion = nn.BCEWithLogitsLoss(\n        pos_weight=torch.tensor([positive_class_weight], device=device)\n    )\n\n    best_auc = 0.0\n    patience = 3\n    counter = 0\n\n    for epoch in range(6):\n        train_loss = train_one_epoch(\n            model,\n            train_loader,\n            criterion,\n            optimizer\n        )\n\n        val_loss, val_auc, val_recall, val_precision, val_f1 = evaluate(\n            model,\n            val_loader,\n            criterion\n        )\n\n        trial.report(val_auc, epoch)\n\n        if trial.should_prune():\n            raise optuna.exceptions.TrialPruned()\n\n        if val_auc > best_auc:\n            best_auc = val_auc\n            counter = 0\n        else:\n            counter += 1\n\n        if counter >= patience:\n            break\n\n    return best_auc\n\n\nstudy = optuna.create_study(direction=\"maximize\")\n\nstudy.optimize(\n    objective,\n    n_trials=20\n)\n\nprint(\"Meilleurs paramètres :\")\nprint(study.best_params)\n\nprint(\"Meilleur AUC validation :\")\nprint(study.best_value)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-28T16:49:27.671609Z","iopub.execute_input":"2026-06-28T16:49:27.672069Z","iopub.status.idle":"2026-06-28T16:49:28.017555Z","shell.execute_reply.started":"2026-06-28T16:49:27.672036Z","shell.execute_reply":"2026-06-28T16:49:28.016868Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"########### entrainer densenet avec hyperparamétres Optuma\n\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nbatch_size = 32\nlearning_rate = 0.0006545776317255478\ndropout_rate = 0.3350745495831822\ndense_units = 512\nweight_decay = 1.7703130607090214e-06\npositive_class_weight = 2.6448777190609833\nnum_epochs = 20\n\ncriterion = nn.BCEWithLogitsLoss(\n    pos_weight=torch.tensor(\n        [positive_class_weight],\n        device=device\n    )\n)\n\nfrom torchvision import models\nimport torch.nn as nn\n\ndensenet_optuna = models.densenet121(\n    weights=models.DenseNet121_Weights.IMAGENET1K_V1\n)\n\n# =====================================================\n# Gel des couches\n# =====================================================\n\nfor param in densenet_optuna.parameters():\n    param.requires_grad = False\n\nfor param in densenet_optuna.features.parameters():\n    param.requires_grad = False\n\nfor param in list(densenet_optuna.features.parameters())[-100:]:\n    param.requires_grad = True\n\nnum_features = densenet_optuna.classifier.in_features\n\ndensenet_optuna.classifier = nn.Sequential(\n    nn.Dropout(dropout_rate),\n    nn.Linear(num_features, dense_units),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(dense_units, 1)\n)\n\ndensenet_optuna = densenet_optuna.to(device)\n\noptimizer = optim.AdamW(\n    filter(lambda p: p.requires_grad, densenet_optuna.parameters()),\n    lr=learning_rate,\n    weight_decay=weight_decay\n)\n\nearly_stopping = EarlyStopping(\n    patience=5,\n    min_delta=0.001\n)\n\nbest_val_loss = float(\"inf\")\n\ntrain_losses = []\nval_losses = []\n\ntrain_accuracies = []\nval_accuracies = []\n\nfor epoch in range(num_epochs):\n\n    # =====================================================\n    # TRAIN\n    # =====================================================\n\n    densenet_optuna.train()\n\n    running_loss = 0\n\n    correct = 0\n    total = 0\n\n    for images, labels in tqdm(train_loader):\n\n        images = images.to(device)\n        labels = labels.to(device).unsqueeze(1).float()\n\n        optimizer.zero_grad()\n\n        outputs = densenet_optuna(images)\n\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n\n        optimizer.step()\n\n        running_loss += loss.item()\n\n        preds = (torch.sigmoid(outputs) >= 0.5).float()\n\n        correct += (preds == labels).sum().item()\n\n        total += labels.size(0)\n\n    train_loss = running_loss / len(train_loader)\n\n    train_acc = correct / total\n\n    # =====================================================\n    # VALIDATION\n    # =====================================================\n\n    densenet_optuna.eval()\n\n    running_val_loss = 0\n\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n\n        for images, labels in val_loader:\n\n            images = images.to(device)\n\n            labels = labels.to(device).unsqueeze(1).float()\n\n            outputs = densenet_optuna(images)\n\n            loss = criterion(outputs, labels)\n\n            running_val_loss += loss.item()\n\n            preds = (torch.sigmoid(outputs) >= 0.5).float()\n\n            correct += (preds == labels).sum().item()\n\n            total += labels.size(0)\n\n    val_loss = running_val_loss / len(val_loader)\n\n    val_acc = correct / total\n\n    train_losses.append(train_loss)\n    val_losses.append(val_loss)\n\n    train_accuracies.append(train_acc)\n    val_accuracies.append(val_acc)\n\n    print(\n        f\"Epoch {epoch+1}/{num_epochs} | \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Val Loss: {val_loss:.4f} | \"\n        f\"Train Acc: {train_acc:.4f} | \"\n        f\"Val Acc: {val_acc:.4f}\"\n    )\n\n    if val_loss < best_val_loss:\n\n        best_val_loss = val_loss\n\n        torch.save(\n            densenet_optuna.state_dict(),\n            \"/kaggle/working/best_densenet121_optunaV3.pth\"\n        )\n\n        print(\"Best model saved.\")\n\n    early_stopping(val_loss)\n\n    if early_stopping.early_stop:\n\n        print(\"Early stopping\")\n\n        break\n\n\n# =====================================================\n# Sauvegarde historique\n# =====================================================\n\nhistory = pd.DataFrame({\n\n    \"Epoch\": range(1, len(train_losses)+1),\n\n    \"Train Loss\": train_losses,\n\n    \"Validation Loss\": val_losses,\n\n    \"Train Accuracy\": train_accuracies,\n\n    \"Validation Accuracy\": val_accuracies\n\n})\n\nhistory.to_csv(\n    \"/kaggle/working/densenet_optuna_history.csv\",\n    index=False\n)\n\nprint(\"Historique sauvegardé.\")\n\n\n# =====================================================\n# Courbe LOSS\n# =====================================================\n\nplt.figure(figsize=(8,5))\n\nplt.plot(\n    history[\"Epoch\"],\n    history[\"Train Loss\"],\n    marker=\"o\",\n    label=\"Train Loss\"\n)\n\nplt.plot(\n    history[\"Epoch\"],\n    history[\"Validation Loss\"],\n    marker=\"s\",\n    label=\"Validation Loss\"\n)\n\nplt.xticks(np.arange(2, len(history)+1, 2))\n\nplt.xlabel(\"Époque\")\nplt.ylabel(\"Loss\")\nplt.title(\"Courbe d'apprentissage - DenseNet121 (Optuna)\")\nplt.grid(True)\nplt.legend()\n\nplt.savefig(\n    \"/kaggle/working/densenet_optuna_loss_curve.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()\n\n\n# =====================================================\n# Courbe ACCURACY\n# =====================================================\n\nplt.figure(figsize=(8,5))\n\nplt.plot(\n    history[\"Epoch\"],\n    history[\"Train Accuracy\"],\n    marker=\"o\",\n    label=\"Train Accuracy\"\n)\n\nplt.plot(\n    history[\"Epoch\"],\n    history[\"Validation Accuracy\"],\n    marker=\"s\",\n    label=\"Validation Accuracy\"\n)\n\nplt.xticks(np.arange(2, len(history)+1, 2))\n\nplt.xlabel(\"Époque\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Accuracy par époque - DenseNet121 (Optuna)\")\nplt.grid(True)\nplt.legend()\n\nplt.savefig(\n    \"/kaggle/working/densenet_optuna_accuracy_curve.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-08T18:24:25.891797Z","iopub.execute_input":"2026-07-08T18:24:25.892263Z","iopub.status.idle":"2026-07-08T18:58:51.482626Z","shell.execute_reply.started":"2026-07-08T18:24:25.892226Z","shell.execute_reply":"2026-07-08T18:58:51.481635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"########### evaluation de densenet avac hyperpara OPTUMA\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    classification_report,\n    roc_auc_score\n)\n\nall_labels = []\nall_preds = []\nall_probs = []\n\n\n\ndensenet_optuna.load_state_dict(\n    torch.load(\n        \"/kaggle/working/best_densenet121_optunaV3.pth\",\n        map_location=device\n    )\n)\n\ndensenet_optuna.eval()\n\n \nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n        labels = labels.to(device).unsqueeze(1)\n\n        outputs = densenet_optuna(images)\n        probs = torch.sigmoid(outputs)\n\n        preds = (probs >= 0.5).float()\n\n        all_labels.extend(labels.cpu().numpy().ravel())\n        all_preds.extend(preds.cpu().numpy().ravel())\n        all_probs.extend(probs.cpu().numpy().ravel())\n\naccuracy = accuracy_score(all_labels, all_preds)\nprecision = precision_score(all_labels, all_preds,\n    zero_division=0)\nrecall = recall_score(all_labels, all_preds,\n    zero_division=0)\nf1 = f1_score(all_labels, all_preds,\n    zero_division=0)\nauc = roc_auc_score(all_labels, all_probs)\ncm = confusion_matrix(all_labels, all_preds)\n\nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\n\nprint(\"\\nMatrice de confusion :\")\nprint(cm)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-07T15:37:51.263158Z","iopub.execute_input":"2026-07-07T15:37:51.263607Z","iopub.status.idle":"2026-07-07T15:38:29.672017Z","shell.execute_reply.started":"2026-07-07T15:37:51.263572Z","shell.execute_reply":"2026-07-07T15:38:29.671213Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport pydicom\nimport numpy as np;\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\n\ntrain_labels = pd.read_csv(\"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv\")\nclass_info = pd.read_csv(\"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_detailed_class_info.csv\")\n\nclass_counts = class_info[\"class\"].value_counts()\n\nprint(class_counts)\n\nplt.figure(figsize=(6,4))\n\nclass_counts.plot(\n    kind='bar',\n    color=['steelblue','orange','green']\n)\n\nplt.title(\"Distribution des classes\")\nplt.ylabel(\"Nombre d'images\")\nplt.xticks(rotation=20)\nplt.show()\n\n(class_counts / class_counts.sum() * 100).round(2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T00:58:44.339691Z","iopub.execute_input":"2026-06-29T00:58:44.340407Z","iopub.status.idle":"2026-06-29T00:58:44.714599Z","shell.execute_reply.started":"2026-06-29T00:58:44.340372Z","shell.execute_reply":"2026-06-29T00:58:44.713563Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_folder=\"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_train_images\"\n\nfiles=os.listdir(image_folder)\n\nwidths=[]\nheights=[]\n\nfor f in tqdm(files):\n\n    ds=pydicom.dcmread(os.path.join(image_folder,f))\n\n    img=ds.pixel_array\n\n    h,w=img.shape\n\n    heights.append(h)\n    widths.append(w)\n\n\nresolution_df=pd.DataFrame({\n    \"Height\":heights,\n    \"Width\":widths\n})\n\nresolution_df.describe()\n\nplt.figure(figsize=(10,4))\n\nplt.subplot(1,2,1)\nplt.hist(widths,bins=20)\nplt.title(\"Largeur\")\n\nplt.subplot(1,2,2)\nplt.hist(heights,bins=20)\nplt.title(\"Hauteur\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T19:16:03.150104Z","iopub.execute_input":"2026-07-12T19:16:03.150866Z","iopub.status.idle":"2026-07-12T19:18:28.952547Z","shell.execute_reply.started":"2026-07-12T19:16:03.150833Z","shell.execute_reply":"2026-07-12T19:18:28.951765Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport pydicom\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\n\n\nimage_folder=\"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_train_images\"\n\nfiles=os.listdir(image_folder)\n\n\nsharpness=[]\n\n\nfor f in tqdm(files):\n\n    ds=pydicom.dcmread(os.path.join(image_folder,f))\n\n    img=ds.pixel_array\n\n    score=cv2.Laplacian(img,cv2.CV_64F).var()\n\n    sharpness.append(score)\n\nresolution_df[\"Sharpness\"]=sharpness\n\nplt.figure(figsize=(6,4))\n\nplt.hist(sharpness,bins=40)\n\nplt.title(\"de la netteté\")\nplt.xlabel(\"Variance du Laplacien\")\nplt.ylabel(\"Nombre d'images\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T19:18:36.986640Z","iopub.execute_input":"2026-07-12T19:18:36.986931Z","iopub.status.idle":"2026-07-12T19:22:37.348026Z","shell.execute_reply.started":"2026-07-12T19:18:36.986907Z","shell.execute_reply":"2026-07-12T19:22:37.347276Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mean_pixels=[]\nstd_pixels=[]\n\nfor f in tqdm(files):\n\n    ds=pydicom.dcmread(os.path.join(image_folder,f))\n\n    img=ds.pixel_array.astype(np.float32)\n\n    mean_pixels.append(img.mean())\n    std_pixels.append(img.std())\n\n\nresolution_df[\"MeanIntensity\"]=mean_pixels\nresolution_df[\"StdIntensity\"]=std_pixels\n\nplt.figure(figsize=(10,4))\n\nplt.subplot(1,2,1)\nplt.hist(mean_pixels,bins=40)\nplt.title(\"Moyenne des intensités\")\n\nplt.subplot(1,2,2)\nplt.hist(std_pixels,bins=40)\nplt.title(\"Écart-type des intensités\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T19:23:18.850685Z","iopub.execute_input":"2026-07-12T19:23:18.851262Z","iopub.status.idle":"2026-07-12T19:26:18.647500Z","shell.execute_reply.started":"2026-07-12T19:23:18.851235Z","shell.execute_reply":"2026-07-12T19:26:18.646795Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install grad-cam -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T20:56:46.737047Z","iopub.execute_input":"2026-07-12T20:56:46.737845Z","iopub.status.idle":"2026-07-12T20:56:57.259482Z","shell.execute_reply.started":"2026-07-12T20:56:46.737809Z","shell.execute_reply":"2026-07-12T20:56:57.258488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom pytorch_grad_cam import GradCAM\nfrom pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget\nfrom pytorch_grad_cam.utils.image import show_cam_on_image\n\n\n\nfrom torchvision import models\nimport torch.nn as nn\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ndropout_rate = 0.3350745495831822\ndense_units = 512\n\nmodel = models.densenet121(\n    weights=None\n)\n\nnum_features = model.classifier.in_features\n\nmodel.classifier = nn.Sequential(\n    nn.Dropout(dropout_rate),\n    nn.Linear(num_features, dense_units),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(dense_units, 1)\n)\n\nmodel.load_state_dict(\n    torch.load(\n        \"/kaggle/input/models/souhailmdaouer/best-densenet121-optunav3/pytorch/default/1/best_densenet121_optunaV3.pth\",\n        map_location=device\n    )\n)\n\nmodel.to(device)\nmodel.eval()\n\ntarget_layers = [model.features.denseblock4]\n\"\"\"TP : [0, 6, 53, 57, 60, 64, 65, 68, 74, 83]\nTN : [1, 2, 3, 4, 5, 7, 8, 9, 10, 11]\nFP : [25, 32, 33, 37, 44, 48, 50, 55, 62, 63]\nFN : [24, 42, 66, 90, 131, 169, 191, 198, 213, 215\"\"\"\nidx = 191\n\nimage, label = test_dataset[idx]\n\ninput_tensor = image.unsqueeze(0).to(device)\n\nrgb_img = image.permute(1,2,0).numpy()\n\nmean = np.array([0.485,0.456,0.406])\nstd = np.array([0.229,0.224,0.225])\n\nrgb_img = rgb_img * std + mean\nrgb_img = np.clip(rgb_img,0,1)\n\n\nwith torch.no_grad():\n\n    output = model(input_tensor)\n\n    probability = torch.sigmoid(output).item()\n\nprediction = 1 if probability >= 0.5 else 0\n\nprint(\"Label réel :\", label)\nprint(\"Classe prédite :\", prediction)\nprint(\"Probabilité pneumonie :\", probability)\n\ncam = GradCAM(\n    model=model,\n    target_layers=target_layers\n)\n\ngrayscale_cam = cam(\n    input_tensor=input_tensor,\n    targets=[ClassifierOutputTarget(0)]\n)[0]\n\nvisualization = show_cam_on_image(\n    rgb_img,\n    grayscale_cam,\n    use_rgb=True\n)\n\nplt.figure(figsize=(15,5))\n\n\"\"\"  plt.subplot(1,3,1)\nplt.imshow(rgb_img)\nplt.title(\"Image originale\")\nplt.axis(\"off\")\n\nplt.subplot(1,3,2)\nplt.imshow(grayscale_cam,cmap=\"jet\")\nplt.title(\"Grad-CAM\")\nplt.axis(\"off\")\n \"\"\"\nplt.subplot(1,3,3)\nplt.imshow(visualization)\nplt.title(\"Superposition du Grad-CAM\")\nplt.axis(\"off\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-12T21:39:23.749620Z","iopub.execute_input":"2026-07-12T21:39:23.750330Z","iopub.status.idle":"2026-07-12T21:39:24.298280Z","shell.execute_reply.started":"2026-07-12T21:39:23.750298Z","shell.execute_reply":"2026-07-12T21:39:24.297633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nimport torch\nimport torch.nn as nn\nfrom torchvision import models\nfrom thop import profile, clever_format\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nbatch_size = 32\nlearning_rate = 0.0006545776317255478\ndropout_rate = 0.3350745495831822\ndense_units = 512\nweight_decay = 1.7703130607090214e-06\npositive_class_weight = 2.6448777190609833\nnum_epochs = 20\n\nmodel = models.densenet121(weights=None)\n\n# Geler toutes les couches\nfor param in model.parameters():\n    param.requires_grad = False\n\n# ==========================================================\n# Classifier\n# ==========================================================\n\nnum_features = model.classifier.in_features\n\nmodel.classifier = nn.Sequential(\n    nn.Dropout(dropout_rate),\n    nn.Linear(num_features, dense_units),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(dense_units, 1)\n)\n\n# ==========================================================\n# Fine tuning\n# ==========================================================\n# Geler toutes les couches\nfor param in model.parameters():\n    param.requires_grad = False\n\n# Dégeler les 100 derniers paramètres du backbone\nfor param in list(model.features.parameters())[-100:]:\n    param.requires_grad = True\n\n# Le classifieur est toujours entraînable\nfor param in model.classifier.parameters():\n    param.requires_grad = True\n# ==========================================================\n# Charger le meilleur modèle DenseNet + Segmentation\n# ==========================================================\n\nmodel.load_state_dict(\n    torch.load(\n        \"/kaggle/input/models/souhailmdaouer/best-densenet121-segmented/pytorch/default/1/best_densenet121_segmented.pth\",\n        map_location=device\n    )\n)\n\nmodel = model.to(device)\nmodel.eval()\n\n# ==========================================================\n# Nombre de paramètres\n# ==========================================================\n\ndef model_statistics(model):\n\n    total = sum(p.numel() for p in model.parameters())\n    trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)\n    frozen = total - trainable\n\n    print(f\"Total parameters      : {total:,}\")\n    print(f\"Trainable parameters  : {trainable:,}\")\n    print(f\"Frozen parameters     : {frozen:,}\")\n\nmodel_statistics(model)\n\n# ==========================================================\n# Coût de calcul (GFLOPs)\n# ==========================================================\n\ndummy = torch.randn(1, 3, 224, 224).to(device)\n\nflops, params = profile(\n    model,\n    inputs=(dummy,),\n    verbose=False\n)\n\nflops, params = clever_format([flops, params], \"%.3f\")\n\nprint(f\"FLOPs : {flops}\")\nprint(f\"Nombre de paramètres : {params}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-20T21:38:40.467040Z","iopub.execute_input":"2026-07-20T21:38:40.467307Z","iopub.status.idle":"2026-07-20T21:38:41.133489Z","shell.execute_reply.started":"2026-07-20T21:38:40.467285Z","shell.execute_reply":"2026-07-20T21:38:41.132887Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"##############\"\"\" optuma densnet\nTotal parameters      : 7,479,169\nTrainable parameters  : 3,210,753\nFrozen parameters     : 4,268,416\nFLOPs : 2.897G\nNombre de paramètres : 7.479M\ntmps dentrainement :28 minutes 37 s\n\n########### densnet+segmentation\nTotal parameters      : 7,479,169\nTrainable parameters  : 3,210,753\nFrozen parameters     : 4,268,416\nFLOPs : 2.897G\nNombre de paramètres : 7.479M\ntmps dentrainement : 26 minutes 18 secondes\n\n##################\" \"  densnet\nTotal parameters      : 7,479,682\nTrainable parameters  : 2,685,954\nFrozen parameters     : 4,793,728\nFLOPs : 2.897G\nNombre de paramètres : 7.480M\ntmps dentrainement :23 minutes 42 secondes\n\n###########\"\"\"\"  Resnet\nTotal parameters      : 24,558,146\nTrainable parameters  : 16,014,850\nFrozen parameters     : 8,543,296\nFLOPs : 4.133G\nNombre de paramètres : 24.558M\ntmps dentrainement : 59 minutes 30 secondes\n\n#############\" u-net\"\n24.09 GFLOPs\ntmps segmenter tous les images : 48 minutes 36 secondes\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install thop","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-20T20:42:15.016193Z","iopub.execute_input":"2026-07-20T20:42:15.017000Z","iopub.status.idle":"2026-07-20T20:42:21.002940Z","shell.execute_reply.started":"2026-07-20T20:42:15.016967Z","shell.execute_reply":"2026-07-20T20:42:21.002220Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"##################          dzns+op\n\nTotal parameters      : 7,479,169\nTrainable parameters  : 3,210,753\nFrozen parameters     : 4,268,416","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"########### evaluation de densenet avac hyperpara OPTUMA\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    classification_report,\n    roc_auc_score\n)\n\nimport torch\nimport torch.nn as nn\nfrom torchvision import models\n\n\nlabels_optuna =  []\npreds_optuna = []\nprobs_optuna =  []\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ndropout_rate = 0.3350745495831822\ndense_units = 512\n\nmodel = models.densenet121(\n    weights=None\n)\n\nnum_features = model.classifier.in_features\n\nmodel.classifier = nn.Sequential(\n    nn.Dropout(dropout_rate),\n    nn.Linear(num_features, dense_units),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(dense_units, 1)\n)\n\nmodel.load_state_dict(\n    torch.load(\n        \"/kaggle/input/models/souhailmdaouer/best-densenet121-optunav3/pytorch/default/1/best_densenet121_optunaV3.pth\",\n        map_location=device\n    )\n)\n\nmodel.to(device)\nmodel.eval()\n\n \nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n        labels = labels.to(device).unsqueeze(1)\n\n        outputs = model(images)\n        probs = torch.sigmoid(outputs)\n\n        preds = (probs >= 0.5).float()\n\n        labels_optuna.extend(labels.cpu().numpy().ravel())\n        preds_optuna.extend(preds.cpu().numpy().ravel())\n        probs_optuna.extend(probs.cpu().numpy().ravel())\n\naccuracy = accuracy_score(labels_optuna, preds_optuna)\nprecision = precision_score(labels_optuna, preds_optuna,\n    zero_division=0)\nrecall = recall_score(labels_optuna, preds_optuna,\n    zero_division=0)\nf1 = f1_score(labels_optuna, preds_optuna,\n    zero_division=0)\nauc = roc_auc_score(labels_optuna, probs_optuna)\n \ncm = confusion_matrix(labels_optuna, preds_optuna)\nprint(\"\\nMatrice de confusion :\")\nprint(cm)\nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\n\n\nlabels_optuna = np.array(labels_optuna)\npreds_optuna = np.array(preds_optuna)\nprobs_optuna = np.array(probs_optuna)\n\n\nprint(labels_optuna.shape)\nprint(preds_optuna.shape)\nprint(probs_optuna.shape)\n\nprint(np.unique(labels_optuna))\nprint(np.unique(preds_optuna))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T17:18:38.470755Z","iopub.execute_input":"2026-07-21T17:18:38.471586Z","iopub.status.idle":"2026-07-21T17:19:22.381205Z","shell.execute_reply.started":"2026-07-21T17:18:38.471546Z","shell.execute_reply":"2026-07-21T17:19:22.380326Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.save(\"labels_optuna.npy\", labels_optuna)\nnp.save(\"preds_optuna.npy\", preds_optuna)\nnp.save(\"probs_optuna.npy\", probs_optuna)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T14:54:12.228051Z","iopub.execute_input":"2026-07-21T14:54:12.228551Z","iopub.status.idle":"2026-07-21T14:54:12.233824Z","shell.execute_reply.started":"2026-07-21T14:54:12.228520Z","shell.execute_reply":"2026-07-21T14:54:12.233133Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"########### evaluation de densenet segmented\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    classification_report,\n    roc_auc_score\n)\n\nimport torch\nimport torch.nn as nn\nfrom torchvision import models\n\n\n\n\n\n\nlabels_seg =  []\npreds_seg = []\nprobs_seg =  []\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ndropout_rate = 0.3350745495831822\ndense_units = 512\n\nmodel = models.densenet121(\n    weights=None\n)\n\nnum_features = model.classifier.in_features\n\nmodel.classifier = nn.Sequential(\n    nn.Dropout(dropout_rate),\n    nn.Linear(num_features, dense_units),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(dense_units, 1)\n)\n\nmodel.load_state_dict(\n    torch.load(\n        \"/kaggle/input/models/souhailmdaouer/best-densenet121-segmented/pytorch/default/1/best_densenet121_segmented.pth\",\n        map_location=device\n    )\n)\n\nmodel.to(device)\nmodel.eval()\n\n \nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n        labels = labels.to(device).unsqueeze(1)\n\n        outputs = model(images)\n        probs = torch.sigmoid(outputs)\n\n        preds = (probs >= 0.5).float()\n\n        labels_seg.extend(labels.cpu().numpy().ravel())\n        preds_seg.extend(preds.cpu().numpy().ravel())\n        probs_seg.extend(probs.cpu().numpy().ravel())\n\naccuracy = accuracy_score(labels_seg, preds_seg)\nprecision = precision_score(labels_seg, preds_seg,\n    zero_division=0)\nrecall = recall_score(labels_seg, preds_seg,\n    zero_division=0)\nf1 = f1_score(labels_seg, preds_seg,\n    zero_division=0)\nauc = roc_auc_score(labels_seg, probs_seg)\n\ncm = confusion_matrix(labels_seg, preds_seg)\n\nprint(\"\\nMatrice de confusion :\")\nprint(cm)\n\nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\n\n\nlabels_seg = np.array(labels_seg)\npreds_seg = np.array(preds_seg)\nprobs_seg = np.array(probs_seg)\n\n\nprint(labels_seg.shape)\nprint(preds_seg.shape)\nprint(probs_seg.shape)\n\nprint(np.unique(labels_seg))\nprint(np.unique(preds_seg))\n\nnp.save(\"labels_seg.npy\", labels_seg)\nnp.save(\"preds_seg.npy\", preds_seg)\nnp.save(\"probs_seg.npy\", probs_seg)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T16:52:04.085411Z","iopub.execute_input":"2026-07-21T16:52:04.086007Z","iopub.status.idle":"2026-07-21T16:53:05.397222Z","shell.execute_reply.started":"2026-07-21T16:52:04.085976Z","shell.execute_reply":"2026-07-21T16:53:05.396360Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n########### evaluation de Resnet50\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    classification_report,\n    roc_auc_score\n)\n\nimport torch\nimport torch.nn as nn\nfrom torchvision import models\n\n\nlabels_Resnet=  []\npreds_Resnet = []\nprobs_Resnet =  []\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n\ndropout_rate = 0.3\n\n\nmodel = models.resnet50(weights=None)\n\nnum_features = model.fc.in_features\n\nmodel.fc = nn.Sequential(\n    nn.Linear(num_features, 512),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(512, 2)\n)\n\nmodel.load_state_dict(\n    torch.load(\n        \"/kaggle/input/models/souhailmdaouer/best-resnet50/pytorch/default/1/best_resnet50.pth\",\n        map_location=device\n    )\n)\n\nmodel.to(device)\nmodel.eval()\n\n \nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = model(images)\n\n        probs = torch.softmax(outputs, dim=1)\n\n        preds = torch.argmax(probs, dim=1)\n\n        positive_probs = probs[:,1]\n\n        labels_Resnet.extend(labels.cpu().numpy())\n\n        preds_Resnet.extend(preds.cpu().numpy())\n\n        probs_Resnet.extend(positive_probs.cpu().numpy())\n\n\naccuracy = accuracy_score(labels_Resnet, preds_Resnet)\nprecision = precision_score(labels_Resnet, preds_Resnet,\n    zero_division=0)\nrecall = recall_score(labels_Resnet, preds_Resnet,\n    zero_division=0)\nf1 = f1_score(labels_Resnet, preds_Resnet,\n    zero_division=0)\nauc = roc_auc_score(labels_Resnet, probs_Resnet)\n \nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\n\n\nlabels_Resnet = np.array(labels_Resnet)\npreds_Resnet = np.array(preds_Resnet)\nprobs_Resnet = np.array(probs_Resnet)\n\n\nprint(labels_Resnet.shape)\nprint(preds_Resnet.shape)\nprint(probs_Resnet.shape)\n\nprint(np.unique(labels_Resnet))\nprint(np.unique(preds_Resnet))\n\nnp.save(\"labels_resnet.npy\", labels_Resnet)\nnp.save(\"preds_resnet.npy\", preds_Resnet)\nnp.save(\"probs_resnet.npy\", probs_Resnet)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T15:10:33.330765Z","iopub.execute_input":"2026-07-21T15:10:33.331093Z","iopub.status.idle":"2026-07-21T15:11:19.622615Z","shell.execute_reply.started":"2026-07-21T15:10:33.331063Z","shell.execute_reply":"2026-07-21T15:11:19.621735Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n########### evaluation de Resnet50\nfrom sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    confusion_matrix,\n    classification_report,\n    roc_auc_score\n)\n\nimport torch\nimport torch.nn as nn\nfrom torchvision import models\n\n\nlabels_Densenet =  []\npreds_Densenet = []\nprobs_Densenet =  []\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n\ndropout_rate = 0.3\n\n\nmodel = models.densenet121(\n    weights=None\n)\n\nnum_features = model.classifier.in_features\n\nmodel.classifier = nn.Sequential(\n    nn.Linear(num_features, 512),\n    nn.ReLU(),\n    nn.Dropout(dropout_rate),\n    nn.Linear(512, 2)\n)\n\n\nmodel.load_state_dict(\n    torch.load(\n        \"/kaggle/input/models/souhailmdaouer/best-densenet121/pytorch/default/1/best_densenet121.pth\",\n        map_location=device\n    )\n)\n\nmodel.to(device)\nmodel.eval()\n\n \nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = model(images)\n\n        probs = torch.softmax(outputs, dim=1)\n\n        preds = torch.argmax(probs, dim=1)\n\n        positive_probs = probs[:,1]\n\n        labels_Densenet.extend(labels.cpu().numpy())\n\n        preds_Densenet.extend(preds.cpu().numpy())\n\n        probs_Densenet.extend(positive_probs.cpu().numpy())\n\n\naccuracy = accuracy_score(labels_Densenet, preds_Densenet)\nprecision = precision_score(labels_Densenet, preds_Densenet,\n    zero_division=0)\nrecall = recall_score(labels_Densenet, preds_Densenet,\n    zero_division=0)\nf1 = f1_score(labels_Densenet, preds_Densenet,\n    zero_division=0)\nauc = roc_auc_score(labels_Densenet, probs_Densenet)\n\ncm = confusion_matrix(labels_Densenet, preds_Densenet)\n\nprint(\"\\nMatrice de confusion :\")\nprint(cm)\nprint(\"Accuracy :\", accuracy)\nprint(\"Precision :\", precision)\nprint(\"Recall :\", recall)\nprint(\"F1-score :\", f1)\nprint(\"AUC :\", auc)\n\n\nlabels_Densenet = np.array(labels_Densenet)\npreds_Densenet = np.array(preds_Densenet)\nprobs_Densenet = np.array(probs_Densenet)\n\n\nprint(labels_Densenet.shape)\nprint(preds_Densenet.shape)\nprint(probs_Densenet.shape)\n\nprint(np.unique(labels_Densenet))\nprint(np.unique(preds_Densenet))\n\nnp.save(\"labels_densenet.npy\", labels_Densenet)\nnp.save(\"preds_densenet.npy\", preds_Densenet)\nnp.save(\"probs_densenet.npy\", probs_Densenet)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T17:10:49.545329Z","iopub.execute_input":"2026-07-21T17:10:49.546073Z","iopub.status.idle":"2026-07-21T17:11:34.546145Z","shell.execute_reply.started":"2026-07-21T17:10:49.545997Z","shell.execute_reply":"2026-07-21T17:11:34.545136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nlabels_optuna = np.load(\"/kaggle/working/labels_optuna.npy\")\npreds_optuna  = np.load(\"/kaggle/working/preds_optuna.npy\")\nprobs_optuna  = np.load(\"/kaggle/working/probs_optuna.npy\")\n\nlabels_dense = np.load(\"/kaggle/working/labels_densenet.npy\")\npreds_dense  = np.load(\"/kaggle/working/preds_densenet.npy\")\nprobs_dense  = np.load(\"/kaggle/working/probs_densenet.npy\")\n\nlabels_seg = np.load(\"/kaggle/working/labels_seg.npy\")\npreds_seg  = np.load(\"/kaggle/working/preds_seg.npy\")\nprobs_seg  = np.load(\"/kaggle/working/probs_seg.npy\")\n\nlabels_resnet = np.load(\"/kaggle/working/labels_resnet.npy\")\npreds_resnet  = np.load(\"/kaggle/working/preds_resnet.npy\")\nprobs_resnet  = np.load(\"/kaggle/working/probs_resnet.npy\")\n\n\nprint(np.array_equal(labels_optuna, labels_dense))\nprint(np.array_equal(labels_optuna, labels_seg))\nprint(np.array_equal(labels_optuna, labels_resnet))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T15:30:37.515941Z","iopub.execute_input":"2026-07-21T15:30:37.516946Z","iopub.status.idle":"2026-07-21T15:30:37.528593Z","shell.execute_reply.started":"2026-07-21T15:30:37.516905Z","shell.execute_reply":"2026-07-21T15:30:37.527560Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score, precision_score\nfrom sklearn.metrics import recall_score, f1_score, roc_auc_score\n\nimport numpy as np\n\ndef bootstrap_metrics(y_true,\n                      y_pred,\n                      y_prob,\n                      n_bootstrap=1000):\n\n    rng = np.random.default_rng(42)\n\n    acc=[]\n    prec=[]\n    rec=[]\n    f1=[]\n    auc=[]\n\n    N=len(y_true)\n\n    for i in range(n_bootstrap):\n\n        idx=rng.choice(N,N,replace=True)\n\n        yt=y_true[idx]\n        yp=y_pred[idx]\n        ys=y_prob[idx]\n\n        acc.append(accuracy_score(yt,yp))\n        prec.append(precision_score(yt,yp))\n        rec.append(recall_score(yt,yp))\n        f1.append(f1_score(yt,yp))\n        auc.append(roc_auc_score(yt,ys))\n\n    return {\n        \"Accuracy\":(np.mean(acc),\n                    np.percentile(acc,2.5),\n                    np.percentile(acc,97.5)),\n\n        \"Precision\":(np.mean(prec),\n                     np.percentile(prec,2.5),\n                     np.percentile(prec,97.5)),\n\n        \"Recall\":(np.mean(rec),\n                  np.percentile(rec,2.5),\n                  np.percentile(rec,97.5)),\n\n        \"F1\":(np.mean(f1),\n              np.percentile(f1,2.5),\n              np.percentile(f1,97.5)),\n\n        \"AUC\":(np.mean(auc),\n               np.percentile(auc,2.5),\n               np.percentile(auc,97.5))\n    }\n\nresults=bootstrap_metrics(\n    labels_optuna,\n    preds_optuna,\n    probs_optuna\n)\n\nfor k,v in results.items():\n\n    print(k)\n\n    print(f\"{v[0]:.4f}\")\n\n    print(f\"IC95% [{v[1]:.4f} ; {v[2]:.4f}]\")\n\n    print()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T15:31:59.100519Z","iopub.execute_input":"2026-07-21T15:31:59.101252Z","iopub.status.idle":"2026-07-21T15:32:09.138614Z","shell.execute_reply.started":"2026-07-21T15:31:59.101222Z","shell.execute_reply":"2026-07-21T15:32:09.137817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results=bootstrap_metrics(\n    labels_seg,\n    preds_seg,\n    probs_seg\n)\n\nfor k,v in results.items():\n\n    print(k)\n\n    print(f\"{v[0]:.4f}\")\n\n    print(f\"IC95% [{v[1]:.4f} ; {v[2]:.4f}]\")\n\n    print()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T15:33:07.994033Z","iopub.execute_input":"2026-07-21T15:33:07.994443Z","iopub.status.idle":"2026-07-21T15:33:18.002156Z","shell.execute_reply.started":"2026-07-21T15:33:07.994411Z","shell.execute_reply":"2026-07-21T15:33:18.001537Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results=bootstrap_metrics(\n    labels_Densenet,\n    preds_Densenet,\n    probs_Densenet\n)\n\nfor k,v in results.items():\n\n    print(k)\n\n    print(f\"{v[0]:.4f}\")\n\n    print(f\"IC95% [{v[1]:.4f} ; {v[2]:.4f}]\")\n\n    print()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T15:34:03.262827Z","iopub.execute_input":"2026-07-21T15:34:03.263260Z","iopub.status.idle":"2026-07-21T15:34:13.316707Z","shell.execute_reply.started":"2026-07-21T15:34:03.263230Z","shell.execute_reply":"2026-07-21T15:34:13.316000Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results=bootstrap_metrics(\n    labels_Resnet,\n    preds_Resnet,\n    probs_Resnet\n)\n\nfor k,v in results.items():\n\n    print(k)\n\n    print(f\"{v[0]:.4f}\")\n\n    print(f\"IC95% [{v[1]:.4f} ; {v[2]:.4f}]\")\n\n    print()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T15:34:55.383837Z","iopub.execute_input":"2026-07-21T15:34:55.384268Z","iopub.status.idle":"2026-07-21T15:35:05.881724Z","shell.execute_reply.started":"2026-07-21T15:34:55.384230Z","shell.execute_reply":"2026-07-21T15:35:05.880840Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install statsmodels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T15:37:36.594354Z","iopub.execute_input":"2026-07-21T15:37:36.594841Z","iopub.status.idle":"2026-07-21T15:37:41.532755Z","shell.execute_reply.started":"2026-07-21T15:37:36.594810Z","shell.execute_reply":"2026-07-21T15:37:41.531679Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from statsmodels.stats.contingency_tables import mcnemar\n\ndef mcnemar_test(y_true,\n                 pred1,\n                 pred2):\n\n    b=0\n    c=0\n\n    for yt,p1,p2 in zip(y_true,pred1,pred2):\n\n        ok1=(p1==yt)\n        ok2=(p2==yt)\n\n        if ok1 and not ok2:\n            b+=1\n\n        elif ok2 and not ok1:\n            c+=1\n\n    table=[[0,b],\n           [c,0]]\n\n    result=mcnemar(table,\n                   exact=True)\n\n    print(table)\n\n    print(result.pvalue)\n\nmcnemar_test(\n    labels_optuna,\n    preds_optuna,\n    preds_Densenet)\n\nmcnemar_test(\n    labels_optuna,\n    preds_optuna,\n    preds_seg)\n\nmcnemar_test(\n    labels_optuna,\n    preds_optuna,\n    preds_Resnet\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-21T15:40:54.940408Z","iopub.execute_input":"2026-07-21T15:40:54.940977Z","iopub.status.idle":"2026-07-21T15:40:55.033274Z","shell.execute_reply.started":"2026-07-21T15:40:54.940937Z","shell.execute_reply":"2026-07-21T15:40:55.032367Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"############### Resnet\nAccuracy\n0.7371\nIC95% [0.7232 ; 0.7507]\n\nPrecision\n0.4530\nIC95% [0.4279 ; 0.4782]\n\nRecall\n0.8021\nIC95% [0.7740 ; 0.8274]\n\nF1\n0.5789\nIC95% [0.5560 ; 0.6019]\n\nAUC\n0.8354\nIC95% [0.8213 ; 0.8489]\n\n\n############## Densenet\n\nAccuracy\n0.7506\nIC95% [0.7367 ; 0.7634]\n\nPrecision\n0.4702\nIC95% [0.4461 ; 0.4962]\n\nRecall\n0.8422\nIC95% [0.8179 ; 0.8662]\n\nF1\n0.6034\nIC95% [0.5819 ; 0.6264]\n\nAUC\n0.8673\nIC95% [0.8549 ; 0.8800]\n\n################\" segmentatio\"\nAccuracy\n0.4710\nIC95% [0.4559 ; 0.4869]\n\nPrecision\n0.2814\nIC95% [0.2641 ; 0.2982]\n\nRecall\n0.8668\nIC95% [0.8440 ; 0.8881]\n\nF1\n0.4248\nIC95% [0.4041 ; 0.4449]\n\nAUC\n0.6983\nIC95% [0.6790 ; 0.7179]\n\n\n##########\" optuna\"\nAccuracy\n0.8032\nIC95% [0.7909 ; 0.8156]\n\nPrecision\n0.5452\nIC95% [0.5175 ; 0.5722]\n\nRecall\n0.7657\nIC95% [0.7364 ; 0.7932]\n\nF1\n0.6368\nIC95% [0.6128 ; 0.6600]\n\nAUC\n0.8756\nIC95% [0.8630 ; 0.8882]\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\nlabels_df = pd.read_csv(\n    \"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv\"\n)\n\nlabels_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-11T13:29:24.185999Z","iopub.execute_input":"2026-07-11T13:29:24.186539Z","iopub.status.idle":"2026-07-11T13:29:24.222647Z","shell.execute_reply.started":"2026-07-11T13:29:24.186511Z","shell.execute_reply":"2026-07-11T13:29:24.221954Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport pydicom\nimport torch\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nfrom pytorch_grad_cam import GradCAM\nfrom pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget\nfrom pytorch_grad_cam.utils.image import show_cam_on_image\n\n# ==========================================================\n# Choisir un indice du test_dataset (TP)\n# ==========================================================\n\n\"\"\"TP : [0, 6, 53, 57, 60, 64, 65, 68, 74, 83]\nFN : [24, 42, 66, 90, 131, 169, 191, 198, 213, 215\"\"\"\nidx = 66  # <-- remplacer par un vrai TP\n\nimage_tensor, label = test_dataset[idx]\n\n# ==========================================================\n# Chemin DICOM\n# ==========================================================\n\ndicom_path = test_df.iloc[idx][\"path\"]\n\npatient_id = dicom_path.split(\"/\")[-1].replace(\".dcm\",\"\")\n\n# ==========================================================\n# Chargement image originale\n# ==========================================================\n\ndcm = pydicom.dcmread(dicom_path)\n\nimage = dcm.pixel_array.astype(np.float32)\n\nimage = image - image.min()\nimage = image / image.max()\n\nimage_uint8 = (255*image).astype(np.uint8)\n\nimage_rgb = cv2.cvtColor(image_uint8, cv2.COLOR_GRAY2RGB)\n\n# ==========================================================\n# Bounding boxes\n# ==========================================================\n\nboxes = labels_df[\n    labels_df[\"patientId\"] == patient_id\n]\n\n# ==========================================================\n# Prédiction\n# ==========================================================\n\ninput_tensor = image_tensor.unsqueeze(0).to(device)\n\nwith torch.no_grad():\n\n    output = model(input_tensor)\n\n    probability = torch.sigmoid(output).item()\n\nprediction = 1 if probability >= 0.5 else 0\n\nprint(\"Patient :\", patient_id)\nprint(\"Label réel :\", label.item())\nprint(\"Classe prédite :\", prediction)\nprint(\"Probabilité :\", probability)\n\n# ==========================================================\n# Préparer image RGB normalisée pour GradCAM\n# ==========================================================\n\nrgb_img = image_tensor.permute(1,2,0).numpy()\n\nmean = np.array([0.485,0.456,0.406])\nstd = np.array([0.229,0.224,0.225])\n\nrgb_img = rgb_img*std + mean\nrgb_img = np.clip(rgb_img,0,1)\n\n# ==========================================================\n# GradCAM\n# ==========================================================\n\ntarget_layers = [model.features.norm5]\n\ncam = GradCAM(\n    model=model,\n    target_layers=target_layers\n)\n\ngrayscale_cam = cam(\n    input_tensor=input_tensor,\n    targets=[ClassifierOutputTarget(0)]\n)[0]\n\noverlay = show_cam_on_image(\n    rgb_img,\n    grayscale_cam,\n    use_rgb=True\n)\n\n# ==========================================================\n# Figure finale\n# ==========================================================\n\nfig = plt.figure(figsize=(16,12))\n\n# ----------------------------------------------------------\n\nax1 = plt.subplot(2,2,1)\n\nax1.imshow(image_rgb,cmap=\"gray\")\n\nax1.set_title(\"Image originale\")\n\nax1.axis(\"off\")\n\n# ----------------------------------------------------------\n\nax2 = plt.subplot(2,2,2)\n\nax2.imshow(image_rgb,cmap=\"gray\")\n\nfor _, row in boxes.iterrows():\n\n    rect = patches.Rectangle(\n        (row[\"x\"],row[\"y\"]),\n        row[\"width\"],\n        row[\"height\"],\n        linewidth=2,\n        edgecolor='lime',\n        facecolor='none'\n    )\n\n    ax2.add_patch(rect)\n\nax2.set_title(\"Annotation RSNA (Bounding Boxes)\")\n\nax2.axis(\"off\")\n\n# ----------------------------------------------------------\n\nax3 = plt.subplot(2,2,3)\n\nax3.imshow(grayscale_cam,cmap=\"jet\")\n\nax3.set_title(\"Carte Grad-CAM\")\n\nax3.axis(\"off\")\n\n# ----------------------------------------------------------\n\nax4 = plt.subplot(2,2,4)\n\nax4.imshow(overlay)\n\nax4.set_title(\"Grad-CAM superposé\")\n\nax4.axis(\"off\")\n\nplt.tight_layout()\n\nplt.savefig(\n    \"/kaggle/working/GradCAM_RSNA_TP.png\",\n    dpi=300,\n    bbox_inches=\"tight\"\n)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-11T14:12:44.684384Z","iopub.execute_input":"2026-07-11T14:12:44.685182Z","iopub.status.idle":"2026-07-11T14:12:47.851901Z","shell.execute_reply.started":"2026-07-11T14:12:44.685136Z","shell.execute_reply":"2026-07-11T14:12:47.851082Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport torch\n\nmodel.eval()\n\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():\n\n    for images, labels in test_loader:\n\n        images = images.to(device)\n\n        outputs = model(images)\n\n        probs = torch.sigmoid(outputs)\n\n        preds = (probs >= 0.5).long().squeeze()\n\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.numpy())\n\n# ==========================================================\n# Récupération des indices\n# ==========================================================\n\nTP = []\nTN = []\nFP = []\nFN = []\n\nfor i, (y_true, y_pred) in enumerate(zip(all_labels, all_preds)):\n\n    if y_true == 1 and y_pred == 1:\n        TP.append(i)\n\n    elif y_true == 0 and y_pred == 0:\n        TN.append(i)\n\n    elif y_true == 0 and y_pred == 1:\n        FP.append(i)\n\n    elif y_true == 1 and y_pred == 0:\n        FN.append(i)\n\nprint(\"TP :\", len(TP))\nprint(\"TN :\", len(TN))\nprint(\"FP :\", len(FP))\nprint(\"FN :\", len(FN))\n\nprint(\"\\nQuelques indices :\")\nprint(\"TP :\", TP[:10])\nprint(\"TN :\", TN[:10])\nprint(\"FP :\", FP[:10])\nprint(\"FN :\", FN[:10])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-08T20:57:11.976788Z","iopub.execute_input":"2026-07-08T20:57:11.977593Z","iopub.status.idle":"2026-07-08T20:57:48.902168Z","shell.execute_reply.started":"2026-07-08T20:57:11.977562Z","shell.execute_reply":"2026-07-08T20:57:48.901428Z"}},"outputs":[],"execution_count":null}]}