{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","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":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Initial Setup: Libraries, device and seed","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torchvision import models, transforms\nfrom torch.utils.data import Dataset, DataLoader, random_split\nfrom torchmetrics.classification import BinaryAccuracy\nfrom torchinfo import summary\n\nimport os\nfrom PIL import Image\nimport matplotlib.pyplot as plt\n\nimport random\n\nimport zipfile","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:48:34.610910Z","iopub.execute_input":"2025-06-05T05:48:34.611132Z","iopub.status.idle":"2025-06-05T05:48:53.620101Z","shell.execute_reply.started":"2025-06-05T05:48:34.611109Z","shell.execute_reply":"2025-06-05T05:48:53.619481Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# setup the device (GPU or CPU)\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:50:41.791356Z","iopub.execute_input":"2025-06-05T05:50:41.791679Z","iopub.status.idle":"2025-06-05T05:50:41.796204Z","shell.execute_reply.started":"2025-06-05T05:50:41.791654Z","shell.execute_reply":"2025-06-05T05:50:41.795523Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# set seed for reproduceability\ndef manual_seed(seed=42):\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.Generator().manual_seed(seed)\n\nmanual_seed(seed=23)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:33.343917Z","iopub.execute_input":"2025-06-05T05:53:33.344605Z","iopub.status.idle":"2025-06-05T05:53:33.349326Z","shell.execute_reply.started":"2025-06-05T05:53:33.344562Z","shell.execute_reply":"2025-06-05T05:53:33.348634Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Image Data Preparation","metadata":{}},{"cell_type":"markdown","source":"## 1. Extract the \"train.zip\" into kanggle working directory","metadata":{}},{"cell_type":"code","source":"# location of train.zip\npath_to_zip_file = \"/kaggle/input/dogs-vs-cats/train.zip\"\n\n# target directory\ndirectory_to_extract_to = \"/kaggle/working/\"\n\n# running the following code will extract the \"train.zip\" into \"/kaggle/working/train\"\nwith zipfile.ZipFile(path_to_zip_file, 'r') as zip_ref:\n    zip_ref.extractall(directory_to_extract_to)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:50:46.583282Z","iopub.execute_input":"2025-06-05T05:50:46.583905Z","iopub.status.idle":"2025-06-05T05:50:56.599880Z","shell.execute_reply.started":"2025-06-05T05:50:46.583877Z","shell.execute_reply":"2025-06-05T05:50:56.599311Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Create a custom Dataset's class","metadata":{}},{"cell_type":"code","source":"class CatsAndDogsDataset(Dataset):\n\n    def __init__(self, root_dir, transform=None):\n        self.root_dir = root_dir\n        self.image_files = [f for f in os.listdir(root_dir) if f.endswith(\".jpg\")]\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_files)   \n    \n    def __getitem__(self, index):\n        \n        # filename and filepath\n        filename = self.image_files[index]\n        filepath = os.path.join(self.root_dir, filename)\n\n        # load image\n        image = Image.open(filepath).convert(\"RGB\")\n\n        # Label: 0 for cat, 1 for dog\n        label = 0 if \"cat\" in filename.lower() else 1\n\n        # Apply transform\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:41.344496Z","iopub.execute_input":"2025-06-05T05:53:41.345207Z","iopub.status.idle":"2025-06-05T05:53:41.350312Z","shell.execute_reply.started":"2025-06-05T05:53:41.345181Z","shell.execute_reply":"2025-06-05T05:53:41.349617Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Turn the image files into PyTorch Dataset","metadata":{}},{"cell_type":"code","source":"# Data directory\nroot_dir = \"/kaggle/working/train\"\n\n# Pretrained model parameters and transformation\nweights = models.EfficientNet_B0_Weights.DEFAULT\ndata_transfoms = weights.transforms()\nprint(data_transfoms)\n\n# train dataset\ntrain_dataset = CatsAndDogsDataset(root_dir=root_dir, transform=data_transfoms)\n\n# split train dataset to train_set and val_set\ntotal_size = len(train_dataset)\ntrain_size = int(0.8*total_size)\nval_size = total_size - train_size\ntrain_set, val_set = random_split(train_dataset, [train_size, val_size])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:42.931319Z","iopub.execute_input":"2025-06-05T05:53:42.932014Z","iopub.status.idle":"2025-06-05T05:53:42.954543Z","shell.execute_reply.started":"2025-06-05T05:53:42.931990Z","shell.execute_reply":"2025-06-05T05:53:42.953830Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Create PyTorch DataLoader and preview random preprocessed input image","metadata":{}},{"cell_type":"code","source":"# Create DataLoaders\nBATCH_SIZE = 32\ntrain_loader = DataLoader(train_set, batch_size=BATCH_SIZE, shuffle=True)\nval_loader = DataLoader(val_set, batch_size=BATCH_SIZE)\n\n# Visualize transformed image\nimg_batch, label_batch = next(iter(train_loader))\n\ndef show_random_image(img_batch, label_batch):\n    random_index = random.randint(0,31)\n    img_batch_preview = img_batch[random_index,:,:,:].permute(1,2,0)\n    label_batch_preview = label_batch[random_index]\n    label = \"cat\" if label_batch_preview == 0 else \"dog\"\n    plt.imshow(img_batch_preview)\n    plt.title(f\"label:{label}\")\n    plt.axis(\"off\")\n    plt.show()\n\nshow_random_image(img_batch, label_batch)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:44.784361Z","iopub.execute_input":"2025-06-05T05:53:44.784862Z","iopub.status.idle":"2025-06-05T05:53:45.046736Z","shell.execute_reply.started":"2025-06-05T05:53:44.784837Z","shell.execute_reply":"2025-06-05T05:53:45.046062Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# CNN Model Setup: Resnet18","metadata":{}},{"cell_type":"markdown","source":"## 1. Download and preview the Resnet18","metadata":{}},{"cell_type":"code","source":"# download Resnet18 CNN Model\nmodel = models.efficientnet_b0(weights=weights).to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:48.129406Z","iopub.execute_input":"2025-06-05T05:53:48.129775Z","iopub.status.idle":"2025-06-05T05:53:48.269783Z","shell.execute_reply.started":"2025-06-05T05:53:48.129752Z","shell.execute_reply":"2025-06-05T05:53:48.269137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# preview the Resnet18 Architecture: input and ouput of each layers\nsummary(\n    model=model,\n    input_size=(32,3,224,224),\n    col_names=[\"input_size\",\"output_size\",\"num_params\",\"trainable\"],\n    col_width=20,\n    row_settings = ['var_names']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:49.481722Z","iopub.execute_input":"2025-06-05T05:53:49.482234Z","iopub.status.idle":"2025-06-05T05:53:49.584737Z","shell.execute_reply.started":"2025-06-05T05:53:49.482212Z","shell.execute_reply":"2025-06-05T05:53:49.584159Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Freeze the model's parameters","metadata":{}},{"cell_type":"code","source":"# first, freeze the model parameter in \"features\" layer\nfor param in model.features.parameters():\n    param.requires_grad = False\n\n# determine the output shape based on the total number of class\noutput_shape = 1\n\n# redifine the \"classifier\" layer\nmodel.classifier = nn.Sequential(\n    nn.Dropout(p=0.2, inplace=True),\n    nn.Linear(in_features=1280,\n             out_features=output_shape, # we only change this part\n             bias=True)\n).to(device)\n\n# preview the model architecture after we modify the \"classifier\" layer\nsummary(\n    model = model,\n    input_size = (32, 3, 224, 224),\n    col_names = [\"input_size\", \"output_size\", \"num_params\", \"trainable\"],\n    col_width = 20,\n    row_settings = [\"var_names\"]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:51.893991Z","iopub.execute_input":"2025-06-05T05:53:51.894511Z","iopub.status.idle":"2025-06-05T05:53:52.181183Z","shell.execute_reply.started":"2025-06-05T05:53:51.894489Z","shell.execute_reply":"2025-06-05T05:53:52.180628Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Training","metadata":{}},{"cell_type":"code","source":"# Loss, Optimizer and Accuracy\ncriterion = nn.BCEWithLogitsLoss()\n\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\ntrain_acc_metric = BinaryAccuracy().to(device)\n\nval_acc_metric = BinaryAccuracy().to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:54.359786Z","iopub.execute_input":"2025-06-05T05:53:54.360041Z","iopub.status.idle":"2025-06-05T05:53:54.367232Z","shell.execute_reply.started":"2025-06-05T05:53:54.360023Z","shell.execute_reply":"2025-06-05T05:53:54.366701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Training\n\n# tracking loss and accuracy\ntrain_losses, val_losses = [], []\ntrain_accuracies, val_accuracies = [], []\n\n# training loop\n\nnum_epochs = 50\nfor epoch in range(num_epochs):\n\n    #------------TRAINING---------------\n    \n    model.train()\n\n    train_loss = 0.0\n    train_acc_metric.reset()\n\n    for images, labels in train_loader:\n        \n        images = images.to(device)\n        labels = labels.to(device).float().unsqueeze(1)\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        train_loss += loss.item() * images.size(0)\n        train_acc_metric.update(torch.sigmoid(outputs), labels)\n    \n    avg_train_loss = train_loss / len(train_loader.dataset)\n    train_accuracy = train_acc_metric.compute().item()\n\n    train_losses.append(avg_train_loss)\n    train_accuracies.append(train_accuracy)\n\n    #----------Validation-----------------\n    \n    model.eval()\n\n    val_loss = 0.0\n\n    val_acc_metric.reset()\n\n    with torch.inference_mode():\n        \n        for images, labels in val_loader:\n\n            images = images.to(device)\n            labels = labels.to(device).float().unsqueeze(1)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            val_loss += loss.item() * images.size(0)\n            val_acc_metric.update(torch.sigmoid(outputs), labels)\n\n    avg_val_loss = val_loss / len(val_loader.dataset)\n    val_accuracy = val_acc_metric.compute().item()\n\n    val_losses.append(avg_val_loss)\n    val_accuracies.append(val_accuracy)\n\n    print(f\"Epoch [{epoch+1}/{num_epochs}] \"\n          f\"Train Loss: {avg_train_loss:.4f}, Train Acc: {train_accuracy:.4f}, \"\n          f\"Val Loss: {avg_val_loss:.4f}, Val Acc: {val_accuracy:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T05:53:55.885869Z","iopub.execute_input":"2025-06-05T05:53:55.886366Z","iopub.status.idle":"2025-06-05T08:01:37.849463Z","shell.execute_reply.started":"2025-06-05T05:53:55.886341Z","shell.execute_reply":"2025-06-05T08:01:37.848817Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Saves the loss into csv file","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\nresult = pd.DataFrame({\"train_loss\":train_losses,\n                       \"train_acc\":train_accuracies,\n                       \"val_loss\":val_losses,\n                       \"val_acc\":val_accuracies})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T08:06:01.883038Z","iopub.execute_input":"2025-06-05T08:06:01.883294Z","iopub.status.idle":"2025-06-05T08:06:01.887605Z","shell.execute_reply.started":"2025-06-05T08:06:01.883276Z","shell.execute_reply":"2025-06-05T08:06:01.886989Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"result.head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T08:06:10.727659Z","iopub.execute_input":"2025-06-05T08:06:10.727899Z","iopub.status.idle":"2025-06-05T08:06:10.753284Z","shell.execute_reply.started":"2025-06-05T08:06:10.727882Z","shell.execute_reply":"2025-06-05T08:06:10.752743Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# save to Kaggle working directory\nresult.to_csv(\"/kaggle/working/result_20250605_efficientnet.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T08:07:19.127229Z","iopub.execute_input":"2025-06-05T08:07:19.127997Z","iopub.status.idle":"2025-06-05T08:07:19.141458Z","shell.execute_reply.started":"2025-06-05T08:07:19.127971Z","shell.execute_reply":"2025-06-05T08:07:19.140748Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Performances","metadata":{}},{"cell_type":"code","source":"# ----- Plotting -----\nepochs = range(1, num_epochs + 1)\n\nplt.figure(figsize=(12, 5))\n\n# Loss plot\nplt.subplot(1, 2, 1)\nplt.plot(epochs, train_losses, 'bo-', label='Train Loss')\nplt.plot(epochs, val_losses, 'ro-', label='Val Loss')\nplt.title('Loss per Epoch')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\n\n# Accuracy plot\nplt.subplot(1, 2, 2)\nplt.plot(epochs, train_accuracies, 'bo-', label='Train Acc')\nplt.plot(epochs, val_accuracies, 'ro-', label='Val Acc')\nplt.title('Accuracy per Epoch')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-05T08:06:16.447292Z","iopub.execute_input":"2025-06-05T08:06:16.448011Z","iopub.status.idle":"2025-06-05T08:06:16.802028Z","shell.execute_reply.started":"2025-06-05T08:06:16.447985Z","shell.execute_reply":"2025-06-05T08:06:16.801443Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# THANK YOU","metadata":{}}]}