{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Library","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom PIL import Image\nimport zipfile\nimport os\nimport shutil\nimport torch\nimport torch.nn as nn\nfrom torchvision import transforms, datasets, models\nfrom torch.utils.data import DataLoader, random_split\nimport torch.optim as optim\nfrom sklearn.model_selection import train_test_split\nfrom torch.utils.data import Subset\nfrom collections import Counter","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:52:39.319398Z","iopub.execute_input":"2025-12-03T12:52:39.319609Z","iopub.status.idle":"2025-12-03T12:52:41.796379Z","shell.execute_reply.started":"2025-12-03T12:52:39.319584Z","shell.execute_reply":"2025-12-03T12:52:41.795600Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Dataset","metadata":{}},{"cell_type":"code","source":"# buat folder output\nos.makedirs(\"/kaggle/working/data/train\", exist_ok=True)\n\n# unzip train.zip\nwith zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/train.zip\", 'r') as zip_ref:\n    zip_ref.extractall(\"/kaggle/working/data/train\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:52:41.797355Z","iopub.execute_input":"2025-12-03T12:52:41.797759Z","iopub.status.idle":"2025-12-03T12:52:49.896699Z","shell.execute_reply.started":"2025-12-03T12:52:41.797732Z","shell.execute_reply":"2025-12-03T12:52:49.896124Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base_path = \"/kaggle/working/data/train/train\" \noutput_path = \"/kaggle/working/data/train_split\"\n\nos.makedirs(f\"{output_path}/cat\", exist_ok=True)\nos.makedirs(f\"{output_path}/dog\", exist_ok=True)\n\nfor fname in os.listdir(base_path):\n    if fname.startswith(\"cat\"):\n        shutil.move(os.path.join(base_path, fname), f\"{output_path}/cat/{fname}\")\n    elif fname.startswith(\"dog\"):\n        shutil.move(os.path.join(base_path, fname), f\"{output_path}/dog/{fname}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:52:49.898430Z","iopub.execute_input":"2025-12-03T12:52:49.898631Z","iopub.status.idle":"2025-12-03T12:52:51.378465Z","shell.execute_reply.started":"2025-12-03T12:52:49.898615Z","shell.execute_reply":"2025-12-03T12:52:51.377268Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base_path = \"/kaggle/working/data/train_split\"\n\nclasses = [\"cat\", \"dog\"]\nnum_images = 5\n\nplt.figure(figsize=(15, 6))\n\nindex = 1\nfor cls in classes:\n    class_path = os.path.join(base_path, cls)\n    images = os.listdir(class_path)[:num_images]\n\n    for img_name in images:\n        img_path = os.path.join(class_path, img_name)\n        img = Image.open(img_path)\n\n        plt.subplot(2, num_images, index)\n        plt.imshow(img)\n        plt.title(f\"{cls}\")\n        plt.axis(\"off\")\n\n        index += 1\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:52:51.379110Z","iopub.execute_input":"2025-12-03T12:52:51.379352Z","iopub.status.idle":"2025-12-03T12:52:52.489413Z","shell.execute_reply.started":"2025-12-03T12:52:51.379326Z","shell.execute_reply":"2025-12-03T12:52:52.488516Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform = 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\ndataset = datasets.ImageFolder(\"/kaggle/working/data/train_split\", transform=transform)\n\n# Ambil semua indeks & label\nindices = list(range(len(dataset)))\nlabels = [dataset.samples[i][1] for i in indices]\n\n# Stratified split\ntrain_idx, val_idx = train_test_split(\n    indices,\n    test_size=0.2,\n    stratify=labels,\n    random_state=42\n)\n\nprint(\"=== Distribusi Kelas Overall ===\")\nprint(Counter(labels))\n\nprint(\"\\n=== Distribusi Kelas TRAIN ===\")\nprint(Counter([labels[i] for i in train_idx]))\n\nprint(\"\\n=== Distribusi Kelas VAL ===\")\nprint(Counter([labels[i] for i in val_idx]))\n\n# Buat subset sesuai indeks hasil split\ntrain_ds = Subset(dataset, train_idx)\nval_ds = Subset(dataset, val_idx)\n\n# DataLoader\ntrain_loader = DataLoader(train_ds, batch_size=32, shuffle=True)\nval_loader = DataLoader(val_ds, batch_size=32, shuffle=False)\n\nprint(\"\\nTotal:\", len(dataset))\nprint(\"Train:\", len(train_ds))\nprint(\"Val:\", len(val_ds))\n\n# Mapping\nprint(\"\\nClass mapping:\", dataset.class_to_idx)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:52:52.490229Z","iopub.execute_input":"2025-12-03T12:52:52.490486Z","iopub.status.idle":"2025-12-03T12:52:52.574179Z","shell.execute_reply.started":"2025-12-03T12:52:52.490468Z","shell.execute_reply":"2025-12-03T12:52:52.573451Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Device:\", device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:52:52.574901Z","iopub.execute_input":"2025-12-03T12:52:52.575095Z","iopub.status.idle":"2025-12-03T12:52:53.011015Z","shell.execute_reply.started":"2025-12-03T12:52:52.575077Z","shell.execute_reply":"2025-12-03T12:52:53.010395Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ResNet50\nmodel = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)\n\n# Freeze semua feature layers\nfor param in model.parameters():\n    param.requires_grad = False\n\n# Ganti FC untuk 2 kelas\nnum_features = model.fc.in_features\nmodel.fc = nn.Linear(num_features, 2)\n\nmodel = model.to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.fc.parameters(), lr=1e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:53:02.249184Z","iopub.execute_input":"2025-12-03T12:53:02.249491Z","iopub.status.idle":"2025-12-03T12:53:03.455378Z","shell.execute_reply.started":"2025-12-03T12:53:02.249466Z","shell.execute_reply":"2025-12-03T12:53:03.454619Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(model, train_loader, val_loader, criterion, optimizer, epochs=5):\n    for epoch in range(epochs):\n        model.train()\n        train_loss = 0\n        correct = 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\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n\n            train_loss += loss.item()\n            _, preds = torch.max(outputs, 1)\n            correct += torch.sum(preds == labels).item()\n\n        train_acc = correct / len(train_loader.dataset)\n\n        # ---- Validation ----\n        model.eval()\n        val_loss = 0\n        val_correct = 0\n\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\n                loss = criterion(outputs, labels)\n                val_loss += loss.item()\n\n                _, preds = torch.max(outputs, 1)\n                val_correct += torch.sum(preds == labels).item()\n\n        val_acc = val_correct / len(val_loader.dataset)\n\n        print(f\"Epoch {epoch+1}/{epochs}\")\n        print(f\"  Train Loss: {train_loss/len(train_loader):.4f}  Acc: {train_acc:.4f}\")\n        print(f\"  Val   Loss: {val_loss/len(val_loader):.4f}  Acc: {val_acc:.4f}\\n\")\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:53:09.345195Z","iopub.execute_input":"2025-12-03T12:53:09.345900Z","iopub.status.idle":"2025-12-03T12:53:09.354032Z","shell.execute_reply.started":"2025-12-03T12:53:09.345873Z","shell.execute_reply":"2025-12-03T12:53:09.353109Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train Model","metadata":{}},{"cell_type":"code","source":"model = train_model(model, train_loader, val_loader, criterion, optimizer, epochs=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T12:53:12.271705Z","iopub.execute_input":"2025-12-03T12:53:12.272176Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Save Model","metadata":{}},{"cell_type":"code","source":"torch.save(model.state_dict(), \"resnet50_dogcat.pth\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}