{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":44527,"databundleVersionId":5167437,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:38:59.077785Z","iopub.execute_input":"2025-08-18T16:38:59.078386Z","iopub.status.idle":"2025-08-18T16:39:17.868908Z","shell.execute_reply.started":"2025-08-18T16:38:59.078362Z","shell.execute_reply":"2025-08-18T16:39:17.868151Z"},"jupyter":{"outputs_hidden":true},"collapsed":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, glob\n\ncandidates = [p for p in glob.glob('/kaggle/input/*') \n              if os.path.exists(os.path.join(p, 'train_gender.csv'))]\n\nprint(\"Candidats trouvés:\", candidates)\nBASE_DIR = candidates[0] if candidates else None\nprint(\"BASE_DIR =\", BASE_DIR)\n\nif BASE_DIR:\n    print(\"Contenu de BASE_DIR:\", os.listdir(BASE_DIR))\n    # Affiche les 20 premiers fichiers PNG s'ils sont à la racine ou dans un sous-dossier courant\n    for root, _, files in os.walk(BASE_DIR):\n        pngs = [f for f in files if f.lower().endswith('.png')]\n        if pngs:\n            print(\"Exemples d'images:\", pngs[:20])\n            break\nelse:\n    print(\"Aucun dossier avec train_gender.csv trouvé. Vérifie que tu as bien ajouté le Competition Data.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:39:25.503705Z","iopub.execute_input":"2025-08-18T16:39:25.504389Z","iopub.status.idle":"2025-08-18T16:39:25.557013Z","shell.execute_reply.started":"2025-08-18T16:39:25.504364Z","shell.execute_reply":"2025-08-18T16:39:25.556454Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ntrain_df = pd.read_csv(os.path.join(BASE_DIR, 'train_gender.csv'))\nsample_submission = pd.read_csv(os.path.join(BASE_DIR, 'sample_submission_gender.csv'))\n\nprint(\"Aperçu du train_df:\")\nprint(train_df.head())\n\nprint(\"\\nAperçu du sample_submission:\")\nprint(sample_submission.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:39:33.1935Z","iopub.execute_input":"2025-08-18T16:39:33.193765Z","iopub.status.idle":"2025-08-18T16:39:33.20966Z","shell.execute_reply.started":"2025-08-18T16:39:33.193745Z","shell.execute_reply":"2025-08-18T16:39:33.208936Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\npng_files = [f for f in os.listdir(BASE_DIR) if f.endswith('.png')]\nprint(png_files[:10])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:39:36.968841Z","iopub.execute_input":"2025-08-18T16:39:36.969368Z","iopub.status.idle":"2025-08-18T16:39:36.973678Z","shell.execute_reply.started":"2025-08-18T16:39:36.969344Z","shell.execute_reply":"2025-08-18T16:39:36.973087Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for root, dirs, files in os.walk(BASE_DIR):\n    for file in files:\n        if file.endswith('.png'):\n            print(os.path.join(root, file))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:39:39.31921Z","iopub.execute_input":"2025-08-18T16:39:39.319872Z","iopub.status.idle":"2025-08-18T16:39:40.649336Z","shell.execute_reply.started":"2025-08-18T16:39:39.319849Z","shell.execute_reply":"2025-08-18T16:39:40.648536Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nimport torch\n\nBASE_DIR = \"/kaggle/input/spr-x-ray-gender\"\ntrain_df = pd.read_csv(os.path.join(BASE_DIR, \"train_gender.csv\"))\n\nIMG_DIR = os.path.join(BASE_DIR, \"kaggle/kaggle/train\")\n\nclass XRayDataset(Dataset):\n    def __init__(self, df, img_dir, transform=None):\n        self.df = df\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        filename = str(row['imageId']).zfill(6) + '.png'\n        img_path = os.path.join(self.img_dir, filename)\n        image = Image.open(img_path).convert('L') # L = niveaux de gris\n        if self.transform:\n            image = self.transform(image)\n        label = torch.tensor(row['gender'], dtype=torch.float32)\n        return image, label\n\nclass TestDataset(Dataset):\n    def __init__(self, df, img_dir, transform=None):\n        self.df = df\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        filename = str(row['imageId_str']) + '.png'\n        img_path = os.path.join(self.img_dir, filename)\n        image = Image.open(img_path).convert('L') # L = grayscale\n        if self.transform:\n            image = self.transform(image)\n        return image, row['imageId']\n\n# Transformations pour les images d'entraînement (avec data augmentation)\ntrain_transform = transforms.Compose([\n    transforms.RandomRotation(10), # Rotation aléatoire de -10 à 10 degrés\n    transforms.RandomHorizontalFlip(), # Retournement horizontal aléatoire\n    transforms.ColorJitter(brightness=0.1, contrast=0.1), # Légères variations de luminosité et de contraste\n    transforms.Resize((128, 128)),\n    transforms.ToTensor(),\n])\n\n# Transformations pour les images de test (sans data augmentation)\ntest_transform = transforms.Compose([\n    transforms.Resize((128, 128)),\n    transforms.ToTensor(),\n])\n\n# Création des DataLoaders\ntrain_dataset = XRayDataset(train_df, IMG_DIR, transform=train_transform)\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n\ntest_dataset = TestDataset(test_df, img_dir=TEST_DIR, transform=test_transform)\ntest_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:39:45.61747Z","iopub.execute_input":"2025-08-18T16:39:45.617739Z","iopub.status.idle":"2025-08-18T16:39:45.633734Z","shell.execute_reply.started":"2025-08-18T16:39:45.617719Z","shell.execute_reply":"2025-08-18T16:39:45.633008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass GenderCNN(nn.Module):\n    def __init__(self):\n        super(GenderCNN, self).__init__()\n        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, padding=1)\n        self.pool = nn.MaxPool2d(2, 2)\n        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)\n        self.conv3 = nn.Conv2d(32, 64, kernel_size=3, padding=1)\n        self.dropout = nn.Dropout(0.5) # Couche de dropout avec une probabilité de 50%\n        self.fc1 = nn.Linear(64 * 16 * 16, 128)\n        self.fc2 = nn.Linear(128, 1)\n\n    def forward(self, x):\n        x = self.pool(F.relu(self.conv1(x)))\n        x = self.pool(F.relu(self.conv2(x)))\n        x = self.pool(F.relu(self.conv3(x)))\n        x = x.view(-1, 64 * 16 * 16)\n        x = self.dropout(x) # Application de la couche dropout avant la couche entièrement connectée\n        x = F.relu(self.fc1(x))\n        x = self.fc2(x)\n        return x\n\n# Instanciation du modèle\nmodel = GenderCNN()\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:39:49.150695Z","iopub.execute_input":"2025-08-18T16:39:49.150958Z","iopub.status.idle":"2025-08-18T16:39:49.180257Z","shell.execute_reply.started":"2025-08-18T16:39:49.150939Z","shell.execute_reply":"2025-08-18T16:39:49.179711Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.optim as optim\n\n\nmodel = GenderCNN()\n\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)\n\n\ncriterion = nn.BCEWithLogitsLoss() \n\n\noptimizer = optim.Adam(model.parameters(), lr=0.001)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:39:56.614794Z","iopub.execute_input":"2025-08-18T16:39:56.615098Z","iopub.status.idle":"2025-08-18T16:39:56.637866Z","shell.execute_reply.started":"2025-08-18T16:39:56.615071Z","shell.execute_reply":"2025-08-18T16:39:56.637346Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n\nmodel = model.to(device)  \ncriterion = nn.BCEWithLogitsLoss()  \noptimizer = optim.Adam(model.parameters(), lr=1e-3)\n\nnum_epochs = 5  \n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    \n    train_loader_iter = tqdm(train_loader, desc=f\"Training Epoch {epoch+1}/{num_epochs}\")\n    \n    for images, labels in train_loader_iter:\n        images, labels = images.to(device), labels.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(images)\n        \n        \n        labels = labels.view(-1, 1).float()  \n        \n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item()\n        train_loader_iter.set_postfix(loss=running_loss / (train_loader_iter.n + 1))\n    \n    print(f\"Epoch [{epoch+1}/{num_epochs}] - Loss: {running_loss/len(train_loader):.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T16:40:00.227763Z","iopub.execute_input":"2025-08-18T16:40:00.228031Z","iopub.status.idle":"2025-08-18T17:10:28.136365Z","shell.execute_reply.started":"2025-08-18T16:40:00.228Z","shell.execute_reply":"2025-08-18T17:10:28.135759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\ntorch.save(model.state_dict(), \"final_model.pth\")\nprint(\"✅ Modèle final sauvegardé sous final_model.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T17:10:35.568063Z","iopub.execute_input":"2025-08-18T17:10:35.568338Z","iopub.status.idle":"2025-08-18T17:10:35.589664Z","shell.execute_reply.started":"2025-08-18T17:10:35.568314Z","shell.execute_reply":"2025-08-18T17:10:35.588773Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = model.to(device)\nmodel.load_state_dict(torch.load(\"final_model.pth\", map_location=device))\nmodel.to(device)\nmodel.eval()\nprint(\"✅ Modèle rechargé et prêt pour prédiction\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T17:10:38.226758Z","iopub.execute_input":"2025-08-18T17:10:38.227505Z","iopub.status.idle":"2025-08-18T17:10:38.241132Z","shell.execute_reply.started":"2025-08-18T17:10:38.227469Z","shell.execute_reply":"2025-08-18T17:10:38.240405Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport os\n\nTEST_DIR = \"/kaggle/input/spr-x-ray-gender/kaggle/kaggle/test\"\n\ntest_files = [f for f in os.listdir(TEST_DIR) if f.endswith('.png')]\n\ntest_df = pd.DataFrame({\n    \"imageId\": [int(f.split(\".\")[0]) for f in test_files],\n    \"imageId_str\": [f.split(\".\")[0] for f in test_files]\n})\n\ntest_df.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T17:10:41.22196Z","iopub.execute_input":"2025-08-18T17:10:41.222644Z","iopub.status.idle":"2025-08-18T17:10:41.24794Z","shell.execute_reply.started":"2025-08-18T17:10:41.222619Z","shell.execute_reply":"2025-08-18T17:10:41.247393Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nimport torch\nimport pandas as pd\nfrom tqdm import tqdm\n\n\nclass TestDataset(Dataset):\n    def __init__(self, df, img_dir, transform=None):\n        self.df = df\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        filename = str(row['imageId_str']) + '.png'\n        img_path = os.path.join(self.img_dir, filename)\n        image = Image.open(img_path).convert('L')  # L = grayscale\n        if self.transform:\n            image = self.transform(image)\n        return image, row['imageId']\n\n\ntest_dataset = TestDataset(test_df, img_dir=TEST_DIR, transform=transform)\ntest_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)\n\n\nmodel.eval()\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n\n\npredictions = []\nimage_ids = []\n\nwith torch.no_grad():\n    for images, ids in tqdm(test_loader, desc=\"Predicting\"):\n        images = images.to(device)\n        outputs = model(images)\n        probs = torch.sigmoid(outputs).cpu().squeeze().numpy()  # convertir logits en probabilités\n        predictions.extend(probs.tolist())\n        image_ids.extend(ids.tolist())\n\n\nsubmission_df = pd.DataFrame({\n    \"imageId\": image_ids,\n    \"gender\": predictions  \n})\n\nsubmission_df.to_csv(\"submission.csv\", index=False)\nprint(\"Fichier submission.csv créé !\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T17:10:44.03235Z","iopub.execute_input":"2025-08-18T17:10:44.033064Z","iopub.status.idle":"2025-08-18T17:15:30.448705Z","shell.execute_reply.started":"2025-08-18T17:10:44.03302Z","shell.execute_reply":"2025-08-18T17:15:30.448086Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndf_sub = pd.read_csv(\"submission.csv\")\nprint(df_sub.head())\nprint(df_sub.info())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T17:16:43.395196Z","iopub.execute_input":"2025-08-18T17:16:43.395451Z","iopub.status.idle":"2025-08-18T17:16:43.410807Z","shell.execute_reply.started":"2025-08-18T17:16:43.395434Z","shell.execute_reply":"2025-08-18T17:16:43.410101Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"missing_ids = set(test_df[\"imageId\"]) - set(df_sub[\"imageId\"])\nextra_ids = set(df_sub[\"imageId\"]) - set(test_df[\"imageId\"])\n\nprint(\"Images manquantes :\", len(missing_ids))\nprint(\"Images en trop :\", len(extra_ids))\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T17:16:47.353207Z","iopub.execute_input":"2025-08-18T17:16:47.353475Z","iopub.status.idle":"2025-08-18T17:16:47.363782Z","shell.execute_reply.started":"2025-08-18T17:16:47.353453Z","shell.execute_reply":"2025-08-18T17:16:47.363073Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\n\nTEST_DIR = \"/kaggle/input/spr-x-ray-gender/kaggle/kaggle/test\"\n\n\ntest_files = sorted([f for f in os.listdir(TEST_DIR) if f.endswith(\".png\")])\ntest_df = pd.DataFrame({\n    \"imageId_str\": [f[:-4] for f in test_files]  \n})\ntest_df[\"imageId\"] = test_df[\"imageId_str\"].astype(int)  \n\n\nsubmission_df = pd.read_csv(\"submission.csv\")\nsubmission_df[\"imageId\"] = submission_df[\"imageId\"].astype(int)\n\n\nvis_df = submission_df.merge(\n    test_df[[\"imageId\", \"imageId_str\"]],\n    on=\"imageId\",\n    how=\"inner\"\n)\n\n\nif len(vis_df) != len(submission_df):\n    print(f\"⚠️ Attention: submission={len(submission_df)} lignes, après fusion={len(vis_df)}\")\nelse:\n    print(\"✅ Fusion OK : correspondance imageId ↔ imageId_str trouvée pour toutes les lignes.\")\n\n\nsample = vis_df.sample(9, random_state=42).reset_index(drop=True)\n\nplt.figure(figsize=(12, 12))\nfor i, row in sample.iterrows():\n    img_file = os.path.join(TEST_DIR, row[\"imageId_str\"] + \".png\")  # nom EXACT du fichier\n    if not os.path.exists(img_file):\n        raise FileNotFoundError(f\"Fichier introuvable: {img_file}\")\n\n    img = Image.open(img_file).convert(\"L\")\n    prob = float(row[\"gender\"])\n    label = \"Male\" if prob >= 0.5 else \"Female\"\n\n    plt.subplot(3, 3, i + 1)\n    plt.imshow(img, cmap=\"gray\")\n    plt.title(f\"ID: {row['imageId']} | {label} ({prob:.2f})\")\n    plt.axis(\"off\")\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T17:16:50.301333Z","iopub.execute_input":"2025-08-18T17:16:50.301862Z","iopub.status.idle":"2025-08-18T17:16:51.857082Z","shell.execute_reply.started":"2025-08-18T17:16:50.30184Z","shell.execute_reply":"2025-08-18T17:16:51.856364Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import roc_auc_score\n\n \ntrue_labels = []\npredicted_probs = []\n\nmodel.eval()\nwith torch.no_grad():\n    for images, labels in train_loader:\n        images, labels = images.to(device), labels.to(device)\n        outputs = model(images)\n        probs = torch.sigmoid(outputs).cpu().squeeze().numpy()\n        \n        true_labels.extend(labels.cpu().numpy())\n        predicted_probs.extend(probs)\n\n\nauc_score = roc_auc_score(true_labels, predicted_probs)\nprint(f\"Training AUC: {auc_score}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T17:17:01.577533Z","iopub.execute_input":"2025-08-18T17:17:01.578154Z","iopub.status.idle":"2025-08-18T17:23:04.51052Z","shell.execute_reply.started":"2025-08-18T17:17:01.578129Z","shell.execute_reply":"2025-08-18T17:23:04.50985Z"}},"outputs":[],"execution_count":null}]}