{"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":"nvidiaTeslaT4","dataSources":[{"sourceType":"datasetVersion","sourceId":15300209,"datasetId":9786732,"databundleVersionId":16203903}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torchvision.transforms import v2\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nimport torch.optim as optim\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import (\n    accuracy_score, precision_score, recall_score, f1_score, \n    roc_auc_score, roc_curve, auc, confusion_matrix, ConfusionMatrixDisplay\n)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-21T11:19:14.041746Z","iopub.execute_input":"2026-03-21T11:19:14.042035Z","iopub.status.idle":"2026-03-21T11:19:23.314220Z","shell.execute_reply.started":"2026-03-21T11:19:14.042000Z","shell.execute_reply":"2026-03-21T11:19:23.313618Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_PATH = \"/kaggle/input/datasets/kushubhai/dogcat/catdog/training_set\"\nTEST_PATH = \"/kaggle/input/datasets/kushubhai/dogcat/catdog/test_set\"\nEPOCHS = 15\nBATCH_SIZE = 32\nLEARNING_RATE = 0.0005\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ndata_transforms = v2.Compose([\n    v2.ToImage(),\n    v2.CenterCrop(224),\n    v2.ToDtype(torch.float32, scale=True),\n    v2.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T11:19:23.315508Z","iopub.execute_input":"2026-03-21T11:19:23.315972Z","iopub.status.idle":"2026-03-21T11:19:23.573892Z","shell.execute_reply.started":"2026-03-21T11:19:23.315947Z","shell.execute_reply":"2026-03-21T11:19:23.573104Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CatDogDataset(Dataset):\n    def __init__(self, root_dir, transform=None):\n\n        self.root_dir = root_dir\n        self.transform = transform\n        self.images = []\n        self.labels = []\n\n        self.class_to_idx = {'cats': 0, 'dogs': 1}\n\n        for label_name in ['cats', 'dogs']:\n            class_path = os.path.join(self.root_dir, label_name)\n                \n            for img_name in os.listdir(class_path):\n                if img_name.lower().endswith(('.jpg')):\n                    self.images.append(os.path.join(class_path, img_name))\n                    self.labels.append(self.class_to_idx[label_name])\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        img_path = self.images[idx]\n        image = Image.open(img_path).convert(\"RGB\")\n        label = self.labels[idx]\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T11:19:23.574995Z","iopub.execute_input":"2026-03-21T11:19:23.575296Z","iopub.status.idle":"2026-03-21T11:19:23.687288Z","shell.execute_reply.started":"2026-03-21T11:19:23.575272Z","shell.execute_reply":"2026-03-21T11:19:23.686608Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# class CustomCatDogCNN224(nn.Module):\n#     def __init__(self):\n#         super(CustomCatDogCNN224, self).__init__()\n\n#         self.conv1 = nn.Conv2d(3, 32, 3, padding=1)\n#         self.conv2 = nn.Conv2d(32, 64, 3, padding=1)\n#         self.conv3 = nn.Conv2d(64, 128, 3, padding=1)\n#         self.conv4 = nn.Conv2d(128, 256, 3, padding=1)\n#         self.conv5 = nn.Conv2d(256, 512, 3, padding=1)\n        \n#         self.pool = nn.MaxPool2d(2, 2)\n#         self.bn1 = nn.BatchNorm2d(32)\n#         self.bn2 = nn.BatchNorm2d(64)\n#         self.bn3 = nn.BatchNorm2d(128)\n#         self.bn4 = nn.BatchNorm2d(256)\n#         self.bn5 = nn.BatchNorm2d(512)\n\n#         self.adaptive_pool = nn.AdaptiveAvgPool2d((7, 7))\n\n#         self.fc1 = nn.Linear(512 * 7 * 7, 512)\n#         self.dropout = nn.Dropout(0.5)\n#         self.fc2 = nn.Linear(512, 2)\n\n#     def forward(self, x):\n#         x = self.pool(F.relu(self.bn1(self.conv1(x))))\n#         x = self.pool(F.relu(self.bn2(self.conv2(x))))\n#         x = self.pool(F.relu(self.bn3(self.conv3(x))))\n#         x = self.pool(F.relu(self.bn4(self.conv4(x))))\n#         x = self.pool(F.relu(self.bn5(self.conv5(x))))\n        \n#         x = self.adaptive_pool(x)\n#         x = x.view(x.size(0), -1)\n        \n#         x = F.relu(self.fc1(x))\n#         x = self.dropout(x)\n#         return self.fc2(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T11:19:23.688210Z","iopub.execute_input":"2026-03-21T11:19:23.688488Z","iopub.status.idle":"2026-03-21T11:19:23.701339Z","shell.execute_reply.started":"2026-03-21T11:19:23.688466Z","shell.execute_reply":"2026-03-21T11:19:23.700620Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CustomCatDogCNN224(nn.Module): # 224x224x3\n    def __init__(self):\n        super(CustomCatDogCNN224, self).__init__()\n        self.features = nn.Sequential(\n            nn.Conv2d(3, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2),\n            nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(2),\n            nn.Conv2d(64, 128, 3, padding=1), nn.BatchNorm2d(128), nn.ReLU(), nn.MaxPool2d(2),\n            nn.Conv2d(128, 256, 3, padding=1), nn.BatchNorm2d(256), nn.ReLU(), nn.MaxPool2d(2),\n            nn.Conv2d(256, 512, 3, padding=1), nn.BatchNorm2d(512), nn.ReLU(), nn.MaxPool2d(2)\n        )\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(512 * 7 * 7, 512),\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(512, 2)\n        )\n\n    def forward(self, x):\n        return self.classifier(self.features(x))\n\nmodel = CustomCatDogCNN224().to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T11:19:23.703175Z","iopub.execute_input":"2026-03-21T11:19:23.703438Z","iopub.status.idle":"2026-03-21T11:19:23.717054Z","shell.execute_reply.started":"2026-03-21T11:19:23.703416Z","shell.execute_reply":"2026-03-21T11:19:23.716239Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T11:19:24.189740Z","iopub.execute_input":"2026-03-21T11:19:24.190115Z","iopub.status.idle":"2026-03-21T11:19:24.196235Z","shell.execute_reply.started":"2026-03-21T11:19:24.190088Z","shell.execute_reply":"2026-03-21T11:19:24.195353Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(model, train_loader, val_loader, epochs=10):\n    history = {'train_loss': [], 'val_loss': [], 'train_acc': [], 'val_acc': []}\n    \n    for epoch in range(epochs):\n        model.train()\n        train_loss, train_correct = 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            train_loss += loss.item() * images.size(0)\n            _, preds = torch.max(outputs, 1)\n            train_correct += torch.sum(preds == labels.data)\n\n        model.eval()\n        val_loss, val_correct = 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                val_loss += loss.item() * images.size(0)\n                _, preds = torch.max(outputs, 1)\n                val_correct += torch.sum(preds == labels.data)\n\n        train_loss /= len(train_loader.dataset)\n        train_acc = train_correct.double() / len(train_loader.dataset)\n        val_loss /= len(val_loader.dataset)\n        val_acc = val_correct.double() / len(val_loader.dataset)\n        \n        history['train_loss'].append(train_loss)\n        history['val_loss'].append(val_loss)\n        history['train_acc'].append(train_acc.item())\n        history['val_acc'].append(val_acc.item())\n        \n        print(f\"Epoch {epoch+1}/{epochs} | Train Loss: {train_loss:.4f} | Val Acc: {val_acc:.4f}\")\n        \n    return history\n\ndef plot_results(history):\n    epochs = range(1, len(history['train_loss']) + 1)\n    \n    plt.figure(figsize=(12, 5))\n\n    plt.subplot(1, 2, 1)\n    plt.plot(epochs, history['train_loss'], 'r-', label='Training Loss')\n    plt.plot(epochs, history['val_loss'], 'b-', label='Validation Loss')\n    plt.title('Loss Over Epochs')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.legend()\n\n    plt.subplot(1, 2, 2)\n    plt.plot(epochs, history['train_acc'], 'r-', label='Training Acc')\n    plt.plot(epochs, history['val_acc'], 'b-', label='Validation Acc')\n    plt.title('Accuracy Over Epochs')\n    plt.xlabel('Epochs')\n    plt.ylabel('Accuracy')\n    plt.legend()\n    \n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T11:19:23.717869Z","iopub.execute_input":"2026-03-21T11:19:23.718056Z","iopub.status.idle":"2026-03-21T11:19:23.730449Z","shell.execute_reply.started":"2026-03-21T11:19:23.718038Z","shell.execute_reply":"2026-03-21T11:19:23.729773Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr = LEARNING_RATE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T11:19:23.731307Z","iopub.execute_input":"2026-03-21T11:19:23.731616Z","iopub.status.idle":"2026-03-21T11:19:24.188761Z","shell.execute_reply.started":"2026-03-21T11:19:23.731550Z","shell.execute_reply":"2026-03-21T11:19:24.187917Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = CatDogDataset(root_dir = TRAIN_PATH, transform = data_transforms)\ntrain_loader = DataLoader(train_dataset, batch_size = BATCH_SIZE, shuffle = True)\ntest_dataset = CatDogDataset(root_dir = TEST_PATH, transform = data_transforms)\ntest_loader = DataLoader(test_dataset, batch_size = BATCH_SIZE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T10:30:48.197481Z","iopub.execute_input":"2026-03-21T10:30:48.197797Z","iopub.status.idle":"2026-03-21T10:30:48.317397Z","shell.execute_reply.started":"2026-03-21T10:30:48.197761Z","shell.execute_reply":"2026-03-21T10:30:48.316386Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = train_model(model, train_loader, test_loader, epochs = EPOCHS)\nplot_results(history)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T10:30:48.318681Z","iopub.execute_input":"2026-03-21T10:30:48.319006Z","iopub.status.idle":"2026-03-21T10:45:03.854550Z","shell.execute_reply.started":"2026-03-21T10:30:48.318971Z","shell.execute_reply":"2026-03-21T10:45:03.853872Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_and_show(model, transform, class_names, device):\n    sample_images = {\n        \"cat\": os.path.join(TEST_PATH, \"cats\", os.listdir(os.path.join(TEST_PATH, \"cats\"))[0]),\n        \"dog\": os.path.join(TEST_PATH, \"dogs\", os.listdir(os.path.join(TEST_PATH, \"dogs\"))[0])\n    }\n\n    model.eval()\n    plt.figure(figsize=(10, 5))\n\n    for i, (label, img_path) in enumerate(sample_images.items()):\n        raw_img = Image.open(img_path).convert(\"RGB\")\n        input_tensor = transform(raw_img).unsqueeze(0).to(device)\n\n        with torch.no_grad():\n            output = model(input_tensor)\n            prob = F.softmax(output, dim=1)\n            conf, pred = torch.max(prob, 1)\n\n        plt.subplot(1, 2, i+1)\n        plt.imshow(raw_img)\n        color = \"green\" if class_names[pred.item()] == label else \"red\"\n        plt.title(f\"Actual: {label}\\nPred: {class_names[pred.item()]} ({conf.item()*100:.2f}%)\", color=color)\n        plt.axis(\"off\")\n    \n    plt.tight_layout()\n    plt.show()\n\nclass_names = ['cats', 'dogs']\npredict_and_show(model, data_transforms, class_names, device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T10:45:03.855591Z","iopub.execute_input":"2026-03-21T10:45:03.856006Z","iopub.status.idle":"2026-03-21T10:45:04.156519Z","shell.execute_reply.started":"2026-03-21T10:45:03.855967Z","shell.execute_reply":"2026-03-21T10:45:04.155671Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate_and_plot(model, test_loader, device):\n    model.eval()\n    all_preds = []\n    all_labels = []\n    all_probs = []\n\n    with torch.no_grad():\n        for images, labels in test_loader:\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n\n            probs = F.softmax(outputs, dim=1)[:, 1]\n            _, preds = torch.max(outputs, 1)\n\n            all_preds.extend(preds.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n            all_probs.extend(probs.cpu().numpy())\n\n    acc = accuracy_score(all_labels, all_preds)\n    prec = precision_score(all_labels, all_preds)\n    rec = recall_score(all_labels, all_preds)\n    f1 = f1_score(all_labels, all_preds)\n    roc_auc = roc_auc_score(all_labels, all_probs)\n\n    print(f\"Final Metrics:\\nAccuracy: {acc:.4f}\")\n    print(f\"Precision: {prec:.4f}\")\n    print(f\"Recall: {rec:.4f}\")\n    print(f\"F1: {f1:.4f}\")\n    print(f\"ROC AUC: {roc_auc:.4f}\")\n\n    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))\n\n    cm = confusion_matrix(all_labels, all_preds)\n    disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=['Cat', 'Dog'])\n    disp.plot(cmap='Blues', ax=ax1, colorbar=False)\n    ax1.set_title(\"Confusion Matrix\")\n\n    fpr, tpr, _ = roc_curve(all_labels, all_probs)\n    ax2.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})')\n    ax2.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\n    ax2.set_xlim([0.0, 1.0])\n    ax2.set_ylim([0.0, 1.05])\n    ax2.set_xlabel('False Positive Rate (FPR)')\n    ax2.set_ylabel('True Positive Rate (TPR)')\n    ax2.set_title('Receiver Operating Characteristic (ROC)')\n    ax2.legend(loc=\"lower right\")\n    ax2.grid(alpha=0.3)\n\n    plt.tight_layout()\n    plt.show()\n\nevaluate_and_plot(model, test_loader, device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-21T10:45:04.157564Z","iopub.execute_input":"2026-03-21T10:45:04.157924Z","iopub.status.idle":"2026-03-21T10:45:11.444049Z","shell.execute_reply.started":"2026-03-21T10:45:04.157900Z","shell.execute_reply":"2026-03-21T10:45:11.443406Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visit torchvision.models for pretrained models\n# Visit tensorflow playground to try ANN without code\n# Don't forget to upvote the notebook if the workshop helped :)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}