{"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":"gpu","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom time import time as timer\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\nimport torchvision.transforms as transforms\nfrom torch.utils.data import random_split, Dataset, DataLoader","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:51:24.241635Z","iopub.execute_input":"2025-03-05T17:51:24.241930Z","iopub.status.idle":"2025-03-05T17:51:32.776212Z","shell.execute_reply.started":"2025-03-05T17:51:24.241892Z","shell.execute_reply":"2025-03-05T17:51:32.775123Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:51:32.777300Z","iopub.execute_input":"2025-03-05T17:51:32.777779Z","iopub.status.idle":"2025-03-05T17:51:32.784530Z","shell.execute_reply.started":"2025-03-05T17:51:32.777745Z","shell.execute_reply":"2025-03-05T17:51:32.783413Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Looking at Sample Submissions, and labels CSV","metadata":{}},{"cell_type":"code","source":"labels_csv = pd.read_csv(\"/kaggle/input/cifar-10/trainLabels.csv\")\nlabels_csv","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:51:32.785449Z","iopub.execute_input":"2025-03-05T17:51:32.785787Z","iopub.status.idle":"2025-03-05T17:51:32.914762Z","shell.execute_reply.started":"2025-03-05T17:51:32.785752Z","shell.execute_reply":"2025-03-05T17:51:32.913887Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_csv.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:51:32.916842Z","iopub.execute_input":"2025-03-05T17:51:32.917053Z","iopub.status.idle":"2025-03-05T17:51:32.941699Z","shell.execute_reply.started":"2025-03-05T17:51:32.917034Z","shell.execute_reply":"2025-03-05T17:51:32.940989Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Since the labels are stored as text in the DataFrame, we'll have to the map the labels to numbers\nlabel_mapping = {label: idx for idx, label in enumerate(labels_csv['label'].unique())}\n\n# Now let's encode them, and rename the text column\nlabels_csv.rename({\"label\": \"label_txt\"}, axis=1, inplace=True)\nlabels_csv['label'] = labels_csv['label_txt'].map(label_mapping)\n\nlabel_mapping, labels_csv","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:51:32.942921Z","iopub.execute_input":"2025-03-05T17:51:32.943148Z","iopub.status.idle":"2025-03-05T17:51:32.956795Z","shell.execute_reply.started":"2025-03-05T17:51:32.943127Z","shell.execute_reply":"2025-03-05T17:51:32.956112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_submissions_csv = pd.read_csv(\"/kaggle/input/cifar-10/sampleSubmission.csv\")\nsample_submissions_csv.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:51:32.957602Z","iopub.execute_input":"2025-03-05T17:51:32.957900Z","iopub.status.idle":"2025-03-05T17:51:33.069240Z","shell.execute_reply.started":"2025-03-05T17:51:32.957879Z","shell.execute_reply":"2025-03-05T17:51:33.068370Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Extracting the training images from the 7z folder","metadata":{}},{"cell_type":"code","source":"!pip install py7zr\nimport py7zr\nimport os\n\n# Define paths\narchive_path = [\"/kaggle/input/cifar-10/train.7z\", \"/kaggle/input/cifar-10/test.7z\"]  # Change this to your actual 7z file\nextract_path = \"/kaggle/working\"    # Temporary folder\n\n# Extract files\nfor i in range(2):\n    with py7zr.SevenZipFile(archive_path[i], mode='r') as archive:\n        archive.extractall(path=extract_path)\n\nprint(\"Extraction done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:51:33.070120Z","iopub.execute_input":"2025-03-05T17:51:33.070424Z","iopub.status.idle":"2025-03-05T17:54:11.711071Z","shell.execute_reply.started":"2025-03-05T17:51:33.070376Z","shell.execute_reply":"2025-03-05T17:54:11.710005Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Checking the data","metadata":{}},{"cell_type":"code","source":"train_dir = \"/kaggle/working/train\"\ntest_dir = \"/kaggle/working/test\"\n\ntrain_imgs = os.listdir(train_dir)\n\nprint(f\"Number of train images: {len(train_imgs)}\")\nprint(f\"First 5 images' paths: {train_imgs[:5]}\")\n\nimg1 = Image.open(train_dir + \"/\" + train_imgs[0])\nimg1.size","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:11.712179Z","iopub.execute_input":"2025-03-05T17:54:11.712533Z","iopub.status.idle":"2025-03-05T17:54:11.774340Z","shell.execute_reply.started":"2025-03-05T17:54:11.712498Z","shell.execute_reply":"2025-03-05T17:54:11.773507Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Applying Image Transforms (For Sample Image)","metadata":{}},{"cell_type":"code","source":"transform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5,), (0.5,))    # To shift each pixel's value between -1 and 1, helping avoid exploding and vanishing gradients\n])\n\nimg_tensor = transform(img1)\nimg_tensor.shape   # Shape is 32x32x3, which tracks with how it should be","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:11.775372Z","iopub.execute_input":"2025-03-05T17:54:11.775730Z","iopub.status.idle":"2025-03-05T17:54:11.866342Z","shell.execute_reply.started":"2025-03-05T17:54:11.775696Z","shell.execute_reply":"2025-03-05T17:54:11.865466Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Applying Train Image Transforms (utilizing Data Augmentation), and Test Image Transforms","metadata":{}},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.RandomHorizontalFlip(p=0.2),   # Randomly flip about 20% images horizontally, to help model learn right-left symmetry\n    transforms.ToTensor(),\n    transforms.Normalize((0.5,), (0.5,)),   # To shift each pixel's value between [-1, 1], helping avoid exploding and vanishing gradients\n])\n\n# RandomCrop not applied, because Cropping an image in the CIFAR-10 dataset may make the image inconclusive.\n\ntest_transform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5,), (0.5,))\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:11.867419Z","iopub.execute_input":"2025-03-05T17:54:11.867797Z","iopub.status.idle":"2025-03-05T17:54:11.873352Z","shell.execute_reply.started":"2025-03-05T17:54:11.867762Z","shell.execute_reply":"2025-03-05T17:54:11.872474Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Loading Data to DataLoaders","metadata":{}},{"cell_type":"markdown","source":"#### Creating a Custom Dataset class in order to load the images as a torch.Dataset","metadata":{}},{"cell_type":"code","source":"class CustomDataset(Dataset):\n    def __init__(self, images_dir, labels_csv, transform=None):\n        self.transforms = transform\n        self.imgs_dir = images_dir\n        self.labels_csv = labels_csv\n\n    def __len__(self):\n        return len(self.labels_csv)\n    \n    # Function to load each image, since we don't have the default structure that PyTorch wants it's Datasets to have\n    def __getitem__(self, idx):\n        img_name = str(self.labels_csv.iloc[idx, 0]) + \".png\"    # Get file name of image with index \"idx\"\n        label = self.labels_csv.loc[idx, \"label\"]   # Get label of the image with index \"idx\"\n\n        img_path = os.path.join(self.imgs_dir, img_name)\n\n        img = Image.open(img_path).convert(\"RGB\")   # Open the image in a PIL.Image format, and convert it to an RGB image\n\n        # Applying transforms\n        if self.transforms:\n            img = self.transforms(img)\n        \n        return img, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:11.874280Z","iopub.execute_input":"2025-03-05T17:54:11.874615Z","iopub.status.idle":"2025-03-05T17:54:11.889163Z","shell.execute_reply.started":"2025-03-05T17:54:11.874583Z","shell.execute_reply":"2025-03-05T17:54:11.888164Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Creating the DataLoader for training data\ntrain_dataset = CustomDataset(\n    images_dir = \"/kaggle/working/train\", labels_csv = labels_csv,\n    transform = transform\n)\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:11.890160Z","iopub.execute_input":"2025-03-05T17:54:11.890467Z","iopub.status.idle":"2025-03-05T17:54:11.904598Z","shell.execute_reply.started":"2025-03-05T17:54:11.890438Z","shell.execute_reply":"2025-03-05T17:54:11.903762Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Creating a CNN Architecture\n\nFor the purpose of this problem, we'll be using the following architecture:\n\n* Input [ Dimensions: (Batch_size (32), 3, 32, 32) ]\n* First Convolution Layer [ (32, 3, 32, 32) -> (32, 32, 32, 32) ]\n* Second Convolution Layer [ (32, 32, 32, 32) -> (32, 64, 32, 32) ]\n* First Max Pooling Layer [ (32, 64, 32, 32) -> (32, 64, 16, 16) ]\n* Third Convolution Layer [ (32, 64, 16, 16) -> (32, 128, 16, 16) ]\n* Second Max Pooling Layer [ (32, 128, 16, 16) -> (32, 128, 8, 8) ]\n* Flatten Layer [ (32, 128, 8, 8) -> (32, 8192) ]\n* First FC Layer [ (32, 8192) -> (32, 256) ]\n* Second FC Layer [ (32, 256) -> (32, 128) ]\n* Dropout with keep_probability = 0.3\n* Output Layer [ (32, 128) -> (32, 10) ]","metadata":{}},{"cell_type":"code","source":"class CNN(nn.Module):\n    def __init__(self, dropout_prob=0.5):\n        super(CNN, self).__init__()\n        self.conv_layers = nn.Sequential(\n            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=1),  # Conv Layer 1\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),  # Conv Layer 2\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2),  # Max Pooling 1\n            \n            nn.Conv2d(64, 128, kernel_size=3, padding=1),  # Conv Layer 3\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2)  # Max Pooling 2\n        )\n        \n        self.fc_layers = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(128 * 8 * 8, 256),  # Fully connected layer 1\n            nn.BatchNorm1d(256),\n            nn.ReLU(),\n            nn.Linear(256, 128),  # Fully connected layer 2\n            nn.BatchNorm1d(128),\n            nn.ReLU(),\n            nn.Dropout(dropout_prob),  # Dropout for regularization, increased to 0.5\n            nn.Linear(128, 10)  # Output layer (10 classes)\n        )\n    \n    def forward(self, x):\n        x = self.conv_layers(x)\n        x = self.fc_layers(x)\n        \n        return x\n\n# Initialize model\nmodel = CNN()\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:35.802285Z","iopub.execute_input":"2025-03-05T17:54:35.802655Z","iopub.status.idle":"2025-03-05T17:54:35.827226Z","shell.execute_reply.started":"2025-03-05T17:54:35.802623Z","shell.execute_reply":"2025-03-05T17:54:35.826538Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Shapes of all parameters\nfor param in model.parameters():\n    print(param.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:37.711529Z","iopub.execute_input":"2025-03-05T17:54:37.711860Z","iopub.status.idle":"2025-03-05T17:54:37.719205Z","shell.execute_reply.started":"2025-03-05T17:54:37.711833Z","shell.execute_reply":"2025-03-05T17:54:37.716888Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"For this particular problem, we'll be using the Cross Entropy loss function, and the Adam optimizer so we'll go ahead and set that up, before we make the training loop","metadata":{}},{"cell_type":"code","source":"# Setting the Loss criteria and the optimizer\nloss = nn.CrossEntropyLoss()   # Not MSE as this is not a regression problem, but a multi classification problem\noptimizer = optim.Adam(model.parameters(), lr=1e-3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:39.254702Z","iopub.execute_input":"2025-03-05T17:54:39.255094Z","iopub.status.idle":"2025-03-05T17:54:39.260346Z","shell.execute_reply.started":"2025-03-05T17:54:39.255063Z","shell.execute_reply":"2025-03-05T17:54:39.259488Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Migrating model to GPU, if available","metadata":{}},{"cell_type":"code","source":"if torch.cuda.is_available():\n    device = torch.device(\"cuda\")\nelse:\n    device = torch.device(\"cpu\")\n\ndevice","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:39.539891Z","iopub.execute_input":"2025-03-05T17:54:39.540205Z","iopub.status.idle":"2025-03-05T17:54:39.602882Z","shell.execute_reply.started":"2025-03-05T17:54:39.540183Z","shell.execute_reply":"2025-03-05T17:54:39.602014Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# You cannot calculate metrics on your training data, and you can't do it on the testing set as well,\n# so we first need to create a training and validation set as well\ntrain_split = int(0.8 * len(train_dataset))\nval_split = int(0.2 * len(train_dataset))\ntrain_split, val_split\n\ntrain_dataset, val_dataset = random_split(train_dataset, [train_split, val_split])\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:39.671042Z","iopub.execute_input":"2025-03-05T17:54:39.671495Z","iopub.status.idle":"2025-03-05T17:54:39.689694Z","shell.execute_reply.started":"2025-03-05T17:54:39.671460Z","shell.execute_reply":"2025-03-05T17:54:39.687538Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Early Stopping and Metrics Calculation","metadata":{}},{"cell_type":"code","source":"class EarlyStopping:\n    def __init__(self, patience = 5, min_change = 0.001, checkpoint_pth = \"best_model.pth\"):\n        self.patience = patience\n        self.min_change = min_change\n        self.best_loss = float(\"inf\")\n        self.counter = 0\n        self.early_stop = False\n        self.checkpoint_pth = checkpoint_pth\n\n    def __call__(self, val_loss, model):\n        if val_loss < self.best_loss - self.min_change:\n            \n            self.reset()\n            \n            self.best_loss = val_loss\n\n            torch.save(model.state_dict(), self.checkpoint_pth)\n        else:\n            self.counter += 1\n\n            print(f\"Early Stopping Counter: {self.counter}/{self.patience}\")\n            \n            if self.counter >= self.patience:\n                self.early_stop = True\n\n    def reset(self):\n        self.counter = 0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:41.179816Z","iopub.execute_input":"2025-03-05T17:54:41.180140Z","iopub.status.idle":"2025-03-05T17:54:41.185639Z","shell.execute_reply.started":"2025-03-05T17:54:41.180111Z","shell.execute_reply":"2025-03-05T17:54:41.184726Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_train_and_val_losses(train_losses, val_losses):\n    plt.plot(train_losses, color='b', label='Training Loss')\n    plt.plot(val_losses, color='g', label='Validation Loss')\n\n    plt.title(\"Loss VS Epochs\")\n    plt.xlabel(\"Number of Epochs\")\n    plt.ylabel(\"Loss Value\")\n    plt.legend()\n    \n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:41.325430Z","iopub.execute_input":"2025-03-05T17:54:41.325764Z","iopub.status.idle":"2025-03-05T17:54:41.330183Z","shell.execute_reply.started":"2025-03-05T17:54:41.325735Z","shell.execute_reply":"2025-03-05T17:54:41.329196Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calculate_metrics(model, val_loader, device):\n    model.eval()    # Set the model to evaluation mode\n\n    all_labels = []\n    all_preds = []\n    \n    with torch.no_grad():\n        for images, labels in val_loader:\n            images, labels = images.to(device), labels.to(device)\n\n            outputs = model(images)\n            preds = torch.argmax(F.softmax(outputs, dim=1), dim=1)\n\n            all_preds.extend(preds.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n\n    accuracy = accuracy_score(all_labels, all_preds)\n    precision = precision_score(all_labels, all_preds, average='weighted', zero_division=0)\n    recall = recall_score(all_labels, all_preds, average='weighted', zero_division=0)\n    f1 = f1_score(all_labels, all_preds, average='weighted', zero_division=0)\n\n    return accuracy, precision, recall, f1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:54:43.293719Z","iopub.execute_input":"2025-03-05T17:54:43.294000Z","iopub.status.idle":"2025-03-05T17:54:43.299323Z","shell.execute_reply.started":"2025-03-05T17:54:43.293978Z","shell.execute_reply":"2025-03-05T17:54:43.298451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Creating a more complex training function, with EarlyStopping and calculating all evaluation metrics\nearly_stopper = EarlyStopping()\n\ndef train(model, train_loader, val_loader, loss_fn, optimizer, early_stopper, device, n_epochs=5):\n    print(f\"Device being used: {device}\")\n    \n    model.to(device)\n    train_losses = []\n    val_losses = []\n    \n    for epoch_num in range(1, n_epochs+1):\n        start_time = timer()\n        model.train()    # Set the model to training model\n        train_loss = 0.0\n\n        # Training step\n        for images, labels in train_loader:\n            images = images.to(device)\n            labels = labels.to(device)\n    \n            optimizer.zero_grad()             # Reset gradients to 0\n\n            outputs = model(images)           # Forward pass\n            loss = loss_fn(outputs, labels)\n            loss.backward()                   # Start Backprop\n            \n            optimizer.step()                  # Update gradients\n    \n            train_loss += loss.item()\n    \n        train_loss /= len(train_loader)\n        accuracy, precision, recall, f1 = calculate_metrics(model, val_loader, device)\n\n        train_losses.append(train_loss)\n    \n        # Validation step\n        model.eval()    # Set the model to evaluation mode\n        val_loss = 0.0\n        \n        with torch.no_grad():\n            for images, labels in val_loader:\n                images, labels = images.to(device), labels.to(device)\n\n                outputs = model(images)\n                loss = loss_fn(outputs, labels)\n                val_loss += loss.item()\n\n        val_loss /= len(val_loader)\n        \n        val_losses.append(val_loss)\n        \n        print(\n            f\"Epoch {epoch_num}/{n_epochs} |  Time: {timer()-start_time:.2f} | Train Loss = {train_loss:.4f} |\",\n            f\"Validation Loss: {val_loss:.4f} |\",\n            f\"Accuracy: {accuracy:.4f} | Precision: {precision:.4f} | Recall: {recall:.4f} | F1: {f1:.4f}\"\n        )\n        \n        # Call Early Stopping\n        early_stopper(val_loss, model)\n        \n        if early_stopper.early_stop:\n            print(f\"Early Stopping Triggered. Best model saved at {early_stopper.checkpoint_pth}\")\n            break\n\n    return train_losses, val_losses","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:58:38.085127Z","iopub.execute_input":"2025-03-05T17:58:38.085477Z","iopub.status.idle":"2025-03-05T17:58:38.093869Z","shell.execute_reply.started":"2025-03-05T17:58:38.085448Z","shell.execute_reply":"2025-03-05T17:58:38.092993Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Increasing the Learning Rate\nmodel = CNN(dropout_prob=0.7)\noptimizer = optim.Adam(model.parameters(), lr=1e-3)\n\nearlyStopper = EarlyStopping()\n\ntrain_losses, val_losses = train(model, train_loader, val_loader, loss, optimizer, earlyStopper, device, 10)\nplot_train_and_val_losses(train_losses, val_losses)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T17:58:38.822730Z","iopub.execute_input":"2025-03-05T17:58:38.823043Z","iopub.status.idle":"2025-03-05T18:05:20.701610Z","shell.execute_reply.started":"2025-03-05T17:58:38.823018Z","shell.execute_reply":"2025-03-05T18:05:20.700705Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Evaluating on the test set","metadata":{}},{"cell_type":"code","source":"# Creating a custom Dataset class and the DataLoader\n\nclass CustomTestDataset(Dataset):\n    def __init__(self, images_dir, transform=None):\n        self.images_dir = images_dir\n        self.transform = transform\n        self.img_files = sorted(os.listdir(images_dir))  # Ensure correct order\n\n    def __len__(self):\n        return len(self.img_files)\n\n    def __getitem__(self, idx):\n        img_path = os.path.join(self.images_dir, self.img_files[idx])\n        image = Image.open(img_path).convert(\"RGB\")  # Open image as RGB\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, self.img_files[idx]\n\n\ntest_dataset = CustomTestDataset(\"/kaggle/working/test\", test_transform)\ntest_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T18:05:20.702904Z","iopub.execute_input":"2025-03-05T18:05:20.703237Z","iopub.status.idle":"2025-03-05T18:05:20.958182Z","shell.execute_reply.started":"2025-03-05T18:05:20.703203Z","shell.execute_reply":"2025-03-05T18:05:20.957489Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Loading best model\nmodel = CNN()\nmodel.load_state_dict(torch.load(\"best_model.pth\"))  \nmodel.to(device)\nmodel.eval()     # Set to evaluation mode","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T18:05:59.407053Z","iopub.execute_input":"2025-03-05T18:05:59.407345Z","iopub.status.idle":"2025-03-05T18:05:59.455035Z","shell.execute_reply.started":"2025-03-05T18:05:59.407322Z","shell.execute_reply":"2025-03-05T18:05:59.454215Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Make predictions\nall_preds = []\nimg_idxs = []\n\nwith torch.no_grad():\n    for images, idxs in test_loader:\n        images = images.to(device)\n\n        outputs = model(images)\n\n        _, preds = torch.max(outputs, 1)\n\n        all_preds.extend(preds.cpu().numpy())\n        img_idxs.extend(idxs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T18:06:01.619379Z","iopub.execute_input":"2025-03-05T18:06:01.619715Z","iopub.status.idle":"2025-03-05T18:08:28.655731Z","shell.execute_reply.started":"2025-03-05T18:06:01.619689Z","shell.execute_reply":"2025-03-05T18:08:28.654784Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Re-mapping predictions\nidxs_to_classes = {v: k for k, v in label_mapping.items()}\n\npredicted_labels = [idxs_to_classes[pred] for pred in all_preds]\n\nsubmission_df = pd.DataFrame({\n    \"id\": [int(file.split(\".\")[0]) for file in img_idxs],  \n    \"label\": predicted_labels\n})\n\n# Sort by ID to ensure correct order\nsubmission_df = submission_df.sort_values(by=\"id\")\n\n# Save to CSV\nsubmission_df.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T18:08:28.656948Z","iopub.execute_input":"2025-03-05T18:08:28.657260Z","iopub.status.idle":"2025-03-05T18:08:29.147812Z","shell.execute_reply.started":"2025-03-05T18:08:28.657227Z","shell.execute_reply":"2025-03-05T18:08:29.146872Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-05T18:12:12.847605Z","iopub.execute_input":"2025-03-05T18:12:12.847897Z","iopub.status.idle":"2025-03-05T18:12:12.856382Z","shell.execute_reply.started":"2025-03-05T18:12:12.847875Z","shell.execute_reply":"2025-03-05T18:12:12.855471Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}