{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":87323,"databundleVersionId":9934427,"sourceType":"competition"}],"dockerImageVersionId":30823,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from __future__ import print_function\n\nimport os\nimport random\nimport pandas as pd\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom PIL import Image\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, roc_auc_score\nfrom torch.optim.lr_scheduler import StepLR\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision import transforms, models\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#################################\n# Configuration & Reproductibilité\n#################################\nbatch_size = 8\nepochs = 20\nlr = 5e-5\ngamma = 0.7\nseed = 42\n\n\ndef seed_everything(seed):\n    \"\"\"\n    Sets the random seed for reproducibility in python, numpy, and torch.\n    \"\"\"\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic = True\n\nseed_everything(seed)\n\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(\"Device used :\", device)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# -- Chemins vers les données --\nbase_dir = '/kaggle/input/cidaut-ai-fake-scene-classification-2024'\ntrain_csv_path = os.path.join(base_dir, 'train.csv')\nimages_dir = os.path.join(base_dir, 'Train')\n\n# -- Lecture du CSV --\ndf = pd.read_csv(train_csv_path)\n# df contient deux colonnes : \"images\" (nom du fichier .jpg, .png, etc.) et \"label\" (\"editada\" ou \"real\")\n\n# -- Conversion des labels editada -> 0, real -> 1 --\ncls_to_idx = {'editada': 0, 'real': 1}\ndf['label'] = df['label'].map(cls_to_idx)\n\n# -- Récupération des chemins d'images et des labels --\n# on suppose que la colonne 'images' contient les noms de fichiers (ex: \"image123.jpg\")\nall_image_paths = [os.path.join(images_dir, img_name) for img_name in df['image']]\nall_labels = df['label'].values\n\n# -- Split 70% / 15% / 15% --\ntrain_paths, temp_paths, train_labels, temp_labels = train_test_split(\n    all_image_paths,\n    all_labels,\n    test_size=0.20,        # 30% à répartir ensuite pour val et test\n    stratify=all_labels,\n    random_state=seed\n)\n\nval_paths, test_paths, val_labels, test_labels = train_test_split(\n    temp_paths,\n    temp_labels,\n    test_size=0.70,        # la moitié de 30% -> 15%\n    stratify=temp_labels,\n    random_state=seed\n)\n\nprint(f\"Train Data: {len(train_paths)}\")\nprint(f\"Validation Data: {len(val_paths)}\")\nprint(f\"Test Data: {len(test_paths)}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# -- Transforms --\n\ntrain_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomResizedCrop(224),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225]),\n])\n\nval_transforms = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225]),\n])\n\ntest_transforms = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225]),\n])\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\n# -- Transforms --\ntrain_transforms = transforms.Compose([\n    transforms.Resize((518, 518)),  # Redimensionner à la hauteur correcte\n    transforms.RandomHorizontalFlip(),  # Ajout d'augmentation\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225]),\n])\n\nval_transforms = transforms.Compose([\n    transforms.Resize((518, 518)),  # Redimensionner à 518x518\n    transforms.CenterCrop(518),    # Crop au centre si nécessaire\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225]),\n])\n\ntest_transforms = transforms.Compose([\n    transforms.Resize((518, 518)),  # Redimensionner à 518x518\n    transforms.CenterCrop(518),    # Crop au centre\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225]),\n])\n\"\"\"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# -- Custom Dataset --\nclass FakeRealDataset(Dataset):\n    def __init__(self, file_list, labels, transform=None):\n        self.file_list = file_list\n        self.labels = labels\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.file_list)\n\n    def __getitem__(self, idx):\n        img_path = self.file_list[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n        if self.transform:\n            img = self.transform(img)\n        label = self.labels[idx]\n        return img, label\n\n# -- Datasets et DataLoaders --\ntrain_data = FakeRealDataset(train_paths, train_labels, transform=train_transforms)\nval_data   = FakeRealDataset(val_paths,   val_labels,   transform=val_transforms)\ntest_data  = FakeRealDataset(test_paths,  test_labels,  transform=test_transforms)\n\ntrain_loader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True,  num_workers=4)\nval_loader   = DataLoader(dataset=val_data,   batch_size=batch_size, shuffle=False, num_workers=4)\ntest_loader  = DataLoader(dataset=test_data,  batch_size=batch_size, shuffle=False, num_workers=4)\n\nprint(f\"Train Dataset size: {len(train_data)}\")\nprint(f\"Validation Dataset size: {len(val_data)}\")\nprint(f\"Test Dataset size: {len(test_data)}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# -- Chargement du modèle ViT pré-entraîné --\nmodel = models.efficientnet_v2_l(weights=models.EfficientNet_V2_L_Weights.DEFAULT)\nmodel.to(device)\n\n# Récupération du nombre de features en sortie de la penultimate layer\nnum_ftrs = model.classifier[1].in_features\n\n# On remplace la couche fully-connected par une couche linéaire 2 classes\nmodel.classifier[1] = nn.Linear(num_ftrs, 2)\n\n\n# -- Si plusieurs GPUs sont disponibles --\nmodel = nn.DataParallel(model)\nmodel.to(device)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# -- Setup perte, optimizer, scheduler --\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=lr)\nscheduler = StepLR(optimizer, step_size=1, gamma=gamma)\n\ntrain_losses = []\ntrain_accuracies = []\nval_losses = []\nval_accuracies = []","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# -- Entraînement --\nfor epoch in range(epochs):\n    model.train()\n    epoch_loss = 0\n    epoch_accuracy = 0\n\n    for data, label in tqdm(train_loader, desc=f\"Training Epoch {epoch+1}\"):\n        data = data.to(device)\n        label = label.to(device)\n\n        optimizer.zero_grad()\n        output = model(data)\n        loss = criterion(output, label)\n        loss.backward()\n        optimizer.step()\n\n        epoch_loss += loss.item()\n        preds = output.argmax(dim=1)\n        acc = (preds == label).float().mean()\n        epoch_accuracy += acc.item()\n\n    # Moyenne des pertes et accuracy par batch\n    epoch_loss /= len(train_loader)\n    epoch_accuracy /= len(train_loader)\n\n    train_losses.append(epoch_loss)\n    train_accuracies.append(epoch_accuracy)\n\n    # -- Validation --\n    model.eval()\n    val_loss = 0\n    val_acc = 0\n    val_preds_list = []\n    val_labels_list = []\n\n    with torch.no_grad():\n        for data, label in tqdm(val_loader, desc=f\"Validation Epoch {epoch+1}\"):\n            data = data.to(device)\n            label = label.to(device)\n\n            output = model(data)\n            loss = criterion(output, label)\n            val_loss += loss.item()\n\n            preds = output.argmax(dim=1)\n            acc = (preds == label).float().mean()\n            val_acc += acc.item()\n\n            # Pour calculer l'AUC plus tard\n            val_preds_list.extend(output[:, 1].cpu().numpy())  # probabilité de la classe 1\n            val_labels_list.extend(label.cpu().numpy())\n\n    val_loss /= len(val_loader)\n    val_acc /= len(val_loader)\n\n    # -- AUC sur la validation --\n    # On compare la \"probabilité\" de la classe 1 (sortie du réseau) avec le label 0 ou 1\n    val_auc = roc_auc_score(val_labels_list, val_preds_list)\n\n    val_losses.append(val_loss)\n    val_accuracies.append(val_acc)\n\n    print(\n        f\"Epoch [{epoch+1}/{epochs}] \"\n        f\"Train Loss: {epoch_loss:.4f} | Train Acc: {epoch_accuracy:.4f} | \"\n        f\"Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f} | Val AUC: {val_auc:.4f}\"\n    )\n\n    scheduler.step()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# -- Courbes de Training / Validation --\nplt.figure(figsize=(10,5))\nplt.title(\"Training and Validation Loss\")\nplt.plot(train_losses, label=\"Training\")\nplt.plot(val_losses, label=\"Validation\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.show()\n\nplt.figure(figsize=(10,5))\nplt.title(\"Training and Validation Accuracy\")\nplt.plot(train_accuracies, label=\"Training\")\nplt.plot(val_accuracies, label=\"Validation\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# -- Test --\nmodel.eval()\ntest_loss = 0\ntest_acc = 0\ntest_preds_list = []\ntest_labels_list = []\n\nwith torch.no_grad():\n    for data, label in tqdm(test_loader, desc=\"Testing\"):\n        data = data.to(device)\n        label = label.to(device)\n\n        output = model(data)\n        loss = criterion(output, label)\n        test_loss += loss.item()\n\n        preds = output.argmax(dim=1)\n        acc = (preds == label).float().mean()\n        test_acc += acc.item()\n\n        test_preds_list.extend(output[:, 1].cpu().numpy())  # prob classe 1\n        test_labels_list.extend(label.cpu().numpy())\n\ntest_loss /= len(test_loader)\ntest_acc /= len(test_loader)\ntest_auc = roc_auc_score(test_labels_list, test_preds_list)\n\nprint(f\"Test Loss: {test_loss:.4f} - Test Accuracy: {test_acc:.4f} - Test AUC: {test_auc:.4f}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}