{"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":3649,"databundleVersionId":46718}],"dockerImageVersionId":31329,"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 . optim as optim\nimport torchvision\nimport torchvision . transforms as transforms\nimport matplotlib . pyplot as plt\nimport numpy as np\nimport pandas as pd\nfrom sklearn.metrics import confusion_matrix\nimport seaborn as sns\nimport torch.nn as nn\nimport time","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:19:19.755040Z","iopub.execute_input":"2026-05-19T15:19:19.755765Z","iopub.status.idle":"2026-05-19T15:19:19.760013Z","shell.execute_reply.started":"2026-05-19T15:19:19.755732Z","shell.execute_reply":"2026-05-19T15:19:19.759379Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#dataset\ntransform = transforms . Compose (\n[\ntransforms . ToTensor () ,\ntransforms . Normalize ((0.5 ,0.5 ,0.5) ,\n(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\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\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T08:51:51.492780Z","iopub.execute_input":"2026-05-19T08:51:51.493105Z","iopub.status.idle":"2026-05-19T08:51:53.201757Z","shell.execute_reply.started":"2026-05-19T08:51:51.493080Z","shell.execute_reply":"2026-05-19T08:51:53.201076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# bt1. In kích thước dataset\nprint(\"Kích thước train dataset:\", len(trainset))\nprint(\"Kích thước test dataset:\", len(testset))\n\n# 2. Hiển thị 10 ảnh đầu tiên\nclasses = ('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')\ndataiter = iter(trainloader) \nimages, labels = next(dataiter) \n\nfor i in range(10):\n    img = images[i] / 2 + 0.5  # Giải chuẩn hóa (un-normalize)\n    plt.imshow(np.transpose(img.numpy(), (1, 2, 0)))\n    plt.title(classes[labels[i]])\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T08:51:53.203010Z","iopub.execute_input":"2026-05-19T08:51:53.203386Z","iopub.status.idle":"2026-05-19T08:51:54.662375Z","shell.execute_reply.started":"2026-05-19T08:51:53.203361Z","shell.execute_reply":"2026-05-19T08:51:54.661384Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#bt3\nclass CNN(nn.Module):\n    def __init__(self):\n        super(CNN, self).__init__()\n        self.conv1 = nn.Conv2d(3, 64, 3, padding=1) \n        self.conv2 = nn.Conv2d(64, 128, 3, padding=1)\n        self.pool = nn.MaxPool2d(2, 2)\n        # Cập nhật input features từ 64*8*8 lên 128*8*8\n        self.fc1 = nn.Linear(128 * 8 * 8, 512) \n        self.fc2 = nn.Linear(512, 10)\n        self.relu = nn.ReLU()\n\n    def forward(self, x):\n        x = self.pool(self.relu(self.conv1(x)))\n        x = self.pool(self.relu(self.conv2(x)))\n        # Cập nhật kích thước khi duỗi phẳng\n        x = x.view(-1, 128 * 8 * 8) \n        x = self.relu(self.fc1(x))\n        x = self.fc2(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T08:51:54.663513Z","iopub.execute_input":"2026-05-19T08:51:54.663862Z","iopub.status.idle":"2026-05-19T08:51:54.670126Z","shell.execute_reply.started":"2026-05-19T08:51:54.663825Z","shell.execute_reply":"2026-05-19T08:51:54.669480Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#bt4\nclass CNN(nn.Module):\n    def __init__(self):\n        super(CNN, self).__init__()\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=1) \n        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)\n        \n        # Thêm lớp conv3\n        self.conv3 = nn.Conv2d(64, 128, 3, padding=1) \n        \n        self.pool = nn.MaxPool2d(kernel_size=2, stride=2) \n        \n        # Qua 3 lần pool (32 -> 16 -> 8 -> 4), kích thước feature map còn 4x4.\n        self.fc1 = nn.Linear(128 * 4 * 4, 512) \n        self.fc2 = nn.Linear(512, 10) \n        self.relu = nn.ReLU() \n\n    def forward(self, x):\n        x = self.pool(self.relu(self.conv1(x)))\n        x = self.pool(self.relu(self.conv2(x))) \n        \n        # Truyền qua lớp conv3\n        x = self.pool(self.relu(self.conv3(x))) \n        \n        # Cập nhật kích thước khi duỗi phẳng\n        x = x.view(-1, 128 * 4 * 4) \n        x = self.relu(self.fc1(x)) \n        x = self.fc2(x) \n        return x \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T08:51:54.671673Z","iopub.execute_input":"2026-05-19T08:51:54.672037Z","iopub.status.idle":"2026-05-19T08:51:54.697742Z","shell.execute_reply.started":"2026-05-19T08:51:54.672011Z","shell.execute_reply":"2026-05-19T08:51:54.697044Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#bt5\nclass CNN_Bai5(nn.Module):\n    def __init__(self):\n        super(CNN_Bai5, self).__init__()\n        self.conv1 = nn.Conv2d(3, 32, 3, padding=1) \n        self.conv2 = nn.Conv2d(32, 64, 3, padding=1) \n        self.pool = nn.MaxPool2d(2, 2) \n        \n        # Thêm lớp Dropout theo yêu cầu\n        self.dropout = nn.Dropout(0.5)\n        \n        self.fc1 = nn.Linear(64 * 8 * 8, 512)\n        self.fc2 = nn.Linear(512, 10) \n        self.relu = nn.ReLU()\n\n    def forward(self, x):\n        print(f\"1. Input ban đầu:              {x.shape}\")\n        \n        x = self.pool(self.relu(self.conv1(x)))\n        print(f\"2. Sau Conv1 + MaxPool:        {x.shape}\")\n        \n        x = self.pool(self.relu(self.conv2(x)))\n        print(f\"3. Sau Conv2 + MaxPool:        {x.shape}\")\n        \n        x = x.view(-1, 64 * 8 * 8)\n        print(f\"4. Sau khi Duỗi phẳng (View):  {x.shape}\")\n        \n        # Áp dụng Dropout (thường đặt sau khi duỗi phẳng hoặc giữa các lớp FC)\n        x = self.dropout(x)\n        print(f\"5. Sau Dropout (Shape giữ nguyên, giá trị bị tắt ngẫu nhiên 50%): {x.shape}\")\n        \n        x = self.relu(self.fc1(x))\n        x = self.fc2(x)\n        print(f\"6. Output cuối cùng:           {x.shape}\")\n        return x\n\n# --- CHẠY THỬ ---\nmodel_bai5 = CNN_Bai5()\ndummy_image = torch.randn(1, 3, 32, 32)\n\nprint(\"=== QUÁ TRÌNH TRUYỀN QUA MẠNG CÓ DROPOUT ===\")\noutput = model_bai5(dummy_image)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T08:51:54.699036Z","iopub.execute_input":"2026-05-19T08:51:54.699343Z","iopub.status.idle":"2026-05-19T08:51:54.739311Z","shell.execute_reply.started":"2026-05-19T08:51:54.699316Z","shell.execute_reply":"2026-05-19T08:51:54.738558Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#bt6\n# 1. Khai báo mô hình CNN (sạch sẽ, không lỗi)\nclass CNN(nn.Module):\n    def __init__(self):\n        super(CNN, self).__init__()\n        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)\n        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)\n        self.pool = nn.MaxPool2d(2, 2)\n        self.fc1 = nn.Linear(64 * 8 * 8, 512)\n        self.fc2 = nn.Linear(512, 10)\n        self.relu = nn.ReLU()\n\n    def forward(self, x):\n        x = self.pool(self.relu(self.conv1(x)))\n        x = self.pool(self.relu(self.conv2(x)))\n        x = x.view(-1, 64 * 8 * 8)\n        x = self.relu(self.fc1(x))\n        x = self.fc2(x)\n        return x\n\n# 2. Hàm huấn luyện và đánh giá tự động theo Batch Size\ndef evaluate_batch_size(b_size, epochs=2):\n    # Chuẩn bị dữ liệu với batch_size tương ứng\n    transform = transforms.Compose([\n        transforms.ToTensor(),\n        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n    ])\n    \n    trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)\n    trainloader = torch.utils.data.DataLoader(trainset, batch_size=b_size, shuffle=True)\n    \n    testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)\n    testloader = torch.utils.data.DataLoader(testset, batch_size=b_size, shuffle=False)\n    \n    model = CNN()\n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.Adam(model.parameters(), lr=0.001)\n    \n    # Bắt đầu đo thời gian và huấn luyện\n    start_time = time.time()\n    for epoch in range(epochs):\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            \n    train_time = time.time() - start_time\n    \n    # Đánh giá Accuracy\n    correct = 0\n    total = 0\n    with torch.no_grad():\n        for images, labels in testloader:\n            outputs = model(images)\n            predicted = torch.max(outputs, 1)[1]\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n            \n    accuracy = 100 * correct / total\n    \n    # In kết quả ra màn hình giống định dạng trong ảnh\n    print(f\"Training batch_size={b_size}...\")\n    print(f\"  Time: {train_time:.1f}s | Accuracy: {accuracy:.2f}%\")\n    \n    return train_time, accuracy\n\n# 3. Tiến hành chạy thử nghiệm\nbatch_sizes = [32, 64, 128]\ntimes = []\naccuracies = []\n\nprint(\"=== BẮT ĐẦU CHẠY BÀI 6 ===\")\n# Lưu ý: Đang đặt epochs=2 để chạy nhanh. Bạn có thể đổi lên 5 hoặc 10 tùy ý.\nfor b in batch_sizes:\n    t, a = evaluate_batch_size(b, epochs=2) \n    times.append(t)\n    accuracies.append(a)\n\n# 4. Vẽ 2 biểu đồ cột cạnh nhau giống hình mẫu\nbatch_labels = [str(b) for b in batch_sizes]\n\n# Tạo khung vẽ có 1 hàng, 2 cột\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))\n\n# Vẽ biểu đồ thời gian ở cột trái (ax1)\nax1.bar(batch_labels, times, color='#1f77b4') \nax1.set_title('Thời gian huấn luyện')\nax1.set_xlabel('Batch Size')\nax1.set_ylabel('Thời gian (s)')\n\n# Vẽ biểu đồ Accuracy ở cột phải (ax2)\nax2.bar(batch_labels, accuracies, color='#1f77b4')\nax2.set_title('Accuracy')\nax2.set_xlabel('Batch Size')\nax2.set_ylabel('Accuracy (%)')\n\n# Chỉnh bố cục cho gọn gàng và hiển thị\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:25:46.703004Z","iopub.execute_input":"2026-05-19T15:25:46.703694Z","iopub.status.idle":"2026-05-19T15:31:22.120894Z","shell.execute_reply.started":"2026-05-19T15:25:46.703637Z","shell.execute_reply":"2026-05-19T15:31:22.120029Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#bt7\ntransform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])\n\ntrainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)\ntrainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)\n\n# 3. Cài đặt các thông số huấn luyện\nlearning_rates = [0.01, 0.001, 0.0001]\nepochs = 5  # Bạn có thể tăng lên nếu muốn train lâu hơn\nresults = {\"Epoch\": list(range(epochs))}\n\nprint(\"BẮT ĐẦU QUÁ TRÌNH HUẤN LUYỆN TỰ ĐỘNG...\")\nprint(f\"Tổng số Epoch mỗi LR: {epochs}\\n\")\n\n# 4. Vòng lặp huấn luyện tự động lấy số liệu\nfor lr in learning_rates:\n    print(f\"-> Đang huấn luyện với Learning Rate = {lr} ...\")\n    model = CNN()\n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.Adam(model.parameters(), lr=lr)\n    \n    loss_history = []\n    \n    for epoch in range(epochs):\n        running_loss = 0.0\n        for images, labels in trainloader:\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 loss trung bình của epoch và lưu lại\n        avg_loss = running_loss / len(trainloader)\n        loss_history.append(round(avg_loss, 4))\n        print(f\"   Epoch {epoch}: Loss = {avg_loss:.4f}\")\n        \n    results[f\"LR = {lr}\"] = loss_history\n    print(\"-\" * 40)\n\n# 5. In bảng kết quả tự động bằng Pandas\nprint(\"\\n=== BẢNG TỔNG HỢP LOSS ===\")\ndf_results = pd.DataFrame(results)\nprint(df_results.to_string(index=False))\n\n# 6. Tự động vẽ biểu đồ bằng Matplotlib\nplt.figure(figsize=(8, 5))\nplt.plot(results[\"Epoch\"], results[\"LR = 0.01\"], marker='o', color='red', label='LR = 0.01')\nplt.plot(results[\"Epoch\"], results[\"LR = 0.001\"], marker='s', color='green', label='LR = 0.001')\nplt.plot(results[\"Epoch\"], results[\"LR = 0.0001\"], marker='^', color='blue', label='LR = 0.0001')\n\nplt.title('So sánh mức độ giảm Loss theo Learning Rate', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch', fontsize=12)\nplt.ylabel('Training Loss (Average)', fontsize=12)\nplt.xticks(results[\"Epoch\"])\nplt.legend()\nplt.grid(True, linestyle='--', alpha=0.7)\n\nprint(\"\\nĐang mở biểu đồ...\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T08:51:54.750320Z","iopub.execute_input":"2026-05-19T08:51:54.750649Z","iopub.status.idle":"2026-05-19T09:05:18.809585Z","shell.execute_reply.started":"2026-05-19T08:51:54.750615Z","shell.execute_reply":"2026-05-19T09:05:18.808714Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#bt8\nclass CNN(nn.Module):\n    def __init__(self):\n        super(CNN, self).__init__()\n        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)\n        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)\n        self.pool = nn.MaxPool2d(2, 2)\n        self.fc1 = nn.Linear(64 * 8 * 8, 512)\n        self.fc2 = nn.Linear(512, 10)\n        self.relu = nn.ReLU()\n\n    def forward(self, x):\n        x = self.pool(self.relu(self.conv1(x)))\n        x = self.pool(self.relu(self.conv2(x)))\n        x = x.view(-1, 64 * 8 * 8)\n        x = self.relu(self.fc1(x))\n        x = self.fc2(x)\n        return x\n\n# 2. Định nghĩa Transforms (Không Aug vs Có Aug)\ntransform_base = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])\n\ntransform_aug = transforms.Compose([\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])\n\n# 3. Tải dữ liệu\ntrainset_base = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_base)\ntrainloader_base = torch.utils.data.DataLoader(trainset_base, batch_size=64, shuffle=True)\n\ntrainset_aug = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_aug)\ntrainloader_aug = torch.utils.data.DataLoader(trainset_aug, batch_size=64, shuffle=True)\n\ntestset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_base)\ntestloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False)\n\n# 4. Hàm huấn luyện và đánh giá\ndef train_and_evaluate(trainloader, model_name, epochs=5):\n    print(f\"-> Đang huấn luyện: {model_name}...\")\n    model = CNN()\n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.Adam(model.parameters(), lr=0.001)\n    \n    loss_history = []\n    \n    for epoch in range(epochs):\n        running_loss = 0.0\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        avg_loss = running_loss / len(trainloader)\n        loss_history.append(round(avg_loss, 4))\n        print(f\"   Epoch {epoch}: Loss = {avg_loss:.4f}\")\n        \n    # Đánh giá Accuracy\n    correct = 0\n    total = 0\n    with torch.no_grad():\n        for images, labels in testloader:\n            outputs = model(images)\n            predicted = torch.max(outputs, 1)[1]\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n            \n    accuracy = 100 * correct / total\n    return loss_history, accuracy\n\n# 5. Thực thi so sánh\nprint(\"=== BẮT ĐẦU CHẠY BÀI 8: SO SÁNH DATA AUGMENTATION ===\\n\")\nepochs_to_run = 5\nloss_base, acc_base = train_and_evaluate(trainloader_base, \"Baseline (Không Aug)\", epochs_to_run)\nprint(f\">> Test Accuracy (Baseline): {acc_base:.2f}%\\n\")\n\nloss_aug, acc_aug = train_and_evaluate(trainloader_aug, \"Augmented (Có Aug)\", epochs_to_run)\nprint(f\">> Test Accuracy (Augmented): {acc_aug:.2f}%\\n\")\n\n# 6. In bảng so sánh\ndf_results = pd.DataFrame({\n    \"Epoch\": list(range(epochs_to_run)),\n    \"Loss Baseline\": loss_base,\n    \"Loss Augmented\": loss_aug\n})\nprint(\"=== BẢNG SO SÁNH LOSS ===\")\nprint(df_results.to_string(index=False))\n\n# 7. Vẽ biểu đồ\nplt.figure(figsize=(8, 5))\nepochs_range = list(range(epochs_to_run))\nplt.plot(epochs_range, loss_base, marker='o', color='red', label=f'Baseline (Acc: {acc_base:.2f}%)')\nplt.plot(epochs_range, loss_aug, marker='s', color='green', label=f'Augmented (Acc: {acc_aug:.2f}%)')\n\nplt.title('So sánh hiệu suất: Có vs Không sử dụng Data Augmentation', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch', fontsize=12)\nplt.ylabel('Training Loss (Average)', fontsize=12)\nplt.xticks(epochs_range)\nplt.legend()\nplt.grid(True, linestyle='--', alpha=0.7)\n\nprint(\"\\nĐang mở biểu đồ...\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T09:05:18.810939Z","iopub.execute_input":"2026-05-19T09:05:18.811552Z","iopub.status.idle":"2026-05-19T09:14:30.043414Z","shell.execute_reply.started":"2026-05-19T09:05:18.811523Z","shell.execute_reply":"2026-05-19T09:14:30.042738Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#bt9\nclass CNN(nn.Module):\n    def __init__(self):\n        super(CNN, self).__init__()\n        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)\n        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)\n        self.pool = nn.MaxPool2d(2, 2)\n        self.fc1 = nn.Linear(64 * 8 * 8, 512)\n        self.fc2 = nn.Linear(512, 10)\n        self.relu = nn.ReLU()\n\n    def forward(self, x):\n        x = self.pool(self.relu(self.conv1(x)))\n        x = self.pool(self.relu(self.conv2(x)))\n        x = x.view(-1, 64 * 8 * 8)\n        x = self.relu(self.fc1(x))\n        x = self.fc2(x)\n        return x\n\n# 2. Chuẩn bị dữ liệu\ntransform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])\n\ntrainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)\ntrainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)\n\ntestset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)\ntestloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False)\n\nclasses = ('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')\n\n# 3. Huấn luyện nhanh (1 Epoch) để mô hình không đoán mò 100%\nprint(\"Đang huấn luyện nhanh (1 Epoch) để lấy trọng số...\")\nmodel = CNN()\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001)\n\nfor images, labels in trainloader:\n    optimizer.zero_grad()\n    outputs = model(images)\n    loss = criterion(outputs, labels)\n    loss.backward()\n    optimizer.step()\n\n# 4. Chạy dự đoán trên tập Test và gom dữ liệu Labels\nprint(\"Đang chạy dự đoán trên tập Test để tính Confusion Matrix...\")\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():\n    for images, labels in testloader:\n        outputs = model(images)\n        preds = torch.max(outputs, 1)[1]\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n# 5. Tính toán Confusion Matrix\ncm = confusion_matrix(all_labels, all_preds)\n\n# 6. Vẽ Heatmap bằng Seaborn\nplt.figure(figsize=(10, 8))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=classes, yticklabels=classes)\n\nplt.title('Confusion Matrix - CIFAR-10', fontsize=16, fontweight='bold')\nplt.xlabel('Predicted Labels', fontsize=12)\nplt.ylabel('True Labels', fontsize=12)\n\n# Xoay chữ ở trục X để dễ đọc hơn\nplt.xticks(rotation=45)\nplt.yticks(rotation=0)\n\nprint(\"Đang mở biểu đồ Heatmap...\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T09:14:30.044559Z","iopub.execute_input":"2026-05-19T09:14:30.044958Z","iopub.status.idle":"2026-05-19T09:15:25.240650Z","shell.execute_reply.started":"2026-05-19T09:14:30.044882Z","shell.execute_reply":"2026-05-19T09:15:25.239950Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# bt10\n#1. Data Augmentation (Cải thiện theo mục 10.8)\ntransform_train = transforms.Compose([\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])\n\ntransform_test = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])\n\n# Tải dữ liệu\ntrainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train)\ntrainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True) # Dùng Batch size 128\n\ntestset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)\ntestloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False)\n\n# 2. Kiến trúc CNN cải tiến (Kết hợp mục 10.3, 10.4, 10.5)\nclass ImprovedCNN(nn.Module):\n    def __init__(self):\n        super(ImprovedCNN, self).__init__()\n        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)\n        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)\n        self.conv3 = nn.Conv2d(64, 128, 3, padding=1)\n        self.pool = nn.MaxPool2d(2, 2)\n        self.dropout = nn.Dropout(0.3) # Giảm dropout nhẹ để học nhanh hơn\n        self.fc1 = nn.Linear(128 * 4 * 4, 512)\n        self.fc2 = nn.Linear(512, 10)\n        self.relu = nn.ReLU()\n\n    def forward(self, x):\n        x = self.pool(self.relu(self.conv1(x))) # 32x32 -> 16x16\n        x = self.pool(self.relu(self.conv2(x))) # 16x16 -> 8x8\n        x = self.pool(self.relu(self.conv3(x))) # 8x8 -> 4x4\n        x = x.view(-1, 128 * 4 * 4)\n        x = self.dropout(x)\n        x = self.relu(self.fc1(x))\n        x = self.fc2(x)\n        return x\n\n# 3. Huấn luyện mô hình\nmodel = ImprovedCNN()\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001) # Learning rate tối ưu 0.001\n\nepochs = 10 # Tăng epoch để đạt accuracy cao\nloss_history = []\n\nprint(\"BẮT ĐẦU HUẤN LUYỆN MÔ HÌNH CẢI TIẾN...\")\nfor epoch in range(epochs):\n    running_loss = 0.0\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    avg_loss = running_loss / len(trainloader)\n    loss_history.append(avg_loss)\n    print(f\"Epoch {epoch+1}/{epochs} - Loss: {avg_loss:.4f}\")\n\n# 4. Đánh giá cuối cùng\ncorrect = 0\ntotal = 0\nmodel.eval() # Chuyển sang chế độ đánh giá\nwith torch.no_grad():\n    for images, labels in testloader:\n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\nfinal_accuracy = 100 * correct / total\nprint(f\"\\n>> ĐỘ CHÍNH XÁC CUỐI CÙNG: {final_accuracy:.2f}%\")\n\n# 5. Bảng và Biểu đồ so sánh\ndf_loss = pd.DataFrame({\"Epoch\": list(range(1, epochs+1)), \"Loss\": loss_history})\nprint(\"\\n=== BẢNG THEO DÕI LOSS ===\")\nprint(df_loss.to_string(index=False))\n\nplt.figure(figsize=(8, 5))\nplt.plot(df_loss[\"Epoch\"], df_loss[\"Loss\"], marker='o', color='purple', label='Training Loss')\nplt.title(f'Quá trình huấn luyện (Final Accuracy: {final_accuracy:.2f}%)', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.grid(True, linestyle='--')\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T09:15:25.242629Z","iopub.execute_input":"2026-05-19T09:15:25.243387Z","iopub.status.idle":"2026-05-19T09:25:01.686140Z","shell.execute_reply.started":"2026-05-19T09:15:25.243358Z","shell.execute_reply":"2026-05-19T09:25:01.685377Z"}},"outputs":[],"execution_count":null}]}