{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":30698,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 🔶 PCA & KNN 🔶\n### 🔸 XỬ LÝ ẢNH • <b></b> Principal Component Analysis & K-Nearest Neighbors 🔸","metadata":{}},{"cell_type":"markdown","source":"<div style=\"border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left;\">\nCơ sở dữ liệu <b>MNIST</b> (<i>Cơ sở dữ liệu của Viện Tiêu chuẩn và Công nghệ Quốc gia được Sửa đổi</i>) là một cơ sở dữ liệu lớn chứa các <b>chữ số viết tay</b> thường được sử dụng để huấn luyện các hệ thống xử lý hình ảnh khác nhau. Khi các kỹ thuật học máy mới xuất hiện, MNIST vẫn là một nguồn tài nguyên đáng tin cậy cho các nhà nghiên cứu và người học.<br><br>\nCác <b>hình ảnh đen trắng</b> gốc đã được chuẩn hóa để vừa với một <b>hộp giới hạn 28x28 điểm ảnh</b> và được làm mịn, điều này đã giới thiệu các <b>mức xám</b>. Cơ sở dữ liệu MNIST gốc chứa 60,000 hình ảnh huấn luyện và 10,000 hình ảnh kiểm tra, nhưng trong cuộc thi này, chúng tôi được cung cấp <b>42,000 hình ảnh huấn luyện</b> (70%) và <b>18,000 hình ảnh kiểm tra</b> (30%).<br><br>\nCác kỹ thuật <i>tân tiến nhất</i> để tiếp cận bất kỳ vấn đề Phân loại Hình ảnh nào thường phụ thuộc vào <i>Mạng nơ-ron tích chập</i>. Loại kiến trúc này rất tốt trong việc trích xuất các mẫu từ hình ảnh. Tuy nhiên, vì MNIST là một vấn đề đơn giản, trong sổ ghi chú này, chúng tôi sẽ áp dụng <i>Các Thuật toán Học máy đơn giản</i> (và chúng tôi sẽ đạt được độ chính xác tốt!).<br><br>\nTrước tiên, chúng tôi sẽ thực hiện một <b>Phân tích thành phần chính (PCA)</b> để giảm chiều dữ liệu (từ 784 giá trị điểm ảnh xuống còn 10 thành phần chính). Sau đó, chúng tôi sẽ chạy thuật toán <i>lười biếng yêu thích</i> của chúng tôi: <b>Người láng giềng gần nhất (K-Nearest Neighbors)</b>. Kết hợp này đạt được <b>Độ chính xác Kiểm tra là 0.9271</b>, điều này khá đáng nể!!!<br><br>\n<b>Mục lục:</b>\n<ul>\n    <li>🔎 <b>Đọc & Khám phá Dữ liệu</b> 🔍:<br>\n    - Kiểm tra xem có <i>giá trị còn thiếu</i> không.<br>\n    - Kiểm tra <i>phạm vi</i> của các giá trị điểm ảnh.<br>\n    - <i>Hiển thị</i> dữ liệu (Hình ảnh).<br>\n    - Kiểm tra xem mục tiêu có <i>cân bằng</i> không.</li><br>\n    <li> 🔶 <b>PCA</b> 🔶:<br>\n        - Giới thiệu thuật toán PCA.<br>\n        - Tiền xử lý (<i>chuẩn hóa</i>) dữ liệu.<br>\n        - Thực hiện thuật toán PCA bằng thư viện <code>Sklearn</code>.<br>\n        - Nhúng các mô-đun vào một <code>Ống dẫn Sklearn</code>.<br>\n        - Vẽ biểu đồ <i>phương sai được giải thích</i> của các thành phần.<br>\n        - Hiển thị hai chiều của dữ liệu bằng cách sử dụng <code>seaborn</code>.<br>\n        - Hiển thị ba chiều động của dữ liệu bằng cách sử dụng <code>plotly</code>.<br>\n        - Hiển thị <i>phản chiếu nghịch đảo</i> của dữ liệu.<br>\n        - Kiểm tra xem các thành phần có <i>không tương quan</i> không.</li><br>\n    <li> 🔶 <b>Bộ phân loại KNN</b> 🔶:<br>\n        - Xây dựng một Bộ phân loại KNN bằng cách sử dụng <code>Sklearn</code>.<br>\n        - Sử dụng kỹ thuật PCA như một <i>trích xuất đặc trưng</i> cho KNN.<br>\n        - Chạy một <i>Lưới tìm kiếm Chéo</i> để tối ưu hóa các siêu tham số <code>n_neighbors</code>.<br>\n        - So sánh các chỉ số đo cho tập huấn luyện và tập kiểm tra để kiểm tra xem có <i>quá mức</i> không.<br>\n        - Vẽ <i>Ma trận nhầm lẫn</i>.<br>\n        - Tính toán <i>Báo cáo Phân loại</i>.<br>\n    </ul>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# **Phần 1: Import thư viện cần thiết**","metadata":{}},{"cell_type":"code","source":"## NHẬP THƯ VIỆN PYTHON CẦN THIẾT\n\n# Đọc và khám phá dữ liệu\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.preprocessing import MinMaxScaler\n\n# Phân tích thành phần chính (PCA)\nfrom sklearn.decomposition import PCA\nfrom sklearn.pipeline import Pipeline\nimport plotly.express as px\n\n# K-Bộ phân loại hàng xóm gần nhất (KNN)\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.model_selection import GridSearchCV\nfrom sklearn.metrics import classification_report, \\\n    confusion_matrix, ConfusionMatrixDisplay \n\n# VHT\nfrom scipy.ndimage import rotate","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:32:41.461363Z","iopub.execute_input":"2024-05-29T01:32:41.462021Z","iopub.status.idle":"2024-05-29T01:32:46.090051Z","shell.execute_reply.started":"2024-05-29T01:32:41.461989Z","shell.execute_reply":"2024-05-29T01:32:46.088537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def func_array1d_rotate2d(Narray1d, rotate_angle, sizePixel):\n    \"\"\"\n    Hàm này xoay các hình ảnh trong mảng 1 chiều Narray1d theo một góc quay cho trước.\n\n    Parameters:\n    - Narray1d: Mảng 1 chiều chứa các hình ảnh.\n    - rotate_angle: Góc quay được chỉ định để xoay hình ảnh.\n    - sizePixel: Kích thước của mỗi hình ảnh (chiều rộng hoặc chiều cao).\n\n    Returns:\n    - Narray1d_rotate: Mảng 1 chiều chứa các hình ảnh sau khi đã được xoay.\n    \"\"\"\n    \n    # Lấy số lượng hình ảnh trong mảng\n    N = Narray1d.shape[0]\n\n    # Tạo một mảng mới để lưu trữ các hình ảnh đã được xoay\n    Narray1d_rotate = np.zeros(Narray1d.shape)\n    \n    # Duyệt qua từng hình ảnh trong mảng\n    for i in range(N):\n        # Chuyển hình ảnh từ dạng mảng 1 chiều thành dạng ma trận\n        image = np.reshape(Narray1d[i, :], (sizePixel, sizePixel))\n        # Xoay hình ảnh với góc quay được chỉ định\n        rotated_image = rotate(image, rotate_angle, reshape=False)\n        # Chuyển hình ảnh từ dạng ma trận thành dạng mảng 1 chiều\n        rotated_array1d = np.reshape(rotated_image, (1, -1))\n        # Lưu trữ hình ảnh đã được xoay vào mảng mới\n        Narray1d_rotate[i, :] = rotated_array1d\n        \n    # Trả về mảng chứa các hình ảnh đã được xoay\n    return Narray1d_rotate\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:32:46.092708Z","iopub.execute_input":"2024-05-29T01:32:46.093802Z","iopub.status.idle":"2024-05-29T01:32:46.106275Z","shell.execute_reply.started":"2024-05-29T01:32:46.093735Z","shell.execute_reply":"2024-05-29T01:32:46.102642Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Phần 2: Đọc dữ liệu và khai thác dữ liệu**","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-size: 1.2em; color: green; font-weight: bold; border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left\">\nTrong phần này, chúng ta sẽ sử dụng thư viện Pandas nổi tiếng của Python để đọc dữ liệu từ các tệp .csv.\n\nChúng ta sẽ trực quan hóa phân phối các giá trị pixel (biểu đồ histogram) bằng cách sử dụng các thư viện Seaborn và Matplotlib của Python. Chúng ta cũng sẽ kiểm tra để đảm bảo rằng không có giá trị nào bị thiếu và rằng các lớp mục tiêu được cân bằng.\n\nCuối cùng, chúng ta sẽ trực quan hóa các hình ảnh bằng cách vẽ một hình ảnh từ mỗi lớp.\n</div>\n","metadata":{}},{"cell_type":"code","source":"# Đọc các tệp dữ liệu bằng Pandas\n# df_train chứa dữ liệu huấn luyện từ tệp 'train.csv'\ndf_train = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\n\n# df_test chứa dữ liệu kiểm tra từ tệp 'test.csv'\ndf_test =  pd.read_csv('/kaggle/input/digit-recognizer/test.csv')\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:32:46.108091Z","iopub.execute_input":"2024-05-29T01:32:46.108554Z","iopub.status.idle":"2024-05-29T01:32:53.121038Z","shell.execute_reply.started":"2024-05-29T01:32:46.108515Z","shell.execute_reply":"2024-05-29T01:32:53.119755Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Kiểm tra kích thước của DataFrame huấn luyện và kiểm tra\n# In ra kích thước của df_train, chứa 70% dữ liệu huấn luyện\nprint(\"df_train shape:\", df_train.shape)\n\n# In ra kích thước của df_test, chứa 30% dữ liệu kiểm tra\nprint(\"df_test shape: \", df_test.shape)\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:32:53.123340Z","iopub.execute_input":"2024-05-29T01:32:53.123674Z","iopub.status.idle":"2024-05-29T01:32:53.129417Z","shell.execute_reply.started":"2024-05-29T01:32:53.123646Z","shell.execute_reply":"2024-05-29T01:32:53.128254Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Kiểm tra xem có giá trị thiếu nào trong df_train không và in ra tổng số lượng giá trị thiếu\nprint(\n    \"Giá trị bị thiếu trong file df_train: \", \\\n    df_train.isna().sum().sum()\n)\n\n# Kiểm tra xem có giá trị thiếu nào trong df_test không và in ra tổng số lượng giá trị thiếu\nprint(\n    \"Giá trị bị thiếu trong file df_test: \", \\\n    df_test.isna().sum().sum()\n)\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:32:53.131252Z","iopub.execute_input":"2024-05-29T01:32:53.131633Z","iopub.status.idle":"2024-05-29T01:32:53.202437Z","shell.execute_reply.started":"2024-05-29T01:32:53.131601Z","shell.execute_reply":"2024-05-29T01:32:53.201124Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hiển thị năm hàng đầu tiên của dữ liệu huấn luyện\ndf_train.head()\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:32:53.203877Z","iopub.execute_input":"2024-05-29T01:32:53.204204Z","iopub.status.idle":"2024-05-29T01:32:53.239793Z","shell.execute_reply.started":"2024-05-29T01:32:53.204177Z","shell.execute_reply":"2024-05-29T01:32:53.238285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chia dữ liệu thành hai phần: đặc trưng (X_train) và nhãn (y_train)\n# X_train chứa các cột trừ cột 'label' và chuyển đổi sang dạng mảng numpy\nX_train = df_train.drop(columns=['label']).values\n\n# y_train chứa cột 'label' và chuyển đổi sang dạng mảng numpy\ny_train = df_train['label'].values\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:32:53.241677Z","iopub.execute_input":"2024-05-29T01:32:53.242167Z","iopub.status.idle":"2024-05-29T01:32:53.382364Z","shell.execute_reply.started":"2024-05-29T01:32:53.242128Z","shell.execute_reply":"2024-05-29T01:32:53.381082Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Trực quan hóa phân phối các giá trị pixel trong dữ liệu huấn luyện\n# Sử dụng seaborn.histplot để tạo histogram\n# Màu của histogram được thiết lập thành '#4895EE'\nax = sns.histplot(x=X_train.flatten(), color='#4895EE')\n\n# Đặt tiêu đề và nhãn cho biểu đồ\n_ = ax.set(title='Biểu đồ giá trị Pixel', xlabel='Giá trị Pixel')\n\n# Như chúng ta có thể thấy, giá trị 0 đại diện cho nền\n# và các giá trị >0 đại diện cho các chữ số trong hình ảnh\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:32:53.383609Z","iopub.execute_input":"2024-05-29T01:32:53.383952Z","iopub.status.idle":"2024-05-29T01:33:11.295248Z","shell.execute_reply.started":"2024-05-29T01:32:53.383925Z","shell.execute_reply":"2024-05-29T01:33:11.294112Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Kiểm tra xem phân phối của nhãn có cân bằng không\n# Sử dụng seaborn.countplot để đếm số lượng mẫu cho mỗi nhãn\n# Màu của đếm được thiết lập thành '#4895EE'\nax = sns.countplot(x=y_train, color='#4895EE')\n\n# Đặt tiêu đề và nhãn cho biểu đồ\nax.set(\n    title='Phân bổ mục tiêu có cân bằng không? Đúng!',\n    xlabel='Nhãn'\n)\n\n# Vẽ một đường thẳng ngang biểu thị số lượng mẫu mong đợi cho mỗi lớp trong trường hợp phân phối cân bằng\n_ = ax.axhline(y=4200, color='#12086F', linestyle='--')\n\n# Vì các lớp nhãn được cân bằng, chúng ta không cần phải lo lắng về các phương pháp lấy mẫu và sai lệch trong các mô hình của chúng ta.\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:11.297042Z","iopub.execute_input":"2024-05-29T01:33:11.297728Z","iopub.status.idle":"2024-05-29T01:33:11.659105Z","shell.execute_reply.started":"2024-05-29T01:33:11.297686Z","shell.execute_reply":"2024-05-29T01:33:11.657795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tiếp theo, hãy trực quan hóa một hình ảnh từ mỗi lớp nhãn.\n# Chúng ta sẽ sử dụng hàm 'imshow' của matplotlib.\n\n# Tạo một lưới các biểu đồ với 2 hàng và 5 cột\nfig, axs = plt.subplots(nrows=2, ncols=5, figsize=(10, 5))\n\n# Duyệt qua các nhãn từ 0 đến 9\nfor i in range(10):\n    # Tìm chỉ số của mẫu đầu tiên có nhãn i\n    idx = np.where(y_train == i)[0][2]\n    \n    # Trực quan hóa hình ảnh tương ứng với nhãn i\n    # Hình ảnh được chuyển đổi lại thành ma trận 28x28 và sử dụng màu 'Blues'\n    img = axs[int(i >= 5), i % 5].imshow(\n        X_train[idx].reshape(28, 28),\n        cmap='Blues'\n    )\n    \n    # Ẩn trục y và trục x\n    img.axes.get_yaxis().set_visible(False)\n    img.axes.get_xaxis().set_visible(False)\n    \n    # Đặt tiêu đề cho mỗi hình ảnh với nhãn tương ứng\n    img.axes.set_title(f'Nhãn: {i}')\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:11.664608Z","iopub.execute_input":"2024-05-29T01:33:11.664982Z","iopub.status.idle":"2024-05-29T01:33:12.216214Z","shell.execute_reply.started":"2024-05-29T01:33:11.664952Z","shell.execute_reply":"2024-05-29T01:33:12.214859Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ---------- Khai báo ----------\nnumRotate_prior = 10  # Số lần xoay trước\n\nangleOffset_prior = -20  # Góc xoay ban đầu\nangleRange_prior = 45  # Phạm vi góc xoay\n\n# Tập hợp các chỉ số hình ảnh và các offset góc tương ứng\nX_train_pick2rotate = {\n    'index': np.array([1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309,\n                       1310, 1311, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319,\n                       1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329,\n                       1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339,\n                       1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349,\n                       1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359,\n                       1360, 1361, 1362, 1363, 1364, 1365, 1366, 1367, 1368, 1369,\n                       1370, 1371, 1372, 1373, 1374, 1375, 1376, 1377, 1378, 1379,\n                       1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389,\n                       1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399,\n                       1700, 1701, 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709,\n                       1710, 1711, 1712, 1713, 1714, 1715, 1716, 1717, 1718, 1719,\n                       1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, 1728, 1729,\n                       1730, 1731, 1732, 1733, 1734, 1735, 1736, 1737, 1738, 1739,\n                       1740, 1741, 1742, 1743, 1744, 1745, 1746, 1747, 1748, 1749,\n                       1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1759,\n                       1760, 1761, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769,\n                       1770, 1771, 1772, 1773, 1774, 1775, 1776, 1777, 1778, 1779,\n                       1780, 1781, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789,\n                       1790 ,1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799,\n                       3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009,\n                       3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019,\n                       3020, 3021, 3022, 3023, 3024, 3025, 3026, 3027, 3028, 3029,\n                       3030, 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039,\n                       3040, 3041, 3042, 3043, 3044, 3045, 3046, 3047, 3048, 3049,\n                       3050, 3051, 3052, 3053, 3054, 3055, 3056, 3057, 3058, 3059,\n                       3060, 3061, 3062, 3063, 3064, 3065, 3066, 3067, 3068, 3069,\n                       3070, 3071, 3072, 3073, 3074, 3075, 3076, 3077, 3078, 3079,\n                       3080, 3081, 3082, 3083, 3084, 3085, 3086, 3087, 3088, 3089,\n                       3090, 3091, 3092, 3093, 3094, 3095, 3096, 3097, 3098, 3099]),\n    'offset': np.array([\n        [30, 22, 10, 0, 10, 10, 10, 5, 20, 30 ], # Lệch cho chỉ mục hình cho bộ 1\n        [0, 0, 0, 0, 0, -5, -30, 0, 10, 7 ], # Lệch cho chỉ mục hình cho bộ 2\n        [45, 0, 0, 0, 0, 0, 20, 0, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 3\n        [45, 10, -20, 0, 0, 0, -15, 0, 20, 27 ], # Lệch cho chỉ mục hình cho bộ 4\n        [20, 30, 0, 0, -10, 20, -5, -10, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 5\n        [0, 5, -12, 0, 5, 8, -50, -7, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 6\n        [0, -5, 0, 0, 0, 0, -25, 0, -20, 0 ], # Lệch cho chỉ mục hình cho bộ 7\n        [0, 17, 0, 10, 0, 0, -30, 0, 0, 35 ], # Lệch cho chỉ mục hình cho bộ 8\n        [0, 33, 0, 0, 0, 0, -30, 0, 30, 0 ], # Lệch cho chỉ mục hình cho bộ 9\n        [20, 20, 20, 20, 20, 0, -10, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 10\n        [0, -10, -10, 0, 5, 10, -18, 0, 5, 5 ], # Lệch cho chỉ mục hình cho bộ 11\n        [0, 10, -5, 0, 5, 0, -25, 0, -15, 5 ], # Lệch cho chỉ mục hình cho bộ 12\n        [15, 30, -7, 5, 25, 0, -20, 10, 7, 35 ], # Lệch cho chỉ mục hình cho bộ 13\n        [12, 8, 10, 0, 23, 0, -22, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 14\n        [20, -10, 10, 5, -10, -10, -25, 8, 5, 15 ], # Lệch cho chỉ mục hình cho bộ 15\n        [5, -5, 0, 0, 5, 10, -35, 0, 5, -5 ], # Lệch cho chỉ mục hình cho bộ 16\n        [0, 5, 0, 0, 5, 0, -25, 0, -10, 15 ], # Lệch cho chỉ mục hình cho bộ 17\n        [8, 0, 0, 10, 0, 0, -15, 7, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 18\n        [15, 20, 0, 0, 30, 0, 0, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 19\n        [20, 20, -5, 0, 12, 0, -10, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 20\n        [20, 20, 5, 0, 10, 10, 10, -5, 12, 25 ], # Lệch cho chỉ mục hình cho bộ 21\n        [5, 0, -3, 0, 0, -5, -40, 0, 5, 7 ], # Lệch cho chỉ mục hình cho bộ 22\n        [25, 15, -10, 0, 27, 0, -7, 0, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 23\n        [45, 10, -20, 0, 0, 0, -15, 0, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 24\n        [20, 0, -15, 5, -10, -12, -5, 0, 10, 15 ], # Lệch cho chỉ mục hình cho bộ 25\n        [0, 2, -5, 0, 5, 10, -30, 0, 0-15, -5 ], # Lệch cho chỉ mục hình cho bộ 26\n        [0, 10, 0, 0, 0, 0, -10, -15, -10, 0 ], # Lệch cho chỉ mục hình cho bộ 27\n        [0, 20, 0, 10, 8, 0, -30, 0, 0, -20 ], # Lệch cho chỉ mục hình cho bộ 28\n        [0, -5, 0, 8, 0, 15, -35, -5, 25, 0 ], # Lệch cho chỉ mục hình cho bộ 29\n        [0, 30, 20, -15, 17, 0, -20, -15, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 30\n        [20, 15, 5, 0, 13, 10, 10, 0, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 31\n        [0, 15, 0, 0, 0, -5, -30, -5, 15, 10 ], # Lệch cho chỉ mục hình cho bộ 32\n        [45, 0, 0, 0, 0, -5, -35, 0, 15, 25 ], # Lệch cho chỉ mục hình cho bộ 33\n        [45, 10, -20, 0, 13, 0, -15, 0, -5, 12 ], # Lệch cho chỉ mục hình cho bộ 34\n        [5, -5, 15, 0, 10, 20, -5, -10, -5, 0 ], # Lệch cho chỉ mục hình cho bộ 35\n        [0, 5, -5, -5, 20, 7, -10, 0, 0-15, 20 ], # Lệch cho chỉ mục hình cho bộ 36\n        [0, 15, 0, 20, 0, 0, -25, 20, -10, 0 ], # Lệch cho chỉ mục hình cho bộ 37\n        [0, 0, 0, 0, 23, 0, -30, 0, 0, 10 ], # Lệch cho chỉ mục hình cho bộ 38\n        [0, 5, 0, -5, 10, 5, 0, 0, 22, 5 ], # Lệch cho chỉ mục hình cho bộ 39\n        [10, 7, 0, -0, 20, 0, -20, -10, 5, 22 ], # Lệch cho chỉ mục hình cho bộ 40\n        [10, 5, 10, 0, 10, 10, 0, 0, 15, 0 ], # Lệch cho chỉ mục hình cho bộ 41\n        [0, 30, 0, 0, 20, 0, -25, 0, 10, 7 ], # Lệch cho chỉ mục hình cho bộ 42\n        [45, 0, 0, 0, 0, 0, 10, -5, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 43\n        [45, 25, -20, 0, 20, 0, -15, 0, 0, 15 ], # Lệch cho chỉ mục hình cho bộ 44\n        [20, 20, 0, 0, -10, 7, -10, -10, 10, 15 ], # Lệch cho chỉ mục hình cho bộ 45\n        [0, 25, -5, 0, 25, 0, -10, 0, 0, 15 ], # Lệch cho chỉ mục hình cho bộ 46\n        [-5, 10, 0, 0, 0, 0, -30, -15, -5, 0 ], # Lệch cho chỉ mục hình cho bộ 47\n        [10, 20, 0, 10, 10, 5, -30, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 48\n        [5, 0, 0, 8, 15, 10, 0, -10, 15, 0 ], # Lệch cho chỉ mục hình cho bộ 49\n        [0, 30, 5, -15, 0, 0, -10, 10, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 50    \n        [0, 0, 5, 15, -10, 10, -10, 5, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 51\n        [0, 10, 0, 0, 0, 0, -15, 0, 0, 5 ], # Lệch cho chỉ mục hình cho bộ 52\n        [12, 0, -18, 10, 5, 0, 0, 10, 0, 20 ], # Lệch cho chỉ mục hình cho bộ 53\n        [12, 0, 0, 0, 10, 0, 0, 10, 10, 10 ], # Lệch cho chỉ mục hình cho bộ 54\n        [20, 20, -10, -10, 0, 20, -25, 8, 5, 0 ], # Lệch cho chỉ mục hình cho bộ 55\n        [10, 5, 15, 0, 5, 10, 0, 5, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 56\n        [-10, 20, 0, 10, 0, 0, -10, 0, 30, 10 ], # Lệch cho chỉ mục hình cho bộ 57\n        [0, 30, 0, 10, 0, 0, -20, -10, 0, 30 ], # Lệch cho chỉ mục hình cho bộ 58\n        [0, 20, 0, 0, 10, 0, 0, -10, 20, 10 ], # Lệch cho chỉ mục hình cho bộ 59\n        [20, 10, 0, 0, 0, 0, 0, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 60\n        [30, 10, -10, 0, 0, 10, -20, -10, 20, 25 ], # Lệch cho chỉ mục hình cho bộ 61 \n        [10, 10, 0, 0, 0, -5, -20, 0, 10, 15 ], # Lệch cho chỉ mục hình cho bộ 62\n        [45, -10, 0, 0, 5, 0, -10, 0, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 63\n        [45, 10, -20, 0, 0, 0, 10, 0, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 64\n        [20, 30, 0, 10, 20, 20, 10, -10, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 65\n        [0, 5, -5, -5, 5, 10, 20, 0, -5, 10 ], # Lệch cho chỉ mục hình cho bộ 66\n        [0, 5, 0, 0, 0, 0, -10, 30, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 67\n        [0, 20, 0, 0, 0, 0, 0, 0, 5, -20 ], # Lệch cho chỉ mục hình cho bộ 68\n        [0, 10, -20, 8, 10, 0, -15, 10, -20, -5 ], # Lệch cho chỉ mục hình cho bộ 69\n        [0, 0, 0, 15, 0, 0, 0, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 70\n        [0, -18, -20, -5, 5, 10, 0, 0, -5, 10 ], # Lệch cho chỉ mục hình cho bộ 71 \n        [0, 20, 0, 0, 0, 0, -35, 15, 20, -10 ], # Lệch cho chỉ mục hình cho bộ 72\n        [5, 5, 0, 0, 0, 0, -8, -5, 5, 0 ], # Lệch cho chỉ mục hình cho bộ 73\n        [15, 35, 0, 0, 7, 0, 0, 10, 0, 5 ], # Lệch cho chỉ mục hình cho bộ 74\n        [10, 0, 0, 8, 15, 0, 0, 0, 0, -5 ], # Lệch cho chỉ mục hình cho bộ 75\n        [10, 5, -5, -5, 25, 10, -10, -10, -5, 10 ], # Lệch cho chỉ mục hình cho bộ 76\n        [0, -5, 0, 0, -10, 0, -15, 15, 0, 10 ], # Lệch cho chỉ mục hình cho bộ 77\n        [10, 5, 0, 0, 0, 5, -25, 0, 20, -20 ], # Lệch cho chỉ mục hình cho bộ 78\n        [0, 10, -10, 8, 10, 0, -5, 10, 0, -5 ], # Lệch cho chỉ mục hình cho bộ 79\n        [20, 0, -10, 0, -5, 0, 0, 0, 0, 20 ], # Lệch cho chỉ mục hình cho bộ 80\n        [0, 8, -5, 5, 15, 0, -50, 0, -5, 25 ], # Lệch cho chỉ mục hình cho bộ 81\n        [0, 7, 0, 5, 30, 0, -10, 0, 0, 25 ], # Lệch cho chỉ mục hình cho bộ 82\n        [0, 10, -8, 10, 20, 0, 0, 0, 0, -15 ], # Lệch cho chỉ mục hình cho bộ 83\n        [10, 8, -15, 10, 20, 0, -15, 10, 0, 15 ], # Lệch cho chỉ mục hình cho bộ 84\n        [0, 0, 0, 0, 20, 0, -10, 0, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 85\n        [0, 15, -5, -20, 15, 10, -15, 5, 20, 20 ], # Lệch cho chỉ mục hình cho bộ 86\n        [0, 15, 0, 0, 5, 0, -30, -5, 5, 15 ], # Lệch cho chỉ mục hình cho bộ 87\n        [0, 0, 0, 5, 25, 0, -10, 0, 5, -20 ], # Lệch cho chỉ mục hình cho bộ 88\n        [0, 18, -20, 8, 10, 0, -15, 10, 10, 5 ], # Lệch cho chỉ mục hình cho bộ 89 \n        [0, 0, -10, 10, 30, 0, -20, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 90\n        [0, 20, -5, 5, 8, 10, -15, 0, -5, 20 ], # Lệch cho chỉ mục hình cho bộ 91\n        [15, 5, -5, 0, 22, 0, -33, 10, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 92\n        [0, 8, -10, 5, -5, 0, -20, 0, 5, 0 ], # Lệch cho chỉ mục hình cho bộ 93\n        [0, 10, 10, 0, 0, 0, -25, -5, -20, 20 ], # Lệch cho chỉ mục hình cho bộ 94 \n        [0, 25, 0, 10, 30, 0, -20, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 95\n        [10, 5, 0, 5, 0, 0, -20, -5, -5, 0 ], # Lệch cho chỉ mục hình cho bộ 96\n        [0, 5, 0, 0, 0, 0, -10, 0, 0, 10 ], # Lệch cho chỉ mục hình cho bộ 97\n        [0, 10, 0, 0, 10, 0, -15, 0, 5, 10 ], # Lệch cho chỉ mục hình cho bộ 98\n        [0, 5, -10, 8, 10, 20, -15, 5, 0, 5 ], # Lệch cho chỉ mục hình cho bộ 99\n        [0, 15, 0, 15, 25, 0, -30, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 100\n        [30, 22, 10, 0, 10, 10, 10, 5, 20, 30 ], # Lệch cho chỉ mục hình cho bộ 101\n        [0, 0, 0, 0, 0, -5, -30, 0, 10, 7 ], # Lệch cho chỉ mục hình cho bộ 102\n        [45, 0, 0, 0, 0, 0, 20, 0, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 103\n        [45, 10, -20, 0, 0, 0, -15, 0, 20, 27 ], # Lệch cho chỉ mục hình cho bộ 104\n        [20, 30, 0, 0, -10, 20, -5, -10, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 105\n        [0, 5, -12, 0, 5, 8, -50, -7, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 106\n        [0, -5, 0, 0, 0, 0, -25, 0, -20, 0 ], # Lệch cho chỉ mục hình cho bộ 107\n        [0, 17, 0, 10, 0, 0, -30, 0, 0, 35 ], # Lệch cho chỉ mục hình cho bộ 108\n        [0, 33, 0, 0, 0, 0, -30, 0, 30, 0 ], # Lệch cho chỉ mục hình cho bộ 109\n        [20, 20, 20, 20, 20, 0, -10, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 101\n        [0, -10, -10, 0, 5, 10, -18, 0, 5, 5 ], # Lệch cho chỉ mục hình cho bộ 111\n        [0, 10, -5, 0, 5, 0, -25, 0, -15, 5 ], # Lệch cho chỉ mục hình cho bộ 112\n        [15, 30, -7, 5, 25, 0, -20, 10, 7, 35 ], # Lệch cho chỉ mục hình cho bộ 113\n        [12, 8, 10, 0, 23, 0, -22, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 114\n        [20, -10, 10, 5, -10, -10, -25, 8, 5, 15 ], # Lệch cho chỉ mục hình cho bộ 115\n        [5, -5, 0, 0, 5, 10, -35, 0, 5, -5 ], # Lệch cho chỉ mục hình cho bộ 116\n        [0, 5, 0, 0, 5, 0, -25, 0, -10, 15 ], # Lệch cho chỉ mục hình cho bộ 117\n        [8, 0, 0, 10, 0, 0, -15, 7, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 118\n        [15, 20, 0, 0, 30, 0, 0, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 119\n        [20, 20, -5, 0, 12, 0, -10, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 120\n        [20, 20, 5, 0, 10, 10, 10, -5, 12, 25 ], # Lệch cho chỉ mục hình cho bộ 121\n        [5, 0, -3, 0, 0, -5, -40, 0, 5, 7 ], # Lệch cho chỉ mục hình cho bộ 122\n        [25, 15, -10, 0, 27, 0, -7, 0, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 123\n        [45, 10, -20, 0, 0, 0, -15, 0, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 124\n        [20, 0, -15, 5, -10, -12, -5, 0, 10, 15 ], # Lệch cho chỉ mục hình cho bộ 125\n        [0, 2, -5, 0, 5, 10, -30, 0, 0-15, -5 ], # Lệch cho chỉ mục hình cho bộ 126\n        [0, 10, 0, 0, 0, 0, -10, -15, -10, 0 ], # Lệch cho chỉ mục hình cho bộ 127\n        [0, 20, 0, 10, 8, 0, -30, 0, 0, -20 ], # Lệch cho chỉ mục hình cho bộ 128\n        [0, -5, 0, 8, 0, 15, -35, -5, 25, 0 ], # Lệch cho chỉ mục hình cho bộ 129\n        [0, 30, 20, -15, 17, 0, -20, -15, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 130 \n        [20, 15, 5, 0, 13, 10, 10, 0, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 131\n        [0, 15, 0, 0, 0, -5, -30, -5, 15, 10 ], # Lệch cho chỉ mục hình cho bộ 132\n        [45, 0, 0, 0, 0, -5, -35, 0, 15, 25 ], # Lệch cho chỉ mục hình cho bộ 133\n        [45, 10, -20, 0, 13, 0, -15, 0, -5, 12 ], # Lệch cho chỉ mục hình cho bộ 134\n        [5, -5, 15, 0, 10, 20, -5, -10, -5, 0 ], # Lệch cho chỉ mục hình cho bộ 135\n        [0, 5, -5, -5, 20, 7, -10, 0, 0-15, 20 ], # Lệch cho chỉ mục hình cho bộ 136\n        [0, 15, 0, 20, 0, 0, -25, 20, -10, 0 ], # Lệch cho chỉ mục hình cho bộ 137\n        [0, 0, 0, 0, 23, 0, -30, 0, 0, 10 ], # Lệch cho chỉ mục hình cho bộ 138\n        [0, 5, 0, -5, 10, 5, 0, 0, 22, 5 ], # Lệch cho chỉ mục hình cho bộ 139\n        [10, 7, 0, -0, 20, 0, -20, -10, 5, 22 ], # Lệch cho chỉ mục hình cho bộ 140\n        [10, 5, 10, 0, 10, 10, 0, 0, 15, 0 ], # Lệch cho chỉ mục hình cho bộ 141\n        [0, 30, 0, 0, 20, 0, -25, 0, 10, 7 ], # Lệch cho chỉ mục hình cho bộ 142\n        [45, 0, 0, 0, 0, 0, 10, -5, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 143\n        [45, 25, -20, 0, 20, 0, -15, 0, 0, 15 ], # Lệch cho chỉ mục hình cho bộ 144\n        [20, 20, 0, 0, -10, 7, -10, -10, 10, 15 ], # Lệch cho chỉ mục hình cho bộ 145\n        [0, 25, -5, 0, 25, 0, -10, 0, 0, 15 ], # Lệch cho chỉ mục hình cho bộ 146\n        [-5, 10, 0, 0, 0, 0, -30, -15, -5, 0 ], # Lệch cho chỉ mục hình cho bộ 147\n        [10, 20, 0, 10, 10, 5, -30, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ #148\n        [5, 0, 0, 8, 15, 10, 0, -10, 15, 0 ], # Lệch cho chỉ mục hình cho bộ 149\n        [0, 30, 5, -15, 0, 0, -10, 10, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 150\n        [0, 0, 5, 15, -10, 10, -10, 5, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 151\n        [0, 10, 0, 0, 0, 0, -15, 0, 0, 5 ], # Lệch cho chỉ mục hình cho bộ 152\n        [12, 0, -18, 10, 5, 0, 0, 10, 0, 20 ], # Lệch cho chỉ mục hình cho bộ 153\n        [12, 0, 0, 0, 10, 0, 0, 10, 10, 10 ], # Lệch cho chỉ mục hình cho bộ 154\n        [20, 20, -10, -10, 0, 20, -25, 8, 5, 0 ], # Lệch cho chỉ mục hình cho bộ 155 \n        [10, 5, 15, 0, 5, 10, 0, 5, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 156\n        [-10, 20, 0, 10, 0, 0, -10, 0, 30, 10 ], # Lệch cho chỉ mục hình cho bộ 157 \n        [0, 30, 0, 10, 0, 0, -20, -10, 0, 30 ], # Lệch cho chỉ mục hình cho bộ 158\n        [0, 20, 0, 0, 10, 0, 0, -10, 20, 10 ], # Lệch cho chỉ mục hình cho bộ 159\n        [20, 10, 0, 0, 0, 0, 0, 0, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 160\n        [30, 10, -10, 0, 0, 10, -20, -10, 20, 25 ], # Lệch cho chỉ mục hình cho bộ 161\n        [10, 10, 0, 0, 0, -5, -20, 0, 10, 15 ], # Lệch cho chỉ mục hình cho bộ 162\n        [45, -10, 0, 0, 5, 0, -10, 0, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 163\n        [45, 10, -20, 0, 0, 0, 10, 0, 20, 0 ], # Lệch cho chỉ mục hình cho bộ 164\n        [20, 30, 0, 10, 20, 20, 10, -10, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 165\n        [0, 5, -5, -5, 5, 10, 20, 0, -5, 10 ], # Lệch cho chỉ mục hình cho bộ 166\n        [0, 5, 0, 0, 0, 0, -10, 30, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 167\n        [0, 20, 0, 0, 0, 0, 0, 0, 5, -20 ], # Lệch cho chỉ mục hình cho bộ 168\n        [0, 10, -20, 8, 10, 0, -15, 10, -20, -5 ], # Lệch cho chỉ mục hình cho bộ 169 \n        [0, 0, 0, 15, 0, 0, 0, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 170\n        [0, -18, -20, -5, 5, 10, 0, 0, -5, 10 ], # Lệch cho chỉ mục hình cho bộ 171 \n        [0, 20, 0, 0, 0, 0, -35, 15, 20, -10 ], # Lệch cho chỉ mục hình cho bộ 172\n        [5, 5, 0, 0, 0, 0, -8, -5, 5, 0 ], # Lệch cho chỉ mục hình cho bộ 173\n        [15, 35, 0, 0, 7, 0, 0, 10, 0, 5 ], # Lệch cho chỉ mục hình cho bộ 174\n        [10, 0, 0, 8, 15, 0, 0, 0, 0, -5 ], # Lệch cho chỉ mục hình cho bộ 175\n        [10, 5, -5, -5, 25, 10, -10, -10, -5, 10 ], # Lệch cho chỉ mục hình cho bộ 176\n        [0, -5, 0, 0, -10, 0, -15, 15, 0, 10 ], # Lệch cho chỉ mục hình cho bộ 177\n        [10, 5, 0, 0, 0, 5, -25, 0, 20, -20 ], # Lệch cho chỉ mục hình cho bộ 178\n        [0, 10, -10, 8, 10, 0, -5, 10, 0, -5 ], # Lệch cho chỉ mục hình cho bộ 179\n        [20, 0, -10, 0, -5, 0, 0, 0, 0, 20 ], # Lệch cho chỉ mục hình cho bộ 180\n        [0, 8, -5, 5, 15, 0, -50, 0, -5, 25 ], # Lệch cho chỉ mục hình cho bộ 181\n        [0, 7, 0, 5, 30, 0, -10, 0, 0, 25 ], # Lệch cho chỉ mục hình cho bộ 182\n        [0, 10, -8, 10, 20, 0, 0, 0, 0, -15 ], # Lệch cho chỉ mục hình cho bộ 183\n        [10, 8, -15, 10, 20, 0, -15, 10, 0, 15 ], # Lệch cho chỉ mục hình cho bộ 184\n        [0, 0, 0, 0, 20, 0, -10, 0, 10, 0 ], # Lệch cho chỉ mục hình cho bộ 185\n        [0, 15, -5, -20, 15, 10, -15, 5, 20, 20 ], # Lệch cho chỉ mục hình cho bộ 186 \n        [0, 15, 0, 0, 5, 0, -30, -5, 5, 15 ], # Lệch cho chỉ mục hình cho bộ 187\n        [0, 0, 0, 5, 25, 0, -10, 0, 5, -20 ], # Lệch cho chỉ mục hình cho bộ 188\n        [0, 18, -20, 8, 10, 0, -15, 10, 10, 5 ], # Lệch cho chỉ mục hình cho bộ 189\n        [0, 0, -10, 10, 30, 0, -20, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 190\n        [0, 20, -5, 5, 8, 10, -15, 0, -5, 20 ], # Lệch cho chỉ mục hình cho bộ 191\n        [15, 5, -5, 0, 22, 0, -33, 10, 0, 0 ], # Lệch cho chỉ mục hình cho bộ 192\n        [0, 8, -10, 5, -5, 0, -20, 0, 5, 0 ], # Lệch cho chỉ mục hình cho bộ 193\n        [0, 10, 10, 0, 0, 0, -25, -5, -20, 20 ], # Lệch cho chỉ mục hình cho bộ 194\n        [0, 25, 0, 10, 30, 0, -20, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 195\n        [10, 5, 0, 5, 0, 0, -20, -5, -5, 0 ], # Lệch cho chỉ mục hình cho bộ 196\n        [0, 5, 0, 0, 0, 0, -10, 0, 0, 10 ], # Lệch cho chỉ mục hình cho bộ 197\n        [0, 10, 0, 0, 10, 0, -15, 0, 5, 10 ], # Lệch cho chỉ mục hình cho bộ 198\n        [0, 5, -10, 8, 10, 20, -15, 5, 0, 5 ], # Lệch cho chỉ mục hình cho bộ 199\n        [0, 15, 0, 15, 25, 0, -30, 0, 0, 12 ], # Lệch cho chỉ mục hình cho bộ 200\n        [50, 5, 18, 0, 10, -10, -15, 0, 0, 19], # Lệch cho chỉ mục hình 3000\n        [10, 10, -9, -15, 26, 0, -20, -10, 30, 1], # Lệch cho chỉ mục hình 3001\n        [-125, -9, -5, -5, 27, -20, -25, 15, -13, 5], # Lệch cho chỉ mục hình 3002\n        [10, -185, -10, -25, 28, 0, 0, 0, -5, 15], # Lệch cho chỉ mục hình 3003\n        [0, 5, -3, -2, 19, 0, -5, 15, 21, 15], # Lệch cho chỉ mục hình 3004\n        [-40, 20, 0, 5, 0, 0, 0, 0, -2, 10], # Lệch cho chỉ mục hình 3005\n        [35, 19, -3, 0, 15, 0, 0, 5, 0, -15], # Lệch cho chỉ mục hình 3006\n        [30, 10, 0, 0, 4, 5, 0, 11, 1, -8], # Lệch cho chỉ mục hình 3007\n        [20, -18, -11, 12, 2, 2, 0, 0, 15, 15], # Lệch cho chỉ mục hình 3008\n        [30, -3, -1, 1, 15, -5, -3, 5, -1, 15], # Lệch cho chỉ mục hình 3009\n        [1, 13, -2, 0, 15, 0, 0, 0, 0, 0], # Lệch cho chỉ mục hình 3010\n        [0, -11, -5, -9, -14, 1, -8, 0, -3, 20], # Lệch cho chỉ mục hình 3011\n        [0, -150, 0, 0, 25, 0, 0, 0, 4, -10], # Lệch cho chỉ mục hình 3012\n        [5, 31, 0, 0, 15, 0, -5, -5, 0, 9], # Lệch cho chỉ mục hình 3013\n        [10, 18, -5, -3, 0, 0, 0, 0, 1, 0], # Lệch cho chỉ mục hình 3014\n        [0, 7, 0, 3, 10, 0, -10, 5, 0, 5], # Lệch cho chỉ mục hình 3015\n        [-15, 30, 0, 0, 0, 1, -5, -5, 0, 15], # Lệch cho chỉ mục hình 3016\n        [10, 10, 0, 0, 0, 0, -55, 0, 5, 13], # Lệch cho chỉ mục hình 3017\n        [0, 37, -10, -5, 10, 0, -6, 0, 0, 5], # Lệch cho chỉ mục hình 3018\n        [0, -30, -5, 1, 15, 0, -40, 0, 12, -3], # Lệch cho chỉ mục hình 3019\n        [0, 43, 0, -3, 13, 0, 0, 5, 0, 7], # Lệch cho chỉ mục hình 3020\n        [0, 40, -5, 0, 0, 0, 0, 0, 0, 12], # Lệch cho chỉ mục hình 3021\n        [0, 3, 0, 0, 17, 0, 0, 0, 0, 18], # Lệch cho chỉ mục hình 3022\n        [30, 15, -4, -15, 0, 0, -3, 5, 0, 17], # Lệch cho chỉ mục hình 3023\n        [45, 5, -5, -5, -4, -5, 0, 0, 10, 10], # Lệch cho chỉ mục hình 3024\n        [0, -3, -4, 12, 0, 0, -3, 0, 8, 0], # Lệch cho chỉ mục hình 3025\n        [0, 10, -2, -3, 8, 0, -53, 0, 0, 17], # Lệch cho chỉ mục hình 3026\n        [15, 30, -5, 0, 0, 0, 0, 0, 19, -1], # Lệch cho chỉ mục hình 3027\n        [90, 5, -5, 0, 12, 0, -5, 0, 0, 10], # Lệch cho chỉ mục hình 3028\n        [30, 27, 0, 0, 9, 0, 0, 0, 0, 2], # Lệch cho chỉ mục hình 3029\n        [-8, 11, 2, 8, 5, 1, 0, 0, -1, 3], # Lệch cho chỉ mục hình 3030\n        [15, -13, 5, 0, 29, 1, 1, 7, 5, 13], # Lệch cho chỉ mục hình 3031\n        [0, 9, -2, 0, 17, 5, -1, -5, 1, 17], # Lệch cho chỉ mục hình 3032\n        [25, 0, -1, -1, 30, 3, -40, 0, -4, 15], # Lệch cho chỉ mục hình 3033\n        [0, 18, 0, -1, 15, 1, 0, -5, 7, 1], # Lệch cho chỉ mục hình 3034\n        [30, 0, 0, -5, 6, -2, -15, 7, -17, 2], # Lệch cho chỉ mục hình 3035\n        [10, 1, 0, 0, 3, -1, 0, 0, -1, 1], # Lệch cho chỉ mục hình 3036\n        [8, 1, 0, 0, 5, 1, 1, 0, -3, -14], # Lệch cho chỉ mục hình 3037\n        [0, 13, 1, 0, 31, 3, -15, 0, -20, 0], # Lệch cho chỉ mục hình 3038\n        [90, 17, 2, 3, 29, -1, -2, 0, 15, -3], # Lệch cho chỉ mục hình 3039\n        [40, 35, -4, -2, 6, 9, 0, 0, 1, 16], # Lệch cho chỉ mục hình 3040\n        [1, 0, -5, 9, -2, 2, 0, 5, 0, -14], # Lệch cho chỉ mục hình 3041\n        [5, 19, -11, -4, -1, -4, -15, 10, 3, 7], # Lệch cho chỉ mục hình 3042\n        [3, 37, -5, -6, 2, -1, -15, 0, 0, 20], # Lệch cho chỉ mục hình 3043\n        [10, 9, -7, -2, 14, 1, 0, 0, 0, 25], # Lệch cho chỉ mục hình 3044\n        [0, 4, -5, -20, 17, 0, -1, 0, 1, -1], # Lệch cho chỉ mục hình 3045\n        [0, 5, 0, -15, 24, -21, -15, 0, 1, 18], # Lệch cho chỉ mục hình 3046\n        [0, 7, -1, 0, 0, 1, 0, 0, -6, -1], # Lệch cho chỉ mục hình 3047\n        [9, 15, -5, 0, 5, 5, 0, 0, 4, 43], # Lệch cho chỉ mục hình 3048\n        [60, 23, -2, 1, 27, 0, 0, -5, 17, 11], # Lệch cho chỉ mục hình 3049\n        [12, -2, -7, 0, 15, -7, 0, 0, 5, 10], # Lệch cho chỉ mục hình 3050\n        [1, 18, -1, 1, 5, 1, 0, -5, 6, 3], # Lệch cho chỉ mục hình 3051\n        [55, -5, -2, 0, 0, 1, -6, -3, -12, 13], # Lệch cho chỉ mục hình 3052\n        [19, 17, -5, 0, -5, 0, 0, 12, 14, 8], # Lệch cho chỉ mục hình 3053\n        [10, -2, -3, -18, 0, 1, -35, 1, 15, 12], # Lệch cho chỉ mục hình 3054\n        [10, -5, -1, 3, 10, 0, -15, -5, -17, -5], # Lệch cho chỉ mục hình 3055\n        [30, 40, -10, -5, 19, 17, -48, -1, 16, -9], # Lệch cho chỉ mục hình 3056\n        [25, 35, -8, -20, 5, 7, 0, -1, 1, 20], # Lệch cho chỉ mục hình 3057\n        [0, 0, 0, -3, 0, 0, 0, -1, 4, 0], # Lệch cho chỉ mục hình 3058\n        [1, 7, 0, 0, 27, -5, -3, -5, 29, 3], # Lệch cho chỉ mục hình 3059\n        [0, 14, 0, 0, -5, 0, -15, 0, -2, 1], # Lệch cho chỉ mục hình 3060\n        [0, 13, -3, 21, 11, 0, -13, 0, 1, -37], # Lệch cho chỉ mục hình 3061\n        [0, 12, 0, 0, 0, 0, -1, 0, 0, -10], # Lệch cho chỉ mục hình 3062\n        [0, 28, -2, 0, -1, 5, -23, -7, 5, 30], # Lệch cho chỉ mục hình 3063\n        [0, 0, -6, 15, 17, 0, -25, -1, 2, -3], # Lệch cho chỉ mục hình 3064\n        [0, 7, -9, 1, 0, 0, 0, 1, 5, 7], # Lệch cho chỉ mục hình 3065\n        [7, 7, 0, 15, 21, 11, -8, -5, 8, 0], # Lệch cho chỉ mục hình 3066\n        [20, 1, -3, 3, 0, 1, 0, -3, -8, 0], # Lệch cho chỉ mục hình 3067\n        [80, -12, -10, -2, 0, 0, 0, 0, 0, 9], # Lệch cho chỉ mục hình 3068\n        [-10, 31, -19, -1, 1, 0, -4, 0, 1, 39], # Lệch cho chỉ mục hình 3069\n        [40, 40, -18, 0, 23, 0, -5, -4, -2, 21], # Lệch cho chỉ mục hình 3070\n        [0, 5, 0, 13, 20, 0, -9, 0, 10, 0], # Lệch cho chỉ mục hình 3071\n        [8, 25, 0, -2, 18, 5, -3, 0, 5, 3], # Lệch cho chỉ mục hình 3072\n        [60, -5, 0, 0, -2, -4, 0, 0, 0, 15], # Lệch cho chỉ mục hình 3073\n        [15, 15, 20, -2, 9, 0, 0, 0, 0, 5], # Lệch cho chỉ mục hình 3074\n        [50, 18, -8, -10, 13, 0, 0, 0, 3, 9], # Lệch cho chỉ mục hình 3075\n        [40, 27, 0, -3, 7, 5, -40, 0, 5, 0], # Lệch cho chỉ mục hình 3076\n        [10, 5, 4, 0, 0, 20, 0, 0, 10, -4], # Lệch cho chỉ mục hình 3077\n        [-3, 5, -1, 0, -1, 0, 5, 0, -5, 1], # Lệch cho chỉ mục hình 3078\n        [0, 15, -5, 9, 4, -5, 0, 0, 1, 33], # Lệch cho chỉ mục hình 3079\n        [15, 3, 0, 0, 15, 0, 0, 0, -8, 33], # Lệch cho chỉ mục hình 3080\n        [40, 0, 0, -15, -9, 3, -10, 0, 9, 1], # Lệch cho chỉ mục hình 3081\n        [0, 0, 0, 0, 0, 0, -8, 3, 5, 19], # Lệch cho chỉ mục hình 3082\n        [0, 20, 0, 0, 0, 0, -10, -3, 5, 0], # Lệch cho chỉ mục hình 3083\n        [65, 10, 0, 0, 0, 0, -5, 0, 4, 5], # Lệch cho chỉ mục hình 3084\n        [3, 38, -5, 0, 17, 0, 0, 7, 3, 9], # Lệch cho chỉ mục hình 3085\n        [10, 25, 3, 10, 24, 0, 5, -5, -2, 20], # Lệch cho chỉ mục hình 3086\n        [0, 21, -5, 5, 8, 0, 3, 0, 1, -9], # Lệch cho chỉ mục hình 3087\n        [60, 5, -3, 3, 20, 1, 0, 0, 3, 30], # Lệch cho chỉ mục hình 3088\n        [60, -6, 0, 0, 40, 5, 0, -5, 0, 15], # Lệch cho chỉ mục hình 3089\n        [0, 3, 0, -3, 9, 3, 1, -1, 0, 29], # Lệch cho chỉ mục hình 3090\n        [30, 33, 0, -5, 15, 3, 0, 0, 1, -1], # Lệch cho chỉ mục hình 3091\n        [0, 33, 0, -5, 0, 0, -5, 2, 0, 0], # Lệch cho chỉ mục hình 3092\n        [0, 18, 2, -1, 0, 0, -2, 0, 16, 19], # Lệch cho chỉ mục hình 3093\n        [25, 0, -5, -3, -2, -1, 0, -1, 0, 5], # Lệch cho chỉ mục hình 3094\n        [0, 5, 0, 2, 4, 2, 2, 0, 1, 25], # Lệch cho chỉ mục hình 3095\n        [20, 15, -5, -5, 5, -10, -9, 10, 20, 25], # Lệch cho chỉ mục hình 3096\n        [2, 10, 3, 15, 46, 0, 0, -1, -6, 1], # Lệch cho chỉ mục hình 3097\n        [0, 11, -4, 5, -8, 0, 0, 0, -3, 24], # Lệch cho chỉ mục hình 3098\n        [30, 0, -3, 0, 30, 0, 0, -10, 4, 19], # Lệch cho chỉ mục hình 3099\n    ])\n}\n\n# Số lượng hình ảnh được chọn để xoay\nnumPickRotate = len(X_train_pick2rotate['index'])\n\nprint('numPickRotate = ', numPickRotate)\n\n# -------------------------------------------------- Xoay hình ảnh\n\n# Khởi tạo các mảng để lưu trữ hình ảnh sau khi xoay và nhãn của chúng\nX_sample_rotate = np.zeros((10,numRotate_prior,numPickRotate,X_train.shape[1]))\ny_sample_rotate = np.zeros((10,numRotate_prior,numPickRotate))\n\nfor k0 in range(10):\n    for k1 in range(numRotate_prior):\n        for k2 in range(numPickRotate):\n            \n                idxPick_X_train = X_train_pick2rotate['index'][k2]\n                \n                idx = np.where(y_train==k0)[0][idxPick_X_train]\n                \n                y_sample_rotate[k0,k1,k2] = k0 * numRotate_prior + k1\n                \n                angleRotate = (k1/numRotate_prior * angleRange_prior) + angleOffset_prior + X_train_pick2rotate['offset'][k2][k0]\n                \n                X_sample_rotate[k0,k1,k2,:] = func_array1d_rotate2d(X_train[idx][np.newaxis,:],angleRotate, 28)\n\n                \nX_sample_rotate[X_sample_rotate < 10] = 0\n#X_sample_rotate[X_sample_rotate > 300] = 0\n\n# Chuẩn hóa giá trị pixel về khoảng [0,1]\n# Công thức: MinMaxscale(X) = (X - X_min) / (X_max - X_min)\n\nprint(\"Trước khi áp dụng MinMax Scaler:\")\nprint(\"  - Giá trị nhỏ nhất:\", X_sample_rotate.min())\nprint(\"  - Giá trị lớn nhất:\", X_sample_rotate.max())\n\nminmax_scaler = MinMaxScaler()\n\nfor k0 in range(10):\n    for k1 in range(numRotate_prior):\n        X_sample_rotate[k0,k1,:,:] = minmax_scaler.fit_transform(X_sample_rotate[k0,k1,:,:]) * 255\n\nprint(\"\\nSau khi áp dụng MinMax Scaler:\")\nprint(\"  - Giá trị nhỏ nhất:\", X_sample_rotate.min())\nprint(\"  - Giá trị lớn nhất:\", X_sample_rotate.max())\n\n\n\n# Tiếp theo, hãy trực quan hóa một hình ảnh từ mỗi lớp.\n# Chúng ta sẽ sử dụng hàm 'imshow' của matplotlib.\n\n# for k2 in range(numPickRotate):\nfor k2 in range(5,10):\n    k1 = 0\n    # k1 = numRotate_prior - 1\n\n    fig, axs = plt.subplots(nrows=2, ncols=5, figsize=(10,5))\n\n    for k0 in range(10):\n        img = axs[int(k0 >= 5), k0%5].imshow(\n            X=X_sample_rotate[k0,k1,k2,:].reshape(28, 28),\n            cmap='Blues'\n        )\n        img.axes.get_yaxis().set_visible(False)\n        img.axes.get_xaxis().set_visible(False)\n        img.axes.set_title(f'Nhãn: {k0}')\n        \nX_train_rotate = np.reshape(X_sample_rotate, (-1,784))\ny_train_rotate = np.reshape(y_sample_rotate, (-1,1)).flatten()\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:12.218414Z","iopub.execute_input":"2024-05-29T01:33:12.218903Z","iopub.status.idle":"2024-05-29T01:33:19.684313Z","shell.execute_reply.started":"2024-05-29T01:33:12.218861Z","shell.execute_reply":"2024-05-29T01:33:19.683179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Phần 3: Phân tích thành phần chính**\n","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-size: 1.2em; color: green; font-weight: bold; border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left\">Phân tích thành phần chính (PCA) là một kỹ thuật thống kê để giảm chiều dữ liệu của một tập dữ liệu. Điều này được thực hiện bằng cách biến đổi tuyến tính dữ liệu thành một hệ tọa độ mới, trong đó (phần lớn) sự biến đổi trong dữ liệu có thể được mô tả với ít chiều hơn so với dữ liệu ban đầu.\n\nThành phần chính đầu tiên có thể được định nghĩa tương đương như một hướng tối đa hóa phương sai của dữ liệu đã được chiếu. Thành phần chính thứ i có thể được xem là một hướng vuông góc với các thành phần chính đầu tiên i-1 mà tối đa hóa phương sai của dữ liệu đã được chiếu.\n\nsklearn.decomposition.PCA: Giảm chiều tuyến tính bằng cách sử dụng Phân tích Giá trị Kỳ dị (SVD) của dữ liệu để chiếu nó vào không gian có chiều thấp hơn.\n\nỞ đây chúng ta sẽ sử dụng 10 thành phần chính hàng đầu, giải thích gần 50% phương sai trong dữ liệu. Điều này có nghĩa là chúng ta có thể biểu diễn 50% phương sai của dữ liệu với một không gian tính năng nhỏ hơn >98% so với không gian ban đầu (10 tính năng so với 784 tính năng).</div>","metadata":{}},{"cell_type":"code","source":"# Chuẩn hóa giá trị pixel về khoảng [0,1]\n# Công thức: MinMaxscale(X) = (X - X_min) / (X_max - X_min)\n\n# In ra giá trị nhỏ nhất và lớn nhất của dữ liệu trước khi áp dụng MinMax Scaler\nprint(\"Trước khi áp dụng MinMax Scaler:\")\nprint(\"  - Giá trị nhỏ nhất:\", X_train.min())\nprint(\"  - Giá trị lớn nhất:\", X_train.max())\n\n# Tạo một đối tượng MinMaxScaler\nminmax_scaler = MinMaxScaler()\n\n# Áp dụng MinMaxScaler để chuẩn hóa dữ liệu X_train_rotate\nX_train_rotate_scaled = minmax_scaler.fit_transform(X_train_rotate)\n\n# In ra giá trị nhỏ nhất và lớn nhất của dữ liệu sau khi áp dụng MinMax Scaler\nprint(\"\\nSau khi áp dụng MinMax Scaler:\")\nprint(\"  - Giá trị nhỏ nhất:\", X_train_rotate_scaled.min())\nprint(\"  - Giá trị lớn nhất:\", X_train_rotate_scaled.max())\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:19.686320Z","iopub.execute_input":"2024-05-29T01:33:19.686824Z","iopub.status.idle":"2024-05-29T01:33:19.823327Z","shell.execute_reply.started":"2024-05-29T01:33:19.686780Z","shell.execute_reply":"2024-05-29T01:33:19.822143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Thực tế, chúng ta sẽ tích hợp các module MinMaxScaler và PCA\n# vào một Pipeline của Sklearn.\n\n# Tạo một Pipeline với các bước:\n# - 'scaler': Chuẩn hóa giá trị pixel sử dụng MinMaxScaler\n# - 'pca': Giảm chiều dữ liệu sử dụng PCA với số thành phần chính là 40\npca = Pipeline(steps=[\n    ('scaler', MinMaxScaler()),\n    ('pca', PCA(n_components=40))\n])\n\n# Áp dụng Pipeline để chuẩn hóa và giảm chiều dữ liệu\nX_train_rotate_pca = pca.fit_transform(X=X_train_rotate)\n\n# In ra kích thước của dữ liệu sau khi áp dụng PCA\nprint('X_train_pca shape:', X_train_rotate_pca.shape)\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:19.824841Z","iopub.execute_input":"2024-05-29T01:33:19.825159Z","iopub.status.idle":"2024-05-29T01:33:21.257078Z","shell.execute_reply.started":"2024-05-29T01:33:19.825132Z","shell.execute_reply":"2024-05-29T01:33:21.255274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Trích xuất tỷ lệ phương sai được giải thích\n# Và tính tổng lũy kế của chúng\n\n# Lấy đối tượng PCA từ Pipeline\npca_object = pca.named_steps['pca']\n\n# Trích xuất tỷ lệ phương sai được giải thích cho từng thành phần chính\nvar_ratio = pd.Series(\n    data=pca_object.explained_variance_ratio_,  # Dữ liệu là tỷ lệ phương sai được giải thích\n    index=range(1, 41)  # Đặt chỉ số từ 1 đến 10 tương ứng với các thành phần chính\n)\n\n# Tính tổng lũy kế của tỷ lệ phương sai được giải thích\ncumsum_var_ratio = var_ratio.cumsum()\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:21.259944Z","iopub.execute_input":"2024-05-29T01:33:21.261204Z","iopub.status.idle":"2024-05-29T01:33:21.277442Z","shell.execute_reply.started":"2024-05-29T01:33:21.261140Z","shell.execute_reply":"2024-05-29T01:33:21.275909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Vẽ biểu đồ tỷ lệ phương sai được giải thích cho từng thành phần chính (trái)\n# Và vẽ biểu đồ tổng lũy kế của chúng (phải)\n\n# Tạo một figure với hai subplots đặt cạnh nhau\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15,5))\n\n# Biểu đồ thanh cho tỷ lệ phương sai được giải thích của từng thành phần chính\nsns.barplot(\n    x=var_ratio.index,  # Trục x là chỉ số của các thành phần chính\n    y=var_ratio.values,  # Trục y là giá trị tỷ lệ phương sai được giải thích\n    color='#4895EE',  # Màu của thanh biểu đồ\n    ax=ax1  # Vẽ lên subplot ax1\n)\n# Thiết lập tiêu đề và nhãn cho trục x và y của biểu đồ thứ nhất\nax1.set(\n    title='Giải thích tỷ lệ phương sai PCA',  # Tiêu đề\n    xlabel='Component',  # Nhãn trục x\n    ylabel='Var Ratio',  # Nhãn trục y\n)\n# Thiết lập giới hạn cho trục y của biểu đồ thứ nhất\nax1.set_ylim(0, 0.5)\n\n# Biểu đồ thanh cho tổng lũy kế của tỷ lệ phương sai được giải thích\nsns.barplot(\n    x=cumsum_var_ratio.index,  # Trục x là chỉ số của các thành phần chính\n    y=cumsum_var_ratio.values,  # Trục y là giá trị tổng lũy kế của tỷ lệ phương sai được giải thích\n    color='#4895EE',  # Màu của thanh biểu đồ\n    ax=ax2  # Vẽ lên subplot ax2\n)\n# Thiết lập tiêu đề và nhãn cho trục x và y của biểu đồ thứ hai\nax2.set(\n    title='Tỷ lệ phương sai giải thích tích lũy PCA',  # Tiêu đề\n    xlabel='Component',  # Nhãn trục x\n    ylabel='Cumulative Var Ratio',  # Nhãn trục y\n)\n# Thiết lập giới hạn cho trục y của biểu đồ thứ hai\n_ = ax2.set_ylim(0, 0.5)\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:21.280668Z","iopub.execute_input":"2024-05-29T01:33:21.283122Z","iopub.status.idle":"2024-05-29T01:33:22.596286Z","shell.execute_reply.started":"2024-05-29T01:33:21.283067Z","shell.execute_reply":"2024-05-29T01:33:22.594991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chuyển các thành phần chính thành định dạng DataFrame của Pandas\n# Bằng cách này chúng ta có thể sử dụng các thư viện seaborn và plotly\n# để trực quan hóa dữ liệu.\n# (các gói này hoạt động tốt hơn với Pandas DataFrames)\n\n# Tạo một DataFrame từ dữ liệu đã giảm chiều\ndf_train_pca = pd.DataFrame(\n    data=X_train_rotate_pca[:,:3],  # Lấy 3 thành phần chính đầu tiên\n    columns=['x', 'y', 'z']  # Đặt tên cho các cột là 'x', 'y', 'z'\n)\n\n# Thêm cột 'target' để xác định màu sắc của các điểm trong trực quan hóa\ndf_train_pca['target'] = y_train_rotate\n\n# In ra kích thước của DataFrame\nprint(df_train_pca.shape)\n\n# Hiển thị 5 hàng đầu tiên của DataFrame\ndf_train_pca.head()\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:22.597826Z","iopub.execute_input":"2024-05-29T01:33:22.598213Z","iopub.status.idle":"2024-05-29T01:33:22.617049Z","shell.execute_reply.started":"2024-05-29T01:33:22.598180Z","shell.execute_reply":"2024-05-29T01:33:22.615516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Trực quan hóa dữ liệu hai chiều\n# Ở đây hai thành phần chính hàng đầu được vẽ biểu đồ\n\n# Đặt phong cách biểu đồ là 'dark'\nsns.set_style(\"dark\")\n\n# Tạo biểu đồ tán xạ với seaborn\nax = sns.scatterplot(\n    data=df_train_pca,  # Dữ liệu để vẽ biểu đồ là DataFrame df_train_pca\n    x='x',  # Trục x là cột 'x' (thành phần chính thứ nhất)\n    y='y',  # Trục y là cột 'y' (thành phần chính thứ hai)\n    hue='target',  # Màu sắc của các điểm dựa trên giá trị của cột 'target'\n    palette='Blues',  # Bảng màu sử dụng là 'Blues'\n)\n\n# Thiết lập tiêu đề và nhãn cho trục x và y của biểu đồ\nax.set(\n    title='Biểu đồ không gian 2 chiều PCA',  # Tiêu đề của biểu đồ\n    xlabel='1st Comp.',  # Nhãn trục x (thành phần chính thứ nhất)\n    ylabel='2nd Comp.'  # Nhãn trục y (thành phần chính thứ hai)\n)\n\n# Thêm đường ngang tại y=0 với đường nét đứt và màu '#FFBF00'\nax.axhline(y=0, linestyle='--', color='#FFBF00', zorder=-1)\n\n# Thêm đường dọc tại x=0 với đường nét đứt và màu '#FFBF00'\nax.axvline(x=0, linestyle='--', color='#FFBF00', zorder=-1)\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:22.618906Z","iopub.execute_input":"2024-05-29T01:33:22.619524Z","iopub.status.idle":"2024-05-29T01:33:24.429174Z","shell.execute_reply.started":"2024-05-29T01:33:22.619457Z","shell.execute_reply":"2024-05-29T01:33:24.428022Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Trực quan hóa dữ liệu ba chiều\n# Ở đây ba thành phần chính hàng đầu được vẽ biểu đồ\n\n# Đặt phong cách biểu đồ là 'white'\nsns.set_style(\"white\")\n\n# Tạo biểu đồ tán xạ 3D với plotly\nfig = px.scatter_3d(\n    data_frame=df_train_pca,  # Dữ liệu để vẽ biểu đồ là DataFrame df_train_pca\n    x='x',  # Trục x là cột 'x' (thành phần chính thứ nhất)\n    y='y',  # Trục y là cột 'y' (thành phần chính thứ hai)\n    z='z',  # Trục z là cột 'z' (thành phần chính thứ ba)\n    labels={  # Thiết lập nhãn cho các trục\n        'x': '1st Comp.',  # Nhãn trục x\n        'y': '2nd Comp.',  # Nhãn trục y\n        'z': '3rd Comp.'  # Nhãn trục z\n    },\n    color='target',  # Màu sắc của các điểm dựa trên giá trị của cột 'target'\n    title='Biểu đồ không gian 3 chiều PCA',  # Tiêu đề của biểu đồ\n    color_continuous_scale='blues',  # Bảng màu sử dụng là 'blues'\n    template='ggplot2'  # Template của biểu đồ là 'ggplot2'\n)\n\n# Hiển thị biểu đồ\nfig.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:24.430504Z","iopub.execute_input":"2024-05-29T01:33:24.430915Z","iopub.status.idle":"2024-05-29T01:33:26.896470Z","shell.execute_reply.started":"2024-05-29T01:33:24.430881Z","shell.execute_reply":"2024-05-29T01:33:26.895228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Bây giờ, chúng ta sẽ áp dụng inverse_transform để tính\n# sự xấp xỉ của các hình ảnh dựa trên 10 thành phần chính.\n# Có thể thấy rằng chỉ với 10 thành phần, chúng ta đã truyền tải được\n# một phần lớn thông tin của mỗi hình ảnh.\n\npca_object = pca.named_steps['pca']\n\nfig, axs = plt.subplots(nrows=10, ncols=2, figsize=(6,20))\n\nfor i in range(10):\n    idx = np.where(y_train==i)[0][0]\n    original_img = axs[i, 0].imshow(\n        X=X_train_rotate[idx].reshape(28, 28),\n        cmap='Blues'\n    )\n    original_img.axes.get_yaxis().set_visible(False)\n    original_img.axes.get_xaxis().set_visible(False)\n    original_img.axes.set_title(f'Original [{i}]:')\n    \n    approx_img_ = pca_object.inverse_transform(X=X_train_rotate_pca[idx])\n    approx_img = axs[i, 1].imshow(\n        X=approx_img_.reshape(28, 28),\n        cmap='Blues'\n    )\n    approx_img.axes.get_yaxis().set_visible(False)\n    approx_img.axes.get_xaxis().set_visible(False)\n    approx_img.axes.set_title(f'Approximation [{i}]:')\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:26.897816Z","iopub.execute_input":"2024-05-29T01:33:26.898190Z","iopub.status.idle":"2024-05-29T01:33:27.987055Z","shell.execute_reply.started":"2024-05-29T01:33:26.898159Z","shell.execute_reply":"2024-05-29T01:33:27.985782Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hiển thị rằng các thành phần PCA không tương quan\n# (Tuyên bố này là đúng theo định nghĩa và xây dựng\n# của thuật toán Phân tích thành phần chính)\n\ncorr = np.corrcoef(x=X_train_rotate_pca, rowvar=False)\ncorr = np.around(corr, decimals=2)\n\nax = sns.heatmap(\n    data=corr, \n    vmax=1, \n    vmin=-1,\n    square=True,\n    linewidths=.5,\n    annot=True,\n    cmap='Blues',\n)\n\n_ = ax.set(\n    title='Correlation PCA Components [Uncorrelated]',\n    xlabel='Components',\n    ylabel='Components'\n)\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:27.988538Z","iopub.execute_input":"2024-05-29T01:33:27.988933Z","iopub.status.idle":"2024-05-29T01:33:32.859004Z","shell.execute_reply.started":"2024-05-29T01:33:27.988900Z","shell.execute_reply":"2024-05-29T01:33:32.857853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Phần 4: Sử dụng K-Nearest Neighbors Classifier phân loại 10 thành phần chính hàng đầu từ PCA**","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-size: 1.2em; color: green; font-weight: bold; border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left\">Thuật toán K-Nearest Neighbors (KNN) là một bộ phân loại học có giám sát, phi tham số, sử dụng độ gần để thực hiện các phân loại hoặc dự đoán về sự nhóm của một điểm dữ liệu cá nhân. Dự đoán: Một nhãn lớp được gán dựa trên cơ sở của một cuộc bỏ phiếu đa số, nhãn được đại diện nhiều nhất xung quanh một điểm dữ liệu cho trước.\n\nTrong dự án này, đầu vào của Bộ phân loại KNN sẽ là 10 thành phần chính hàng đầu từ PCA.\n\nsklearn.neighbors.KNeighborsClassifier: Bộ phân loại thực hiện việc bỏ phiếu của k-nearest neighbors. Chúng ta sẽ chạy GridSearchCV để tối ưu hóa siêu tham số n_neighbors.</div>","metadata":{}},{"cell_type":"code","source":"knn_clf = Pipeline(steps=[\n    ('scaler', MinMaxScaler()),\n    ('pca', PCA(n_components=10)),\n    ('knn', KNeighborsClassifier())\n])\n\nknn_clf_params = {\n    'knn__n_neighbors': [2, 4, 8, 16, 32]\n}\n\nknn_clf_gs = GridSearchCV(\n    estimator=knn_clf, \n    param_grid=knn_clf_params,\n    scoring='accuracy',\n    n_jobs=-1,\n    cv=5,\n    verbose=1,\n    return_train_score=True\n)\n\nknn_clf_gs.fit(X=X_train_rotate_pca, y=y_train_rotate)\n\nprint('best PCA+KNN params:', knn_clf_gs.best_params_)\nprint('best PCA+KNN cv accuracy:', knn_clf_gs.best_score_)\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:32.860743Z","iopub.execute_input":"2024-05-29T01:33:32.861449Z","iopub.status.idle":"2024-05-29T01:33:50.304461Z","shell.execute_reply.started":"2024-05-29T01:33:32.861410Z","shell.execute_reply":"2024-05-29T01:33:50.302031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Vẽ biểu đồ cho độ chính xác của tập huấn luyện và tập validation\n# cho mỗi giá trị siêu tham số được thử nghiệm trong quá trình\n# tìm kiếm trên lưới.\n# Biểu đồ này cho phép chúng ta kiểm tra sự quá mức (overfitting).\n\nfig, ax = plt.subplots()\n\nax.plot(\n    range(len(knn_clf_params['knn__n_neighbors'])), \n    knn_clf_gs.cv_results_['mean_train_score'],\n    label='train',\n    color='blue'\n)\nax.plot(\n    range(len(knn_clf_params['knn__n_neighbors'])),\n    knn_clf_gs.cv_results_['mean_test_score'],\n    label='test',\n    color='orange'\n)\nax.set_xticks(\n    ticks=range(len(knn_clf_params['knn__n_neighbors'])),\n    labels=knn_clf_params['knn__n_neighbors']\n)\n\nii = knn_clf_params['knn__n_neighbors'].index(\n    knn_clf_gs.best_params_['knn__n_neighbors']\n)\n\nplt.axvline(x=ii, linestyle='--', color='red', label='best', alpha=0.3)\nplt.legend()\nplt.xlabel(\"'n_neighbors' vals\")\nplt.ylabel('Accuracy')\nplt.title(\"PCA+KNN GridSearchCV Results\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:50.310377Z","iopub.execute_input":"2024-05-29T01:33:50.311963Z","iopub.status.idle":"2024-05-29T01:33:50.719970Z","shell.execute_reply.started":"2024-05-29T01:33:50.311888Z","shell.execute_reply":"2024-05-29T01:33:50.718454Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Phần 5: Tạo một tệp tin chứa các dự đoán của mô hình học máy trên tập dữ liệu Test**","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score\n\n# Tính toán và hiển thị Ma trận nhầm lẫn\n# Lưu ý: toàn bộ tập dữ liệu huấn luyện được sử dụng ở đây\n\nX_val = X_train_rotate_pca\nY_val = y_train_rotate\n\nbest_cv_clf = knn_clf_gs.best_estimator_\ny_pred = best_cv_clf.predict(X=X_val)\n\ny_val_number = Y_val // numRotate_prior\ny_pred_number = y_pred // numRotate_prior\n\ny_val_angle = (Y_val % numRotate_prior)\ny_pred_angle = (y_pred % numRotate_prior)\n\nprint('KNN number + angle, Acc({:.2f}%)', accuracy_score(Y_val, y_pred))\nprint('KNN number, Acc({:.2f}%)', accuracy_score(y_val_number, y_pred_number))\nprint('KNN angle, Acc({:.2f}%)', accuracy_score(y_val_angle, y_pred_angle))\n\ny_val_angle_degree = y_val_angle/numRotate_prior * angleRange_prior + angleOffset_prior\ny_pred_angle_degree = y_pred_angle/numRotate_prior * angleRange_prior + angleOffset_prior\n\n\nconf_matrix = confusion_matrix(y_val_number, y_pred_number)\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')\n\nconf_matrix = confusion_matrix(y_val_angle, y_pred_angle)\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:50.721576Z","iopub.execute_input":"2024-05-29T01:33:50.722023Z","iopub.status.idle":"2024-05-29T01:33:54.366432Z","shell.execute_reply.started":"2024-05-29T01:33:50.721985Z","shell.execute_reply":"2024-05-29T01:33:54.365016Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_val = pca.transform(X_train)\nY_val = y_train\n\nbest_cv_clf = knn_clf_gs.best_estimator_\ny_pred = best_cv_clf.predict(X=X_val)\n\ny_pred_number = y_pred\ny_val_number = Y_val\n\nprint('KNN number, Acc({:.2f}%)', accuracy_score(y_val_number, y_pred_number))\n\nconf_matrix = confusion_matrix(y_val_number, y_pred_number)\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')\n\ny_pred_angle = (y_pred % numRotate_prior)\n\ny_pred_angle_degree = y_pred_angle/numRotate_prior * angleRange_prior + angleOffset_prior\n\nprint('y_pred_angle_degree =', y_pred_angle_degree)","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:54.368137Z","iopub.execute_input":"2024-05-29T01:33:54.368507Z","iopub.status.idle":"2024-05-29T01:34:27.990866Z","shell.execute_reply.started":"2024-05-29T01:33:54.368476Z","shell.execute_reply":"2024-05-29T01:34:27.989689Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Bây giờ, hãy áp dụng biến đổi nghịch đảo để tính toán\n# sự gần đúng của hình ảnh dựa trên nguyên tắc 10 thành phần.\n# Chúng ta có thể thấy rằng chỉ với 10 thành phần chúng ta đã truyền tải được một lượng lớn\n# một phần thông tin của mỗi hình ảnh.\npca_object = pca.named_steps['pca']\n\nfig, axs = plt.subplots(nrows=10, ncols=2, figsize=(6,20))\n\nfor i in range(10):\n    idx = np.where(y_train == i )[0][0]\n    original_img = axs[i, 0].imshow(\n        X=X_train[idx].reshape(28, 28),\n        cmap='Blues'\n    )\n    original_img.axes.get_yaxis().set_visible(False)\n    original_img.axes.get_xaxis().set_visible(False)\n    original_img.axes.set_title(f'Original [{i}]:')\n    \n    approx_img_ = pca_object.inverse_transform(X=X_val[idx])\n    approx_img = axs[i, 1].imshow(\n        X=approx_img_.reshape(28, 28),\n        cmap='Blues'\n    )\n    approx_img.axes.get_yaxis().set_visible(False)\n    approx_img.axes.get_xaxis().set_visible(False)\n    approx_img.axes.set_title(f'Predicted angle [{y_pred_angle_degree[idx]}]:')","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:27.992620Z","iopub.execute_input":"2024-05-29T01:34:27.993053Z","iopub.status.idle":"2024-05-29T01:34:29.078746Z","shell.execute_reply.started":"2024-05-29T01:34:27.993019Z","shell.execute_reply":"2024-05-29T01:34:29.077861Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import plotly.express as px\nfrom plotly.offline import init_notebook_mode, iplot\nimport plotly.graph_objects as go\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:29.080236Z","iopub.execute_input":"2024-05-29T01:34:29.080819Z","iopub.status.idle":"2024-05-29T01:34:29.086250Z","shell.execute_reply.started":"2024-05-29T01:34:29.080786Z","shell.execute_reply":"2024-05-29T01:34:29.085129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_confusion_matrix(cm, labels, title):\n# cm : danh sách ma trận nhầm lẫn(danh sách)\n# labels : tên danh sách dữ liệu(str)\n# title : tiêu đề cho bản đồ nhiệt\n    data = go.Heatmap(z=cm, y=labels, x=labels)\n    annotations = []\n    for i, row in enumerate(cm):\n        for j, value in enumerate(row):\n            annotations.append(\n                {\n                    \"x\": labels[i],\n                    \"y\": labels[j],\n                    \"font\": {\"color\": \"white\"},\n                    \"text\": str(value),\n                    \"xref\": \"x1\",\n                    \"yref\": \"y1\",\n                    \"showarrow\": False\n                }\n            )\n    layout = {\n        \"title\": title,\n        \"xaxis\": {\"title\": \"Predicted value\"},\n        \"yaxis\": {\"title\": \"Real value\"},\n        \"annotations\": annotations\n    }\n    fig = go.Figure(data=data, layout=layout)\n    return fig","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:29.087846Z","iopub.execute_input":"2024-05-29T01:34:29.088292Z","iopub.status.idle":"2024-05-29T01:34:29.099525Z","shell.execute_reply.started":"2024-05-29T01:34:29.088253Z","shell.execute_reply":"2024-05-29T01:34:29.098416Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras import utils\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping\nfrom tensorflow.keras import datasets, layers, models","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:29.107108Z","iopub.execute_input":"2024-05-29T01:34:29.108073Z","iopub.status.idle":"2024-05-29T01:34:44.540691Z","shell.execute_reply.started":"2024-05-29T01:34:29.108034Z","shell.execute_reply":"2024-05-29T01:34:44.539335Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train_cnn = X_train_rotate.reshape(-1,28,28,1)\n# X_val = np.array(pca.transform(X_train)).reshape(-1,28,28,1)\ny_train_cnn = utils.to_categorical(np.array(y_train_rotate// numRotate_prior))\nX_val = X_train.reshape(-1,28,28,1)\nY_val = utils.to_categorical(np.array(y_train))","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:44.542286Z","iopub.execute_input":"2024-05-29T01:34:44.543009Z","iopub.status.idle":"2024-05-29T01:34:44.552695Z","shell.execute_reply.started":"2024-05-29T01:34:44.542977Z","shell.execute_reply":"2024-05-29T01:34:44.551490Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train_cnn.shape","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:44.554402Z","iopub.execute_input":"2024-05-29T01:34:44.555177Z","iopub.status.idle":"2024-05-29T01:34:44.592581Z","shell.execute_reply.started":"2024-05-29T01:34:44.555123Z","shell.execute_reply":"2024-05-29T01:34:44.591383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# y_train_rotate// numRotate_prior","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:44.593871Z","iopub.execute_input":"2024-05-29T01:34:44.594190Z","iopub.status.idle":"2024-05-29T01:34:44.602194Z","shell.execute_reply.started":"2024-05-29T01:34:44.594163Z","shell.execute_reply":"2024-05-29T01:34:44.601136Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# X_train_rotate = np.array(X_train_rotate).reshape(-1,28,28,1)\n# # X_val = np.array(pca.transform(X_train)).reshape(-1,28,28,1)\n# X_val = np.array(X_test_crop).reshape(-1,28,28,1)\n# y_train_rotate = utils.to_categorical(np.array(y_train_rotate))\n# Y_val = utils.to_categorical(np.array(y_test_crop))","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:44.603431Z","iopub.execute_input":"2024-05-29T01:34:44.603893Z","iopub.status.idle":"2024-05-29T01:34:44.612964Z","shell.execute_reply.started":"2024-05-29T01:34:44.603855Z","shell.execute_reply":"2024-05-29T01:34:44.612103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Khởi tạo một mô hình tuần tự\nmodel = models.Sequential()\n\n# Thêm một layer Convolutional với 32 filters, kích thước kernel là (5,5), hàm kích hoạt là relu,\n# đầu vào có kích thước (28,28,1) là ảnh grayscale kích thước 28x28.\nmodel.add(layers.Conv2D(filters=32, kernel_size=(5,5), padding='Same', \n                 activation='relu', input_shape=(28,28,1)))\n\n# Thêm một layer Convolutional với 32 filters, kích thước kernel là (5,5), hàm kích hoạt là relu.\nmodel.add(layers.Conv2D(filters=32, kernel_size=(5,5), padding='Same', \n                 activation='relu'))\n\n# Thêm một layer MaxPooling với kích thước pool là (2,2) để giảm kích thước của feature maps.\nmodel.add(layers.MaxPool2D(pool_size=(2,2)))\n\n# Thêm một layer Batch Normalization để tăng tốc độ học của mô hình và giảm overfitting.\nmodel.add(layers.BatchNormalization())\n\n# Áp dụng kỹ thuật Dropout với tỷ lệ 0.25 để giảm overfitting.\nmodel.add(layers.Dropout(0.25))\n\n# Thêm một layer Convolutional với 64 filters, kích thước kernel là (3,3), hàm kích hoạt là relu.\nmodel.add(layers.Conv2D(filters=64, kernel_size=(3,3), padding='Same', \n                 activation='relu'))\n\n# Thêm một layer Convolutional với 64 filters, kích thước kernel là (3,3), hàm kích hoạt là relu.\nmodel.add(layers.Conv2D(filters=64, kernel_size=(3,3), padding='Same', \n                 activation='relu'))\n\n# Thêm một layer MaxPooling với kích thước pool là (2,2) và bước đi là (2,2) để giảm kích thước của feature maps.\nmodel.add(layers.MaxPool2D(pool_size=(2,2), strides=(2,2)))\n\n# Thêm một layer Batch Normalization để tăng tốc độ học của mô hình và giảm overfitting.\nmodel.add(layers.BatchNormalization())\n\n# Áp dụng kỹ thuật Dropout với tỷ lệ 0.25 để giảm overfitting.\nmodel.add(layers.Dropout(0.25))\n\n# Dùng layer Flatten để chuyển feature maps thành một vector 1 chiều.\nmodel.add(layers.Flatten())\n\n# Thêm một layer Dense với 256 neurons và hàm kích hoạt là relu.\nmodel.add(layers.Dense(256, activation='relu'))\n\n# Áp dụng kỹ thuật Dropout với tỷ lệ 0.5 để giảm overfitting.\nmodel.add(layers.Dropout(0.5))\n\n# Thêm một layer Batch Normalization để tăng tốc độ học của mô hình và giảm overfitting.\nmodel.add(layers.BatchNormalization())\n\n# Thêm một layer Dense cuối cùng với 10 neurons (tương ứng với số lượng lớp trong dữ liệu) và hàm kích hoạt là softmax.\nmodel.add(layers.Dense(10, activation='softmax'))\n\n# In ra tóm tắt của mô hình\nmodel.summary()\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:44.614012Z","iopub.execute_input":"2024-05-29T01:34:44.614421Z","iopub.status.idle":"2024-05-29T01:34:44.934505Z","shell.execute_reply.started":"2024-05-29T01:34:44.614386Z","shell.execute_reply":"2024-05-29T01:34:44.933364Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint\n\nmodel_path = './best_model.keras'\ncheckpointer = ModelCheckpoint(\n    filepath=model_path,\n    monitor='val_accuracy',\n    verbose=1,\n    mode='max',\n    save_best_only=True\n)\n\nmodel.compile(\n    loss='categorical_crossentropy',\n    optimizer='adam',\n    metrics=['accuracy']\n)","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:44.936198Z","iopub.execute_input":"2024-05-29T01:34:44.936649Z","iopub.status.idle":"2024-05-29T01:34:44.953235Z","shell.execute_reply.started":"2024-05-29T01:34:44.936619Z","shell.execute_reply":"2024-05-29T01:34:44.951937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = model.fit(X_train_cnn, y_train_cnn, validation_data = (X_val, Y_val), epochs=100, batch_size = 128, callbacks=[checkpointer])","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:34:44.954605Z","iopub.execute_input":"2024-05-29T01:34:44.955582Z","iopub.status.idle":"2024-05-29T02:38:44.957143Z","shell.execute_reply.started":"2024-05-29T01:34:44.955471Z","shell.execute_reply":"2024-05-29T02:38:44.955966Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import plotly.graph_objects as go\nfrom plotly.subplots import make_subplots\nimport numpy as np\n\nfig = go.Figure()\n\nlabels = ['Loss', 'Accuracy']\ncolors = ['rgb(67,67,67)', 'rgb(49,130,189)']\n\nmode_size = [8, 8]\nline_size = [2, 2]\n\nx_data = np.vstack((np.arange(1, 51),)*2)\n\ny_data = np.array([\n    np.array(history.history['loss']), \n                      np.array(history.history['accuracy'])\n])\n\nfor i in range(0, 2):\n    fig.add_trace(go.Scatter(x=x_data[i], y=y_data[i], mode='lines',\n        name=labels[i],\n        line=dict(color=colors[i], width=line_size[i]),\n        connectgaps=True,\n    ))\n\n    # endpoints\n    fig.add_trace(go.Scatter(\n        x=[x_data[i][0], x_data[i][-1]],\n        y=[y_data[i][0], y_data[i][-1]],\n        mode='markers',\n        marker=dict(color=colors[i], size=mode_size[i])\n    ))\n\nfig.update_layout(\n    xaxis=dict(\n        showline=True,\n        showgrid=False,\n        showticklabels=True,\n        linecolor='rgb(204, 204, 204)',\n        linewidth=2,\n        ticks='outside',\n        tickfont=dict(\n            family='Arial',\n            size=12,\n            color='rgb(82, 82, 82)',\n        ),\n    ),\n    yaxis=dict(\n        showgrid=False,\n        zeroline=False,\n        showline=False,\n        showticklabels=False,\n    ),\n    autosize=False,\n    margin=dict(\n        autoexpand=False,\n        l=100,\n        r=20,\n        t=110,\n    ),\n    showlegend=False,\n    plot_bgcolor='white'\n)\n\nannotations = []\n\n# Adding labels\nfor y_trace, label, color in zip(y_data, labels, colors):\n    # labeling the left_side of the plot\n    annotations.append(dict(xref='paper', x=0.05, y=y_trace[0],\n                                  xanchor='right', yanchor='middle',\n                                  text=label + ' {:.2f}'.format(y_trace[0]),\n                                  font=dict(family='Arial',\n                                            size=16),\n                                  showarrow=False))\n    # labeling the right_side of the plot\n    annotations.append(dict(xref='paper', x=0.95, y=y_trace[49],\n                                  xanchor='left', yanchor='middle',\n                                  text='{:.4f}%'.format(y_trace[49]),\n                                  font=dict(family='Arial',\n                                            size=16),\n                                  showarrow=False))\n# Title\nannotations.append(dict(xref='paper', yref='paper', x=0.0, y=1.05,\n                              xanchor='left', yanchor='bottom',\n                              text='Training Loss & Accuracy',\n                              font=dict(family='Arial',\n                                        size=30,\n                                        color='rgb(37,37,37)'),\n                              showarrow=False))\n# Source\nannotations.append(dict(xref='paper', yref='paper', x=0.5, y=-0.1,\n                              xanchor='center', yanchor='top',\n                              text='Epochs',\n                              font=dict(family='Arial',\n                                        size=12,\n                                        color='rgb(150,150,150)'),\n                              showarrow=False))\n\nfig.update_layout(annotations=annotations,height=600, width=1000)\n\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2024-05-29T02:38:44.959406Z","iopub.execute_input":"2024-05-29T02:38:44.959781Z","iopub.status.idle":"2024-05-29T02:38:45.111517Z","shell.execute_reply.started":"2024-05-29T02:38:44.959732Z","shell.execute_reply":"2024-05-29T02:38:45.110131Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.models import load_model\nmodel = load_model('./best_model.keras')\ny_pred = model.predict(X_val)\nM = confusion_matrix(np.argmax(Y_val,1), np.argmax(y_pred,1))\nplot_confusion_matrix(M, sorted(np.unique(np.argmax(Y_val,1))), 'Simple CNN Confusion Matrix, Acc({:.2f}%)'.format(accuracy_score(np.argmax(Y_val,1), np.argmax(y_pred,1))*100))","metadata":{"execution":{"iopub.status.busy":"2024-05-29T03:02:43.412260Z","iopub.execute_input":"2024-05-29T03:02:43.412728Z","iopub.status.idle":"2024-05-29T03:03:26.135502Z","shell.execute_reply.started":"2024-05-29T03:02:43.412693Z","shell.execute_reply":"2024-05-29T03:03:26.134359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n = np.where(np.argmax(y_pred,1)!=np.argmax(Y_val,1))\nn = np.array(n).reshape(-1)\nn","metadata":{"execution":{"iopub.status.busy":"2024-05-29T03:04:29.785254Z","iopub.execute_input":"2024-05-29T03:04:29.785704Z","iopub.status.idle":"2024-05-29T03:04:29.801411Z","shell.execute_reply.started":"2024-05-29T03:04:29.785662Z","shell.execute_reply":"2024-05-29T03:04:29.800349Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_val_vis = np.array(X_val).reshape(X_val.shape[0], 28, 28)\nX_val_vis = X_val_vis\nfig, axis = plt.subplots(int(len(n)/8), 8, figsize=(35, 15))\nfor i, ax in enumerate(axis.flat):\n    ax.imshow(X_val_vis[n[i]], cmap='binary')\n    digit = np.argmax(y_pred,1)[n[i]]\n    digit2 = np.argmax(Y_val,1)[n[i]]\n    ax.set(title = f\"Pred Number is {digit}\\n Real Number is {digit2}\");","metadata":{"execution":{"iopub.status.busy":"2024-05-29T03:04:33.191230Z","iopub.execute_input":"2024-05-29T03:04:33.191666Z","iopub.status.idle":"2024-05-29T03:07:53.804404Z","shell.execute_reply.started":"2024-05-29T03:04:33.191632Z","shell.execute_reply":"2024-05-29T03:07:53.802975Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_test = np.array(df_test).reshape(-1,28,28,1)","metadata":{"execution":{"iopub.status.busy":"2024-05-29T03:08:06.763584Z","iopub.execute_input":"2024-05-29T03:08:06.764871Z","iopub.status.idle":"2024-05-29T03:08:07.296680Z","shell.execute_reply.started":"2024-05-29T03:08:06.764821Z","shell.execute_reply":"2024-05-29T03:08:07.295337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Lấy model máy học với độ chính xác tốt nhật và lưu dưới dạng .csv để tiến hành submit\nmodel = load_model('./best_model.keras')\ny_pred = model.predict(X_test)\ny_pred = np.argmax(y_pred,1)\npd_submit = pd.read_csv('../input/digit-recognizer/sample_submission.csv')\npd_submit['Label'] = y_pred\npd_submit.to_csv('submission.csv', header=True, index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}