{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"},{"sourceId":109110,"sourceType":"modelInstanceVersion","modelInstanceId":91391,"modelId":115614}],"dockerImageVersionId":30747,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Hyperparameter\nIS_TRAIN = True   # if you want to train again from the beginning, set IS_TRAIN = True\n\nBN_MOMENTUM = 0.1\nNUM_EPOCHS = 50\nBATCH_SIZE = 64\nLEARNING_RATE = 0.01 ","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:16:56.866599Z","iopub.execute_input":"2025-06-17T09:16:56.866900Z","iopub.status.idle":"2025-06-17T09:16:56.876890Z","shell.execute_reply.started":"2025-06-17T09:16:56.866849Z","shell.execute_reply":"2025-06-17T09:16:56.876104Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Import library","metadata":{}},{"cell_type":"code","source":"import random\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import DataLoader, Subset, DataLoader, Dataset, TensorDataset","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:16:56.879169Z","iopub.execute_input":"2025-06-17T09:16:56.879452Z","iopub.status.idle":"2025-06-17T09:17:02.583446Z","shell.execute_reply.started":"2025-06-17T09:16:56.879426Z","shell.execute_reply":"2025-06-17T09:17:02.582791Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Read and prepare data","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\ndf.head(5)","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:02.584414Z","iopub.execute_input":"2025-06-17T09:17:02.584908Z","iopub.status.idle":"2025-06-17T09:17:05.397603Z","shell.execute_reply.started":"2025-06-17T09:17:02.584886Z","shell.execute_reply":"2025-06-17T09:17:05.396665Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def split_data(df, test_size=0.2):\n    X = df.iloc[:, 1:].values\n    y = df.iloc[:, 0].values\n    \n    indices = np.random.permutation(len(X))\n    split_index = int(len(X) * (1 - test_size))\n    \n    train_indices = indices[:split_index]\n    val_indices = indices[split_index:]\n    \n    X_train = X[train_indices]\n    y_train = y[train_indices]\n    X_val = X[val_indices]\n    y_val = y[val_indices]\n    \n    return X_train, y_train, X_val, y_val\n\nclass ResizedDigitDataset(Dataset):\n    def __init__(self, X, y):\n        self.X = X.reshape(-1, 28, 28).astype(np.uint8)   # Reshape hình ảnh về (28, 28)\n        self.y = y\n        self.transform = transforms.Compose([\n            transforms.ToPILImage(), \n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize((0.1307,), (0.3081,))\n        ])\n    \n    def __len__(self):\n        return len(self.y)\n    \n    def __getitem__(self, idx):\n        image = self.X[idx]\n        label = self.y[idx]\n        image = self.transform(image) \n        return image, label\n\n# Chia dữ liệu và tạo dataset\nX_train, y_train, X_val, y_val = split_data(df)\ntrain_dataset = ResizedDigitDataset(X_train, y_train)\nval_dataset = ResizedDigitDataset(X_val, y_val)\n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False)\n\nfor data, target in train_loader:\n    print(data.shape)  \n    print(target.shape)\n    plt.imshow(data[0].reshape((224, 224)), cmap='gray')\n    print(target[0].item())\n    break","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:05.398797Z","iopub.execute_input":"2025-06-17T09:17:05.399097Z","iopub.status.idle":"2025-06-17T09:17:06.291779Z","shell.execute_reply.started":"2025-06-17T09:17:05.399071Z","shell.execute_reply":"2025-06-17T09:17:06.290900Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model definition","metadata":{}},{"cell_type":"markdown","source":"The model is referenced from the link https://github.com/HRNet/HRNet-Image-Classification/blob/master/lib/models/cls_hrnet.py","metadata":{}},{"cell_type":"markdown","source":"![](https://production-media.paperswithcode.com/methods/Screen_Shot_2020-06-22_at_11.03.39_PM_64dSeMm.png)","metadata":{}},{"cell_type":"code","source":"class BasicBlock(nn.Module):\n    expansion = 1\n\n    def __init__(self, inplanes, planes, stride=1, downsample=None):\n        super(BasicBlock, self).__init__()\n        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=3, stride=stride, padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)\n        self.relu = nn.ReLU(inplace=True)\n        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)\n        self.downsample = downsample\n\n    def forward(self, x):\n        identity = x\n        if self.downsample is not None:\n            identity = self.downsample(x)\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n        out = self.conv2(out)\n        out = self.bn2(out)\n        out += identity\n        out = self.relu(out)\n        return out","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:06.292937Z","iopub.execute_input":"2025-06-17T09:17:06.293185Z","iopub.status.idle":"2025-06-17T09:17:06.301763Z","shell.execute_reply.started":"2025-06-17T09:17:06.293165Z","shell.execute_reply":"2025-06-17T09:17:06.300802Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Bottleneck(nn.Module):\n    expansion = 4\n\n    def __init__(self, inplanes, planes, stride=1, downsample=None):\n        super(Bottleneck, self).__init__()\n        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)\n        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)\n        self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, bias=False)\n        self.bn3 = nn.BatchNorm2d(planes * self.expansion, momentum=BN_MOMENTUM)\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n\n    def forward(self, x):\n        identity = x\n        if self.downsample is not None:\n            identity = self.downsample(x)\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n        out = self.conv2(out)\n        out = self.bn2(out)\n        out = self.relu(out)\n        out = self.conv3(out)\n        out = self.bn3(out)\n        out += identity\n        out = self.relu(out)\n        return out","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:06.302946Z","iopub.execute_input":"2025-06-17T09:17:06.303294Z","iopub.status.idle":"2025-06-17T09:17:06.320346Z","shell.execute_reply.started":"2025-06-17T09:17:06.303269Z","shell.execute_reply":"2025-06-17T09:17:06.319509Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class HighResolutionModule(nn.Module):\n    def __init__(self, num_branches, block, num_blocks, num_inchannels, num_channels, fuse_method, multi_scale_output=True):\n        super(HighResolutionModule, self).__init__()\n        self.num_branches = num_branches\n        self.num_inchannels = num_inchannels\n        self.fuse_method = fuse_method\n        self.multi_scale_output = multi_scale_output\n\n        self.branches = self._make_branches(num_branches, block, num_blocks, num_channels)\n        self.fuse_layers = self._make_fuse_layers()\n        self.relu = nn.ReLU(False)\n\n    def _make_one_branch(self, branch_index, block, num_blocks, num_channels, stride=1):\n        downsample = None\n        if stride != 1 or self.num_inchannels[branch_index] != num_channels[branch_index] * block.expansion:\n            downsample = nn.Sequential(\n                nn.Conv2d(self.num_inchannels[branch_index], num_channels[branch_index] * block.expansion,\n                          kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(num_channels[branch_index] * block.expansion, momentum=BN_MOMENTUM),\n            )\n        layers = [block(self.num_inchannels[branch_index], num_channels[branch_index], stride, downsample)]\n        self.num_inchannels[branch_index] = num_channels[branch_index] * block.expansion\n        for _ in range(1, num_blocks[branch_index]):\n            layers.append(block(self.num_inchannels[branch_index], num_channels[branch_index]))\n        return nn.Sequential(*layers)\n\n    def _make_branches(self, num_branches, block, num_blocks, num_channels):\n        return nn.ModuleList([self._make_one_branch(i, block, num_blocks, num_channels) for i in range(num_branches)])\n\n    def _make_fuse_layers(self):\n        if self.num_branches == 1:\n            return None\n        num_inchannels = self.num_inchannels\n        fuse_layers = []\n        for i in range(self.num_branches):\n            fuse_layer = []\n            for j in range(self.num_branches):\n                if j > i:\n                    fuse_layer.append(nn.Sequential(\n                        nn.Conv2d(num_inchannels[j], num_inchannels[i], 1, 1, 0, bias=False),\n                        nn.BatchNorm2d(num_inchannels[i], momentum=BN_MOMENTUM),\n                        nn.Upsample(scale_factor=2 ** (j - i), mode='nearest')))\n                elif j == i:\n                    fuse_layer.append(None)\n                else:\n                    conv3x3s = []\n                    for k in range(i - j):\n                        num_outchannels_conv3x3 = num_inchannels[i] if k == i - j - 1 else num_inchannels[j]\n                        conv3x3s.append(nn.Sequential(\n                            nn.Conv2d(num_inchannels[j], num_outchannels_conv3x3, 3, 2, 1, bias=False),\n                            nn.BatchNorm2d(num_outchannels_conv3x3, momentum=BN_MOMENTUM),\n                            nn.ReLU(False)))\n                    fuse_layer.append(nn.Sequential(*conv3x3s))\n            fuse_layers.append(nn.ModuleList(fuse_layer))\n        return nn.ModuleList(fuse_layers)\n\n    def forward(self, x):\n        if self.num_branches == 1:\n            return [self.branches[0](x[0])]\n        for i in range(self.num_branches):\n            x[i] = self.branches[i](x[i])\n        x_fuse = []\n        for i in range(len(self.fuse_layers)):\n            y = x[0] if i == 0 else self.fuse_layers[i][0](x[0])\n            for j in range(1, self.num_branches):\n                y = y + (x[j] if i == j else self.fuse_layers[i][j](x[j]))\n            x_fuse.append(self.relu(y))\n        return x_fuse\n\n    def get_num_inchannels(self):\n        return self.num_inchannels","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:06.324201Z","iopub.execute_input":"2025-06-17T09:17:06.324633Z","iopub.status.idle":"2025-06-17T09:17:06.346306Z","shell.execute_reply.started":"2025-06-17T09:17:06.324606Z","shell.execute_reply":"2025-06-17T09:17:06.345449Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class HighResolutionNet(nn.Module):\n    def __init__(self):\n        super(HighResolutionNet, self).__init__()\n        self.conv1 = nn.Conv2d(1, 64, kernel_size=3, stride=2, padding=1, bias=False)  # Change input channels to 1 for MNIST\n        self.bn1 = nn.BatchNorm2d(64, momentum=BN_MOMENTUM)\n        self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=2, padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(64, momentum=BN_MOMENTUM)\n        self.relu = nn.ReLU(inplace=True)\n\n        # Stage 1\n        self.layer1 = self._make_layer(Bottleneck, 64, 64, 4)\n        stage1_out_channel = Bottleneck.expansion * 64\n\n        # Stage 2\n        num_channels = [18, 36]\n        block = BasicBlock\n        num_channels = [nc * block.expansion for nc in num_channels]\n        self.transition1 = self._make_transition_layer([stage1_out_channel], num_channels)\n        self.stage2, pre_stage_channels = self._make_stage(2, block, [4, 4], num_channels)\n\n        # Stage 3\n        num_channels = [18, 36, 72]\n        block = BasicBlock\n        num_channels = [nc * block.expansion for nc in num_channels]\n        self.transition2 = self._make_transition_layer(pre_stage_channels, num_channels)\n        self.stage3, pre_stage_channels = self._make_stage(3, block, [4, 4, 4], num_channels)\n\n        # Stage 4\n        num_channels = [18, 36, 72, 144]\n        block = BasicBlock\n        num_channels = [nc * block.expansion for nc in num_channels]\n        self.transition3 = self._make_transition_layer(pre_stage_channels, num_channels)\n        self.stage4, pre_stage_channels = self._make_stage(4, block, [4, 4, 4, 4], num_channels)\n\n        self.incre_modules, self.downsamp_modules, self.final_layer = self._make_head(pre_stage_channels)\n\n        self.classifier = nn.Linear(2048, 10)\n\n    def _make_transition_layer(self, num_channels_pre_layer, num_channels_cur_layer):\n        transition_layers = []\n        for i in range(len(num_channels_cur_layer)):\n            if i < len(num_channels_pre_layer):\n                if num_channels_cur_layer[i] != num_channels_pre_layer[i]:\n                    transition_layers.append(nn.Sequential(\n                        nn.Conv2d(num_channels_pre_layer[i], num_channels_cur_layer[i], 3, 1, 1, bias=False),\n                        nn.BatchNorm2d(num_channels_cur_layer[i], momentum=BN_MOMENTUM),\n                        nn.ReLU(inplace=True)))\n                else:\n                    transition_layers.append(None)\n            else:\n                conv3x3s = []\n                for j in range(i + 1 - len(num_channels_pre_layer)):\n                    in_channels = num_channels_pre_layer[-1]\n                    out_channels = num_channels_cur_layer[i] if j == i - len(num_channels_pre_layer) else in_channels\n                    conv3x3s.append(nn.Sequential(\n                        nn.Conv2d(in_channels, out_channels, 3, 2, 1, bias=False),\n                        nn.BatchNorm2d(out_channels, momentum=BN_MOMENTUM),\n                        nn.ReLU(inplace=True)))\n                transition_layers.append(nn.Sequential(*conv3x3s))\n        return nn.ModuleList(transition_layers)\n\n    def _make_layer(self, block, inplanes, planes, blocks, stride=1):\n        downsample = None\n        if stride != 1 or inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                nn.Conv2d(inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(planes * block.expansion, momentum=BN_MOMENTUM),\n            )\n        layers = [block(inplanes, planes, stride, downsample)]\n        inplanes = planes * block.expansion\n        layers.extend(block(inplanes, planes) for _ in range(1, blocks))\n        return nn.Sequential(*layers)\n\n    def _make_stage(self, num_branches, block, num_blocks, num_channels, multi_scale_output=True):\n        num_modules = num_branches\n        num_inchannels = num_channels\n        modules = nn.ModuleList()\n        for i in range(num_modules):\n            reset_multi_scale_output = multi_scale_output if i < num_modules - 1 else False\n            modules.append(HighResolutionModule(num_branches, block, num_blocks, num_inchannels, num_channels, 'SUM', reset_multi_scale_output))\n            num_inchannels = modules[-1].get_num_inchannels()\n        return nn.Sequential(*modules), num_inchannels\n\n    def _make_head(self, pre_stage_channels):\n        head_block = Bottleneck\n        head_channels = [32, 64, 128, 256]\n\n        incre_modules = nn.ModuleList([self._make_layer(head_block, ch, hc, 1, stride=1) \n                                       for ch, hc in zip(pre_stage_channels, head_channels)])\n\n        downsamp_modules = nn.ModuleList([\n            nn.Sequential(\n                nn.Conv2d(hc * head_block.expansion, hc_next * head_block.expansion, 3, 2, 1, bias=False),\n                nn.BatchNorm2d(hc_next * head_block.expansion, momentum=BN_MOMENTUM),\n                nn.ReLU(inplace=True)\n            ) for hc, hc_next in zip(head_channels[:-1], head_channels[1:])\n        ])\n\n        final_layer = nn.Sequential(\n            nn.Conv2d(head_channels[-1] * head_block.expansion, 2048, 1, 1, 0),\n            nn.BatchNorm2d(2048, momentum=BN_MOMENTUM),\n            nn.ReLU(inplace=True)\n        )\n\n        return incre_modules, downsamp_modules, final_layer\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.conv2(x)\n        x = self.bn2(x)\n        x = self.relu(x)\n        x = self.layer1(x)\n\n        x_list = [self.transition1[i](x) if self.transition1[i] is not None else x for i in range(2)]\n        y_list = self.stage2(x_list)\n\n        x_list = [self.transition2[i](y_list[-1]) if self.transition2[i] is not None else y_list[i] for i in range(3)]\n        y_list = self.stage3(x_list)\n\n        x_list = [self.transition3[i](y_list[-1]) if self.transition3[i] is not None else y_list[i] for i in range(4)]\n        y_list = self.stage4(x_list)\n\n        y = self.incre_modules[0](y_list[0])\n        for i in range(len(self.downsamp_modules)):\n            y = self.incre_modules[i + 1](y_list[i + 1]) + self.downsamp_modules[i](y)\n        y = self.final_layer(y)\n\n        if torch._C._get_tracing_state():\n            y = y.flatten(start_dim=2).mean(dim=2)\n        else:\n            y = F.avg_pool2d(y, kernel_size=y.size()[2:]).view(y.size(0), -1)\n        y = self.classifier(y)\n        return y\n\n","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:06.347535Z","iopub.execute_input":"2025-06-17T09:17:06.347840Z","iopub.status.idle":"2025-06-17T09:17:06.373255Z","shell.execute_reply.started":"2025-06-17T09:17:06.347815Z","shell.execute_reply":"2025-06-17T09:17:06.372376Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model training","metadata":{}},{"cell_type":"code","source":"def train(model, device, train_loader, optimizer, criterion):\n    model.train()  # Chuyển mô hình sang chế độ train\n    train_loss = 0.0\n    correct_train = 0\n\n    for data, target in train_loader:\n        data, target = data.to(device), target.to(device)\n        optimizer.zero_grad()\n        output = model(data)\n        loss = criterion(output, target)\n        loss.backward()\n        optimizer.step()\n\n        train_loss += loss.item()\n        pred_train = output.argmax(dim=1, keepdim=True)\n        correct_train += pred_train.eq(target.view_as(pred_train)).sum().item()\n\n    train_loss /= len(train_loader)\n    train_accuracy = 100. * correct_train / len(train_loader.dataset)\n\n    return train_loss, train_accuracy","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:06.374225Z","iopub.execute_input":"2025-06-17T09:17:06.374527Z","iopub.status.idle":"2025-06-17T09:17:06.392063Z","shell.execute_reply.started":"2025-06-17T09:17:06.374507Z","shell.execute_reply":"2025-06-17T09:17:06.391198Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate(model, device, test_loader, criterion):\n    model.eval()  # Chuyển mô hình sang chế độ eval\n    test_loss = 0.0\n    correct_test = 0\n\n    with torch.no_grad():\n        for data, target in test_loader:\n            data, target = data.to(device), target.to(device)\n            output = model(data)\n            test_loss += criterion(output, target).item()\n            pred_test = output.argmax(dim=1, keepdim=True)\n            correct_test += pred_test.eq(target.view_as(pred_test)).sum().item()\n\n    test_loss /= len(test_loader.dataset)\n    test_accuracy = 100. * correct_test / len(test_loader.dataset)\n\n    return test_loss, test_accuracy","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:06.393419Z","iopub.execute_input":"2025-06-17T09:17:06.393725Z","iopub.status.idle":"2025-06-17T09:17:06.408672Z","shell.execute_reply.started":"2025-06-17T09:17:06.393700Z","shell.execute_reply":"2025-06-17T09:17:06.407888Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = HighResolutionNet().to(device)\n\noptimizer = optim.SGD(model.parameters(), lr=LEARNING_RATE, momentum=0.9)\ncriterion = nn.CrossEntropyLoss()","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:06.410101Z","iopub.execute_input":"2025-06-17T09:17:06.410385Z","iopub.status.idle":"2025-06-17T09:17:06.923874Z","shell.execute_reply.started":"2025-06-17T09:17:06.410360Z","shell.execute_reply":"2025-06-17T09:17:06.922534Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if IS_TRAIN:\n    train_losses = []\n    val_losses = []\n    train_accuracies = []\n    val_accuracies = []\n    best_accuracy = 0.0\n    best_model = None\n\n    for epoch in range(NUM_EPOCHS):\n        print(f'Start training epoch: {epoch+1}/{NUM_EPOCHS}')\n\n        # Gọi hàm train\n        epoch_train_loss, train_accuracy = train(model, device, train_loader, optimizer, criterion)\n        train_losses.append(epoch_train_loss)\n        train_accuracies.append(train_accuracy)\n\n        # Gọi hàm evaluate\n        val_loss, val_accuracy = evaluate(model, device, val_loader, criterion)\n        val_losses.append(val_loss)\n        val_accuracies.append(val_accuracy)\n\n        # Lưu mô hình tốt nhất\n        if val_accuracy > best_accuracy:\n            best_accuracy = val_accuracy\n            best_model = model.state_dict()\n\n        print(f'Epoch {epoch + 1}, Train Loss: {epoch_train_loss:.4f}, val Loss: {val_loss:.4f}, '\n              f'Train Accuracy: {train_accuracy:.2f}%, val Accuracy: {val_accuracy:.2f}%')\n\n    # Lưu mô hình tốt nhất\n    torch.save(best_model, 'best_model.pth')","metadata":{"execution":{"iopub.status.busy":"2025-06-17T09:17:06.925335Z","iopub.execute_input":"2025-06-17T09:17:06.925679Z","iopub.status.idle":"2025-06-17T14:25:05.646469Z","shell.execute_reply.started":"2025-06-17T09:17:06.925649Z","shell.execute_reply":"2025-06-17T14:25:05.645606Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if IS_TRAIN:\n    # Plotting the results\n    epochs = range(1, NUM_EPOCHS + 1)\n    plt.figure(figsize=(12, 5))\n\n    # Plot train and val loss\n    plt.subplot(1, 2, 1)\n    plt.plot(epochs, train_losses, 'r', label='Training loss')\n    plt.plot(epochs, val_losses, 'b', label='val loss')\n    plt.title('Training and val loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.legend()\n\n    # Plot train and val accuracy\n    plt.subplot(1, 2, 2)\n    plt.plot(epochs, train_accuracies, 'r', label='Training accuracy')\n    plt.plot(epochs, val_accuracies, 'b', label='val accuracy')\n    plt.title('Training and val accuracy')\n    plt.xlabel('Epochs')\n    plt.ylabel('Accuracy (%)')\n    plt.legend()\n\n    plt.show()\n\n    # Save the best model\n    torch.save(best_model, 'best_model.pth')\n    print(f'Best model saved with accuracy: {best_accuracy:.2f}%')","metadata":{"execution":{"iopub.status.busy":"2025-06-17T14:25:05.647957Z","iopub.execute_input":"2025-06-17T14:25:05.648215Z","iopub.status.idle":"2025-06-17T14:25:06.475582Z","shell.execute_reply.started":"2025-06-17T14:25:05.648194Z","shell.execute_reply":"2025-06-17T14:25:06.474531Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tải mô hình đã lưu\nif IS_TRAIN:\n    model = HighResolutionNet().to(device)\n    model.load_state_dict(torch.load('best_model.pth', map_location=device)) \n    model.eval()\nelse:\n    model = HighResolutionNet().to(device)\n    model.load_state_dict(torch.load('/kaggle/input/hrnet-model/pytorch/default/1/best_model.pth', map_location=device)) \n    model.eval()\n\n\n# Đánh giá mô hình đã lưu trên tập val\nval_loss, val_accuracy = evaluate(model, device, val_loader, criterion)\nprint(f'val Loss: {val_loss:.4f}, val Accuracy: {val_accuracy:.2f}%')","metadata":{"execution":{"iopub.status.busy":"2025-06-17T14:25:06.476918Z","iopub.execute_input":"2025-06-17T14:25:06.477226Z","iopub.status.idle":"2025-06-17T14:25:33.078811Z","shell.execute_reply.started":"2025-06-17T14:25:06.477196Z","shell.execute_reply":"2025-06-17T14:25:33.077912Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Predict the results","metadata":{}},{"cell_type":"code","source":"def predict(model, data_loader):\n    model.eval() \n    all_predictions = []\n    \n    with torch.no_grad():\n        for data, _ in data_loader:\n            outputs = model(data)  # Dự đoán\n            _, predicted = torch.max(outputs, 1)\n            all_predictions.append(predicted)\n    \n    return torch.cat(all_predictions)","metadata":{"execution":{"iopub.status.busy":"2025-06-17T14:25:33.079924Z","iopub.execute_input":"2025-06-17T14:25:33.080175Z","iopub.status.idle":"2025-06-17T14:25:33.084997Z","shell.execute_reply.started":"2025-06-17T14:25:33.080155Z","shell.execute_reply":"2025-06-17T14:25:33.084213Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"indices = random.sample(range(len(val_dataset)), 10)\nsamples = torch.utils.data.Subset(val_dataset, indices)\nsample_loader = DataLoader(samples, batch_size=BATCH_SIZE, shuffle=False)\n\n# Dự đoán và in kết quả\nwith torch.no_grad():\n    for data, target in sample_loader:\n        data, target = data.to(device), target.to(device)\n        output = model(data)\n        pred = output.argmax(dim=1, keepdim=True)\n\n        # In ra kết quả dự đoán\n        for i in range(len(data)):\n            plt.imshow(data[i].cpu().numpy().transpose(1, 2, 0), cmap='gray')\n            plt.title(f'Predicted: {pred[i].item()}, Actual: {target[i].item()}')\n            plt.show()","metadata":{"execution":{"iopub.status.busy":"2025-06-17T14:25:33.086051Z","iopub.execute_input":"2025-06-17T14:25:33.086280Z","iopub.status.idle":"2025-06-17T14:25:35.784461Z","shell.execute_reply.started":"2025-06-17T14:25:33.086262Z","shell.execute_reply":"2025-06-17T14:25:35.783606Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TestDataset(Dataset):\n    def __init__(self, X):\n        self.X = X.reshape(-1, 28, 28).astype(np.uint8)  # Reshape hình ảnh về (28, 28)\n        self.transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize((0.1307,), (0.3081,))\n        ])\n\n    def __len__(self):\n        return len(self.X)\n\n    def __getitem__(self, idx):\n        image = self.X[idx]\n        image = self.transform(image)\n        return image\n\ndf_test = pd.read_csv('/kaggle/input/digit-recognizer/test.csv')\nX_test = df_test.values  \ntest_dataset = TestDataset(X_test)\ntest_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2025-06-17T14:25:35.785483Z","iopub.execute_input":"2025-06-17T14:25:35.785740Z","iopub.status.idle":"2025-06-17T14:25:37.768593Z","shell.execute_reply.started":"2025-06-17T14:25:35.785719Z","shell.execute_reply":"2025-06-17T14:25:37.767669Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(model, data_loader):\n    model.eval()  \n    all_predictions = []\n    \n    with torch.no_grad():\n        for data in data_loader:\n            data = data.to(device)\n            outputs = model(data) \n            _, predicted = torch.max(outputs, 1) \n            all_predictions.extend(predicted.cpu().numpy())  \n    \n    return np.array(all_predictions)","metadata":{"execution":{"iopub.status.busy":"2025-06-17T14:25:37.769834Z","iopub.execute_input":"2025-06-17T14:25:37.770448Z","iopub.status.idle":"2025-06-17T14:25:37.775783Z","shell.execute_reply.started":"2025-06-17T14:25:37.770417Z","shell.execute_reply":"2025-06-17T14:25:37.774918Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions = predict(model, test_loader)","metadata":{"execution":{"iopub.status.busy":"2025-06-17T14:25:37.776650Z","iopub.execute_input":"2025-06-17T14:25:37.776873Z","iopub.status.idle":"2025-06-17T14:27:04.952416Z","shell.execute_reply.started":"2025-06-17T14:25:37.776855Z","shell.execute_reply":"2025-06-17T14:27:04.951723Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def save_predictions(predictions, output_csv_path):\n    df_predictions = pd.DataFrame({\n        'ImageId': np.arange(1, len(predictions) + 1),\n        'Label': predictions \n    })\n    df_predictions.to_csv(output_csv_path, index=False)\n    return df_predictions\n\ndf_predictions = save_predictions(predictions, 'submission.csv')\ndf_predictions.head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-17T14:27:04.953474Z","iopub.execute_input":"2025-06-17T14:27:04.954072Z","iopub.status.idle":"2025-06-17T14:27:04.984097Z","shell.execute_reply.started":"2025-06-17T14:27:04.954037Z","shell.execute_reply":"2025-06-17T14:27:04.983403Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}