{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":87323,"databundleVersionId":9931884,"sourceType":"competition"}],"dockerImageVersionId":30787,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Step 1: Installing necessary libraries","metadata":{}},{"cell_type":"code","source":"# !pip install torch torchvision pandas scikit-learn matplotlib tqdm torchmetrics","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-10-23T10:35:42.482368Z","iopub.execute_input":"2024-10-23T10:35:42.482867Z","iopub.status.idle":"2024-10-23T10:35:42.489122Z","shell.execute_reply.started":"2024-10-23T10:35:42.482810Z","shell.execute_reply":"2024-10-23T10:35:42.487445Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"GPU Check","metadata":{}},{"cell_type":"code","source":"!nvidia-smi","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-10-23T10:35:46.509887Z","iopub.execute_input":"2024-10-23T10:35:46.510274Z","iopub.status.idle":"2024-10-23T10:35:47.586759Z","shell.execute_reply.started":"2024-10-23T10:35:46.510240Z","shell.execute_reply":"2024-10-23T10:35:47.585543Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 2: Importing required libraries","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nimport torchvision.models as models\nfrom torch.utils.data import DataLoader, Dataset, random_split\nfrom sklearn.metrics import accuracy_score, f1_score, roc_auc_score\nfrom torchmetrics.classification import BinaryAccuracy, BinaryF1Score, BinaryAUROC\nimport pandas as pd\nfrom PIL import Image\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os\nfrom tqdm import tqdm\nimport torch.cuda.amp as amp\nfrom glob import glob","metadata":{"execution":{"iopub.status.busy":"2024-10-23T10:35:49.769759Z","iopub.execute_input":"2024-10-23T10:35:49.770172Z","iopub.status.idle":"2024-10-23T10:35:57.112234Z","shell.execute_reply.started":"2024-10-23T10:35:49.770134Z","shell.execute_reply":"2024-10-23T10:35:57.111423Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 3: Custom Dataset Class\n\nThis dataset class will read the image paths and labels from a CSV file, handle basic image augmentations, and load images efficiently.","metadata":{}},{"cell_type":"code","source":"class CustomImageDataset(Dataset):\n    def __init__(self, csv_file, img_dir, transform=None):\n        self.annotations = csv_file\n        self.img_dir     = img_dir #glob(os.path.join(img_dir, \"*.jpg\"))\n        self.transform   = transform\n        assert len(self.img_dir) == len(self.img_dir)\n\n    def __len__(self):\n        return len(self.annotations)\n\n    def __getitem__(self, idx):\n        img_path = os.path.join(self.img_dir, self.annotations.iloc[idx, 0])\n        image    = Image.open(img_path).convert(\"RGB\")\n        label    = torch.tensor(int(self.annotations.iloc[idx, 1]))\n\n        if self.transform:\n            image = self.transform(image)\n            \n        return image, label","metadata":{"execution":{"iopub.status.busy":"2024-10-23T10:35:57.113870Z","iopub.execute_input":"2024-10-23T10:35:57.114333Z","iopub.status.idle":"2024-10-23T10:35:57.121753Z","shell.execute_reply.started":"2024-10-23T10:35:57.114297Z","shell.execute_reply":"2024-10-23T10:35:57.120662Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 4: Creating Data Augmentation and DataLoader\n\nWe’ll use basic augmentation (horizontal flip), and split the dataset into training and validation sets.","metadata":{}},{"cell_type":"code","source":"def create_dataloaders(csv_file, img_dir, batch_size=32, val_split=0.2):\n    # Define transforms with basic augmentations\n    transform = transforms.Compose([\n        transforms.Resize((224, 224)),\n        transforms.ToTensor()\n    ])\n\n    # Initialize dataset\n    dataset = CustomImageDataset(csv_file=csv_file, img_dir=img_dir, transform=transform)\n\n    # Create train/validation split\n    val_size = int(len(dataset) * val_split)\n    train_size = len(dataset) - val_size\n    train_dataset, val_dataset = random_split(dataset, [train_size, val_size])\n\n    # Create DataLoaders\n    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4, pin_memory=True)\n    val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=4, pin_memory=True)\n\n    return train_loader, val_loader","metadata":{"execution":{"iopub.status.busy":"2024-10-23T10:35:57.123067Z","iopub.execute_input":"2024-10-23T10:35:57.123500Z","iopub.status.idle":"2024-10-23T10:35:57.133574Z","shell.execute_reply.started":"2024-10-23T10:35:57.123458Z","shell.execute_reply":"2024-10-23T10:35:57.132736Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 5: Model Defition","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\nimport torch.nn.functional as F\n\nclass SimpleCNN(nn.Module):\n    def __init__(self, num_classes=1):\n        super(SimpleCNN, self).__init__()\n        \n        # First convolutional block\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, padding=1)\n        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n        \n        # Second convolutional block\n        self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, padding=1)\n        \n        # Third convolutional block\n        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)\n\n        # Global Average Pooling layer\n        self.global_avg_pool = nn.AdaptiveAvgPool2d((1, 1))\n        \n        # Fully connected layer\n        self.fc1 = nn.Linear(64, 128)\n        self.fc2 = nn.Linear(128, num_classes)\n\n    def forward(self, x):\n        # First block\n        x = self.pool(F.relu(self.conv1(x)))\n        \n        # Second block\n        x = self.pool(F.relu(self.conv2(x)))\n        \n        # Third block\n        x = self.pool(F.relu(self.conv3(x)))\n        \n        # Flatten the tensor for the fully connected layer\n        x = self.global_avg_pool(x)\n        x = x.view(-1, 64)\n        \n        # Fully connected layers\n        x = F.relu(self.fc1(x))\n        x = self.fc2(x)\n        \n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-10-23T10:35:57.135637Z","iopub.execute_input":"2024-10-23T10:35:57.135976Z","iopub.status.idle":"2024-10-23T10:35:57.148918Z","shell.execute_reply.started":"2024-10-23T10:35:57.135921Z","shell.execute_reply":"2024-10-23T10:35:57.148171Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_model():\n    model = SimpleCNN ()\n    return model","metadata":{"execution":{"iopub.status.busy":"2024-10-23T10:35:57.150028Z","iopub.execute_input":"2024-10-23T10:35:57.150387Z","iopub.status.idle":"2024-10-23T10:35:57.162063Z","shell.execute_reply.started":"2024-10-23T10:35:57.150336Z","shell.execute_reply":"2024-10-23T10:35:57.161253Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 6: Training and Validation Functions\n\nMixed precision training using PyTorch’s AMP (Automatic Mixed Precision) is used to speed up training.","metadata":{}},{"cell_type":"code","source":"def train_one_epoch(model, train_loader, criterion, optimizer, device):\n    model.train()\n    running_loss = 0.0\n    for images, labels in tqdm(train_loader):\n        images, labels = images.to(device), labels.to(device).float().unsqueeze(1)\n\n        optimizer.zero_grad()\n        \n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        # Backward pass and optimization step\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item() * images.size(0)\n\n    epoch_loss = running_loss / len(train_loader.dataset)\n    return epoch_loss\n\n\ndef validate(model, val_loader, criterion, device):\n    model.eval()\n    running_loss = 0.0\n    all_labels = []\n    all_outputs = []\n    \n    with torch.no_grad():\n        for images, labels in tqdm(val_loader):\n            images, labels = images.to(device), labels.to(device).float().unsqueeze(1)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            running_loss += loss.item() * images.size(0)\n            all_labels.append(labels.cpu().numpy())\n            all_outputs.append(outputs.cpu().numpy())\n\n    epoch_loss = running_loss / len(val_loader.dataset)\n    \n    all_labels = np.concatenate(all_labels)\n    all_outputs = np.concatenate(all_outputs)\n    all_outputs = torch.sigmoid(torch.tensor(all_outputs)).numpy()  # Convert logits to probabilities\n    \n    return epoch_loss, all_labels, all_outputs","metadata":{"execution":{"iopub.status.busy":"2024-10-23T10:35:57.163300Z","iopub.execute_input":"2024-10-23T10:35:57.163879Z","iopub.status.idle":"2024-10-23T10:35:57.174075Z","shell.execute_reply.started":"2024-10-23T10:35:57.163837Z","shell.execute_reply":"2024-10-23T10:35:57.173220Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 7: Training Loop and Metrics Calculation","metadata":{}},{"cell_type":"code","source":"def train_model(csv_file, img_dir, num_epochs=10, batch_size=32, lr=1e-4, val_split=0.2, device='cuda'):\n    train_loader, val_loader = create_dataloaders(csv_file, img_dir, batch_size=batch_size, val_split=val_split)\n\n    model     = get_model().to(device) # load the model into the GPU\n    criterion = nn.BCEWithLogitsLoss()\n    optimizer = optim.Adam(model.parameters(), lr=lr)\n\n    train_losses = []\n    val_losses   = []\n    \n    for epoch in range(num_epochs):\n        print(f'Epoch {epoch+1}/{num_epochs}')\n        train_loss = train_one_epoch(model, train_loader, criterion, optimizer, device)\n        val_loss, val_labels, val_outputs = validate(model, val_loader, criterion, device)\n\n        train_losses.append(train_loss)\n        val_losses.append(val_loss)\n        \n        # Calculate metrics on validation set\n        val_preds = (val_outputs > 0.5).astype(int)\n        accuracy = accuracy_score(val_labels, val_preds)\n        print(f'Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}, Val Accuracy: {accuracy:.4f}')\n        \n    # Plot Loss\n    plt.plot(train_losses, label='Train Loss')\n    plt.plot(val_losses, label='Validation Loss')\n    plt.legend()\n    plt.savefig('loss_plot.png')\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-10-23T10:35:57.175176Z","iopub.execute_input":"2024-10-23T10:35:57.175455Z","iopub.status.idle":"2024-10-23T10:35:57.187587Z","shell.execute_reply.started":"2024-10-23T10:35:57.175423Z","shell.execute_reply":"2024-10-23T10:35:57.186767Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 8: Running the Training\n\nYou can run the training with the following command:","metadata":{}},{"cell_type":"code","source":"labels = pd.read_csv (\"/kaggle/input/cidaut-ai-fake-scene-classification-2024/train.csv\")\nlabels[\"label\"] = labels[\"label\"].map({\"editada\":0, \"real\":1})\nlabels.head()","metadata":{"execution":{"iopub.status.busy":"2024-10-23T10:35:57.720965Z","iopub.execute_input":"2024-10-23T10:35:57.721313Z","iopub.status.idle":"2024-10-23T10:35:57.751070Z","shell.execute_reply.started":"2024-10-23T10:35:57.721280Z","shell.execute_reply":"2024-10-23T10:35:57.750167Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"img_dir = \"/kaggle/input/cidaut-ai-fake-scene-classification-2024/Train\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-10-23T10:35:59.393634Z","iopub.execute_input":"2024-10-23T10:35:59.394313Z","iopub.status.idle":"2024-10-23T10:35:59.398395Z","shell.execute_reply.started":"2024-10-23T10:35:59.394272Z","shell.execute_reply":"2024-10-23T10:35:59.397414Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_img = Image.open(\"/kaggle/input/cidaut-ai-fake-scene-classification-2024/Train/110.jpg\")\nprint (sample_img.size)\nsample_img","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-10-23T10:37:32.507275Z","iopub.execute_input":"2024-10-23T10:37:32.508135Z","iopub.status.idle":"2024-10-23T10:37:32.637526Z","shell.execute_reply.started":"2024-10-23T10:37:32.508082Z","shell.execute_reply":"2024-10-23T10:37:32.636497Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_model(labels, img_dir, num_epochs=10, batch_size=32, lr=1e-4)","metadata":{"execution":{"iopub.status.busy":"2024-10-23T10:37:47.929680Z","iopub.execute_input":"2024-10-23T10:37:47.930407Z","iopub.status.idle":"2024-10-23T10:38:38.438398Z","shell.execute_reply.started":"2024-10-23T10:37:47.930365Z","shell.execute_reply":"2024-10-23T10:38:38.437330Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"TIPS\n- check AUC-ROC for validation\n- improve the model `SimpleCNN`\n- inspect the data\n- image augmentations\n- do the code for predicting the images on `Test/` based on the `validate` function\n- submit the `sample_submission.csv` and check the format\n","metadata":{}}]}