{"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":"none","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Import Libraries","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\n\nimport torchvision\nfrom torchvision import transforms, models\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom PIL import Image\nfrom tqdm import tqdm\nimport copy\nimport os\nimport requests\nimport random\nimport zipfile\nimport shutil","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:10:50.802290Z","iopub.execute_input":"2025-12-07T16:10:50.802534Z","iopub.status.idle":"2025-12-07T16:11:06.169745Z","shell.execute_reply.started":"2025-12-07T16:10:50.802509Z","shell.execute_reply":"2025-12-07T16:11:06.168960Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Define Constant","metadata":{}},{"cell_type":"code","source":"TRAIN_PATH = '/kaggle/working/train'\nTEST_PATH  = '/kaggle/working/test1'\nWORK_DIR  = '/kaggle/working/dogs-vs-cats'\n\nBATCH_SIZE = 64\nEPOCHS = 5\nLEARNING_RATE = 1e-3\nNUM_WORKERS = 2\nMOMENTUM = 0.9\nSTEP_SIZE = 7\nGAMMA = 0.1\nRANDOM_STATE = 25\n\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:23:18.163584Z","iopub.execute_input":"2025-12-07T16:23:18.164346Z","iopub.status.idle":"2025-12-07T16:23:18.169215Z","shell.execute_reply.started":"2025-12-07T16:23:18.164313Z","shell.execute_reply":"2025-12-07T16:23:18.168307Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Prepare Dataset","metadata":{}},{"cell_type":"code","source":"for dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n!unzip -q \"/kaggle/input/dogs-vs-cats/train.zip\"\n!unzip -q \"/kaggle/input/dogs-vs-cats/test1.zip\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:11:23.459047Z","iopub.execute_input":"2025-12-07T16:11:23.459363Z","iopub.status.idle":"2025-12-07T16:11:41.899064Z","shell.execute_reply.started":"2025-12-07T16:11:23.459342Z","shell.execute_reply":"2025-12-07T16:11:41.897876Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_files = os.listdir(TRAIN_PATH)\n\ncats = [f for f in train_files if 'cat' in f]\ndogs = [f for f in train_files if 'dog' in f]\n\nprint(f\"Total files of train set: {len(train_files)}\")\n\nprint(f\"Cats: {len(cats)}\")\nprint(f\"Dogs: {len(dogs)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:11:58.258124Z","iopub.execute_input":"2025-12-07T16:11:58.258472Z","iopub.status.idle":"2025-12-07T16:11:58.283642Z","shell.execute_reply.started":"2025-12-07T16:11:58.258424Z","shell.execute_reply":"2025-12-07T16:11:58.282729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_images(image_list, n=10):\n    fig, axes = plt.subplots(2, 5, figsize=(16, 10))\n    axes = axes.ravel()\n    \n    for i, img_name in enumerate(random.sample(image_list, n)):\n        img_path = os.path.join(TRAIN_PATH, img_name)\n        img = Image.open(img_path)\n        axes[i].imshow(img)\n        axes[i].set_title(img_name.split('.')[0].upper())\n        axes[i].axis('off')\n    \n    plt.tight_layout()\n    plt.show()\n\nshow_images(train_files)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:12:04.586046Z","iopub.execute_input":"2025-12-07T16:12:04.586343Z","iopub.status.idle":"2025-12-07T16:12:06.209171Z","shell.execute_reply.started":"2025-12-07T16:12:04.586323Z","shell.execute_reply":"2025-12-07T16:12:06.208169Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Prepare Dataset","metadata":{}},{"cell_type":"code","source":"class DogsVsCatsDataset(Dataset):\n    def __init__(self, root_dir, transform=None):\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        \n    def __len__(self):\n        return len(self.images)\n    \n    def __getitem__(self, idx):\n        img_name = self.images[idx]\n        img_path = os.path.join(self.root_dir, img_name)\n        image = Image.open(img_path).convert('RGB')\n        \n        label = 0 if 'cat' in img_name else 1\n        \n        if self.transform:\n            image = self.transform(image)\n        \n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:13:16.504592Z","iopub.execute_input":"2025-12-07T16:13:16.505320Z","iopub.status.idle":"2025-12-07T16:13:16.511619Z","shell.execute_reply.started":"2025-12-07T16:13:16.505292Z","shell.execute_reply":"2025-12-07T16:13:16.510791Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(15),\n    transforms.ColorJitter(brightness=0.15, contrast=0.15, saturation=0.15),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\nall_files = os.listdir(TRAIN_PATH)\ntrain_files, val_files = train_test_split(all_files, test_size=0.2, random_state=RANDOM_STATE)\n\nos.makedirs('data/train', exist_ok=True)\nos.makedirs('data/val', exist_ok=True)\n\n\nfor f in tqdm(train_files, desc='Copying train files'):\n    shutil.copy(os.path.join(TRAIN_PATH, f), os.path.join('data/train', f))\n\nfor f in tqdm(val_files, desc='Copying val files'):\n    shutil.copy(os.path.join(TRAIN_PATH, f), os.path.join('data/val', f))\n\nprint(f\"Train set samples: {len(train_files)}\")\nprint(f\"Val set samples: {len(val_files)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:14:44.455490Z","iopub.execute_input":"2025-12-07T16:14:44.456284Z","iopub.status.idle":"2025-12-07T16:14:47.341245Z","shell.execute_reply.started":"2025-12-07T16:14:44.456259Z","shell.execute_reply":"2025-12-07T16:14:47.340310Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = DogsVsCatsDataset('data/train', transform=train_transform)\nval_dataset = DogsVsCatsDataset('data/val', transform=val_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=NUM_WORKERS)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS)\n\nprint(f\"Train set batches: {len(train_loader)}\")\nprint(f\"Valid set batches: {len(val_loader)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:15:06.207289Z","iopub.execute_input":"2025-12-07T16:15:06.207647Z","iopub.status.idle":"2025-12-07T16:15:06.233637Z","shell.execute_reply.started":"2025-12-07T16:15:06.207623Z","shell.execute_reply":"2025-12-07T16:15:06.232452Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Define Pre-Trained Model ResNet18 with Augmentation","metadata":{}},{"cell_type":"code","source":"class DogCatClassifier(nn.Module):\n    def __init__(self, num_classes=2, fine_tune_last_block=True):\n        super(DogCatClassifier, self).__init__()\n        self.model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)\n        \n        for param in self.model.parameters():\n            param.requires_grad = False\n        \n        if fine_tune_last_block:\n            for param in self.model.layer4.parameters():\n                param.requires_grad = True\n        \n        in_features = self.model.fc.in_features\n        self.model.fc = nn.Sequential(\n            nn.Linear(in_features, 256),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),\n            nn.Linear(256, num_classes)\n        )\n    \n    def forward(self, x):\n        return self.model(x)\n\nmodel = DogCatClassifier(num_classes=2).to(DEVICE)\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:21:36.745390Z","iopub.execute_input":"2025-12-07T16:21:36.745710Z","iopub.status.idle":"2025-12-07T16:21:37.260766Z","shell.execute_reply.started":"2025-12-07T16:21:36.745689Z","shell.execute_reply":"2025-12-07T16:21:37.259914Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train Model","metadata":{}},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=LEARNING_RATE, momentum=MOMENTUM)\nscheduler = optim.lr_scheduler.StepLR(optimizer, step_size=STEP_SIZE, gamma=GAMMA)\n\nbest_val_acc = 0.0\ndevice = DEVICE\n\ndef train_epoch(model, loader, criterion, optimizer, device=DEVICE):\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    \n    pbar = tqdm(loader, desc='Training')\n    for images, labels in pbar:\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()\n        _, predicted = outputs.max(1)\n        total += labels.size(0)\n        correct += predicted.eq(labels).sum().item()\n        \n        pbar.set_postfix({'loss': running_loss/len(loader), 'acc': 100.*correct/total})\n    \n    epoch_loss = running_loss / len(loader)\n    epoch_acc = 100. * correct / total\n    return epoch_loss, epoch_acc\n\ndef validate(model, loader, criterion, device=DEVICE):\n    model.eval()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    \n    with torch.no_grad():\n        pbar = tqdm(loader, desc='Validation')\n        for images, labels in pbar:\n            images, labels = images.to(device), labels.to(device)\n            \n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            \n            running_loss += loss.item()\n            _, predicted = outputs.max(1)\n            total += labels.size(0)\n            correct += predicted.eq(labels).sum().item()\n            \n            pbar.set_postfix({'loss': running_loss/len(loader), 'acc': 100.*correct/total})\n    \n    epoch_loss = running_loss / len(loader)\n    epoch_acc = 100. * correct / total\n    return epoch_loss, epoch_acc\n\n\nhistory = {\n    'train_loss': [],\n    'train_acc': [],\n    'val_loss': [],\n    'val_acc': []\n}\n\nfor epoch in range(EPOCHS):\n    print(f'\\nEpoch {epoch+1}/{EPOCHS}')\n    print('-' * 50)\n    \n    train_loss, train_acc = train_epoch(model, train_loader, criterion, optimizer, device)\n    history['train_loss'].append(train_loss)\n    history['train_acc'].append(train_acc)\n    \n    val_loss, val_acc = validate(model, val_loader, criterion, device)\n    history['val_loss'].append(val_loss)\n    history['val_acc'].append(val_acc)\n\n    scheduler.step(val_loss)\n    \n    print(f'\\nTrain Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%')\n    print(f'Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.2f}%')\n    \n    if val_acc > best_val_acc:\n        best_val_acc = val_acc\n        torch.save({\n            'epoch': epoch,\n            'model_state_dict': model.state_dict(),\n            'optimizer_state_dict': optimizer.state_dict(),\n            'val_acc': val_acc,\n            'val_loss': val_loss,\n        }, 'best_model.pth')\n        print(f'\\nSaved best model with accuracy: {val_acc:.2f}%')\n\nprint('\\n' + '='*50)\nprint(f'Training completed! Best validation accuracy: {best_val_acc:.2f}%')\nprint('='*50)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T16:24:44.735422Z","iopub.execute_input":"2025-12-07T16:24:44.735862Z","execution_failed":"2025-12-07T16:56:34.596Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}