{"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":"# 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\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, zipfile, shutil, torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, random_split\nfrom torchvision import datasets, transforms, models\nimport matplotlib.pyplot as plt\n\n\nwith zipfile.ZipFile('/kaggle/input/competitions/dogs-vs-cats/train.zip', 'r') as z:\n    z.extractall('/kaggle/working/')\nprint(\"Extraction done!\")\n\n# ORGANIZE INTO SUBFOLDERS\nsrc = '/kaggle/working/train'\ndst = '/kaggle/working/train_sorted'\n\nos.makedirs(f'{dst}/cat', exist_ok=True)\nos.makedirs(f'{dst}/dog', exist_ok=True)\n\nfor fname in os.listdir(src):\n    if fname.startswith('cat'):\n        shutil.copy(f'{src}/{fname}', f'{dst}/cat/{fname}')\n    elif fname.startswith('dog'):\n        shutil.copy(f'{src}/{fname}', f'{dst}/dog/{fname}')\n\nprint(\"Cats:\", len(os.listdir(f'{dst}/cat')))\nprint(\"Dogs:\", len(os.listdir(f'{dst}/dog')))\n\n# BASELINE TRANSFORMS\ntransform_base = transforms.Compose([\n    transforms.Resize((128, 128)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.5]*3, [0.5]*3)\n])\n\nfull_dataset = datasets.ImageFolder(dst, transform=transform_base)\nprint(\"Classes:\", full_dataset.classes)\n\ntrain_size = int(0.8 * len(full_dataset))\nval_size   = len(full_dataset) - train_size\ntrain_ds, val_ds = random_split(full_dataset, [train_size, val_size])\n\ntrain_loader = DataLoader(train_ds, batch_size=32, shuffle=True,  num_workers=2)\nval_loader   = DataLoader(val_ds,   batch_size=32, shuffle=False, num_workers=2)\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(\"Device:\", device)\n\n# BASELINE CNN \nclass BaseCNN(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.features = nn.Sequential(\n            nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),\n            nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),\n            nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),\n        )\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(128*16*16, 256), nn.ReLU(), nn.Dropout(0.5),\n            nn.Linear(256, 2)\n        )\n    def forward(self, x):\n        return self.classifier(self.features(x))\n\ncriterion = nn.CrossEntropyLoss()\n\n\ndef train_model(model, optimizer, t_loader, v_loader, epochs, name):\n    history = {'train_acc': [], 'val_acc': [], 'train_loss': [], 'val_loss': []}\n    for epoch in range(epochs):\n        model.train()\n        t_loss, correct, total = 0, 0, 0\n        for imgs, labels in t_loader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            optimizer.zero_grad()\n            out = model(imgs)\n            loss = criterion(out, labels)\n            loss.backward(); optimizer.step()\n            t_loss += loss.item()\n            correct += out.argmax(1).eq(labels).sum().item()\n            total += labels.size(0)\n\n        model.eval()\n        v_loss, vc, vt = 0, 0, 0\n        with torch.no_grad():\n            for imgs, labels in v_loader:\n                imgs, labels = imgs.to(device), labels.to(device)\n                out = model(imgs)\n                v_loss += criterion(out, labels).item()\n                vc += out.argmax(1).eq(labels).sum().item()\n                vt += labels.size(0)\n\n        ta, va = 100*correct/total, 100*vc/vt\n        history['train_acc'].append(ta)\n        history['val_acc'].append(va)\n        history['train_loss'].append(t_loss/len(t_loader))\n        history['val_loss'].append(v_loss/len(v_loader))\n        print(f\"[{name}] Epoch {epoch+1}/{epochs} | Train Acc: {ta:.2f}% | Val Acc: {va:.2f}%\")\n    return history\n\n# TRAIN BASELINE \nbase_model = BaseCNN().to(device)\nbase_opt   = optim.Adam(base_model.parameters(), lr=0.001)\nprint(\"\\n===== Baseline CNN =====\")\nbase_history = train_model(base_model, base_opt, train_loader, val_loader, epochs=10, name=\"BaseCNN\")\n\n\n\n#  TRANSFER LEARNING TRANSFORMS \ntransform_tl = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406],\n                         [0.229, 0.224, 0.225])\n])\n\nfull_ds_tl = datasets.ImageFolder(dst, transform=transform_tl)\ntrain_ds_tl, val_ds_tl = random_split(full_ds_tl, [train_size, val_size])\ntrain_loader_tl = DataLoader(train_ds_tl, batch_size=32, shuffle=True,  num_workers=2)\nval_loader_tl   = DataLoader(val_ds_tl,   batch_size=32, shuffle=False, num_workers=2)\n\n# RESNET18 \nprint(\"\\n===== ResNet18 =====\")\nresnet = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)\nfor p in resnet.parameters(): p.requires_grad = False\nresnet.fc = nn.Linear(resnet.fc.in_features, 2)\nresnet = resnet.to(device)\nresnet_opt = optim.Adam(resnet.fc.parameters(), lr=0.001)\nresnet_history = train_model(resnet, resnet_opt, train_loader_tl, val_loader_tl, epochs=5, name=\"ResNet18\")\n\n# VGG16 \nprint(\"\\n===== VGG16 =====\")\nvgg = models.vgg16(weights=models.VGG16_Weights.DEFAULT)\nfor p in vgg.parameters(): p.requires_grad = False\nvgg.classifier[6] = nn.Linear(4096, 2)\nvgg = vgg.to(device)\nvgg_opt = optim.Adam(vgg.classifier[6].parameters(), lr=0.001)\nvgg_history = train_model(vgg, vgg_opt, train_loader_tl, val_loader_tl, epochs=5, name=\"VGG16\")\n\n\nfig, axes = plt.subplots(1, 2, figsize=(14, 5))\n\naxes[0].plot(base_history['val_acc'],   label='Baseline CNN')\naxes[0].plot(resnet_history['val_acc'], label='ResNet18')\naxes[0].plot(vgg_history['val_acc'],    label='VGG16')\naxes[0].set_title('Validation Accuracy Comparison')\naxes[0].set_xlabel('Epoch'); axes[0].set_ylabel('Accuracy (%)')\naxes[0].legend()\n\nnames     = ['Baseline CNN', 'ResNet18', 'VGG16']\nfinal_acc = [base_history['val_acc'][-1], resnet_history['val_acc'][-1], vgg_history['val_acc'][-1]]\naxes[1].bar(names, final_acc, color=['steelblue', 'orange', 'green'])\naxes[1].set_title('Final Validation Accuracy')\naxes[1].set_ylabel('Accuracy (%)')\nfor i, v in enumerate(final_acc):\n    axes[1].text(i, v + 0.3, f\"{v:.2f}%\", ha='center', fontweight='bold')\n\nplt.tight_layout()\nplt.savefig('comparison.png')\nplt.show()\n\nprint(\"\\n========== FINAL SUMMARY ==========\")\nfor n, a in zip(names, final_acc):\n    print(f\"{n:15s} → Val Accuracy: {a:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-24T17:28:12.510769Z","iopub.execute_input":"2026-06-24T17:28:12.511500Z","iopub.status.idle":"2026-06-24T17:51:24.474925Z","shell.execute_reply.started":"2026-06-24T17:28:12.511472Z","shell.execute_reply":"2026-06-24T17:51:24.474040Z"}},"outputs":[],"execution_count":null}]}