{"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 Required Libraries & Setup\nimport torch\nimport torch.nn as nn\nimport torchvision.models as models\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom torch.utils.data import Dataset, DataLoader, random_split\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nfrom torch.cuda.amp import GradScaler, autocast\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n# Automatically select the best available hardware: GPU (CUDA) if available, otherwise CPU\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:05:47.836753Z","iopub.execute_input":"2026-06-14T15:05:47.837275Z","iopub.status.idle":"2026-06-14T15:05:47.843095Z","shell.execute_reply.started":"2026-06-14T15:05:47.837248Z","shell.execute_reply":"2026-06-14T15:05:47.842126Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Initialize the ResNet50 Model","metadata":{}},{"cell_type":"code","source":"# Load ResNet50 with pre-trained weights from ImageNet\ncnn_model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)\n# Freeze the early layers (layer1, layer2, layer3) to retain general features learned from ImageNet\n# This prevents overfitting and speeds up training\nfor name, param in cnn_model.named_parameters():\n    if 'layer1' in name or 'layer2' in name or 'layer3' in name:\n        param.requires_grad = False\n# Get the number of input features for the final fully connected layer\nnum_ftrs = cnn_model.fc.in_features\n# Replace the original ImageNet classifier with a custom one for our binary classification task\n# Dropout(0.5) is added to prevent overfitting by randomly turning off half the neurons\ncnn_model.fc = nn.Sequential(nn.Dropout(0.5), nn.Linear(num_ftrs, 2))\n# Move the entire model to the selected device (GPU/CPU)\ncnn_model = cnn_model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:05:47.844373Z","iopub.execute_input":"2026-06-14T15:05:47.844676Z","iopub.status.idle":"2026-06-14T15:05:48.227080Z","shell.execute_reply.started":"2026-06-14T15:05:47.844651Z","shell.execute_reply":"2026-06-14T15:05:48.225973Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  Define Data Augmentation Pipelines","metadata":{}},{"cell_type":"code","source":"train_transform = A.Compose([\n    A.Resize(224, 224),# Resize all images to the input size expected by ResNet50\n    A.HorizontalFlip(p=0.5), # Flip images horizontally with 50% probability\n    A.RandomRotate90(p=0.2),# Rotate images 90 degrees randomly with 20% probability\n    A.ShiftScaleRotate(0.1, 0.1, 15, p=0.5),# Randomly shift, scale, and rotate images\n    A.RandomBrightnessContrast(p=0.3),# Randomly adjust brightness and contrast\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),# Normalize using ImageNet stats\n    ToTensorV2()  # Convert the image to a PyTorch tensor\n])\n# Validation/Test transforms: Only apply deterministic operations (NO random augmentations!)\nval_test_transform = A.Compose([\n    A.Resize(224, 224),\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:05:48.228324Z","iopub.execute_input":"2026-06-14T15:05:48.228612Z","iopub.status.idle":"2026-06-14T15:05:48.238325Z","shell.execute_reply.started":"2026-06-14T15:05:48.228584Z","shell.execute_reply":"2026-06-14T15:05:48.237751Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Unzip the Competition Data","metadata":{}},{"cell_type":"code","source":"import zipfile\nimport os\n\n\ntrain_zip_path = '/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/train.zip'\ntrain_extract_path = '/kaggle/working/train/'\n\nos.makedirs(train_extract_path, exist_ok=True)\nwith zipfile.ZipFile(train_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(train_extract_path)\n\n\ntest_zip_path = '/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/test.zip'\ntest_extract_path = '/kaggle/working/test/'\n\nos.makedirs(test_extract_path, exist_ok=True)\nwith zipfile.ZipFile(test_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(test_extract_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:05:48.240227Z","iopub.execute_input":"2026-06-14T15:05:48.240436Z","iopub.status.idle":"2026-06-14T15:05:58.776981Z","shell.execute_reply.started":"2026-06-14T15:05:48.240419Z","shell.execute_reply":"2026-06-14T15:05:58.776273Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  Create a Custom Dataset Class","metadata":{}},{"cell_type":"code","source":"class DogsCatsDataset(Dataset):\n    def __init__(self, data_dir, transform, data_type=\"train\"):\n        self.data_type = data_type\n        self.transform = transform\n        if data_type == \"train\":\n            img_dir = os.path.join(data_dir, \"train\", \"train\")\n        else:\n            # Create a list of all valid image file paths in the directory\n            img_dir = os.path.join(data_dir, \"test\", \"test\")\n        self.image_paths = [os.path.join(img_dir, f) for f in os.listdir(img_dir) if f.endswith(\".jpg\")]\n        # For training data: Create labels (0 for cat, 1 for dog) based on the filename\n        if data_type == \"train\":\n            self.labels = [0 if \"cat\" in f else 1 for f in self.image_paths]\n            # For test data: Extract the image ID from the filename (needed for the submission file)\n        else:\n            self.test_ids = [int(os.path.splitext(os.path.basename(f))[0]) for f in self.image_paths]\n\n    def __len__(self):                              #1. Returns the total number of images in the dataset\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):                    # 2. Apply the data augmentation transforms\n        image = np.array(Image.open(self.image_paths[idx]).convert(\"RGB\"))\n        if self.transform:\n            image = self.transform(image=image)[\"image\"]\n        if self.data_type == \"train\":\n            return image, self.labels[idx]\n        else:\n            return image, self.test_ids[idx]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:05:58.778043Z","iopub.execute_input":"2026-06-14T15:05:58.778316Z","iopub.status.idle":"2026-06-14T15:05:58.785436Z","shell.execute_reply.started":"2026-06-14T15:05:58.778285Z","shell.execute_reply":"2026-06-14T15:05:58.784759Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Prepare DataLoaders for Training and Validation","metadata":{}},{"cell_type":"code","source":"data_root = \"/kaggle/working/\"\ntrain_dataset = DogsCatsDataset(data_root, train_transform, \"train\")\nval_dataset = DogsCatsDataset(data_root, val_test_transform, \"train\")\n\nindices = list(range(len(train_dataset)))\ntrain_idx=list(range(20000))\nval_idx=list(range(20000,25000))\n\ntrain_dataset = torch.utils.data.Subset(train_dataset, train_idx)\nval_dataset = torch.utils.data.Subset(val_dataset, val_idx)\n\ntrain_dl = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=8, pin_memory=True,prefetch_factor=2,persistent_workers=True)\nval_dl = DataLoader(val_dataset, batch_size=64, shuffle=False, num_workers=4, pin_memory=True,persistent_workers=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:05:58.786425Z","iopub.execute_input":"2026-06-14T15:05:58.786683Z","iopub.status.idle":"2026-06-14T15:05:59.229543Z","shell.execute_reply.started":"2026-06-14T15:05:58.786663Z","shell.execute_reply":"2026-06-14T15:05:59.228936Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Define Loss Function, Optimizer, and Scheduler","metadata":{}},{"cell_type":"code","source":"import torch.optim as optim\nfrom torch.optim.lr_scheduler import CosineAnnealingWarmRestarts\nfrom torch.amp import GradScaler\nloss_func = nn.CrossEntropyLoss(label_smoothing=0.1)\noptimizer = optim.AdamW([\n    {'params': cnn_model.layer4.parameters(), 'lr': 1e-4},\n    {'params': cnn_model.fc.parameters(), 'lr': 1e-3}\n], weight_decay=1e-4)\nlr_scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=5, T_mult=2)\nscaler = GradScaler()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:05:59.494312Z","iopub.execute_input":"2026-06-14T15:05:59.495146Z","iopub.status.idle":"2026-06-14T15:05:59.500694Z","shell.execute_reply.started":"2026-06-14T15:05:59.495122Z","shell.execute_reply":"2026-06-14T15:05:59.499866Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Define Training and Validation Logic","metadata":{}},{"cell_type":"code","source":"def loss_batch(loss_func, output, target, opt=None, scaler=None):\n    if opt is not None:\n        with autocast():\n            loss = loss_func(output, target)\n        opt.zero_grad()\n        scaler.scale(loss).backward()\n        scaler.step(opt)\n        scaler.update()\n    else:\n        loss = loss_func(output, target)\n    pred = output.argmax(dim=1, keepdim=True)\n    metric_b = pred.eq(target.view_as(pred)).sum().item()\n    return loss.item(), metric_b\n\ndef loss_epoch(model, loss_func, dataset_dl, opt=None, scaler=None):\n    run_loss, t_metric = 0.0, 0.0\n    len_data = len(dataset_dl.dataset)\n    for xb, yb in dataset_dl:\n        xb, yb = xb.to(device), yb.to(device)\n        output = model(xb)\n        loss_b, metric_b = loss_batch(loss_func, output, yb, opt, scaler)\n        run_loss += loss_b\n        t_metric += metric_b\n    return run_loss / len_data, t_metric / len_data\n\ndef train_val(model, epochs=30, patience=5):\n    best_loss = float(\"inf\")\n    early_stop_count = 0\n    for epoch in range(epochs):\n        model.train()\n        train_loss, train_acc = loss_epoch(model, loss_func, train_dl, optimizer, scaler)\n        model.eval()\n        with torch.no_grad():\n            val_loss, val_acc = loss_epoch(model, loss_func, val_dl)\n        lr_scheduler.step()\n        print(f\"Epoch {epoch}: Train Loss {train_loss:.4f}, Acc {train_acc:.4f} | Val Loss {val_loss:.4f}, Acc {val_acc:.4f}\")\n        if val_loss < best_loss:\n            best_loss = val_loss\n            early_stop_count = 0\n            torch.save(model.state_dict(), \"best_resnet.pt\")\n        else:\n            early_stop_count += 1\n            if early_stop_count >= patience:\n                print(\"early stopping has started\")\n                break\n    model.load_state_dict(torch.load(\"best_resnet.pt\"))\n    return model\n\n# Start the training process\ncnn_model = train_val(cnn_model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:05:59.501798Z","iopub.execute_input":"2026-06-14T15:05:59.502053Z","iopub.status.idle":"2026-06-14T15:06:40.826922Z","shell.execute_reply.started":"2026-06-14T15:05:59.502033Z","shell.execute_reply":"2026-06-14T15:06:40.825715Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Test Time Augmentation (TTA) for Final Prediction","metadata":{}},{"cell_type":"code","source":"test_dataset = DogsCatsDataset(data_root, val_test_transform, \"test\")\ntest_dl = DataLoader(test_dataset, batch_size=64, shuffle=False)\n\ndef predict_with_tta(model, test_dl, n_tta=5):\n    model.eval()\n    all_ids, all_probs = [], []\n    with torch.no_grad():\n        for imgs, ids in test_dl:\n            imgs = imgs.to(device)\n            batch_probs = []\n            for _ in range(n_tta):\n                outputs = model(imgs)\n                probs = torch.softmax(outputs, dim=1)[:,1].cpu().numpy()\n                batch_probs.append(probs)\n            avg_probs = np.mean(batch_probs, axis=0)\n            all_ids.extend(ids.numpy())\n            all_probs.extend(avg_probs)\n    return all_ids, all_probs\n\ntest_ids, test_probs = predict_with_tta(cnn_model, test_dl)\npd.DataFrame({\"id\": test_ids, \"label\": test_probs}).to_csv(\"submission_tta.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-14T15:06:40.828074Z","iopub.status.idle":"2026-06-14T15:06:40.828376Z","shell.execute_reply.started":"2026-06-14T15:06:40.828256Z","shell.execute_reply":"2026-06-14T15:06:40.828270Z"}},"outputs":[],"execution_count":null}]}