{"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":"gpu","dataSources":[{"sourceId":94183,"databundleVersionId":11224306,"sourceType":"competition"}],"dockerImageVersionId":31153,"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\nfrom torchinfo import summary\nimport os\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nimport torchvision.transforms.v2 as T\nfrom tqdm import tqdm\nfrom torch.amp import autocast, GradScaler\nimport matplotlib.pyplot as plt\nimport copy\nfrom torchvision.transforms import InterpolationMode\nimport torchvision\nfrom sklearn.metrics import roc_auc_score","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:04.695052Z","iopub.execute_input":"2025-10-20T13:58:04.695321Z","iopub.status.idle":"2025-10-20T13:58:11.835356Z","shell.execute_reply.started":"2025-10-20T13:58:04.695299Z","shell.execute_reply":"2025-10-20T13:58:11.834714Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"batch_size = 64\nimage_res = 224","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:11.836306Z","iopub.execute_input":"2025-10-20T13:58:11.836627Z","iopub.status.idle":"2025-10-20T13:58:11.840085Z","shell.execute_reply.started":"2025-10-20T13:58:11.836601Z","shell.execute_reply":"2025-10-20T13:58:11.839341Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_path = '/kaggle/input/2025-computer-vision-homework-2/cv_data/train'\nval_path = '/kaggle/input/2025-computer-vision-homework-2/cv_data/valid'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:11.840827Z","iopub.execute_input":"2025-10-20T13:58:11.841245Z","iopub.status.idle":"2025-10-20T13:58:11.855911Z","shell.execute_reply.started":"2025-10-20T13:58:11.841226Z","shell.execute_reply":"2025-10-20T13:58:11.855189Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transforms = T.Compose([\n    T.Resize((image_res,image_res)),\n    T.RandomApply([\n        T.ColorJitter()\n    ], p = 0.5),\n    T.ToTensor()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:11.857389Z","iopub.execute_input":"2025-10-20T13:58:11.857584Z","iopub.status.idle":"2025-10-20T13:58:11.8678Z","shell.execute_reply.started":"2025-10-20T13:58:11.857569Z","shell.execute_reply":"2025-10-20T13:58:11.867179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"probs = 0.3","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:11.868512Z","iopub.execute_input":"2025-10-20T13:58:11.868691Z","iopub.status.idle":"2025-10-20T13:58:11.878108Z","shell.execute_reply.started":"2025-10-20T13:58:11.868677Z","shell.execute_reply":"2025-10-20T13:58:11.877423Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transforms = T.Compose([\n    T.Resize((image_res, image_res)),\n\n    # Randomly choose between a crop or an affine transform\n    T.RandomApply([\n        T.RandomAffine(degrees=15, translate=(0.05, 0.05),\n                       scale=(0.85, 1.05), shear=3)\n    ], p = probs),\n    T.RandomApply([\n        T.RandomResizedCrop(image_res, scale=(0.75, 1.0), ratio=(0.7, 1.1))\n\n    ], p = probs),\n    T.RandomApply([\n        T.RandomAutocontrast()\n    ], p = probs),\n    T.RandomApply([\n        T.RandomRotation(20)\n\n    ], p = probs),\n    \n    # 50% chance to flip horizontally\n    T.RandomHorizontalFlip(p = 0.5),\n    T.RandomPerspective(\n        distortion_scale=0.2,   # mild distortion\n        p = probs,                   # 50% chance to apply\n        interpolation=InterpolationMode.BILINEAR,\n        fill=0                   # black fill for empty pixels\n    ),\n    # 30% chance to apply Gaussian blur\n    T.RandomApply([T.GaussianBlur(kernel_size=3, sigma=(0.1, 1.0))], p = probs),\n\n    # 50% chance to apply mild color jitter\n    T.RandomApply([T.ColorJitter(\n        brightness=0.2, contrast=0.2, saturation=0.2, hue=0.02\n    )], p = probs),\n    T.ToTensor(),\n    T.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:11.878715Z","iopub.execute_input":"2025-10-20T13:58:11.879013Z","iopub.status.idle":"2025-10-20T13:58:11.89937Z","shell.execute_reply.started":"2025-10-20T13:58:11.878953Z","shell.execute_reply":"2025-10-20T13:58:11.898617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image = Image.open('/kaggle/input/2025-computer-vision-homework-2/cv_data/train/0_1019118.jpg').convert('RGB')\nimg = transforms(image)\ndef show_tensor_img(tensor_img):\n    img = tensor_img.permute(1, 2, 0)\n\n    # If normalized, unnormalize here (example for ImageNet stats)\n    img = img * torch.tensor([0.229, 0.224, 0.225]) + torch.tensor([0.485, 0.456, 0.406])\n\n    img = img.numpy()\n\n    plt.imshow(img)\n    plt.axis('off')\n    plt.show()\n\nshow_tensor_img(img)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:11.899998Z","iopub.execute_input":"2025-10-20T13:58:11.900189Z","iopub.status.idle":"2025-10-20T13:58:12.147598Z","shell.execute_reply.started":"2025-10-20T13:58:11.900174Z","shell.execute_reply":"2025-10-20T13:58:12.146815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class FoodDataset(Dataset):\n    def __init__(self, root_dir, transform = None): #define variables\n        self.root_dir = root_dir\n        self.transform = transform\n        self.images = [f for f in os.listdir(root_dir) if f.endswith('.jpg')]\n        self.class_to_idx = {cls: idx for idx, cls in enumerate(sorted(set(\n            f.split('_')[0] for f in self.images\n        )))}\n\n    def __len__(self):\n        return len(self.images)\n    def __getitem__(self,idx):\n        \n        filename = self.images[idx]\n        image_path = os.path.join(self.root_dir, filename)\n        image = Image.open(image_path).convert('RGB')\n        \n        label = filename.split('_')[0]\n        if self.transform:\n            image = self.transform(image)\n        label = self.class_to_idx[label]\n        label = torch.tensor(label, dtype=torch.long)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:12.148378Z","iopub.execute_input":"2025-10-20T13:58:12.148585Z","iopub.status.idle":"2025-10-20T13:58:12.154438Z","shell.execute_reply.started":"2025-10-20T13:58:12.148568Z","shell.execute_reply":"2025-10-20T13:58:12.153825Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = FoodDataset(train_path, transform = transforms)\nval_dataset = FoodDataset(val_path, transform = transforms)\n\ntrain_loader = DataLoader(train_dataset, batch_size = batch_size, shuffle = True)\nval_loader = DataLoader(val_dataset, batch_size = batch_size, shuffle = 0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:12.155041Z","iopub.execute_input":"2025-10-20T13:58:12.155307Z","iopub.status.idle":"2025-10-20T13:58:12.286479Z","shell.execute_reply.started":"2025-10-20T13:58:12.155287Z","shell.execute_reply":"2025-10-20T13:58:12.285952Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class cnn(nn.Module):\n    def __init__(self, num_classes):\n        super(cnn,self).__init__()\n        self.conv1 = nn.Conv2d(3, 32, 3, padding = 1, stride = 2)\n        self.conv2 = nn.Conv2d(32, 128, 3, padding = 1, stride = 2)\n        self.conv3 = nn.Conv2d(128, 64, 3, padding = 1, stride = 2)\n\n        self.channel_proj = nn.Conv2d(3, 64, kernel_size=1)\n        self.pool = nn.MaxPool2d(2)\n        self.bn1 = nn.BatchNorm2d(32)\n        self.bn2 = nn.BatchNorm2d(128)\n        self.bn3 = nn.BatchNorm2d(64)\n        self.ac = nn.ReLU()\n        self.fc1 = nn.LazyLinear(64)\n        self.fc2 = nn.LazyLinear( num_classes)\n    def forward(self, x):\n        #conv\n        residual = x\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.ac(x) \n        x = self.pool(x)\n        \n        x = self.conv2(x)\n        x = self.bn2(x)\n        x = self.ac(x) \n        x = self.pool(x)\n\n        x = self.conv3(x)\n        x = self.bn3(x)\n        residual = F.interpolate(residual, size = x.shape[2:], mode = 'bilinear', align_corners = False)\n        residual = self.channel_proj(residual)\n        x = self.ac(x+residual) #+ temp)\n        x = self.pool(x)\n        #fc\n        x = x.view(x.size(0), -1)\n        x = self.ac(self.fc1(x))\n        x = self.fc2(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:12.288286Z","iopub.execute_input":"2025-10-20T13:58:12.288481Z","iopub.status.idle":"2025-10-20T13:58:12.295561Z","shell.execute_reply.started":"2025-10-20T13:58:12.288466Z","shell.execute_reply":"2025-10-20T13:58:12.294789Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#insert resnet type model here\nclass restypenet(nn.Module):\n    def __init__(self, num_classes):\n        super(restypenet,self).__init__()\n        self.conv = nn.Conv2d(3, 32, 3, padding = 1, stride = 2)\n        self.bn = nn.BatchNorm2d(32)\n        \n        self.conv1 = nn.Conv2d(32, 64, 3, padding = 1, stride = 1)\n        self.bn1 = nn.BatchNorm2d(64)\n        self.conv2 = nn.Conv2d(64, 64, 3, padding = 1, stride = 1)\n        self.bn2 = nn.BatchNorm2d(64)\n        \n        self.conv3 = nn.Conv2d(64, 128, 3, padding = 1, stride = 1)\n        self.bn3 = nn.BatchNorm2d(128)\n        self.conv4 = nn.Conv2d(128, 128, 3, padding = 1, stride = 1)\n        self.bn4 = nn.BatchNorm2d(128)\n        \n        self.conv5 = nn.Conv2d(128, 64, 3, padding = 1, stride = 1)\n        self.bn5 = nn.BatchNorm2d(64)\n        self.conv6 = nn.Conv2d(64, 64, 3, padding = 1, stride = 1)\n        self.bn6 = nn.BatchNorm2d(64)\n\n        self.pool = nn.AdaptiveAvgPool2d(1)\n        self.ac = nn.ReLU()\n\n        self.proj1 = nn.Conv2d(32, 64, 1)\n        self.proj2 = nn.Conv2d(64, 128, 1)\n        self.proj3 = nn.Conv2d(128, 64, 1)\n        self.fc1 = nn.LazyLinear(128)\n        self.fc2 = nn.LazyLinear(32)\n        self.fc = nn.LazyLinear(num_classes)\n    def forward(self,x):\n        x = self.conv(x)\n        x = self.bn(x)\n        x = self.ac(x)\n        #block 1\n        residual = x\n        x = self.bn1(self.conv1(x))\n        x = self.ac(x)\n        x = self.bn2(self.conv2(x))\n        residual = F.interpolate(residual, size = x.shape[2:], mode = 'bilinear', align_corners = False)\n        residual = self.proj1(residual)\n        x = self.ac(x + residual)\n        #block 2\n        residual = x\n        x = self.bn3(self.conv3(x))\n        x = self.ac(x)\n        x = self.bn4(self.conv4(x))\n        residual = F.interpolate(residual, size = x.shape[2:], mode = 'bilinear', align_corners = False)\n        residual = self.proj2(residual)\n        x = self.ac(x + residual)\n        #block 3\n        residual = x\n        x = self.bn5(self.conv5(x))\n        x = self.ac(x)\n        x = self.bn6(self.conv6(x))\n        residual = F.interpolate(residual, size = x.shape[2:], mode = 'bilinear', align_corners = False)\n        residual = self.proj3(residual)\n        x = self.ac(x + residual)\n\n        x = self.pool(x)\n        #x = self.dropout(x)\n        x = x.view(x.size(0), -1)\n        \n        x = self.fc1(x)\n        x = self.ac(x)\n        x = self.fc2(x)\n        x = self.ac(x)\n        x = self.fc(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:12.296238Z","iopub.execute_input":"2025-10-20T13:58:12.296423Z","iopub.status.idle":"2025-10-20T13:58:12.30805Z","shell.execute_reply.started":"2025-10-20T13:58:12.296409Z","shell.execute_reply":"2025-10-20T13:58:12.307351Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\ndevice","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T13:58:12.308897Z","iopub.execute_input":"2025-10-20T13:58:12.309147Z","iopub.status.idle":"2025-10-20T13:58:12.368907Z","shell.execute_reply.started":"2025-10-20T13:58:12.309127Z","shell.execute_reply":"2025-10-20T13:58:12.368306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = torchvision.models.efficientnet_v2_s(weights = None).to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T14:30:53.380972Z","iopub.execute_input":"2025-10-20T14:30:53.381262Z","iopub.status.idle":"2025-10-20T14:30:53.769407Z","shell.execute_reply.started":"2025-10-20T14:30:53.38124Z","shell.execute_reply":"2025-10-20T14:30:53.768793Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#model = restypenet(10).to(device)\ncriterion = nn.CrossEntropyLoss().to(device)\noptimizer = torch.optim.AdamW(model.parameters(), lr = 0.0005, weight_decay = 1e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T15:56:53.613124Z","iopub.execute_input":"2025-10-20T15:56:53.613891Z","iopub.status.idle":"2025-10-20T15:56:53.623002Z","shell.execute_reply.started":"2025-10-20T15:56:53.613838Z","shell.execute_reply":"2025-10-20T15:56:53.622225Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dummy = torch.ones([1, 3, image_res, image_res]).to(device)\nprint(model(dummy).shape)\n#print(summary(model, input_size = [1,3,image_res,image_res]))\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T14:30:56.493555Z","iopub.execute_input":"2025-10-20T14:30:56.494286Z","iopub.status.idle":"2025-10-20T14:30:56.55714Z","shell.execute_reply.started":"2025-10-20T14:30:56.494261Z","shell.execute_reply":"2025-10-20T14:30:56.556381Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 50\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T14:31:02.851202Z","iopub.execute_input":"2025-10-20T14:31:02.851701Z","iopub.status.idle":"2025-10-20T14:31:02.855054Z","shell.execute_reply.started":"2025-10-20T14:31:02.851678Z","shell.execute_reply":"2025-10-20T14:31:02.854273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Track metrics\nt = 0\ntrain_losses, val_losses = [], []\ntrain_accs, val_accs = [], []\npatience = 10\nbest_val_acc = 0.0\nepochs_no_improve = 0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T14:31:04.015779Z","iopub.execute_input":"2025-10-20T14:31:04.01648Z","iopub.status.idle":"2025-10-20T14:31:04.020085Z","shell.execute_reply.started":"2025-10-20T14:31:04.016458Z","shell.execute_reply":"2025-10-20T14:31:04.019397Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nscaler = GradScaler(enabled=(device.type == \"cuda\")) # for mixed precision\n\nbest_model_wts = copy.deepcopy(model.state_dict())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T14:31:06.509557Z","iopub.execute_input":"2025-10-20T14:31:06.510453Z","iopub.status.idle":"2025-10-20T14:31:06.564692Z","shell.execute_reply.started":"2025-10-20T14:31:06.510429Z","shell.execute_reply":"2025-10-20T14:31:06.564186Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nfor epoch in range(epochs):\n    train_dataset = FoodDataset(train_path, transform = transforms)\n    val_dataset = FoodDataset(val_path, transform = transforms)\n\n    train_loader = DataLoader(train_dataset, batch_size = batch_size, shuffle = True)\n    val_loader = DataLoader(val_dataset, batch_size = batch_size, shuffle = 0)\n    # ---- TRAIN ----\n    model.train()\n    running_loss, correct, total = 0.0, 0, 0\n    train_bar = tqdm(train_loader, desc=f\"Epoch {t+1}/{epochs} [Train]\", leave=False)\n\n    for image, label in train_bar:\n        image, label = image.to(device), label.to(device)\n\n        optimizer.zero_grad()\n\n        with autocast('cuda'):  # mixed precision forward + loss\n            outputs = model(image)\n            loss = criterion(outputs, label)\n\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n\n        running_loss += loss.item() * image.size(0)\n        _, predicted = outputs.max(1)\n        correct += (predicted == label).sum().item()\n        total += label.size(0)\n\n        train_bar.set_postfix(loss=loss.item())\n\n    train_loss = running_loss / total\n    train_acc = correct / total\n\n    # ---- VALIDATION ----\n    model.eval()\n    val_loss, val_correct, val_total = 0.0, 0, 0\n    val_bar = tqdm(val_loader, desc=f\"Epoch {t+1}/{epochs} [Val]\", leave=False)\n\n    with torch.no_grad():\n        for image, label in val_bar:\n            image, label = image.to(device), label.to(device)\n\n            with autocast('cuda'):\n                outputs = model(image)\n                loss = criterion(outputs, label)\n\n            val_loss += loss.item() * image.size(0)\n            _, predicted = outputs.max(1)\n            val_correct += (predicted == label).sum().item()\n            val_total += label.size(0)\n            val_bar.set_postfix(loss=loss.item())\n\n    val_loss /= val_total\n    val_acc = val_correct / val_total\n\n    # Save metrics for plotting\n    train_losses.append(train_loss)\n    val_losses.append(val_loss)\n    train_accs.append(train_acc)\n    val_accs.append(val_acc)\n\n    # ---- PRINT EPOCH SUMMARY ----\n    print(f\"Epoch {t+1}/{epochs} | \"\n          f\"Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f} | \"\n          f\"Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f}\")\n    t+=1\n    # ---- CHECKPOINT: Save best model ----\n    if val_acc > best_val_acc:\n        best_val_acc = val_acc\n        best_model_wts = copy.deepcopy(model.state_dict())\n        torch.save(model.state_dict(), \"best_model.pth\")\n        print(f\"✅ Best model saved (Val Acc: {best_val_acc:.4f})\")\n        epochs_no_improve = 0\n    else:\n        epochs_no_improve += 1\n\n    # ---- EARLY STOPPING ----\n    if epochs_no_improve >= patience:\n        print(f\"⏹ Early stopping triggered after {epoch+1} epochs.\")\n        break\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T15:57:00.640993Z","iopub.execute_input":"2025-10-20T15:57:00.641661Z","iopub.status.idle":"2025-10-20T16:51:02.466474Z","shell.execute_reply.started":"2025-10-20T15:57:00.641641Z","shell.execute_reply":"2025-10-20T16:51:02.465409Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ---- LOAD BEST MODEL ----\nmodel.load_state_dict(best_model_wts)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T09:39:21.938353Z","iopub.execute_input":"2025-10-20T09:39:21.938597Z","iopub.status.idle":"2025-10-20T09:39:21.966623Z","shell.execute_reply.started":"2025-10-20T09:39:21.938579Z","shell.execute_reply":"2025-10-20T09:39:21.965999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"logits = model(image)\n\n# Convert logits to probabilities\nprobs = F.softmax(logits, dim=1)  # shape: [B, num_classes]\n\n# Convert to numpy\ny_score = probs.detach().cpu().numpy()\ny_true = label.cpu().numpy()\n\n# Compute AUC ROC\nauc = roc_auc_score(y_true, y_score, multi_class='ovr')\nprint(f\"AUC ROC: {auc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T09:39:24.062242Z","iopub.execute_input":"2025-10-20T09:39:24.062523Z","iopub.status.idle":"2025-10-20T09:39:24.213464Z","shell.execute_reply.started":"2025-10-20T09:39:24.062502Z","shell.execute_reply":"2025-10-20T09:39:24.212393Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ---- PLOT METRICS ----\nepochs_range = range(1, len(train_losses) + 1)\n\nplt.figure(figsize=(12, 5))\nplt.ion()\n# Loss plot\nplt.subplot(1, 2, 1)\nplt.plot(epochs_range, train_losses, label='Train Loss')\nplt.plot(epochs_range, val_losses, label='Val Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Loss over Epochs')\nplt.legend()\n\n# Accuracy plot\nplt.subplot(1, 2, 2)\nplt.plot(epochs_range, train_accs, label='Train Acc')\nplt.plot(epochs_range, val_accs, label='Val Acc')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.title('Accuracy over Epochs')\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T09:39:26.932618Z","iopub.execute_input":"2025-10-20T09:39:26.932887Z","iopub.status.idle":"2025-10-20T09:39:27.298975Z","shell.execute_reply.started":"2025-10-20T09:39:26.932868Z","shell.execute_reply":"2025-10-20T09:39:27.298271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class testFoodDataset(Dataset):\n    def __init__(self, root_dir, transform = None): #define variables\n        self.root_dir = root_dir\n        self.transform = transform\n        self.images = [f for f in os.listdir(root_dir) if f.endswith('.jpg')]\n        self.class_to_idx = {cls: idx for idx, cls in enumerate(sorted(set(\n            f.split('_')[0] for f in self.images\n        )))}\n\n    def __len__(self):\n        return len(self.images)\n    def __getitem__(self,idx):\n        \n        filename = self.images[idx]\n        image_path = os.path.join(self.root_dir, filename)\n        image = Image.open(image_path).convert('RGB')\n        \n        if self.transform:\n            image = self.transform(image)\n\n        return image, image_path","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T09:39:31.334495Z","iopub.execute_input":"2025-10-20T09:39:31.335186Z","iopub.status.idle":"2025-10-20T09:39:31.340535Z","shell.execute_reply.started":"2025-10-20T09:39:31.335162Z","shell.execute_reply":"2025-10-20T09:39:31.339742Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test = testFoodDataset('/kaggle/input/2025-computer-vision-homework-2/cv_data/test', transform = transforms)\ntest_loader = DataLoader(test, batch_size = 32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T09:39:33.662517Z","iopub.execute_input":"2025-10-20T09:39:33.662801Z","iopub.status.idle":"2025-10-20T09:39:33.716089Z","shell.execute_reply.started":"2025-10-20T09:39:33.66278Z","shell.execute_reply":"2025-10-20T09:39:33.715536Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Put model in evaluation mode\nmodel.eval()\n\nall_preds = []\nall_files = []\n\n# If you have class names from your dataset\n\nwith torch.no_grad():\n    for image, paths in test_loader:  # test_loader should return (image, path) if no labels\n        images = image.to(device)\n\n        outputs = model(images)\n        _, preds = torch.max(outputs, 1)\n\n        # Store predictions and file paths\n        all_preds.extend(preds.cpu().numpy())\n        all_files.extend(paths)  # paths should be strings to image files\n\n# Convert predicted indices to class names\npredicted_labels = [i for i in all_preds]\n\n# Create DataFrame\ndf = pd.DataFrame({\n    \"id\": [os.path.splitext(os.path.basename(p))[0] for p in all_files],\n    \"label\": predicted_labels\n})\n\n# Save to CSV\ndf.to_csv(\"predictions.csv\", index=False)\n\nprint(\"✅ Predictions saved to predictions.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-20T09:39:35.932387Z","iopub.execute_input":"2025-10-20T09:39:35.932965Z","iopub.status.idle":"2025-10-20T09:40:22.245374Z","shell.execute_reply.started":"2025-10-20T09:39:35.932939Z","shell.execute_reply":"2025-10-20T09:40:22.244492Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T03:46:55.14987Z","iopub.execute_input":"2025-10-19T03:46:55.150534Z","iopub.status.idle":"2025-10-19T03:46:55.172038Z","shell.execute_reply.started":"2025-10-19T03:46:55.15051Z","shell.execute_reply":"2025-10-19T03:46:55.171367Z"}},"outputs":[],"execution_count":null}]}