{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":131587,"databundleVersionId":15786880}],"dockerImageVersionId":31287,"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":"2026-02-22T15:17:20.355997Z","iopub.execute_input":"2026-02-22T15:17:20.356265Z","iopub.status.idle":"2026-02-22T15:17:25.703445Z","shell.execute_reply.started":"2026-02-22T15:17:20.356232Z","shell.execute_reply":"2026-02-22T15:17:25.702647Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models, datasets\nfrom tqdm import tqdm\n\n# Set device to GPU if available\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\n\n# Define paths (Adjust the train_dir if your folders are structured differently)\nBASE_DIR = '/kaggle/input/cs-460-muffin-vs-chihuahua-classification-challenge'\nTRAIN_DIR = f'{BASE_DIR}/train' # Assuming train images are in subfolders: /train/muffin and /train/chihuahua\nTEST_DIR = f'{BASE_DIR}/kaggle_test_final'\n\nBATCH_SIZE = 32\nEPOCHS = 5\nLEARNING_RATE = 0.001","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-22T15:19:30.554400Z","iopub.execute_input":"2026-02-22T15:19:30.554779Z","iopub.status.idle":"2026-02-22T15:19:37.837155Z","shell.execute_reply.started":"2026-02-22T15:19:30.554752Z","shell.execute_reply":"2026-02-22T15:19:37.836440Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Image transformations\ntrain_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(15),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\ntest_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Load Training Data (Assumes ImageFolder structure: /muffin/ and /chihuahua/)\ntry:\n    train_dataset = datasets.ImageFolder(root=TRAIN_DIR, transform=train_transforms)\n    train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2)\n    classes = train_dataset.classes\n    print(f\"Classes found: {classes}\")\nexcept Exception as e:\n    print(\"Error loading training data. Ensure your training folder has 'muffin' and 'chihuahua' subdirectories.\")\n    print(e)\n\n# Custom Dataset for Test Data (since it's just a folder of images without labels)\nclass TestDataset(Dataset):\n    def __init__(self, test_dir, transform=None):\n        self.test_dir = test_dir\n        self.image_files = [f for f in os.listdir(test_dir) if f.endswith('.jpg') or f.endswith('.png')]\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_files)\n\n    def __getitem__(self, idx):\n        img_name = self.image_files[idx]\n        img_path = os.path.join(self.test_dir, img_name)\n        image = Image.open(img_path).convert('RGB')\n        \n        if self.transform:\n            image = self.transform(image)\n            \n        return image, img_name\n\ntest_dataset = TestDataset(test_dir=TEST_DIR, transform=test_transforms)\ntest_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-22T15:19:51.192818Z","iopub.execute_input":"2026-02-22T15:19:51.193263Z","iopub.status.idle":"2026-02-22T15:19:52.636601Z","shell.execute_reply.started":"2026-02-22T15:19:51.193234Z","shell.execute_reply":"2026-02-22T15:19:52.635445Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load pre-trained ResNet18\nmodel = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)\n\n# Modify the final fully connected layer for 2 classes (Muffin vs Chihuahua)\nnum_ftrs = model.fc.in_features\nmodel.fc = nn.Linear(num_ftrs, 2)\n\nmodel = model.to(device)\n\n# Loss function and optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-22T15:20:00.138313Z","iopub.execute_input":"2026-02-22T15:20:00.138875Z","iopub.status.idle":"2026-02-22T15:20:01.010233Z","shell.execute_reply.started":"2026-02-22T15:20:00.138845Z","shell.execute_reply":"2026-02-22T15:20:01.009379Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Starting Training...\")\nfor epoch in range(EPOCHS):\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    \n    for images, labels in tqdm(train_loader, desc=f\"Epoch {epoch+1}/{EPOCHS}\"):\n        images, labels = images.to(device), labels.to(device)\n        \n        # Zero the parameter gradients\n        optimizer.zero_grad()\n        \n        # Forward pass\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        \n        # Backward pass and optimize\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item()\n        \n        # Calculate accuracy\n        _, predicted = torch.max(outputs.data, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n        \n    epoch_loss = running_loss / len(train_loader)\n    epoch_acc = 100 * correct / total\n    print(f\"Epoch [{epoch+1}/{EPOCHS}] - Loss: {epoch_loss:.4f} - Accuracy: {epoch_acc:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-22T15:20:10.068899Z","iopub.execute_input":"2026-02-22T15:20:10.069542Z","iopub.status.idle":"2026-02-22T15:22:50.020577Z","shell.execute_reply.started":"2026-02-22T15:20:10.069511Z","shell.execute_reply":"2026-02-22T15:22:50.019788Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Starting Inference on Test Set...\")\nmodel.eval()\npredictions = []\nfilenames = []\n\n# Map numerical outputs back to string labels\n# Ensure these match the exact string format required by the competition\nidx_to_class = {v: k for k, v in train_dataset.class_to_idx.items()}\n\nwith torch.no_grad():\n    for images, batch_filenames in tqdm(test_loader, desc=\"Predicting\"):\n        images = images.to(device)\n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n        \n        # Convert tensor to list of integers\n        predicted_classes = predicted.cpu().numpy()\n        \n        for idx in predicted_classes:\n            predictions.append(idx_to_class[idx])\n            \n        filenames.extend(batch_filenames)\n\n# Create submission DataFrame\nsubmission_df = pd.DataFrame({\n    'ID': filenames,\n    'Label': predictions # Change to 'Predict' if Kaggle throws a column name error\n})\n\n# Save to CSV\nsubmission_df.to_csv('submission.csv', index=False)\nprint(\"Submission saved successfully as submission.csv!\")\nprint(submission_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-22T15:23:00.268341Z","iopub.execute_input":"2026-02-22T15:23:00.268681Z","iopub.status.idle":"2026-02-22T15:23:09.561313Z","shell.execute_reply.started":"2026-02-22T15:23:00.268648Z","shell.execute_reply":"2026-02-22T15:23:09.560402Z"}},"outputs":[],"execution_count":null}]}