{"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":"gpu","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"}],"dockerImageVersionId":31011,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision\nfrom torchvision.transforms import v2","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-16T06:51:17.742589Z","iopub.execute_input":"2025-04-16T06:51:17.742848Z","iopub.status.idle":"2025-04-16T06:51:24.412407Z","shell.execute_reply.started":"2025-04-16T06:51:17.742825Z","shell.execute_reply":"2025-04-16T06:51:24.411636Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T06:51:24.413196Z","iopub.execute_input":"2025-04-16T06:51:24.413533Z","iopub.status.idle":"2025-04-16T06:51:24.467632Z","shell.execute_reply.started":"2025-04-16T06:51:24.413512Z","shell.execute_reply":"2025-04-16T06:51:24.466950Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = v2.Compose([\n    v2.ToImage(),\n    v2.ToDtype(torch.float32, scale=True),\n    v2.RandomHorizontalFlip(),\n    v2.RandomVerticalFlip(),\n    v2.RandomCrop(32, padding=4),\n    v2.Normalize(mean=[0.485, 0.456, 0.406],\n                 std=[0.229, 0.224, 0.225])\n])\n\ndev_transform = v2.Compose([\n    v2.ToImage(),\n    v2.Resize((32, 32)),\n    v2.ToDtype(torch.float32, scale=True),\n    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\ntest_transform = v2.Compose([\n    v2.ToImage(),\n    v2.ToDtype(torch.float32, scale=True),\n    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\ntrain_datasets = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)\ndev_datasets = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=dev_transform)\ntest_datasets = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=test_transform)\n\ntrain_loader = torch.utils.data.DataLoader(train_datasets, batch_size=256, shuffle=True)\ndev_loader = torch.utils.data.DataLoader(dev_datasets, batch_size=256, shuffle=True)\ntest_loader = torch.utils.data.DataLoader(test_datasets, batch_size=256, shuffle=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T06:51:24.469513Z","iopub.execute_input":"2025-04-16T06:51:24.469781Z","iopub.status.idle":"2025-04-16T06:51:46.732294Z","shell.execute_reply.started":"2025-04-16T06:51:24.469735Z","shell.execute_reply":"2025-04-16T06:51:46.731553Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"classes = train_datasets.classes\nclasses","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T06:51:46.733113Z","iopub.execute_input":"2025-04-16T06:51:46.733359Z","iopub.status.idle":"2025-04-16T06:51:46.738814Z","shell.execute_reply.started":"2025-04-16T06:51:46.733335Z","shell.execute_reply":"2025-04-16T06:51:46.738074Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#ResNet18\nclass Block(nn.Module):\n    def __init__(self, in_channels, out_channels, stride=1, downsample=None):\n        super().__init__()\n        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(out_channels)\n        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(out_channels)\n        self.downsample = downsample\n\n    def forward(self, x):\n        identity = x\n        out = F.relu(self.bn1(self.conv1(x)))\n        out = self.bn2(self.conv2(out))\n\n        if self.downsample is not None:\n            identity = self.downsample(x)\n        out += identity\n        out = F.relu(out)\n        return out\n\nclass ResNet18(nn.Module):\n    def __init__(self, num_classes=10):\n        super().__init__()\n        self.in_channels = 64\n\n        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(64)\n\n        self.layer1 = self.make_layer(64, 2)\n        self.layer2 = self.make_layer(128, 2, stride=2)\n        self.layer3 = self.make_layer(256, 2, stride=2)\n        self.layer4 = self.make_layer(512, 2, stride=2)\n\n        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = nn.Linear(512, num_classes)\n\n    def make_layer(self, out_channels, blocks, stride=1):\n        downsample = None\n        if stride != 1 or self.in_channels != out_channels:\n            downsample = nn.Sequential(\n                nn.Conv2d(self.in_channels, out_channels, 1, stride, bias=False),\n                nn.BatchNorm2d(out_channels)\n            )\n\n        layers = [Block(self.in_channels, out_channels, stride, downsample)]\n        self.in_channels = out_channels\n\n        for _ in range(1, blocks):\n            layers.append(Block(out_channels, out_channels))\n        \n        return nn.Sequential(*layers)\n\n    def forward(self, x):\n        x = F.relu(self.bn1(self.conv1(x)))\n\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n\n        x = self.avgpool(x)\n        x = torch.flatten(x, 1)\n        x = self.fc(x)\n        return x\n\nmodel = ResNet18().to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T06:51:46.739552Z","iopub.execute_input":"2025-04-16T06:51:46.739760Z","iopub.status.idle":"2025-04-16T06:51:47.013102Z","shell.execute_reply.started":"2025-04-16T06:51:46.739716Z","shell.execute_reply":"2025-04-16T06:51:47.012500Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Optimizer\nepochs = 50\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)\n\n\nbest_accuracy = 0.0\npatience = 10\nepochs_no_improve = 0\nbest_model_path = \"best_model.pt\"\n\n# Training\nn_total_steps = len(train_loader)\nfor epoch in range(epochs):\n    running_loss = 0.0\n    n_correct_train = 0\n    n_samples_train = 0\n\n    model.train()\n    for i, (images, labels) in enumerate(train_loader):\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n\n        # Tính train accuracy\n        _, predicted = torch.max(outputs, 1)\n        n_correct_train += (predicted == labels).sum().item()\n        n_samples_train += labels.size(0)\n\n    train_acc = 100.0 * n_correct_train / n_samples_train\n    train_loss = running_loss / len(train_loader)\n\n    # Evaluate on dev set\n    model.eval()\n    dev_loss = 0.0\n    n_correct = 0\n    n_samples = 0\n\n    with torch.no_grad():\n        for images, labels in dev_loader:\n            images = images.to(device)\n            labels = labels.to(device)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            dev_loss += loss.item()\n\n            _, predicted = torch.max(outputs, 1)\n            n_correct += (predicted == labels).sum().item()\n            n_samples += labels.size(0)\n\n    dev_acc = 100.0 * n_correct / n_samples\n    dev_loss = dev_loss / len(dev_loader)\n\n    print(f\"Epoch [{epoch+1}/{epochs}]\")\n    print(f\"  Train Loss     : {train_loss:.4f}\")\n    print(f\"  Train Accuracy : {train_acc:.2f}%\")\n    print(f\"  Dev Loss       : {dev_loss:.4f}\")\n    print(f\"  Dev Accuracy   : {dev_acc:.2f}%\")\n\n    # Early stopping check\n    if dev_acc > best_accuracy:\n        best_accuracy = dev_acc\n        epochs_no_improve = 0\n        torch.save(model.state_dict(), best_model_path)\n    else:\n        epochs_no_improve += 1\n\n    if epochs_no_improve >= patience:\n        print(\"Early stopping\")\n        break\n\nprint(\"Training done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T06:52:09.596999Z","iopub.execute_input":"2025-04-16T06:52:09.597525Z","iopub.status.idle":"2025-04-16T07:36:10.395403Z","shell.execute_reply.started":"2025-04-16T06:52:09.597495Z","shell.execute_reply":"2025-04-16T07:36:10.394729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Testing\n\nwith torch.no_grad():\n    n_correct = 0\n    n_samples = 0\n    n_class_correct = [0 for _ in range(10)]\n    n_class_samples = [0 for _ in range(10)]\n\n    for images, labels in test_loader:\n        images = images.to(device)\n        labels = labels.to(device)\n\n        output = model(images)\n        _, predicted = torch.max(output, 1)\n\n        n_samples += labels.size(0)\n        n_correct += (predicted==labels).sum().item()\n\n        for i in range(labels.size(0)):\n            label = labels[i]\n            pred = predicted[i]\n            if label == pred:\n                n_class_correct[label] += 1\n            n_class_samples[label] += 1\n\n    acc = 100.0 * n_correct / n_samples\n    print(f'Accuracy of the network: {acc:.2f}%')\n\n    for i in range(10):\n        acc = 100.0 * n_class_correct[i] / n_class_samples[i]\n        print(f'Accuracy of {classes[i]}: {acc:.2f}%')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T07:36:15.144490Z","iopub.execute_input":"2025-04-16T07:36:15.144789Z","iopub.status.idle":"2025-04-16T07:36:19.861805Z","shell.execute_reply.started":"2025-04-16T07:36:15.144761Z","shell.execute_reply":"2025-04-16T07:36:19.861186Z"}},"outputs":[],"execution_count":null}]}