{"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":"none","dataSources":[{"sourceType":"competition","sourceId":3649,"databundleVersionId":46718}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision\nimport torchvision.transforms as transforms\nimport matplotlib.pyplot as plt\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-05-25T14:03:27.009635Z","iopub.execute_input":"2026-05-25T14:03:27.009885Z","iopub.status.idle":"2026-05-25T14:03:38.509876Z","shell.execute_reply.started":"2026-05-25T14:03:27.009848Z","shell.execute_reply":"2026-05-25T14:03:38.508857Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\n# Tự động lấy số lõi CPU khả dụng trên Kaggle (thường là 2 hoặc 4)\nNUM_WORKERS = os.cpu_count()\n\ntransform = transforms.Compose(\n    [\n        transforms.RandomCrop(32, padding=4),\n        transforms.RandomHorizontalFlip(), # Lật ảnh ngẫu nhiên\n        transforms.ToTensor(),\n        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n    ]\n)\ntrainset = torchvision.datasets.CIFAR10(\nroot ='./ data ',\ntrain = True ,\ndownload = True ,\ntransform = transform\n)\ntrainloader = torch.utils.data.DataLoader (\ntrainset ,\nbatch_size =64 ,\nshuffle = True,\nnum_workers=NUM_WORKERS, # Sử dụng đa luồng để CPU load ảnh song song\npin_memory=True\n)\ntestset = torchvision.datasets.CIFAR10 (\nroot ='./ data ',\ntrain = False ,\ndownload = True ,\ntransform = transform\n)\ntestloader = torch.utils.data.DataLoader (\ntestset ,\nbatch_size =64 ,\nshuffle = False,\nnum_workers=NUM_WORKERS, # Sử dụng đa luồng để CPU load ảnh song song\npin_memory=True    \n)\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Đang chạy trên thiết bị: {device}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-25T14:03:38.512346Z","iopub.execute_input":"2026-05-25T14:03:38.513667Z","iopub.status.idle":"2026-05-25T14:03:47.256692Z","shell.execute_reply.started":"2026-05-25T14:03:38.513613Z","shell.execute_reply":"2026-05-25T14:03:47.255402Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"classes = ('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')\nprint(f\"Kích thước tập train: {len(trainloader.dataset)} mẫu\")\nprint(f\"Kích thước tập test: {len(testloader.dataset)} mẫu\")\n\n# Lấy 1 batch dữ liệu ra ngoài\ndataiter = iter(trainloader)\nimages, labels = next(dataiter)\n\n# Khởi tạo khung vẽ 5 tấm ảnh\nfig, axes = plt.subplots(1, 5, figsize=(15, 3))\n\nfor i in range(5):\n    # Lấy chính xác tấm ảnh thứ i trong batch và chuyển thành numpy\n    img = images[i].numpy() \n    \n    # Khử chuẩn hóa (Denormalize) để ảnh quay về màu sắc gốc\n    img = img / 2 + 0.5     \n    \n    # Chuyển đổi kích thước từ (C, H, W) sang (H, W, C) để matplotlib vẽ được\n    img = np.transpose(img, (1, 2, 0)) \n    \n    # Vẽ ảnh lên ô tương ứng\n    axes[i].imshow(img)\n    axes[i].set_title(classes[labels[i]])\n    axes[i].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-25T14:03:47.257865Z","iopub.execute_input":"2026-05-25T14:03:47.258171Z","iopub.status.idle":"2026-05-25T14:03:47.875760Z","shell.execute_reply.started":"2026-05-25T14:03:47.258112Z","shell.execute_reply":"2026-05-25T14:03:47.874597Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class HypricPool(nn.Module):\n    def __init__(self, kernel_size=2, stride=2):\n        super().__init__()\n        self.avg = nn.AvgPool2d(kernel_size,stride)\n        self.max = nn.MaxPool2d(kernel_size,stride)\n    def forward(self,x):\n        return 0.5*(self.avg(x)+self.max(x))\n\nclass ResidualDepthwiseBlock(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super().__init__()\n        self.depthwise = nn.Conv2d(\n            in_channels=in_channels,\n            out_channels=in_channels,\n            kernel_size=3,\n            padding=1,\n            groups=in_channels,\n            bias=False          \n        )\n        self.bn1 = nn.BatchNorm2d(in_channels)\n        self.act1 = nn.SiLU(inplace=True) \n        self.pointwise = nn.Conv2d(\n            in_channels=in_channels,\n            out_channels=out_channels,\n            kernel_size=1,\n            bias=False\n        )\n        self.bn2 = nn.BatchNorm2d(out_channels)\n        self.act2 = nn.SiLU(inplace=True)\n        self.shortcut = nn.Sequential()\n        if in_channels != out_channels:\n            self.shortcut = nn.Sequential(\n                nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False),\n                nn.BatchNorm2d(out_channels)\n            )\n\n    def forward(self, x):\n        identity = self.shortcut(x) \n        out = self.depthwise(x)\n        out = self.bn1(out)\n        out = self.act1(out)\n        out = self.pointwise(out)\n        out = self.bn2(out)\n        out += identity \n        out = self.act2(out)\n        return out      ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-25T14:03:47.877823Z","iopub.execute_input":"2026-05-25T14:03:47.878212Z","iopub.status.idle":"2026-05-25T14:03:47.889325Z","shell.execute_reply.started":"2026-05-25T14:03:47.878172Z","shell.execute_reply":"2026-05-25T14:03:47.888382Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CNN(nn.Module):\n    def __init__(self):\n        super(CNN, self).__init__()\n        self.layer1 = nn.Sequential(\n            nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=False),\n            nn.MaxPool2d(2,2)\n        )\n        self.layer2 = nn.Sequential(\n            nn.Dropout(0.1),\n            nn.Conv2d(32, 64, 3, padding=1, bias=False),\n            nn.MaxPool2d(2,2)\n        )\n        self.layer3 = nn.Sequential(\n            nn.Dropout(0.1),\n            nn.Conv2d(64, 80, 3, padding=1, bias=False),\n            nn.MaxPool2d(2,2)\n        )\n        self.flatten = nn.Flatten()\n        self.projection = nn.Sequential(\n            nn.LazyLinear(512),\n            nn.Dropout(0.2),\n            nn.SiLU(inplace=True),\n            nn.LazyLinear(10)\n        )\n    def forward(self,x):\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.flatten(x)\n        x = self.projection(x)\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-25T14:03:47.890545Z","iopub.execute_input":"2026-05-25T14:03:47.890886Z","iopub.status.idle":"2026-05-25T14:03:47.925880Z","shell.execute_reply.started":"2026-05-25T14:03:47.890843Z","shell.execute_reply":"2026-05-25T14:03:47.924873Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CNN_hp(nn.Module):\n    def __init__(self):\n        super(CNN_hp, self).__init__()\n        self.layer1 = nn.Sequential(\n            nn.Conv2d(3, 64, kernel_size=3, padding=1, bias=False),\n            nn.BatchNorm2d(64),\n            nn.SiLU(inplace=True),\n            HypricPool(2,2)\n        )\n        self.layer2 = nn.Sequential(\n            nn.Dropout(0.2),\n            ResidualDepthwiseBlock(64, 128),\n            HypricPool(2,2)\n        )\n        self.layer3 = nn.Sequential(\n            nn.Dropout(0.2),\n            ResidualDepthwiseBlock(128,256),\n            HypricPool(2,2)\n        )\n        self.layer4 = nn.Sequential(\n            nn.Dropout(0.2),\n            ResidualDepthwiseBlock(256,256),\n            HypricPool(2,2)\n        )\n        self.flatten = nn.Flatten(1)\n        self.projection = nn.Sequential(\n            nn.Dropout(0.3),\n            nn.LazyLinear(256),\n            nn.Dropout(0.3),\n            nn.SiLU(inplace=True),\n            nn.LazyLinear(10)\n        )\n    def forward(self,x):\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n        x = self.flatten(x)\n        x = self.projection(x)\n        return x\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-25T14:03:47.927184Z","iopub.execute_input":"2026-05-25T14:03:47.927696Z","iopub.status.idle":"2026-05-25T14:03:47.951199Z","shell.execute_reply.started":"2026-05-25T14:03:47.927640Z","shell.execute_reply":"2026-05-25T14:03:47.950028Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CNN_hp().to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.AdamW(\nmodel.parameters() ,\nlr =0.001,\nweight_decay=1e-4\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-25T14:03:47.954037Z","iopub.execute_input":"2026-05-25T14:03:47.954447Z","iopub.status.idle":"2026-05-25T14:03:47.986551Z","shell.execute_reply.started":"2026-05-25T14:03:47.954414Z","shell.execute_reply":"2026-05-25T14:03:47.985646Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 20\n\n# Đảm bảo mô hình ở chế độ Huấn luyện (Training mode) để kích hoạt Dropout/BatchNorm nếu có\nmodel.train()\n\nfor epoch in range(epochs):\n    running_loss = 0.0\n    \n    for images, labels in trainloader:\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n    \n    epoch_loss = running_loss / len(trainloader)\n    print(f\"Epoch [{epoch + 1}/{epochs}] - Loss: {epoch_loss:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-25T14:03:47.987683Z","iopub.execute_input":"2026-05-25T14:03:47.987968Z","iopub.status.idle":"2026-05-25T15:14:53.972003Z","shell.execute_reply.started":"2026-05-25T14:03:47.987940Z","shell.execute_reply":"2026-05-25T15:14:53.970726Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Đưa mô hình về chế độ đánh giá (Evaluation mode)\nmodel.eval()\n\n# ----------------------------------------------------------------\n# 1. TÍNH ACCURACY TRÊN TẬP HUẤN LUYỆN (TRAIN SET)\n# ----------------------------------------------------------------\ntrain_correct = 0\ntrain_total = 0\n\nprint(\"Đang tính toán độ chính xác trên tập Train (vui lòng đợi)...\")\nwith torch.no_grad():\n    for images, labels in trainloader:\n        # Chuyển dữ liệu về CPU (đồng bộ với model)\n        images, labels = images.to(device), labels.to(device)\n        \n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n        train_total += labels.size(0)\n        train_correct += (predicted == labels).sum().item()\n\ntrain_accuracy = 100 * train_correct / train_total\n\n\n# ----------------------------------------------------------------\n# 2. TÍNH ACCURACY TRÊN TẬP KIỂM THỬ (TEST SET)\n# ----------------------------------------------------------------\ntest_correct = 0\ntest_total = 0\nall_preds = []\nall_labels = []\n\nprint(\"Đang tính toán độ chính xác trên tập Test...\")\nwith torch.no_grad():\n    for images, labels in testloader:\n        # Chuyển dữ liệu về CPU\n        images, labels = images.to(device), labels.to(device)\n        \n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n        test_total += labels.size(0)\n        test_correct += (predicted == labels).sum().item()\n        \n        # Lưu kết quả dự đoán (Dùng trực tiếp cho CPU)\n        all_preds.extend(predicted.numpy())\n        all_labels.extend(labels.numpy())\n\ntest_accuracy = 100 * test_correct / test_total\n\n\n# ----------------------------------------------------------------\n# 3. IN BẢNG KẾT QUẢ SO SÁNH TRỰC QUAN\n# ----------------------------------------------------------------\nprint(\"\\n\" + \"=\"*20 + \" SO SÁNH ĐỘ CHÍNH XÁC \" + \"=\"*20)\nprint(f\"Độ chính xác Tập Huấn Luyện (Train Accuracy): {train_accuracy:.2f}%\")\nprint(f\"Độ chính xác Tập Kiểm Thử (Test Accuracy)  : {test_accuracy:.2f}%\")\nprint(\"=\"*52 + \"\\n\")\n\n\n# ----------------------------------------------------------------\n# 4. IN BÁO CÁO PHÂN LOẠI CHI TIẾT & VẼ MA TRẬN NHẦM LẪN (CONFUSION MATRIX)\n# ----------------------------------------------------------------\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix, classification_report\n\nclasses = ('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')\n\nprint(\"============= BÁO CÁO CHI TIẾT (CLASSIFICATION REPORT) =============\")\nprint(classification_report(all_labels, all_preds, target_names=classes, digits=4))\n\nprint(\"\\n============= ĐANG KHỞI TẠO MA TRẬN NHẦM LẪN =============\")\ncm = confusion_matrix(all_labels, all_preds)\nplt.figure(figsize=(10, 8))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=classes, yticklabels=classes)\nplt.title('Confusion Matrix Heatmap (CPU Mode)', fontsize=14, fontweight='bold')\nplt.xlabel('Predicted Labels (Mô hình dự đoán)', fontsize=12)\nplt.ylabel('True Labels (Nhãn thực tế)', fontsize=12)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-25T15:14:53.973823Z","iopub.execute_input":"2026-05-25T15:14:53.974167Z","iopub.status.idle":"2026-05-25T15:16:39.889032Z","shell.execute_reply.started":"2026-05-25T15:14:53.974106Z","shell.execute_reply":"2026-05-25T15:16:39.888036Z"}},"outputs":[],"execution_count":null}]}