{"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":"competition","sourceId":5441,"databundleVersionId":38425},{"sourceType":"modelInstanceVersion","sourceId":857852,"databundleVersionId":17057428,"modelInstanceId":652046,"modelId":663997},{"sourceType":"modelInstanceVersion","sourceId":857903,"databundleVersionId":17058071,"modelInstanceId":652085,"modelId":664035},{"sourceType":"modelInstanceVersion","sourceId":852001,"databundleVersionId":16970284,"modelInstanceId":647618,"modelId":659589}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 1. Read data & visualize","metadata":{}},{"cell_type":"code","source":"import zipfile\nimport warnings\n\nwarnings.filterwarnings(\"ignore\")\n\nwith zipfile.ZipFile(\"/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/train.zip\", \"r\") as z:\n    z.extractall(\"/kaggle/working\")\n\nwith zipfile.ZipFile(\"/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/test.zip\", \"r\") as z:\n    z.extractall(\"/kaggle/working\")\n\nprint(\"finish\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:14:52.547630Z","iopub.execute_input":"2026-05-04T13:14:52.547921Z","iopub.status.idle":"2026-05-04T13:15:09.496233Z","shell.execute_reply.started":"2026-05-04T13:14:52.547898Z","shell.execute_reply":"2026-05-04T13:15:09.495397Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\ntrain = os.listdir(\"/kaggle/working/train\")\nprint(type(train))\nprint(len(train))\nprint(train[:5])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:09.498073Z","iopub.execute_input":"2026-05-04T13:15:09.498387Z","iopub.status.idle":"2026-05-04T13:15:09.519532Z","shell.execute_reply.started":"2026-05-04T13:15:09.498364Z","shell.execute_reply":"2026-05-04T13:15:09.518716Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test = os.listdir(\"/kaggle/working/test\")\nprint(len(test))\nprint(test[:5])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:09.520810Z","iopub.execute_input":"2026-05-04T13:15:09.521143Z","iopub.status.idle":"2026-05-04T13:15:09.535404Z","shell.execute_reply.started":"2026-05-04T13:15:09.521107Z","shell.execute_reply":"2026-05-04T13:15:09.534513Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n\nTRAIN_PATH = \"/kaggle/working/train\"\n\nplt.figure(figsize = (10,10))\nfor i in range(9):\n    plt.subplot(3,3,i + 1)\n    path = os.path.join(TRAIN_PATH, train[i])\n    img = mpimg.imread(path)\n    print(img.shape)\n    plt.imshow(img)\n    plt.axis(\"off\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:09.536351Z","iopub.execute_input":"2026-05-04T13:15:09.536606Z","iopub.status.idle":"2026-05-04T13:15:10.247115Z","shell.execute_reply.started":"2026-05-04T13:15:09.536578Z","shell.execute_reply":"2026-05-04T13:15:10.246008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cat = 0\nfor img_path in train:\n    if \"cat\" in img_path:\n        cat += 1\ndog = len(train) - cat\n\nprint(\"Target distribution\")\nprint(f\"Cat: {cat * 100 / len(train)}%\")\nprint(f\"Dog: {dog * 100 / len(train)}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:10.248205Z","iopub.execute_input":"2026-05-04T13:15:10.248570Z","iopub.status.idle":"2026-05-04T13:15:10.257590Z","shell.execute_reply.started":"2026-05-04T13:15:10.248546Z","shell.execute_reply":"2026-05-04T13:15:10.256725Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import shutil\n\nos.makedirs(os.path.join(TRAIN_PATH, \"cat\"), exist_ok = True)\nos.makedirs(os.path.join(TRAIN_PATH, \"dog\"), exist_ok = True)\n\nfor f in train:\n    if f.startswith(\"cat\"):\n        shutil.move(os.path.join(TRAIN_PATH, f),\n                   os.path.join(TRAIN_PATH, \"cat\", f)\n                   )\n    elif f.startswith(\"dog\"):\n        shutil.move(os.path.join(TRAIN_PATH, f),\n                   os.path.join(TRAIN_PATH, \"dog\", f)\n                   )\nprint(\"Done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:10.258546Z","iopub.execute_input":"2026-05-04T13:15:10.258895Z","iopub.status.idle":"2026-05-04T13:15:10.880229Z","shell.execute_reply.started":"2026-05-04T13:15:10.258872Z","shell.execute_reply":"2026-05-04T13:15:10.879553Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_folder = os.listdir(TRAIN_PATH)\nprint(train_folder)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:10.882187Z","iopub.execute_input":"2026-05-04T13:15:10.882388Z","iopub.status.idle":"2026-05-04T13:15:10.886435Z","shell.execute_reply.started":"2026-05-04T13:15:10.882369Z","shell.execute_reply":"2026-05-04T13:15:10.885765Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cat_folder = os.listdir(os.path.join(TRAIN_PATH, \"cat\"))\ndog_folder = os.listdir(os.path.join(TRAIN_PATH, \"dog\"))\n\nprint(f\"Number of cat's image: {len(cat_folder)}\")\nprint(f\"Number of dog's image: {len(dog_folder)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:10.887330Z","iopub.execute_input":"2026-05-04T13:15:10.887825Z","iopub.status.idle":"2026-05-04T13:15:10.915562Z","shell.execute_reply.started":"2026-05-04T13:15:10.887791Z","shell.execute_reply":"2026-05-04T13:15:10.914871Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2. Prepare dataset","metadata":{}},{"cell_type":"code","source":"import torch\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import DataLoader, random_split, Subset","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:10.916481Z","iopub.execute_input":"2026-05-04T13:15:10.916872Z","iopub.status.idle":"2026-05-04T13:15:23.887587Z","shell.execute_reply.started":"2026-05-04T13:15:10.916851Z","shell.execute_reply":"2026-05-04T13:15:23.886974Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.RandomResizedCrop(224, scale = (0.7, 1.0)),\n    transforms.RandomHorizontalFlip(p = 0.5),\n    transforms.RandomRotation(degrees = 15),\n    transforms.ColorJitter(\n        brightness = 0.3, saturation = 0.2,\n        contrast = 0.3, hue = 0.05\n    ),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224), \n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:23.888473Z","iopub.execute_input":"2026-05-04T13:15:23.888836Z","iopub.status.idle":"2026-05-04T13:15:23.894706Z","shell.execute_reply.started":"2026-05-04T13:15:23.888801Z","shell.execute_reply":"2026-05-04T13:15:23.893980Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = datasets.ImageFolder(\n    root = TRAIN_PATH,\n    transform = train_transform\n)\n\nprint(\"Class to index mapping:\", train_dataset.class_to_idx)\nprint(\"Classes:\", train_dataset.classes)\n\nimg, label = train_dataset[0]\nprint(type(img))\nprint(\"Label index:\", label)\nprint(\"Label name:\", train_dataset.classes[label])\n\ndataloader = DataLoader(train_dataset, batch_size = 8, shuffle = True)\nimgs, labels = next(iter(dataloader))\n\nfig, axes = plt.subplots(2,4, figsize = (12, 6))\nfor i, ax in enumerate(axes.flat):\n    img = imgs[i].permute(1, 2, 0).numpy()\n    img = img * [0.229, 0.224, 0.225] + [0.485, 0.456, 0.406]  # de-normalize\n    ax.imshow(img.clip(0, 1))\n    ax.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:23.895771Z","iopub.execute_input":"2026-05-04T13:15:23.896242Z","iopub.status.idle":"2026-05-04T13:15:24.559050Z","shell.execute_reply.started":"2026-05-04T13:15:23.896213Z","shell.execute_reply":"2026-05-04T13:15:24.558140Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = datasets.ImageFolder(TRAIN_PATH, transform = train_transform)\nval_dataset = datasets.ImageFolder(TRAIN_PATH, transform = val_transform)\n\ntrain_size = int(0.9 * len(train_dataset))\nval_size = len(train_dataset) - train_size\n\ngenerator = torch.Generator().manual_seed(42)\ntrain_indicies, val_indicies = random_split(\n    range(len(train_dataset)),\n    [train_size, val_size],\n    generator = generator\n)\n\ntrain_set = Subset(train_dataset, train_indicies)\nval_set = Subset(val_dataset, val_indicies)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:24.560117Z","iopub.execute_input":"2026-05-04T13:15:24.560770Z","iopub.status.idle":"2026-05-04T13:15:24.668921Z","shell.execute_reply.started":"2026-05-04T13:15:24.560744Z","shell.execute_reply":"2026-05-04T13:15:24.668073Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# train_labels = torch.tensor([label for _, label in train_set])\n# val_labels = torch.tensor([label for _, label in val_set])\n\n# cat_train = (train_labels == 0).sum().item()\n# dog_train = (train_labels == 1).sum().item()\n\n# cat_val = (val_labels == 0).sum().item()\n# dog_val = (val_labels == 1).sum().item()\n\n# print(\"Distribution after split\")\n# print(f\"Train: Cat = {cat_train * 100 / train_size}%  |  Dog = {dog_train * 100 / train_size}%\")\n# print(f\"Val: Cat = {cat_val * 100 / val_size}%  |  Dog = {dog_val * 100 / val_size}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:24.669930Z","iopub.execute_input":"2026-05-04T13:15:24.670238Z","iopub.status.idle":"2026-05-04T13:15:24.673737Z","shell.execute_reply.started":"2026-05-04T13:15:24.670207Z","shell.execute_reply":"2026-05-04T13:15:24.672883Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Distribution after split\n- Train: Cat = 49.96%  |  Dog = 50.04%\n- Val: Cat = 50.36%  |  Dog = 49.64%","metadata":{}},{"cell_type":"code","source":"train_dataloader = DataLoader(train_set, batch_size = 64, shuffle = True)\nval_dataloader = DataLoader(val_set, batch_size = 64)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:24.674601Z","iopub.execute_input":"2026-05-04T13:15:24.675438Z","iopub.status.idle":"2026-05-04T13:15:24.694343Z","shell.execute_reply.started":"2026-05-04T13:15:24.675412Z","shell.execute_reply":"2026-05-04T13:15:24.693733Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset\nfrom PIL import Image\n\nclass TestDataset(Dataset):\n    def __init__(self, root, transform = None):\n        image_dirs = []\n        self.transform = transform\n        self.root = root\n        \n        try:\n            image_dirs = os.listdir(root)\n        except Exception as e:\n            raise e\n\n        self.image_dirs = sorted(image_dirs, key = lambda x: int(x.split(\".\")[0]))\n        print(f\"First 5 image paths: {self.image_dirs[:5]}\")\n\n    def __len__(self):\n        return len(self.image_dirs)\n\n    def __getitem__(self, idx):\n        img_path = os.path.join(self.root, self.image_dirs[idx])\n        \n        try:\n            image = Image.open(img_path).convert(\"RGB\")\n        except:\n            print(f\"Error at image {idx}, skip to the next imamge...\")\n            next_idx = (idx + 1) % len(self.image_dirs)\n            image = self.__getitem__(next_idx)\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image\n\ntest_dataset = TestDataset(root = \"/kaggle/working/test\", transform = val_transform)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:24.695327Z","iopub.execute_input":"2026-05-04T13:15:24.695970Z","iopub.status.idle":"2026-05-04T13:15:24.725957Z","shell.execute_reply.started":"2026-05-04T13:15:24.695948Z","shell.execute_reply":"2026-05-04T13:15:24.725190Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dataloader = DataLoader(test_dataset, batch_size = 8)\nimgs = next(iter(test_dataloader))\n\nfig, axes = plt.subplots(2,4, figsize = (12, 6))\nfor i, ax in enumerate(axes.flat):\n    img = imgs[i].permute(1, 2, 0).numpy()\n    img = img * [0.229, 0.224, 0.225] + [0.485, 0.456, 0.406]  # de-normalize\n    ax.imshow(img.clip(0, 1))\n    ax.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:24.726881Z","iopub.execute_input":"2026-05-04T13:15:24.727158Z","iopub.status.idle":"2026-05-04T13:15:25.086179Z","shell.execute_reply.started":"2026-05-04T13:15:24.727126Z","shell.execute_reply":"2026-05-04T13:15:25.085235Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dataloader = DataLoader(test_dataset, batch_size = 64)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.087190Z","iopub.execute_input":"2026-05-04T13:15:25.087518Z","iopub.status.idle":"2026-05-04T13:15:25.091614Z","shell.execute_reply.started":"2026-05-04T13:15:25.087494Z","shell.execute_reply":"2026-05-04T13:15:25.090829Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"full_train_dataloader = DataLoader(train_dataset, batch_size = 64)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.092736Z","iopub.execute_input":"2026-05-04T13:15:25.093132Z","iopub.status.idle":"2026-05-04T13:15:25.107007Z","shell.execute_reply.started":"2026-05-04T13:15:25.093094Z","shell.execute_reply":"2026-05-04T13:15:25.106242Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. Train & Evaluate","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\nimport torchvision.models as models","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.107925Z","iopub.execute_input":"2026-05-04T13:15:25.108257Z","iopub.status.idle":"2026-05-04T13:15:25.121102Z","shell.execute_reply.started":"2026-05-04T13:15:25.108213Z","shell.execute_reply":"2026-05-04T13:15:25.120344Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.122115Z","iopub.execute_input":"2026-05-04T13:15:25.122448Z","iopub.status.idle":"2026-05-04T13:15:25.381730Z","shell.execute_reply.started":"2026-05-04T13:15:25.122426Z","shell.execute_reply":"2026-05-04T13:15:25.380859Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"MODELS_PATH = \"/kaggle/working/models\"\n\nos.makedirs(MODELS_PATH, exist_ok = True)\nprint(\"Done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.382765Z","iopub.execute_input":"2026-05-04T13:15:25.383194Z","iopub.status.idle":"2026-05-04T13:15:25.397609Z","shell.execute_reply.started":"2026-05-04T13:15:25.383170Z","shell.execute_reply":"2026-05-04T13:15:25.397036Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndef train_one_epoch(model, dataloader, optimizer, criterion, device):\n    train_loss = 0.0\n    model.train()\n    for images, labels in dataloader:\n        images = images.to(device)\n        labels = labels.float().unsqueeze(1).to(device)\n\n        optimizer.zero_grad()\n        logits = model(images)\n        loss = criterion(logits, labels)\n\n        train_loss += loss.item()\n        loss.backward()\n        optimizer.step()\n    return train_loss / len(dataloader)\n    \n\ndef validate(model, dataloader, criterion, device):\n    val_loss = 0.0\n    model.eval()\n\n    with torch.no_grad():\n        for images, labels in dataloader:\n            images = images.to(device)\n            labels = labels.float().unsqueeze(1).to(device)\n\n            logits = model(images)\n            loss = criterion(logits, labels)\n            val_loss += loss.item()\n    return val_loss / len(dataloader)\n    \n\ndef submit(model, dataloader, device, submission_name = None):\n    sub = pd.read_csv(\"/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/sample_submission.csv\")\n    probs = []\n\n    model.eval()\n    with torch.no_grad():\n        for images in dataloader:\n            images = images.to(device)\n            logits = model(images)\n            probs.extend(torch.sigmoid(logits).squeeze(1).cpu().numpy())\n\n    print(\"Submit successfully.\")\n    sub[\"label\"] = probs\n    if submission_name:\n        sub_name = submission_name + \"_submission.csv\"\n    else:\n        sub_name = \"submission.csv\"\n    sub.to_csv(sub_name, index = False)\n    return sub            ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.400649Z","iopub.execute_input":"2026-05-04T13:15:25.400978Z","iopub.status.idle":"2026-05-04T13:15:25.808442Z","shell.execute_reply.started":"2026-05-04T13:15:25.400957Z","shell.execute_reply":"2026-05-04T13:15:25.807519Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3.1. MobileNet v3 small","metadata":{}},{"cell_type":"code","source":"# mobilenet_v3_small = models.mobilenet_v3_small(weights = models.MobileNet_V3_Small_Weights.IMAGENET1K_V1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.809408Z","iopub.execute_input":"2026-05-04T13:15:25.809878Z","iopub.status.idle":"2026-05-04T13:15:25.813406Z","shell.execute_reply.started":"2026-05-04T13:15:25.809853Z","shell.execute_reply":"2026-05-04T13:15:25.812603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# last_layer = mobilenet_v3_small.classifier[-1]\n# in_features = last_layer.in_features\n# mobilenet_v3_small.classifier[-1] = nn.Linear(in_features, 1)\n# mobilenet_v3_small.classifier","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.814313Z","iopub.execute_input":"2026-05-04T13:15:25.814624Z","iopub.status.idle":"2026-05-04T13:15:25.826638Z","shell.execute_reply.started":"2026-05-04T13:15:25.814601Z","shell.execute_reply":"2026-05-04T13:15:25.826055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# mobilenet_v3_small_gpu = mobilenet_v3_small.to(device)\n\n# for param in mobilenet_v3_small_gpu.parameters():\n#      param.requires_grad = False\n# for param in mobilenet_v3_small_gpu.classifier.parameters():\n#      param.requires_grad = True\n\n# criterion = nn.BCEWithLogitsLoss()\n# optimizer_stage1 = torch.optim.Adam(mobilenet_v3_small_gpu.classifier.parameters(), lr = 1e-3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.827590Z","iopub.execute_input":"2026-05-04T13:15:25.827890Z","iopub.status.idle":"2026-05-04T13:15:25.839188Z","shell.execute_reply.started":"2026-05-04T13:15:25.827840Z","shell.execute_reply":"2026-05-04T13:15:25.838624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# best_model_path = os.path.join(MODELS_PATH, \"mobilenet_v3_small.pth\")\n\n# best_val_loss = float(\"inf\")\n\n# print(\"Stage 1...\")\n# EPOCHS = 5\n# train_s1 = []\n# val_s1 = []\n\n# for e in range(EPOCHS):\n#     train_loss = train_one_epoch(mobilenet_v3_small_gpu, train_dataloader, optimizer_stage1, criterion, device)\n#     val_loss = validate(mobilenet_v3_small_gpu, val_dataloader, criterion, device)\n\n#     if val_loss < best_val_loss:\n#         best_val_loss = val_loss\n#         torch.save(mobilenet_v3_small_gpu.state_dict(), best_model_path)\n\n#     print(f\"EPOCHS [{e + 1}/{EPOCHS}] : train = {train_loss} | val = {val_loss}\")\n#     train_s1.append(train_loss)\n#     val_s1.append(val_loss)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.840151Z","iopub.execute_input":"2026-05-04T13:15:25.840435Z","iopub.status.idle":"2026-05-04T13:15:25.856300Z","shell.execute_reply.started":"2026-05-04T13:15:25.840404Z","shell.execute_reply":"2026-05-04T13:15:25.855601Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# mobilenet_v3_small_gpu.load_state_dict(torch.load(best_model_path))\n# for param in mobilenet_v3_small_gpu.parameters():\n#     param.requires_grad = True\n# optimizer_stage2 = torch.optim.Adam(mobilenet_v3_small_gpu.parameters(), lr = 1e-5)\n\n# print(\"Stage 2...\")\n# EPOCHS_S2 = 10\n# train_s2 = []\n# val_s2 = []\n\n# for e in range(EPOCHS_S2):\n#     train_loss = train_one_epoch(mobilenet_v3_small_gpu, train_dataloader, optimizer_stage2, criterion, device)\n#     val_loss = validate(mobilenet_v3_small_gpu, val_dataloader, criterion, device)\n\n#     if val_loss < best_val_loss:\n#         best_val_loss = val_loss\n#         torch.save(mobilenet_v3_small_gpu.state_dict(), best_model_path)\n\n#     print(f\"EPOCHS [{e + 1}/{EPOCHS_S2}] : train = {train_loss} | val = {val_loss}\")\n#     train_s2.append(train_loss)\n#     val_s2.append(val_loss)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.857166Z","iopub.execute_input":"2026-05-04T13:15:25.857595Z","iopub.status.idle":"2026-05-04T13:15:25.870246Z","shell.execute_reply.started":"2026-05-04T13:15:25.857571Z","shell.execute_reply":"2026-05-04T13:15:25.869440Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# mobilenet_v3_small_gpu.load_state_dict(torch.load(best_model_path))\n# for param in mobilenet_v3_small_gpu.parameters():\n#     param.requires_grad = True\n# optimizer_stage3 = torch.optim.Adam(mobilenet_v3_small_gpu.parameters(), lr = 1e-5)\n\n# print(\"Stage 3...\")\n# EPOCHS_S3 = 5\n\n# for e in range(EPOCHS_S3):\n#     train_loss = train_one_epoch(mobilenet_v3_small_gpu, full_train_dataloader, optimizer_stage3, criterion, device)\n#     print(f\"EPOCHS [{e + 1}/{EPOCHS_S3}] : train = {train_loss}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.871048Z","iopub.execute_input":"2026-05-04T13:15:25.871362Z","iopub.status.idle":"2026-05-04T13:15:25.883128Z","shell.execute_reply.started":"2026-05-04T13:15:25.871317Z","shell.execute_reply":"2026-05-04T13:15:25.882334Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# mobilenet_v3_small_gpu.load_state_dict(torch.load(\"/kaggle/input/models/daoviet/mobilenet-v3-small/pytorch/default/1/mobilenet_v3_small.pth\"))\n# print(\"Load model successfully.\")\n\n# sub = submit(mobilenet_v3_small_gpu, test_dataloader, device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.884262Z","iopub.execute_input":"2026-05-04T13:15:25.884792Z","iopub.status.idle":"2026-05-04T13:15:25.902301Z","shell.execute_reply.started":"2026-05-04T13:15:25.884761Z","shell.execute_reply":"2026-05-04T13:15:25.901780Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3.2. EfficientNet b2","metadata":{}},{"cell_type":"code","source":"efficientnet_b2 = models.efficientnet_b2(weights = models.EfficientNet_B2_Weights)\nprint(\"Load pretrained model successfully.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:25.903180Z","iopub.execute_input":"2026-05-04T13:15:25.903554Z","iopub.status.idle":"2026-05-04T13:15:26.520513Z","shell.execute_reply.started":"2026-05-04T13:15:25.903530Z","shell.execute_reply":"2026-05-04T13:15:26.519608Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"last_layer = efficientnet_b2.classifier[-1]\nin_features = last_layer.in_features\nefficientnet_b2.classifier[-1] = nn.Linear(in_features, 1)\nprint(efficientnet_b2.classifier[-1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:26.521637Z","iopub.execute_input":"2026-05-04T13:15:26.521969Z","iopub.status.idle":"2026-05-04T13:15:26.527986Z","shell.execute_reply.started":"2026-05-04T13:15:26.521934Z","shell.execute_reply":"2026-05-04T13:15:26.527425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for param in efficientnet_b2.parameters():\n    param.require_grad = False\nfor param in efficientnet_b2.classifier.parameters():\n    param.require_grad = True\n\nefficientnet_b2_gpu = efficientnet_b2.to(device)\ncriterion = nn.BCEWithLogitsLoss()\noptimizer_1 = torch.optim.Adam(efficientnet_b2_gpu.classifier.parameters(), lr = 1e-5)\nprint(\"Done setup stage 1.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:26.528991Z","iopub.execute_input":"2026-05-04T13:15:26.529516Z","iopub.status.idle":"2026-05-04T13:15:26.894375Z","shell.execute_reply.started":"2026-05-04T13:15:26.529493Z","shell.execute_reply":"2026-05-04T13:15:26.893728Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Stage 1...\")\n\nmodel_path = os.path.join(MODELS_PATH, \"efficientnet_b2.pth\")\ntrain_loss_s1 = []\nval_loss_s1 = []\nEPOCHS_S1 = 3\nbest_val_loss = float(\"inf\")\n\n# for e in range(EPOCHS_S1):\n#    train = train_one_epoch(efficientnet_b2_gpu, train_dataloader, optimizer_1, criterion, device)\n#    val = validate(efficientnet_b2_gpu, val_dataloader, criterion, device)\n\n#    if val < best_val_loss:\n#        best_val_loss = val\n#        torch.save(efficientnet_b2_gpu.state_dict(), model_path)\n\n#    print(f\"EPOCH [{e + 1}/{EPOCHS_S1}] : train = {train} | val = {val}\")\n#    train_loss_s1.append(train)\n#    val_loss_s1.append(val)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:26.895136Z","iopub.execute_input":"2026-05-04T13:15:26.895453Z","iopub.status.idle":"2026-05-04T13:15:26.900705Z","shell.execute_reply.started":"2026-05-04T13:15:26.895420Z","shell.execute_reply":"2026-05-04T13:15:26.900087Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Stage 2...\")\n\nbest_model_path = \"/kaggle/input/models/daoviet/efficientnet-b2-pretrained/pytorch/default/1/efficientnet_b2 (1).pth\"\n\nefficientnet_b2_gpu.load_state_dict(torch.load(best_model_path))\nprint(\"Load best model successfully.\")\n\nfor param in efficientnet_b2_gpu.parameters():\n    param.require_grad = True\n\noptimizer_2 = torch.optim.Adam(efficientnet_b2_gpu.parameters(), lr = 1e-5)\ntrain_loss_s2 = []\nval_loss_s2 = []\nEPOCHS_S2 = 3\n\nfor e in range(EPOCHS_S2):\n    train = train_one_epoch(efficientnet_b2_gpu, train_dataloader, optimizer_2, criterion, device)\n    val = validate(efficientnet_b2_gpu, val_dataloader, criterion, device)\n\n    if val < best_val_loss:\n        best_val_loss = val\n        torch.save(efficientnet_b2_gpu.state_dict(), model_path)\n\n    print(f\"EPOCH [{e + 1}/{EPOCHS_S2}] : train = {train} | val = {val}\")\n    train_loss_s2.append(train)\n    val_loss_s2.append(val)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:15:26.901581Z","iopub.execute_input":"2026-05-04T13:15:26.901976Z","iopub.status.idle":"2026-05-04T13:38:21.712599Z","shell.execute_reply.started":"2026-05-04T13:15:26.901943Z","shell.execute_reply":"2026-05-04T13:38:21.711865Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"efficientnet_b2_gpu.load_state_dict(torch.load(model_path))\nsubmit(efficientnet_b2_gpu, test_dataloader, device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-04T13:38:21.713721Z","iopub.execute_input":"2026-05-04T13:38:21.714045Z","iopub.status.idle":"2026-05-04T13:39:28.888214Z","shell.execute_reply.started":"2026-05-04T13:38:21.714021Z","shell.execute_reply":"2026-05-04T13:39:28.887597Z"}},"outputs":[],"execution_count":null}]}