{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport random\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nimport torchvision.models as models\n\n# Set seed for reproducibility\ndef seed_everything(seed=42):\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.backends.cudnn.deterministic = True\n\nseed_everything(42)\n\n# Set device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"Using device: {device}\")\n\n# -------------------------------------------------------------\n# Dynamic Path Resolution to fix IsADirectoryError\n# -------------------------------------------------------------\nbase_path = '/kaggle/input'\nfound_labels = None\nfound_train = None\nfound_test = None\n\n# Search for the exact files inside /kaggle/input\nfor root, dirs, files in os.walk(base_path):\n    if 'labels.csv' in files:\n        found_labels = os.path.join(root, 'labels.csv')\n    if 'train' in dirs and found_train is None:\n        found_train = os.path.join(root, 'train')\n    if 'test' in dirs and found_test is None:\n        found_test = os.path.join(root, 'test')\n\nLABELS_CSV = found_labels\nTRAIN_DIR = found_train\nTEST_DIR = found_test\n\nprint(f\"Labels File Path: {LABELS_CSV}\")\nprint(f\"Train Dir Path:  {TRAIN_DIR}\")\nprint(f\"Test Dir Path:   {TEST_DIR}\")\n\n# Read labels CSV\ndf = pd.read_csv(LABELS_CSV)\ndf['file_path'] = df['id'].apply(lambda x: os.path.join(TRAIN_DIR, f\"{x}.jpg\"))\n\n# Encode breeds to integer labels\nbreed_list = sorted(df['breed'].unique())\nbreed_to_idx = {breed: idx for idx, breed in enumerate(breed_list)}\ndf['label'] = df['breed'].map(breed_to_idx)\n\nprint(f\"\\nTotal Training Images: {len(df)}\")\nprint(f\"Total Unique Breeds: {len(breed_list)}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-31T14:27:45.106636Z","iopub.execute_input":"2026-08-31T14:27:45.107467Z","iopub.status.idle":"2026-08-31T14:27:54.467099Z","shell.execute_reply.started":"2026-08-31T14:27:45.107434Z","shell.execute_reply":"2026-08-31T14:27:54.466366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# Split into Train and Validation sets (80% train, 20% val)\ntrain_df, val_df = train_test_split(df, test_size=0.2, random_state=42, stratify=df['label'])\n\n# Image Augmentation Transformers\ntrain_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(15),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\nval_transform = 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# Custom PyTorch Dataset Class\nclass DogDataset(Dataset):\n    def __init__(self, df, transform=None, is_test=False):\n        self.df = df\n        self.transform = transform\n        self.is_test = is_test\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        image = Image.open(row['file_path']).convert('RGB')\n        \n        if self.transform:\n            image = self.transform(image)\n            \n        if self.is_test:\n            return image, row['id']\n        else:\n            return image, torch.tensor(row['label'], dtype=torch.long)\n\n# Create Data Loaders\ntrain_dataset = DogDataset(train_df, transform=train_transform)\nval_dataset = DogDataset(val_df, transform=val_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=2)\n\nprint(f\"Train samples: {len(train_dataset)} | Val samples: {len(val_dataset)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-31T14:27:54.468351Z","iopub.execute_input":"2026-08-31T14:27:54.468722Z","iopub.status.idle":"2026-08-31T14:27:54.490643Z","shell.execute_reply.started":"2026-08-31T14:27:54.468693Z","shell.execute_reply":"2026-08-31T14:27:54.489669Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Initializing ResNet18 model from scratch (100% offline)...\")\n\n# 1. Initialize ResNet18 model without requesting online weights\nmodel = models.resnet18(weights=None)\n\n# 2. Unfreeze all parameters to allow end-to-end training\nfor param in model.parameters():\n    param.requires_grad = True\n\n# 3. Custom classification head for 120 breeds\nnum_ftrs = model.fc.in_features\nmodel.fc = nn.Sequential(\n    nn.Linear(num_ftrs, 512),\n    nn.ReLU(),\n    nn.BatchNorm1d(512),\n    nn.Dropout(0.4),\n    nn.Linear(512, len(breed_list))\n)\n\nmodel = model.to(device)\n\n# 4. Loss Function, Optimizer, and LR Scheduler\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=1e-4)\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer, mode='max', factor=0.5, patience=2\n)\n\n# 5. Training Loop\nEPOCHS = 15\nprint(\"\\nStarting Training Loop...\\n\")\n\nfor epoch in range(EPOCHS):\n    model.train()\n    running_loss, correct, total = 0.0, 0, 0\n    \n    for images, labels in train_loader:\n        images, labels = images.to(device), labels.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item() * images.size(0)\n        _, preds = torch.max(outputs, 1)\n        correct += torch.sum(preds == labels.data)\n        total += labels.size(0)\n        \n    train_loss = running_loss / total\n    train_acc = (correct.double() / total).item()\n\n    # Validation Phase\n    model.eval()\n    val_loss_sum, val_correct, val_total = 0.0, 0, 0\n    with torch.no_grad():\n        for images, labels in val_loader:\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            \n            val_loss_sum += loss.item() * images.size(0)\n            _, preds = torch.max(outputs, 1)\n            val_correct += torch.sum(preds == labels.data)\n            val_total += labels.size(0)\n            \n    val_loss = val_loss_sum / val_total\n    val_acc = (val_correct.double() / val_total).item()\n    \n    scheduler.step(val_acc)\n\n    print(f\"Epoch [{epoch+1:02d}/{EPOCHS}] \"\n          f\"Train Loss: {train_loss:.4f} Acc: {train_acc:.4f} | \"\n          f\"Val Loss: {val_loss:.4f} Acc: {val_acc:.4f}\")\n\n# 6. Generate Submission File\nprint(\"\\nGenerating submission.csv...\")\ntest_files = [f for f in os.listdir(TEST_DIR) if f.endswith('.jpg')]\ntest_df = pd.DataFrame({\n    'id': [os.path.splitext(f)[0] for f in test_files],\n    'file_path': [os.path.join(TEST_DIR, f) for f in test_files]\n})\n\ntest_loader = DataLoader(\n    DogDataset(test_df, transform=val_transform, is_test=True),\n    batch_size=32, shuffle=False, num_workers=2\n)\n\nmodel.eval()\nall_probs, image_ids = [], []\nsoftmax = nn.Softmax(dim=1)\n\nwith torch.no_grad():\n    for images, ids in test_loader:\n        images = images.to(device)\n        outputs = model(images)\n        all_probs.append(softmax(outputs).cpu().numpy())\n        image_ids.extend(ids)\n\nsubmission_df = pd.DataFrame(np.vstack(all_probs), columns=breed_list)\nsubmission_df.insert(0, 'id', image_ids)\nsubmission_df.to_csv('submission.csv', index=False)\nprint(\"Successfully generated 'submission.csv'!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-31T14:27:54.491678Z","iopub.execute_input":"2026-08-31T14:27:54.492112Z"}},"outputs":[],"execution_count":null}]}