{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.13"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"},{"sourceId":15444,"sourceType":"datasetVersion","datasetId":11102},{"sourceId":1782442,"sourceType":"datasetVersion","datasetId":1059701}],"dockerImageVersionId":30664,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# <div style=\"padding: 30px;color:white;margin:10;font-size:60%;text-align:left;display:fill;border-radius:10px;background-color:#A7C7E7;overflow:hidden;\"><b><span style='color:#FFFFFF'>1 |</span></b> <b>GIỚI THIỆU</b></div>\n\n### <b><span style='color:#3B82F6'> 1.1 |</span> Mô tả</b>\nBộ dữ liệu CIFAR-10 bao gồm 60.000 hình ảnh màu 32x32 thuộc 10 lớp khác nhau, với 6.000 hình ảnh mỗi lớp. Bộ dữ liệu được chia thành 50.000 hình ảnh huấn luyện và 10.000 hình ảnh kiểm tra. Các lớp này loại trừ lẫn nhau và không có sự chồng chéo giữa chúng.\nTrong notebook này, chúng tôi cố gắng sử dụng Mạng nơron đa lớp (MLP), Mạng nơron tích chập (CNN) và Học chuyển giao (ResNet50 - TensorFlow) để xây dựng một mô hình có thể nhận dạng hình ảnh của tập dữ liệu thử nghiệm sau khi được huấn luyện trên tập dữ liệu huấn luyện từ CIFAR-10.\n\n### <b><span style='color:#3B82F6'> 1.2 |</span> Đặc điểm</b>\n- Số lượng mẫu: 60.000 (50.000 để huấn luyện, 10.000 để thử nghiệm)\n\n- Kích thước ảnh: 32x32 pixel, ảnh màu (3 kênh màu)\n\n- Số lượng lớp: 10\n\n### <b><span style='color:#3B82F6'> 1.3 |</span> Khả năng truy cập</b>\nBộ dữ liệu CIFAR-10 được sử dụng rộng rãi trong nghiên cứu học máy và thị giác máy tính và có thể dễ dàng truy cập thông qua các thư viện như TensorFlow và PyTorch.\n","metadata":{"jp-MarkdownHeadingCollapsed":true}},{"cell_type":"markdown","source":"# <div style=\"padding: 30px;color:white;margin:10;font-size:60%;text-align:left;display:fill;border-radius:10px;background-color:#FFFFFF;overflow:hidden;background-color:#A7C7E7\"><b><span style='color:#FFFFFF'>2 |</span></b> <b>LOAD AND EXPLORATION</b></div>","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 2.1 |</span> Load</b>","metadata":{}},{"cell_type":"code","source":"!pip install tensorflow","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:28:56.604699Z","iopub.execute_input":"2026-01-18T12:28:56.605328Z","iopub.status.idle":"2026-01-18T12:29:05.887186Z","shell.execute_reply.started":"2026-01-18T12:28:56.605297Z","shell.execute_reply":"2026-01-18T12:29:05.885968Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')\n\nimport tensorflow as tf\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:05.889359Z","iopub.execute_input":"2026-01-18T12:29:05.889702Z","iopub.status.idle":"2026-01-18T12:29:05.896713Z","shell.execute_reply.started":"2026-01-18T12:29:05.889676Z","shell.execute_reply":"2026-01-18T12:29:05.895844Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, sys, platform, subprocess, textwrap\nfrom datetime import datetime\n\ndef run_cmd(cmd):\n    try:\n        out = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, text=True)\n        return out.strip()\n    except Exception as e:\n        return f\"(Không chạy được) {cmd}\\n{e}\"\n\ndef bytes_to_gb(x):\n    return x / (1024**3)\n\nprint(\"=\"*70)\nprint(\"MÔI TRƯỜNG THỰC NGHIỆM (Kaggle)\")\nprint(\"=\"*70)\nprint(\"Thời gian:\", datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"))\nprint(\"Hệ điều hành:\", platform.platform())\nprint(\"Python:\", sys.version.split(\"\\n\")[0])\nprint(\"Processor:\", platform.processor() if platform.processor() else \"(Không xác định)\")\nprint()\n\n# RAM info\ntry:\n    import psutil\n    mem = psutil.virtual_memory()\n    print(f\"RAM tổng: {bytes_to_gb(mem.total):.2f} GB\")\n    print(f\"RAM khả dụng: {bytes_to_gb(mem.available):.2f} GB\")\nexcept:\n    print(\"RAM: (không đọc được - thiếu psutil)\")\n\nprint()\n\n# GPU info via nvidia-smi\nprint(\"GPU (nvidia-smi):\")\nprint(run_cmd(\"nvidia-smi -L\"))\nprint()\n\nprint(\"Chi tiết GPU (nvidia-smi):\")\nprint(run_cmd(\"nvidia-smi\"))\nprint()\n\n# CUDA version (if available)\nprint(\"CUDA (nvcc):\")\nprint(run_cmd(\"nvcc --version\"))\nprint()\n\n# TensorFlow info\nprint(\"=\"*70)\nprint(\"THƯ VIỆN DEEP LEARNING\")\nprint(\"=\"*70)\n\ntry:\n    import tensorflow as tf\n    print(\"TensorFlow:\", tf.__version__)\n    gpus = tf.config.list_physical_devices(\"GPU\")\n    print(\"TF GPU devices:\", gpus if gpus else \"Không có GPU\")\nexcept Exception as e:\n    print(\"TensorFlow: Không có hoặc lỗi import\")\n    print(e)\n\nprint()\n\n# PyTorch info\ntry:\n    import torch\n    print(\"PyTorch:\", torch.__version__)\n    print(\"CUDA available:\", torch.cuda.is_available())\n    if torch.cuda.is_available():\n        print(\"CUDA version (torch):\", torch.version.cuda)\n        print(\"GPU name:\", torch.cuda.get_device_name(0))\nexcept Exception as e:\n    print(\"PyTorch: Không có hoặc lỗi import\")\n    print(e)\n\nprint()\n\n# Common libs\nlibs = [\"numpy\", \"pandas\", \"matplotlib\", \"sklearn\"]\nprint(\"Một số thư viện phổ biến:\")\nfor lib in libs:\n    try:\n        m = __import__(lib)\n        ver = getattr(m, \"__version__\", \"(unknown)\")\n        print(f\"- {lib}: {ver}\")\n    except:\n        print(f\"- {lib}: Không có\")\n\nprint(\"=\"*70)\n","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:05.898810Z","iopub.execute_input":"2026-01-18T12:29:05.899037Z","iopub.status.idle":"2026-01-18T12:29:06.057672Z","shell.execute_reply.started":"2026-01-18T12:29:05.899018Z","shell.execute_reply":"2026-01-18T12:29:06.056740Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import platform, sys\nimport pandas as pd\n\nrows = []\n\nrows.append([\"Hệ điều hành\", platform.platform()])\nrows.append([\"Python\", sys.version.split(\"\\n\")[0]])\n\n# GPU\nimport subprocess\ndef cmd(x):\n    try:\n        return subprocess.check_output(x, shell=True, text=True).strip()\n    except:\n        return \"Không có\"\n\nrows.append([\"GPU\", cmd(\"nvidia-smi -L\")])\n\n# TensorFlow\ntry:\n    import tensorflow as tf\n    rows.append([\"TensorFlow\", tf.__version__])\nexcept:\n    rows.append([\"TensorFlow\", \"Không có\"])\n\n# PyTorch\ntry:\n    import torch\n    rows.append([\"PyTorch\", torch.__version__])\nexcept:\n    rows.append([\"PyTorch\", \"Không có\"])\n\ndf = pd.DataFrame(rows, columns=[\"Thành phần\", \"Thông tin\"])\ndf\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-18T12:29:06.058864Z","iopub.execute_input":"2026-01-18T12:29:06.059130Z","iopub.status.idle":"2026-01-18T12:29:06.104188Z","shell.execute_reply.started":"2026-01-18T12:29:06.059108Z","shell.execute_reply":"2026-01-18T12:29:06.103308Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load the CIFAR-10 dataset\ncifar10 = tf.keras.datasets.cifar10\n(x_train, y_train), (x_test, y_test) = cifar10.load_data()\n\n# Exploring the dataset\nprint(\"Training set shape:\", x_train.shape)\nprint(\"Test set shape:\", x_test.shape)\n","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:06.107292Z","iopub.execute_input":"2026-01-18T12:29:06.107620Z","iopub.status.idle":"2026-01-18T12:29:06.935160Z","shell.execute_reply.started":"2026-01-18T12:29:06.107598Z","shell.execute_reply":"2026-01-18T12:29:06.934237Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Train Images Shape:      ', x_train.shape)\nprint('Train Labels Shape:      ', y_train.shape)\n\nprint('\\nTest Images Shape:       ', x_test.shape)\nprint('Test Labels Shape:       ', y_test.shape)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:06.936463Z","iopub.execute_input":"2026-01-18T12:29:06.936823Z","iopub.status.idle":"2026-01-18T12:29:06.942707Z","shell.execute_reply.started":"2026-01-18T12:29:06.936792Z","shell.execute_reply":"2026-01-18T12:29:06.941625Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 2.2 |</span> Explore</b>","metadata":{}},{"cell_type":"code","source":"# Exploring the CIFAR-10 dataset with and overview\n\n# CIFAR-10 classes\nclass_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']\n               \n# Create a new figure\nplt.figure(figsize=(15,15))\n\n# Loop over the first 25 images\nfor i in range(64):\n    # Create a subplot for each image\n    plt.subplot(8, 8, i+1)\n    plt.xticks([])\n    plt.yticks([])\n    plt.grid(False)\n\n    # Display the image\n    plt.imshow(x_train[i])\n\n    # Set the label as the title\n    plt.title(class_names[y_train[i][0]], fontsize=12)\n\n# Display the figure\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:06.944056Z","iopub.execute_input":"2026-01-18T12:29:06.944410Z","iopub.status.idle":"2026-01-18T12:29:09.366780Z","shell.execute_reply.started":"2026-01-18T12:29:06.944386Z","shell.execute_reply":"2026-01-18T12:29:09.365872Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define class names\nclasses_name = ['Airplane', 'Automobile', 'Bird', 'Cat', 'Deer', 'Dog', 'Frog', 'Horse', 'Ship', 'Truck']\n\n# Get class distribution for training and testing sets\ntrain_classes, train_counts = np.unique(y_train, return_counts=True)\ntest_classes, test_counts = np.unique(y_test, return_counts=True)\n\n# Set figure size and create subplots\nplt.figure(figsize=(15, 5))\n\n# Plot class distribution for training set\nplt.subplot(1, 2, 1)\nplt.barh(classes_name, train_counts, color='blue')\nplt.xlabel('Số lượng mẫu')\nplt.title('Phân bổ lớp trong tập huấn luyện')\n\n# Plot class distribution for testing set\nplt.subplot(1, 2, 2)\nplt.barh(classes_name, test_counts, color='orange')\nplt.xlabel('Số lượng mẫu')\nplt.title('Phân bổ lớp trong tập kiểm thử')\n\n# Adjust layout and display the plot\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <div style=\"padding: 30px;color:white;margin:10;font-size:60%;text-align:left;display:fill;border-radius:10px;background-color:#FFFFFF;overflow:hidden;background-color:#A7C7E7\"><b><span style='color:#FFFFFF'>3 |</span></b> <b>DATA NORMALIZATION</b></div>\n\n","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 3.1 |</span> Normalization to [0,1]</b>\n- Tính toán giá trị pixel nhỏ nhất và lớn nhất trong tập dữ liệu huấn luyện.\n\n- Điều chỉnh tỷ lệ các giá trị pixel để nằm trong khoảng [0,1] bằng công thức Min-Max Scaling.","metadata":{}},{"cell_type":"code","source":"# Phương pháp 1: Chuẩn hóa về [0,1] bằng cách sử dụng phép chia tỷ lệ Min-Max\nx_train_min = np.min(x_train)\nx_train_max = np.max(x_train)\nx_train_normalized = (x_train - x_train_min) / (x_train_max - x_train_min)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:09.861626Z","iopub.execute_input":"2026-01-18T12:29:09.862379Z","iopub.status.idle":"2026-01-18T12:29:10.331646Z","shell.execute_reply.started":"2026-01-18T12:29:09.862346Z","shell.execute_reply":"2026-01-18T12:29:10.330836Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"x_test_normalized = (x_test - x_train_min) / (x_train_max - x_train_min)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:10.332737Z","iopub.execute_input":"2026-01-18T12:29:10.333022Z","iopub.status.idle":"2026-01-18T12:29:10.419996Z","shell.execute_reply.started":"2026-01-18T12:29:10.332998Z","shell.execute_reply":"2026-01-18T12:29:10.419220Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 3.2 |</span> Normalization to [-1,1] (Standardisation)</b>","metadata":{}},{"cell_type":"code","source":"# Phương pháp 2: Chuẩn hóa về [-1,1] theo phân phối chuẩn\nx_train_mean = np.mean(x_train)\nx_train_std = np.std(x_train)\nx_train_standardized = (x_train - x_train_mean) / x_train_std","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:10.421099Z","iopub.execute_input":"2026-01-18T12:29:10.421381Z","iopub.status.idle":"2026-01-18T12:29:11.705174Z","shell.execute_reply.started":"2026-01-18T12:29:10.421360Z","shell.execute_reply":"2026-01-18T12:29:11.704487Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"x_test_standardized = (x_test - x_train_mean) / x_train_std","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:11.706155Z","iopub.execute_input":"2026-01-18T12:29:11.706385Z","iopub.status.idle":"2026-01-18T12:29:11.806313Z","shell.execute_reply.started":"2026-01-18T12:29:11.706365Z","shell.execute_reply":"2026-01-18T12:29:11.805544Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <div style=\"padding: 30px;color:white;margin:10;font-size:60%;text-align:left;display:fill;border-radius:10px;background-color:#FFFFFF;overflow:hidden;background-color:#A7C7E7\"><b><span style='color:#FFFFFF'>4 |</span></b> <b>TRIỂN KHAI MÔ HÌNH PERCEPTRON ĐA LỚP CHO PHÂN LOẠI CIFAR-10</b></div>\n**Objective:** Xây dựng và huấn luyện mạng nơ-ron đa lớp (MLP) để phân loại hình ảnh từ tập dữ liệu CIFAR-10 và đạt độ chính xác >50%.","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 4.1 |</span> Dataset Preparation</b>\n- Tải và chuẩn hóa tập dữ liệu CIFAR-10.\n\n- Làm phẳng ảnh để sử dụng với mạng nơ-ron đa lớp (MLP), vì MLP không tính đến cấu trúc không gian của ảnh.\n\n- Sau khi làm phẳng, mỗi ảnh được biểu diễn dưới dạng mảng một chiều có kích thước 3072 (32 * 32 * 3).\n\n- Thuộc tính shape của mảng cung cấp thông tin về kích thước của mảng.","metadata":{}},{"cell_type":"code","source":"# Làm phẳng\nx_train_flat = x_train_normalized.reshape(x_train_normalized.shape[0], -1)\nx_test_flat = x_test_normalized.reshape(x_test_normalized.shape[0], -1)\n\nprint(\"Flattened Training Images Shape:\", x_train_flat.shape)\nprint(\"Flattened Testing Images Shape:\", x_test_flat.shape)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:11.807345Z","iopub.execute_input":"2026-01-18T12:29:11.807614Z","iopub.status.idle":"2026-01-18T12:29:12.438192Z","shell.execute_reply.started":"2026-01-18T12:29:11.807591Z","shell.execute_reply":"2026-01-18T12:29:12.437311Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 4.2 |</span> Kiến trúc mô hình</b>\n","metadata":{}},{"cell_type":"code","source":"# Define the MLP model\nmodel = tf.keras.models.Sequential(name=\"MLP_model\") \n\n# Add layers to the model\nmodel.add(tf.keras.layers.Dense(512, activation='relu', input_shape=(3072,)))  # 3072 = 32x32x3\nmodel.add(tf.keras.layers.Dense(10, activation='softmax'))","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:12.442057Z","iopub.execute_input":"2026-01-18T12:29:12.442352Z","iopub.status.idle":"2026-01-18T12:29:12.480101Z","shell.execute_reply.started":"2026-01-18T12:29:12.442329Z","shell.execute_reply":"2026-01-18T12:29:12.479421Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 4.3 |</span> Training</b>\n","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.optimizers import Adam\n\n# Compile the model\noptimizer = Adam()  # Use Adam optimizer\nmodel.compile(optimizer=optimizer,\n              loss='sparse_categorical_crossentropy',\n              metrics=['accuracy'])\n\n# Display the model summary\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:12.481343Z","iopub.execute_input":"2026-01-18T12:29:12.481978Z","iopub.status.idle":"2026-01-18T12:29:12.503180Z","shell.execute_reply.started":"2026-01-18T12:29:12.481947Z","shell.execute_reply":"2026-01-18T12:29:12.502363Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Train the model on the CIFAR-10 training set\nhistory = model.fit(x_train_flat, y_train,\n                    epochs=10,  \n                    batch_size=128,  \n                    validation_split=0.1)  # Use 10% of training data as validation\n\n# Evaluate the model on the test set\ntest_loss, test_accuracy = model.evaluate(x_test_flat, y_test)\nprint(\"Test Loss:\", test_loss)\nprint(\"Test Accuracy:\", test_accuracy)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:12.504487Z","iopub.execute_input":"2026-01-18T12:29:12.504819Z","iopub.status.idle":"2026-01-18T12:29:28.624216Z","shell.execute_reply.started":"2026-01-18T12:29:12.504790Z","shell.execute_reply":"2026-01-18T12:29:28.623280Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 4.4 |</span> Evaluation</b>\n\n<span style=\"color: blue;\">\nHiệu năng của mô hình trên tập dữ liệu thử nghiệm CIFAR-10 như sau:\n\n- Test Loss: 1.50\n- Test Accuracy: 0.4587 (45.87%)","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\nĐộ chính xác của mô hình trên tập train tăng dần qua các epoch, đạt khoảng 46% vào cuối quá trình huấn luyện.\n\nTương tự, độ chính xác trên tập valid cũng tăng lên nhưng vẫn thấp hơn một chút so với độ chính xác trên tập train, cho thấy mô hình có khả năng khái quát hóa khá tốt đối với dữ liệu chưa được nhìn thấy.\n\nLoss của mô hình (cả train và valid) giảm đều đặn qua các epoch, điều này là bình thường trong quá trình huấn luyện.\n\nTuy nhiên, vẫn còn chỗ để cải thiện vì độ chính xác vẫn chưa đạt trên 50%.","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\nCác bước tiếp theo có thể để cải thiện:\n\n- Độ phức tạp của mô hình: Thử nghiệm tăng độ phức tạp của mô hình bằng cách thêm nhiều lớp, nơron hoặc sử dụng các kiến ​​trúc khác nhau như mạng nơron tích chập (CNN). CNN đặc biệt hiệu quả cho các tác vụ phân loại hình ảnh như CIFAR-10 vì chúng có thể nắm bắt các mẫu không gian hiệu quả hơn.\n\n- Tinh chỉnh siêu tham số: Thử nghiệm với các siêu tham số khác nhau như tốc độ học, kích thước lô, số lượng epoch và cài đặt tối ưu hóa để tìm cấu hình tối ưu cho mô hình của bạn.\n\n- Tăng cường dữ liệu: Tăng cường dữ liệu huấn luyện bằng các kỹ thuật như xoay, thu phóng, lật hoặc thêm nhiễu để tăng tính đa dạng của các mẫu huấn luyện và cải thiện độ mạnh mẽ của mô hình.","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 4.5 |</span> Hyperparameter Tuning</b>\n\nĐiều chỉnh siêu tham số: Bên cạnh tốc độ học, kích thước lô và số lượng epoch, số lượng và kích thước các lớp của MLP cũng là các siêu tham số có thể được điều chỉnh. Thêm nhiều lớp và/hoặc tăng số lượng nơ-ron trên mỗi lớp có thể cho phép mô hình hóa các hàm phức tạp hơn, nhưng điều này cũng làm tăng nguy cơ quá khớp và chi phí tính toán. Nên bắt đầu với các mô hình đơn giản hơn và tăng dần độ phức tạp. Hãy thử các kiến ​​trúc sau sử dụng ReLU làm hàm kích hoạt cho các lớp ẩn và softmax làm hàm kích hoạt cho lớp đầu ra:\n\n- (1) MLP với một lớp ẩn gồm 128 nơ-ron.\n\n- (2) MLP với một lớp ẩn gồm 256 nơ-ron.\n\n- (3) MLP với hai lớp ẩn gồm lần lượt 256 và 128 nơ-ron.\n","metadata":{}},{"cell_type":"code","source":"# Định nghĩa kiến trúc MLP\ndef create_mlp_model_1():\n    model = tf.keras.models.Sequential([\n              \n        # Hidden layer with 128 neurons and ReLU activation\n        tf.keras.layers.Dense(128, activation='relu', input_shape=(3072,)), # 3072 = 32x32x3\n        \n        # Output layer with softmax activation for multi-class classification\n        tf.keras.layers.Dense(10, activation='softmax')\n    ])\n    return model\n\ndef create_mlp_model_2():\n    model = tf.keras.models.Sequential([\n                \n        # Hidden layer with 256 neurons and ReLU activation\n        tf.keras.layers.Dense(256, activation='relu', input_shape=(3072,)), # 3072 = 32x32x3\n        \n        # Output layer with softmax activation for multi-class classification\n        tf.keras.layers.Dense(10, activation='softmax')\n    ])\n    return model\n\ndef create_mlp_model_3():\n    model = tf.keras.models.Sequential([\n               \n        # First hidden layer with 256 neurons and ReLU activation\n        tf.keras.layers.Dense(256, activation='relu', input_shape=(3072,)), # 3072 = 32x32x3\n        \n        # Second hidden layer with 128 neurons and ReLU activation\n        tf.keras.layers.Dense(128, activation='relu'),\n        \n        # Output layer with softmax activation for multi-class classification\n        tf.keras.layers.Dense(10, activation='softmax')\n    ])\n    return model\n","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:28.625408Z","iopub.execute_input":"2026-01-18T12:29:28.625739Z","iopub.status.idle":"2026-01-18T12:29:28.633245Z","shell.execute_reply.started":"2026-01-18T12:29:28.625715Z","shell.execute_reply":"2026-01-18T12:29:28.632227Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compile and train the model\ndef compile_and_train_model(model):\n    # Compile the model\n    optimizer = Adam()  # Use Adam optimizer\n    model.compile(optimizer=optimizer,\n              loss='sparse_categorical_crossentropy',\n              metrics=['accuracy'])\n    \n    # Train the model on the CIFAR-10 training set\n    history = model.fit(x_train_flat, y_train,\n                        epochs=10,  \n                        batch_size=128,  \n                        validation_split=0.1)  # Use 10% of training data as validation\n\n    # Evaluate the model on the test set\n    test_loss, test_accuracy = model.evaluate(x_test_flat, y_test)\n    print(\"Test Loss:\", test_loss)\n    print(\"Test Accuracy:\", test_accuracy)\n","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:28.634470Z","iopub.execute_input":"2026-01-18T12:29:28.634977Z","iopub.status.idle":"2026-01-18T12:29:28.645304Z","shell.execute_reply.started":"2026-01-18T12:29:28.634947Z","shell.execute_reply":"2026-01-18T12:29:28.644570Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# Create and train models\nprint(\"MLP Model with one hidden layer (128 neurons):\")\nmlp_model_1 = create_mlp_model_1()\ncompile_and_train_model(mlp_model_1)\n\nprint(\"\\nMLP Model with one hidden layer (256 neurons):\")\nmlp_model_2 = create_mlp_model_2()\ncompile_and_train_model(mlp_model_2)\n\nprint(\"\\nMLP Model with two hidden layers (256 and 128 neurons):\")\nmlp_model_3 = create_mlp_model_3()\ncompile_and_train_model(mlp_model_3)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:29:28.646556Z","iopub.execute_input":"2026-01-18T12:29:28.647254Z","iopub.status.idle":"2026-01-18T12:30:14.666596Z","shell.execute_reply.started":"2026-01-18T12:29:28.647224Z","shell.execute_reply":"2026-01-18T12:30:14.665510Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\nCó vẻ như mô hình 3 (MLP với hai lớp ẩn gồm lần lượt 256 và 128 nơron) gần đạt 50% nhất nhưng vẫn chưa đạt. Việc thêm các lớp ẩn dường như cho kết quả tốt hơn.\n\nChúng tôi tiến hành kiểm tra bằng cách:\n- Thêm một lớp nữa với 512 nơron\n- Điều chỉnh số epoch thành 20\n- Điều chỉnh kích thước batch thành 256","metadata":{}},{"cell_type":"code","source":"# Define the MLP model\nmodel = tf.keras.models.Sequential(name=\"MLP_model_3layer\") \n\n# Add layers to the model\nmodel.add(tf.keras.layers.Dense(512, activation='relu', input_shape=(3072,)))  # 3072 = 32x32x3\nmodel.add(tf.keras.layers.Dense(256, activation='relu'))\nmodel.add(tf.keras.layers.Dense(128, activation='relu'))        \nmodel.add(tf.keras.layers.Dense(10, activation='softmax'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-18T12:30:14.669529Z","iopub.execute_input":"2026-01-18T12:30:14.670200Z","iopub.status.idle":"2026-01-18T12:30:14.720078Z","shell.execute_reply.started":"2026-01-18T12:30:14.670176Z","shell.execute_reply":"2026-01-18T12:30:14.719321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compile the model\noptimizer = Adam()  # Use Adam optimizer\nmodel.compile(optimizer=optimizer,\n              loss='sparse_categorical_crossentropy',\n              metrics=['accuracy'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-18T12:30:14.720980Z","iopub.execute_input":"2026-01-18T12:30:14.721198Z","iopub.status.idle":"2026-01-18T12:30:14.732245Z","shell.execute_reply.started":"2026-01-18T12:30:14.721180Z","shell.execute_reply":"2026-01-18T12:30:14.731492Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Train the model on the CIFAR-10 training set\nhistory = model.fit(x_train_flat, y_train,\n                    epochs=20,  \n                    batch_size=256,  \n                    validation_split=0.1)  # Use 10% of training data as validation\n\n# Evaluate the model on the test set\ntest_loss, test_accuracy = model.evaluate(x_test_flat, y_test)\nprint(\"Test Loss:\", test_loss)\nprint(\"Test Accuracy:\", test_accuracy)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-18T12:30:14.733352Z","iopub.execute_input":"2026-01-18T12:30:14.733609Z","iopub.status.idle":"2026-01-18T12:30:35.846498Z","shell.execute_reply.started":"2026-01-18T12:30:14.733590Z","shell.execute_reply":"2026-01-18T12:30:35.845515Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\nSự kết hợp của những thay đổi này đã dẫn đến hiệu suất và độ chính xác được cải thiện (>50%).\n\n- **Thêm một lớp với 512 nơ-ron ở đầu:** Việc thêm nhiều lớp và nơ-ron cho phép mô hình nắm bắt được các mẫu và mối quan hệ phức tạp hơn trong dữ liệu. Với nhiều nơ-ron và lớp hơn, mô hình trở nên biểu cảm hơn và có thể học được các biểu diễn phức tạp hơn của dữ liệu đầu vào. Về bản chất, thay đổi này làm tăng khả năng học hỏi từ dữ liệu của mô hình.\n\n- **Điều chỉnh số epoch thành 20:** Tăng số epoch cho phép mô hình nhìn thấy toàn bộ tập dữ liệu nhiều lần hơn trong quá trình huấn luyện. Thời gian huấn luyện kéo dài này cung cấp nhiều cơ hội hơn cho mô hình học hỏi từ dữ liệu và cải thiện hiệu suất của nó. Tuy nhiên, điều cần thiết là phải thận trọng về hiện tượng quá khớp (overfitting), có thể xảy ra nếu mô hình ghi nhớ dữ liệu huấn luyện thay vì học các mẫu có thể khái quát hóa.\n\n- **Điều chỉnh kích thước batch thành 256:** Kích thước batch xác định số lượng mẫu được sử dụng trong mỗi lần lặp huấn luyện. Kích thước batch lớn hơn có thể dẫn đến sự hội tụ nhanh hơn trong quá trình huấn luyện vì mô hình cập nhật các tham số của nó ít thường xuyên hơn. Phương pháp này cũng có thể cung cấp độ dốc ổn định hơn, dẫn đến khả năng khái quát hóa tốt hơn. Tuy nhiên, kích thước lô lớn hơn có thể yêu cầu nhiều bộ nhớ và tài nguyên tính toán hơn.\n\n","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 4.6 |</span> Kiểm tra trực quan các dự đoán của mô hình MLP (51.8% Accuracy)</b>","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\n# Get predictions for the test set\npredictions_mlp = model.predict(x_test_flat)\npredicted_labels_mlp = np.argmax(predictions_mlp, axis=1)\n\n# Plot sample images with predicted labels\nplt.figure(figsize=(10, 10))\nfor i in range(25):  # Adjust as needed\n    plt.subplot(5, 5, i + 1)\n    plt.xticks([])\n    plt.yticks([])\n    plt.grid(False)\n    plt.imshow(x_test[i])  # Plot original image\n    true_label = class_names[y_test[i][0]]  # Get true label\n    predicted_label_mlp = class_names[predicted_labels_mlp[i]]  # Get predicted label\n    if true_label == predicted_label_mlp:\n        color = 'green'  # Correct prediction\n    else:\n        color = 'red'  # Incorrect prediction\n    plt.xlabel(f\"True: {true_label}\\nPred (MLP): {predicted_label_mlp}\", color=color)\nplt.tight_layout()  # Adjust subplot layout to prevent overlap\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-18T12:30:35.847792Z","iopub.execute_input":"2026-01-18T12:30:35.848473Z","iopub.status.idle":"2026-01-18T12:30:38.879313Z","shell.execute_reply.started":"2026-01-18T12:30:35.848418Z","shell.execute_reply":"2026-01-18T12:30:38.878362Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <div style=\"padding: 30px;color:white;margin:10;font-size:60%;text-align:left;display:fill;border-radius:10px;background-color:#FFFFFF;overflow:hidden;background-color:#A7C7E7\"><b><span style='color:#FFFFFF'>5 |</span></b> <b>XÂY DỰNG MẠNG CNN CHO CIFAR-10</b></div>\n**Objective:** Phát triển một mạng nơ-ron tích chập (CNN) để cải thiện hiệu suất phân loại trên tập dữ liệu CIFAR-10 và đạt độ chính xác >70%.","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#FFCE30'> 5.1 |</span> Dataset Preparation</b>\n- Tải và chuẩn hóa tập dữ liệu CIFAR-10.\n\n- Đảm bảo các hình ảnh được chuẩn hóa về phạm vi phù hợp cho quá trình xử lý CNN.","metadata":{}},{"cell_type":"code","source":"# Convert to float32\nx_train = x_train.astype('float32')\nx_test = x_test.astype('float32')\n\n# Normalize the training set\nx_train_min = x_train.min(axis=(0, 1, 2), keepdims=True)\nx_train_max = x_train.max(axis=(0, 1, 2), keepdims=True)\nx_train_normalized = (x_train - x_train_min) / (x_train_max - x_train_min)\n\n# Normalize the test set using training set statistics\nx_test_normalized = (x_test - x_train_min) / (x_train_max - x_train_min)\n\n# Display the shapes of the normalized datasets\nprint(\"x_train_normalized shape:\", x_train_normalized.shape)\nprint(\"x_test_normalized shape:\", x_test_normalized.shape)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:30:38.880754Z","iopub.execute_input":"2026-01-18T12:30:38.881167Z","iopub.status.idle":"2026-01-18T12:30:39.783976Z","shell.execute_reply.started":"2026-01-18T12:30:38.881133Z","shell.execute_reply":"2026-01-18T12:30:39.783121Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 5.2 |</span>Kiến trúc CNN</b>\n- Thiết kế một mạng CNN bao gồm các lớp tích chập, hàm kích hoạt, lớp gộp và các lớp kết nối đầy đủ.\n\n- Trình bày chi tiết lựa chọn kích thước kernel, kích thước lớp gộp và độ sâu của kiến ​​trúc.","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import layers, models\n\n# Define the CNN model\nmodel = models.Sequential(name=\"CNN_model\")\n\n# Convolutional layers\nmodel.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))\nmodel.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))\nmodel.add(layers.MaxPooling2D((2, 2)))\n\n# Flatten layer to transition from convolutional to dense layers\nmodel.add(layers.Flatten())\n\n# Dense (fully connected) layers\nmodel.add(layers.Dense(128, activation='relu'))\nmodel.add(layers.Dense(10, activation='softmax'))  # Output layer with 10 classes\n","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:30:39.785110Z","iopub.execute_input":"2026-01-18T12:30:39.785370Z","iopub.status.idle":"2026-01-18T12:30:39.860975Z","shell.execute_reply.started":"2026-01-18T12:30:39.785348Z","shell.execute_reply":"2026-01-18T12:30:39.860264Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\n- Lớp đầu tiên có 32 bộ lọc, tiếp theo là hàm kích hoạt ReLU, đệm 'same' và hình dạng đầu vào là (32, 32, 3) tương ứng với ảnh RGB 32x32.\n\n- Các lớp tiếp theo tiếp tục thêm các lớp tích chập với số lượng bộ lọc và hàm kích hoạt ReLU khác nhau.\n\n- Các lớp MaxPooling với kích thước pool là (2, 2) được thêm vào sau các lớp tích chập để giảm kích thước bản đồ đặc trưng.\n\n- Một lớp Flatten được thêm vào để chuyển tiếp từ các lớp tích chập sang các lớp kết nối đầy đủ. Nó làm phẳng đầu vào mà không ảnh hưởng đến kích thước batch.\n\n- Sau đó, chúng ta thêm các lớp kết nối đầy đủ (dense) vào mô hình. Lớp đầu tiên có 128 nơ-ron với hàm kích hoạt ReLU, và lớp đầu ra có 10 nơ-ron (một cho mỗi lớp trong CIFAR-10) với hàm kích hoạt softmax, điển hình cho các bài toán phân loại đa lớp.","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 5.3 |</span> Training</b>\n- Compile mạng CNN với hàm mất mát và thuật toán tối ưu hóa phù hợp.\n- Sử dụng các kỹ thuật như dropout và chuẩn hóa theo lô để ngăn ngừa hiện tượng quá khớp và đảm bảo quá trình huấn luyện ổn định hơn.","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import layers, models\n\n# Define the CNN model\nmodel = models.Sequential(name=\"CNN_model\")\n\n# Convolutional layers\nmodel.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))\nmodel.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Dropout(0.25))  # Dropout layer to prevent overfitting\nmodel.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Dropout(0.25))  # Dropout layer\n\n# Flatten layer to transition from convolutional to dense layers\nmodel.add(layers.Flatten())\n\n# Dense (fully connected) layers with batch normalization and dropout\nmodel.add(layers.Dense(128, activation='relu'))\nmodel.add(layers.BatchNormalization())  # Batch normalization layer\nmodel.add(layers.Dropout(0.5))  # Dropout layer\nmodel.add(layers.Dense(10, activation='softmax'))  # Output layer with 10 classes\n\n# Compile the model with suitable loss function and optimizer\noptimizer = Adam(learning_rate=0.0001)  # Use Adam optimizer\nmodel.compile(optimizer=optimizer,\n              loss='sparse_categorical_crossentropy',\n              metrics=['accuracy'])\n\n# Display the model summary\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:30:39.861965Z","iopub.execute_input":"2026-01-18T12:30:39.862195Z","iopub.status.idle":"2026-01-18T12:30:39.998202Z","shell.execute_reply.started":"2026-01-18T12:30:39.862175Z","shell.execute_reply":"2026-01-18T12:30:39.997401Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 5.4 |</span> Evaluation and Comparison</b>\n- Báo cáo độ chính xác của mô hình trên tập dữ liệu kiểm thử CIFAR-10.\n- So sánh hiệu năng của mạng CNN của bạn với mô hình MLP từ Câu hỏi 2, thảo luận lý do tại sao mạng CNN lại có hiệu năng khác biệt.","metadata":{}},{"cell_type":"code","source":"# Fit the CNN model on the training set\nhistory = model.fit(x_train_normalized, y_train,\n                    epochs=10,  \n                    batch_size=128,  \n                    validation_split=0.1)  # Use 10% of training data as validation\n\n# Evaluate the CNN model on the test set\ntest_loss, test_accuracy = model.evaluate(x_test_normalized, y_test)\nprint(\"Test Loss:\", test_loss)\nprint(\"Test Accuracy:\", test_accuracy)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:30:39.999290Z","iopub.execute_input":"2026-01-18T12:30:39.999548Z","iopub.status.idle":"2026-01-18T12:31:34.688282Z","shell.execute_reply.started":"2026-01-18T12:30:39.999527Z","shell.execute_reply":"2026-01-18T12:31:34.687386Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\nSo sánh hiệu năng của mô hình CNN với mô hình MLP từ Câu hỏi 2, đây là những lý do tiềm tàng dẫn đến sự khác biệt về hiệu năng:\n\n- **Khả năng nắm bắt các mẫu không gian của CNN:** CNN được thiết kế đặc biệt để nắm bắt các mẫu không gian trong hình ảnh. Chúng sử dụng các lớp tích chập để quét hình ảnh đầu vào với các bộ lọc có thể học được nhằm phát hiện các mẫu cục bộ. Khả năng nắm bắt thông tin không gian này có thể dẫn đến hiệu năng tốt hơn trong các tác vụ phân loại hình ảnh như CIFAR-10 so với MLP, vốn coi hình ảnh như các vectơ phẳng và không xem xét các mối quan hệ không gian.\n\n- **Hiệu quả tham số:** CNN thường có ít tham số hơn so với MLP kết nối đầy đủ. CNN chia sẻ trọng số trên các vị trí không gian, cho phép chúng học hiệu quả từ dữ liệu huấn luyện hạn chế. Mặt khác, MLP có các lớp kết nối đầy đủ, có thể dẫn đến số lượng tham số lớn hơn và tăng nguy cơ quá khớp, đặc biệt là trên các tập dữ liệu nhỏ như CIFAR-10.\n\n- **Phân cấp đặc trưng:** CNN học các biểu diễn phân cấp của các đặc trưng từ dữ liệu thô. Các lớp tích chập trích xuất các đặc trưng cấp thấp như cạnh và kết cấu, trong khi các lớp sâu hơn kết hợp các đặc trưng này để học các biểu diễn trừu tượng hơn. Việc học đặc trưng theo thứ bậc này cho phép mạng CNN biểu diễn hiệu quả các mẫu phức tạp trong hình ảnh, điều này có thể dẫn đến hiệu suất tốt hơn trong các nhiệm vụ phân loại hình ảnh.\n\n- **Các kỹ thuật Regularizations:** Trong kiến ​​trúc CNN được cung cấp, các lớp dropout và chuẩn hóa theo lô được sử dụng để ngăn ngừa hiện tượng quá khớp và ổn định quá trình huấn luyện. Các kỹ thuật điều chỉnh này giúp cải thiện khả năng khái quát hóa của mô hình CNN bằng cách giảm nguy cơ quá khớp với dữ liệu huấn luyện.","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 5.5 |</span> Kiểm tra trực quan các dự đoán của mô hình (63.5% Accuracy)</b>","metadata":{}},{"cell_type":"code","source":"# Get predictions for the test set\npredictions_cnn = model.predict(x_test_normalized)\npredicted_labels_cnn = np.argmax(predictions_cnn, axis=1)\n\n# Plot sample images with predicted labels\nplt.figure(figsize=(10, 10))\nfor i in range(25):  # Adjust as needed\n    plt.subplot(5, 5, i + 1)\n    plt.xticks([])\n    plt.yticks([])\n    plt.grid(False)\n    plt.imshow(x_test_normalized[i])  # Plot original image\n    true_label = class_names[y_test[i][0]]  # Get true label\n    predicted_label = class_names[predicted_labels_cnn[i]]  # Get predicted label\n    if true_label == predicted_label:\n        color = 'green'  # Correct prediction\n    else:\n        color = 'red'  # Incorrect prediction\n    plt.xlabel(f\"True: {true_label}\\nPred (CNN): {predicted_label}\", color=color)\nplt.tight_layout()  # Adjust subplot layout to prevent overlap\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:34.689564Z","iopub.execute_input":"2026-01-18T12:31:34.689850Z","iopub.status.idle":"2026-01-18T12:31:36.878725Z","shell.execute_reply.started":"2026-01-18T12:31:34.689828Z","shell.execute_reply":"2026-01-18T12:31:36.877507Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <div style=\"padding: 30px;color:white;margin:10;font-size:60%;text-align:left;display:fill;border-radius:10px;background-color:#FFFFFF;overflow:hidden;background-color:#A7C7E7\"><b><span style='color:#FFFFFF'>6 |</span></b> <b>DÙNG 1 MÔ HÌNH PRETRAINED CHO CIFAR-10</b></div>\n**Objective:** Sử dụng mô hình được huấn luyện trước từ ứng dụng Keras của TensorFlow hoặc mô hình torchvision của PyTorch làm bộ trích xuất đặc trưng hoặc để tinh chỉnh trên tập dữ liệu CIFAR-10, và đạt độ chính xác >90% trên tập dữ liệu thử nghiệm CIFAR-10.","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 6.1 |</span> Dataset Preparation</b>\n- Tải bộ dữ liệu CIFAR-10, áp dụng chuẩn hóa và bất kỳ bước tiền xử lý cần thiết nào để phù hợp với định dạng đầu vào của mô hình đã được huấn luyện trước.","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n    \n- Tập dữ liệu CIFAR-10 được tải bằng hàm tf.keras.datasets.cifar10.load_data().\n\n- Sau khi tải, các ảnh đầu vào được chuyển đổi sang kiểu dữ liệu float32 và được chuẩn hóa.\n\n- Quá trình tiền xử lý được áp dụng bằng hàm tiền xử lý ResNet50 tf.keras.applications.resnet50.preprocess_input().\n\n- Việc chia tập huấn luyện và tập kiểm tra được sử dụng để tách tập huấn luyện ở tỷ lệ 0.1, tỷ lệ này sẽ cần được sử dụng để xác thực sau này (với tăng cường dữ liệu).\n- Ngoài ra, một phép toán OHE cũng được thực hiện để giúp phân biệt các lớp rõ ràng hơn.\n\n</span>","metadata":{}},{"cell_type":"code","source":"# Load CIFAR-10 dataset\n(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:36.879810Z","iopub.execute_input":"2026-01-18T12:31:36.880068Z","iopub.status.idle":"2026-01-18T12:31:37.717784Z","shell.execute_reply.started":"2026-01-18T12:31:36.880048Z","shell.execute_reply":"2026-01-18T12:31:37.716954Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# Preprocess input images\nx_train = x_train.astype('float32')\nx_test = x_test.astype('float32')\n\n# Preprocess input images using ResNet50 preprocessing\nx_train = tf.keras.applications.resnet50.preprocess_input(x_train)\nx_test = tf.keras.applications.resnet50.preprocess_input(x_test)\n","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:37.718932Z","iopub.execute_input":"2026-01-18T12:31:37.719211Z","iopub.status.idle":"2026-01-18T12:31:38.087317Z","shell.execute_reply.started":"2026-01-18T12:31:37.719188Z","shell.execute_reply":"2026-01-18T12:31:38.086487Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nx_train,x_val,y_train,y_val=train_test_split(x_train,y_train,test_size=.1)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:38.088483Z","iopub.execute_input":"2026-01-18T12:31:38.088823Z","iopub.status.idle":"2026-01-18T12:31:38.289487Z","shell.execute_reply.started":"2026-01-18T12:31:38.088794Z","shell.execute_reply":"2026-01-18T12:31:38.288474Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from keras.utils import to_categorical\n\n#One hot encode the labels.Since we have 10 classes we should expect the shape[1] of y_train,y_val and y_test to change from 1 to 10\n\ny_train=to_categorical(y_train)\ny_val=to_categorical(y_val)\ny_test=to_categorical(y_test)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:38.290672Z","iopub.execute_input":"2026-01-18T12:31:38.290953Z","iopub.status.idle":"2026-01-18T12:31:38.296952Z","shell.execute_reply.started":"2026-01-18T12:31:38.290932Z","shell.execute_reply":"2026-01-18T12:31:38.295870Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Print the dimensions of the datasets to check\n\nprint((x_train.shape,y_train.shape))\nprint((x_val.shape,y_val.shape))\nprint((x_test.shape,y_test.shape))","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:38.298328Z","iopub.execute_input":"2026-01-18T12:31:38.298686Z","iopub.status.idle":"2026-01-18T12:31:38.308300Z","shell.execute_reply.started":"2026-01-18T12:31:38.298662Z","shell.execute_reply":"2026-01-18T12:31:38.307416Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n#Data Augmentation Function: Let's define an instance of the ImageDataGenerator class and set the parameters.\ntrain_generator = ImageDataGenerator(\n    rotation_range=10,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    horizontal_flip=True,\n    shear_range=0.1,\n    zoom_range=0.1,)\n\nval_generator = ImageDataGenerator(\n    rotation_range=10,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    horizontal_flip=True,\n    shear_range=0.1,\n    zoom_range=0.1,)\n\ntest_generator = ImageDataGenerator(\n    rotation_range=10,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    horizontal_flip=True,\n    shear_range=0.1,\n    zoom_range=0.1,) ","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:38.309346Z","iopub.execute_input":"2026-01-18T12:31:38.309653Z","iopub.status.idle":"2026-01-18T12:31:38.316693Z","shell.execute_reply.started":"2026-01-18T12:31:38.309633Z","shell.execute_reply":"2026-01-18T12:31:38.315951Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 6.2 |</span> Chiến lược lựa chọn, sửa đổi và huấn luyện mô hình</b>\n- Chọn một mô hình được huấn luyện trước (ví dụ: ResNet, VGG16) và chỉnh sửa nó cho phân loại CIFAR-10.\n\n- Mô tả cách bạn điều chỉnh mô hình cho 10 lớp của CIFAR-10 (ví dụ: chỉnh sửa lớp trên cùng, điều chỉnh kích thước đầu vào).\n\n- Quyết định xem có nên đóng băng trọng số của các lớp được huấn luyện trước và chỉ huấn luyện lớp trên cùng, hay tinh chỉnh toàn bộ mạng.\n","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\n**Chọn mô hình và loại trừ lớp trên cùng**\n- Mô hình được huấn luyện trước đã chọn là ResNet50, được tải bằng hàm tf.keras.applications.ResNet50.\n\n- Mô hình được cấu hình để loại trừ lớp phân loại trên cùng (include_top=False).\n\n- \n</span>","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\n**Điều chỉnh kích thước ảnh đầu vào và sửa đổi lớp trên cùng**\n\n- Ảnh đầu vào cho CIFAR-10 có kích thước 32x32 pixel, trong khi ResNet50 yêu cầu ảnh đầu vào có kích thước 224x224 pixel.\n\n- Do đó, ảnh đầu vào được tăng kích thước để phù hợp với kích thước đầu vào mà ResNet50 yêu cầu.\n\n- Các lớp phân loại được sửa đổi bằng cách thêm một lớp gộp trung bình toàn cục và hai lớp dày đặc.\n\n- Lớp dày đặc cuối cùng có 10 đơn vị với hàm kích hoạt softmax, tương ứng với 10 lớp trong CIFAR-10.0.","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\n**Bỏ đóng băng một số lớp được huấn luyện trước**\n- Trong trường hợp này, một số trọng số của các lớp được huấn luyện trước sẽ không bị đóng băng. Do đó, chúng sẽ được tinh chỉnh trong quá trình huấn luyện.\n\n- Quyết định này cho phép mô hình thích nghi nhẹ với các đặc điểm cụ thể của tập dữ liệu CIFAR-10.\n\n- Nhược điểm là nó có thể cần nhiều tham số hơn và do đó tốn nhiều thời gian tính toán hơn.\n\n\n</span>","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\n**Giải thích cho các lựa chọn**\n- CIFAR-10 là một tập dữ liệu tương đối nhỏ với hình ảnh độ phân giải thấp.\n\n- Việc tinh chỉnh một số lớp trong mạng cho phép mô hình học được các đặc điểm cụ thể hơn, phù hợp với tập dữ liệu CIFAR-10.\n\n- Vì ResNet50 được huấn luyện trước trên một tập dữ liệu lớn (ImageNet) và đã học được các đặc điểm phân cấp có giá trị, việc tinh chỉnh một số lớp trong toàn bộ mạng có thể giúp cải thiện hiệu suất của mô hình trên CIFAR-10.\n\n- Có thể đặt nó như một biến để quyết định số lượng lớp cần bỏ đóng băng.","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.applications import ResNet50  # Import ResNet50 model\nfrom tensorflow.keras.layers import Dense, Flatten, GlobalAveragePooling2D, Dropout, BatchNormalization, UpSampling2D\nfrom tensorflow.keras.models import Model\n\n# Quá trình trích xuất đặc trưng được thực hiện bởi ResNet50 đã được huấn luyện trước trên tập dữ liệu ImageNet.\n\n# Kích thước đầu vào là 224 x 224.\n\ninputs = tf.keras.layers.Input(shape=(32, 32, 3))\n\n# Nâng kích thước ảnh đầu vào lên để phù hợp với kích thước mà ResNet50 yêu cầu\nresized_inputs = UpSampling2D(size=(7, 7))(inputs)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:38.317833Z","iopub.execute_input":"2026-01-18T12:31:38.318127Z","iopub.status.idle":"2026-01-18T12:31:38.332115Z","shell.execute_reply.started":"2026-01-18T12:31:38.318108Z","shell.execute_reply":"2026-01-18T12:31:38.331215Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load the ResNet50 model with pretrained weights\nresnet_model = ResNet50(\n    include_top=False,\n    weights='imagenet',\n    input_tensor=resized_inputs  # Use resized input as input tensor\n)\n\n# Freeze all layers initially\nfor layer in resnet_model.layers:\n    layer.trainable = False\n\n# Unfreeze the last X layers of the ResNet50 model\nNUM_LAYERS_TO_UNFREEZE = 30\nfor layer in resnet_model.layers[-NUM_LAYERS_TO_UNFREEZE:]:\n    layer.trainable = True\n\n# Global average pooling and classification layers\nx = GlobalAveragePooling2D()(resnet_model.output)\nx = Dense(1024, activation=\"relu\")(x)\nx = BatchNormalization()(x)  \nx = Dropout(0.3)(x)  \nx = Dense(512, activation=\"relu\")(x)\nx = BatchNormalization()(x)  \nx = Dropout(0.2)(x)  \nx = Dense(256, activation=\"relu\")(x)\nx = BatchNormalization()(x)  \nx = Dropout(0.2)(x)  \nclassification_output = Dense(10, activation=\"softmax\", name=\"classification\")(x)\n\n# Kết nối các lớp trích xuất đặc trưng và lớp \"phân loại\" để xây dựng mô hình\nResNet_model = Model(inputs=inputs, outputs=classification_output, name=\"ResNet\")\n\n# Compile the model\noptimizer = tf.keras.optimizers.Adam()  # Use Adam optimizer\n\n\n#Chỉ định hàm mất mát là categorical_crossentropy vì nhãn được mã hóa 1-hot.\n\n#Nếu nhãn là số nguyên, hãy sử dụng sparse_categorical_crossentropy làm hàm mất mát.\nResNet_model.compile(optimizer=optimizer,\n              loss='categorical_crossentropy',\n              metrics=['accuracy'])\n\nResNet_model.summary()","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:38.333235Z","iopub.execute_input":"2026-01-18T12:31:38.333501Z","iopub.status.idle":"2026-01-18T12:31:40.577117Z","shell.execute_reply.started":"2026-01-18T12:31:38.333481Z","shell.execute_reply":"2026-01-18T12:31:40.576142Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Count the number of layers\nnum_layers = len(ResNet_model.layers)\nprint(\"Number of layers in the model:\", num_layers)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:40.578465Z","iopub.execute_input":"2026-01-18T12:31:40.578718Z","iopub.status.idle":"2026-01-18T12:31:40.588478Z","shell.execute_reply.started":"2026-01-18T12:31:40.578698Z","shell.execute_reply":"2026-01-18T12:31:40.586181Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 6.3 |</span> Evaluation</b>\n- Kiểm tra mô hình trên tập dữ liệu thử nghiệm CIFAR-10 và báo cáo độ chính xác.\n- So sánh kết quả với các mô hình MLP và CNN từ các câu hỏi trước, thảo luận về ưu điểm và hạn chế của việc sử dụng các mô hình được huấn luyện trước trên tập dữ liệu như CIFAR-10.","metadata":{}},{"cell_type":"code","source":"from keras.callbacks import ReduceLROnPlateau, EarlyStopping\n\n# Add ReduceLROnPlateau callback\n# Ở đây, tốc độ học sẽ giảm đi một nửa (hệ số = 0,5) nếu không thấy sự cải thiện nào về tổn thất trên tập dữ liệu kiểm chứng trong 1 epoch.\nreduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=1, min_lr=0.00001)\n\n# Add EarlyStopping callback\n# Tại đây, quá trình huấn luyện sẽ dừng lại nếu không thấy sự cải thiện nào về tổn thất xác thực trong 5 epoch.\n# Tham số `restore_best_weights` đảm bảo rằng trọng số của mô hình được đặt lại về các giá trị từ epoch\n# có giá trị tốt nhất của đại lượng được theo dõi (trong trường hợp này là 'val_loss').\nearly_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)\n\n\nbatch_size=64\nepochs=15\n\nhistory = ResNet_model.fit(train_generator.flow(x_train,y_train,batch_size=batch_size),\n                                     epochs=epochs,\n                                     steps_per_epoch=x_train.shape[0]//batch_size,\n                                     validation_data=val_generator.flow(x_val,y_val,batch_size=batch_size),\n                                     #validation_steps=250,\n                                     callbacks=[reduce_lr, early_stopping],\n                                     shuffle = True\n                                    )\n# Evaluate the model\ntest_loss, test_accuracy = ResNet_model.evaluate(test_generator.flow(x_test,y_test,batch_size=batch_size))\nprint(\"Test Loss:\", test_loss)\nprint(\"Test Accuracy:\", test_accuracy)","metadata":{"execution":{"iopub.status.busy":"2026-01-18T12:31:40.589675Z","iopub.execute_input":"2026-01-18T12:31:40.589958Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\nSau nhiều lần thử nghiệm, kết hợp và thời gian tính toán, cuối cùng chúng tôi đạt được độ chính xác kiểm thử là 0,914, cao hơn 90%.\nCấu trúc mô hình để đạt được điều này như sau:\n\n- Chia tập huấn luyện và tập kiểm tra để xác thực dữ liệu\n- Phân loại các lớp (OHE)\n- Sử dụng trình tạo ảnh để tăng cường dữ liệu\n- Sử dụng ResNet50 và loại bỏ lớp trên cùng, thêm mạng nơ-ron riêng để chuẩn hóa hàng loạt (Batch Normalization) và loại bỏ dần (dropout) trước khi phân loại thành 10 nút\n- Sử dụng lấy mẫu tăng (7,7) để khớp với ảnh CIFAR-10 (32 x 32) với kích thước đầu vào ResNet 224 x 224\n- Ban đầu đóng băng tất cả các lớp, nhưng sau đó bỏ đóng băng 20 lớp để cho phép một số trọng số trong ResNet50 được cập nhật và cải thiện hiệu suất\n- Sử dụng trình tối ưu hóa Adam thay vì SGD vì nó có tốc độ học thích ứng\n- Bao gồm các chức năng giảm tốc độ học và dừng sớm trong quá trình huấn luyện để đảm bảo mỗi epoch giúp cải thiện kết quả\n- Bao gồm shuffle = True để đảm bảo mô hình không học cả trình tự dữ liệu và gây ra hiện tượng quá khớp (overfitting)","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: blue;\">\n\n**Ưu điểm của việc sử dụng các mô hình được huấn luyện trước như ResNet trên CIFAR-10:**\n\n- Học chuyển giao: Các mô hình được huấn luyện trước có kiến ​​thức được học từ các tập dữ liệu quy mô lớn như ImageNet, có thể được chuyển giao sang CIFAR-10, cải thiện hiệu suất với dữ liệu được gán nhãn hạn chế.\n\n- Trích xuất đặc trưng: Các mô hình được huấn luyện trước có thể trích xuất các đặc trưng có ý nghĩa từ hình ảnh, nắm bắt các biểu diễn phân cấp mà có thể không được học hiệu quả từ đầu.\n\n- Độ chính xác cao hơn: Sử dụng mô hình được huấn luyện trước dựa trên tập dữ liệu lớn hơn nhiều có thể giúp đạt được độ chính xác cao hơn nhiều so với các mô hình khác được xây dựng từ đầu vì dữ liệu huấn luyện lớn hơn nhiều so với những gì chúng ta có.\n\n**Hạn chế của việc sử dụng các mô hình được huấn luyện trước:**\n\n- Thích ứng miền: Các mô hình được huấn luyện trước có thể không hoàn toàn thích ứng với các đặc điểm của CIFAR-10, dẫn đến hiệu suất không tối ưu.\n\n- Tính đặc thù của nhiệm vụ: Các mô hình được huấn luyện trước được thiết kế cho các nhiệm vụ cụ thể và các đặc trưng của chúng có thể không phải lúc nào cũng phù hợp với các nhiệm vụ phân loại CIFAR-10.\n\n- Rủi ro quá khớp: Việc tinh chỉnh toàn bộ mô hình đã được huấn luyện trước trên một tập dữ liệu nhỏ như CIFAR-10 có thể dẫn đến quá khớp nếu không được điều chỉnh đúng cách.","metadata":{}},{"cell_type":"markdown","source":"### <b><span style='color:#A7C7E7'> 6.4 |</span> Kiểm tra trực quan các dự đoán của mô hình (91.4% Accuracy)</b>","metadata":{}},{"cell_type":"code","source":"# Get predictions for the test set\npredictions_resnet = ResNet_model.predict(x_test)\npredicted_labels_resnet = np.argmax(predictions_resnet, axis=1)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot sample images with predicted labels\nplt.figure(figsize=(10, 10))\nfor i in range(25):  # Adjust as needed\n    plt.subplot(5, 5, i + 1)\n    plt.xticks([])\n    plt.yticks([])\n    plt.grid(False)\n    # Revert preprocessing for visualization by reloading CIFAR-10 dataset\n    (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()\n    plt.imshow(x_test[i])  #Plot original picture\n    true_label = class_names[y_test[i][0]]  # Get true label\n    predicted_label = class_names[predicted_labels_resnet[i]]  # Get predicted label\n    if true_label == predicted_label:\n        color = 'green'  # Correct prediction\n    else:\n        color = 'red'  # Incorrect prediction\n    plt.xlabel(f\"True: {true_label}\\nPred (ResNet): {predicted_label}\", color=color)\nplt.tight_layout()  # Adjust subplot layout to prevent overlap\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}