{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","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":30587,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Phần 1: Nhập các thư viện cần thiết\n------","metadata":{"_uuid":"ebd546a2-326f-4a93-9042-31cb470d1cc4","_cell_guid":"fe85bd07-ce02-43ce-933f-2d24429c163b","trusted":true}},{"cell_type":"markdown","source":"<div style=\"border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left;\">\n<b>Cơ sở dữ liệu MNIST</b> (<i>Modified National Institute of Standards and Technology database</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 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 ban đầu</b> màu đen trắng đã được chuẩn hóa để vừa với một <b>khung giới hạn 28x28 pixel</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 ta đượ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 <i>kỹ thuật tiên tiến</i> để tiếp cận bất kỳ vấn đề Phân loại Hình ảnh nào thường dựa vào <i>Mạng Nơ-ron Tích chập</i>. Loại kiến trúc này thực sự tốt trong việc rút trích các mẫu từ hình ảnh. Tuy nhiên, vì MNIST là một vấn đề đơn giản, trong sổ tay này chúng ta sẽ áp dụng các <i>Thuật toán Học máy đơn giản</i> (và chúng ta sẽ đạt được độ chính xác khá cao!).<br><br>\nĐầu tiên, chúng ta sẽ thực hiện <b>Phân tích thành phần chính (PCA)</b> để giảm chiều dữ liệu (từ 784 giá trị pixel xuống 10 thành phần chính). Sau đó, chúng ta sẽ chạy thuật toán <i>lazy learner</i> yêu thích của chúng ta: <b>K-Nearest Neighbors</b>. Sự 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 chú ý !!!<br><br>\n<b>Mục lục:</b>\n<ul>\n    <li>🔎 <b>Đọc và Khám phá Dữ liệu</b> 🔍:<br>\n    - Kiểm tra xem có <i>giá trị thiếu</i> không.<br>\n    - Kiểm tra <i>phạm vi</i> của các giá trị pixel.<br>\n    - <i>Trực quan hóa</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 về 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 module vào một <code>Sklearn Pipeline</code>.<br>\n        - Vẽ biểu đồ <i>phương sai giải thích</i> của các thành phần.<br>\n        - Trực quan hóa hai chiều của dữ liệu bằng <code>seaborn</code>.<br>\n        - Trực quan hóa động ba chiều của dữ liệu bằng <code>plotly</code>.<br>\n        - Trực quan hóa của <i>phép biến đổi ngược</i>.<br>\n        - Kiểm tra xem các thành phần có <i>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 <code>Sklearn</code>.<br>\n        - Sử dụng kỹ thuật PCA như một <i>bộ trích xuất đặc trưng</i> cho KNN.<br>\n        - Chạy một <i>Grid-Search Cross-Validation</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ố huấn luyện và xác thực để kiểm tra việc <i>quá mức</i>.<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":{"_uuid":"e00355b6-e203-4d64-9600-35f736d67626","_cell_guid":"6c36196c-42f6-4b0c-bfbd-a2d573e6a320","trusted":true}},{"cell_type":"code","source":"# Đọ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# Bộ phân loại K-Nearest Neighbor (KNN)\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.model_selection import GridSearchCV\nfrom sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay \n\n# VHT\nfrom scipy.ndimage import rotate","metadata":{"_uuid":"c9f6edfd-588d-4af2-97d8-1073e4883692","_cell_guid":"8d8d769c-d2a0-4558-b63f-86352b4bafb8","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:18:48.816263Z","iopub.execute_input":"2024-06-01T08:18:48.816736Z","iopub.status.idle":"2024-06-01T08:18:48.825357Z","shell.execute_reply.started":"2024-06-01T08:18:48.816698Z","shell.execute_reply":"2024-06-01T08:18:48.824102Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def func_array1d_rotate2d(Narray1d, rotate_angle, sizePixel):\n    #Hàm này thực hiện việc quay ảnh được lưu trữ dưới dạng mảng 1 chiều về 2 chiều.\n\n    #Tham số:\n    #- Narray1d: Mảng 1 chiều chứa các ảnh.\n    #- rotate_angle: Góc quay ảnh (theo độ).\n    #- sizePixel: Kích thước của mỗi bức ảnh theo mỗi chiều.\n\n    #Trả về:\n    #- Narray1d_rotate: Mảng 1 chiều chứa các ảnh sau khi đã được quay.\n    N = Narray1d.shape[0]\n\n    Narray1d_rotate = np.zeros(Narray1d.shape)\n    \n    for i in range(N):\n        image = np.reshape(Narray1d[i,:], (sizePixel, sizePixel))\n        rotated_image = rotate(image, rotate_angle, reshape=False)\n        rotated_array1d = np.reshape(rotated_image, (1, -1))\n        Narray1d_rotate[i,:] = rotated_array1d\n        \n    return Narray1d_rotate\n","metadata":{"_uuid":"76ecec72-7aaa-4e9c-9366-c53a6fb1d164","_cell_guid":"f51ca997-1fbb-4259-94ee-5b05b70a035a","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:18:48.828020Z","iopub.execute_input":"2024-06-01T08:18:48.829084Z","iopub.status.idle":"2024-06-01T08:18:48.840237Z","shell.execute_reply.started":"2024-06-01T08:18:48.828991Z","shell.execute_reply":"2024-06-01T08:18:48.839091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"----\nPhần 2: Đọc dữ liệu và khai thác dữ liệu","metadata":{"_uuid":"0ff27da2-835c-4122-aca0-9e731f3dc7ea","_cell_guid":"6248d1ca-53a8-45d0-9ab6-9003187ef756","trusted":true}},{"cell_type":"markdown","source":"<div style=\"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 Python phổ biến là <code>Pandas</code> để đọc dữ liệu từ các tệp <code>.csv</code>.<br><br>\nChúng ta sẽ trực quan hóa <b>phân phối của các giá trị pixel</b> (biểu đồ histogram) bằng cách sử dụng các thư viện Python <code>Seaborn</code> và <code>Matplotlib</code>. Chúng ta cũng sẽ kiểm tra xem có <b>giá trị thiếu</b> không, và rằng các <b> lớp mục tiêu được cân bằng </b>.<br><br>\nCuối cùng, chúng ta sẽ <b>trực quan hóa các hình ảnh</b> bằng cách vẽ một hình ảnh từ mỗi lớp.\n</div>","metadata":{"_uuid":"08c1c024-9f04-4d45-80af-6f8b9ed71f30","_cell_guid":"e27a867c-ee7f-4c03-85fd-bec46a9f2fd2","trusted":true}},{"cell_type":"code","source":"# Đọc file dữ liệu bằng Pandas\ndf_train = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\ndf_test =  pd.read_csv('/kaggle/input/digit-recognizer/test.csv')","metadata":{"_uuid":"078702a8-cfea-4f1c-bb0b-1018fea19c2c","_cell_guid":"8b4f5e4b-fc80-473b-bd7a-7e620b412c90","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:18:48.841788Z","iopub.execute_input":"2024-06-01T08:18:48.842306Z","iopub.status.idle":"2024-06-01T08:18:54.473090Z","shell.execute_reply.started":"2024-06-01T08:18:48.842265Z","shell.execute_reply":"2024-06-01T08:18:54.471956Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Kiểm tra kích thước của tàu và kiểm tra DataFrames\nprint(\"df_train shape:\", df_train.shape)  # 70%\nprint(\"df_test shape: \", df_test.shape)  # 30%","metadata":{"_uuid":"ca8aa9af-b761-403d-aa85-a3642ea77c5c","_cell_guid":"ba38193d-5d27-48f6-a7b3-f505b93dfe84","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:18:54.475894Z","iopub.execute_input":"2024-06-01T08:18:54.476406Z","iopub.status.idle":"2024-06-01T08:18:54.483094Z","shell.execute_reply.started":"2024-06-01T08:18:54.476362Z","shell.execute_reply":"2024-06-01T08:18:54.482011Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# May mắn thay không có giá trị nào bị thiếu trong dữ liệu của chúng tôi\nprint(\n    \"Missing values in df_train: \", \\\n    df_train.isna().sum().sum()\n)\nprint(\n    \"Missing values in df_test: \", \\\n    df_test.isna().sum().sum()\n)","metadata":{"_uuid":"a10fb707-86d3-4a6b-98f0-ace8a866768f","_cell_guid":"7cede701-9df4-4ac1-a358-2c567d72cd67","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:18:54.484553Z","iopub.execute_input":"2024-06-01T08:18:54.484907Z","iopub.status.idle":"2024-06-01T08:18:54.548182Z","shell.execute_reply.started":"2024-06-01T08:18:54.484877Z","shell.execute_reply":"2024-06-01T08:18:54.547085Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Cùng nhìn lại năm hàng huấn luyện đầu tiên\ndf_train.head()","metadata":{"_uuid":"087da919-79ff-4a00-a357-fdb65381edda","_cell_guid":"cbfdc56f-8ade-4af2-b159-a2f526b273a5","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:18:54.549710Z","iopub.execute_input":"2024-06-01T08:18:54.550056Z","iopub.status.idle":"2024-06-01T08:18:54.572460Z","shell.execute_reply.started":"2024-06-01T08:18:54.550004Z","shell.execute_reply":"2024-06-01T08:18:54.571270Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n#Ta có thể thấy cột đầu tiên là nhãn,và các cột còn lại là 784 pixelbiểu diễn các hình ảnh mức xám 28x28.\n#Hãy tách X và Y\nX_train = df_train.drop(columns=['label']).values\ny_train = df_train['label'].values","metadata":{"_uuid":"2aefa02a-2611-42d1-9764-6ab390b73c0b","_cell_guid":"ccfff756-d687-424c-877d-028bb3180a59","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:18:54.574213Z","iopub.execute_input":"2024-06-01T08:18:54.574682Z","iopub.status.idle":"2024-06-01T08:18:54.668011Z","shell.execute_reply.started":"2024-06-01T08:18:54.574640Z","shell.execute_reply":"2024-06-01T08:18:54.666772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n#Hãy trực quan hóa phân phối của các giá trị pixel\nax = sns.histplot(x=X_train.flatten(), color='#4895EE')\n_ = ax.set(title='Biểu đồ Phân phối Giá trị Pixel', xlabel='Giá trị Pixel')\n\n#Như chúng ta có thể thấy, giá trị 0 đại diện cho phông nền và các giá trị >0 đại diện cho các chữ số trong hình ảnh","metadata":{"_uuid":"7a976629-a208-4444-970a-6f990f01b730","_cell_guid":"f8a9174d-c11e-4467-8dcd-bba2e0d51ac3","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:18:54.669805Z","iopub.execute_input":"2024-06-01T08:18:54.670336Z","iopub.status.idle":"2024-06-01T08:19:12.533961Z","shell.execute_reply.started":"2024-06-01T08:18:54.670292Z","shell.execute_reply":"2024-06-01T08:19:12.532969Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Hãy kiểm tra xem phân phối của mục tiêu có cân bằng không.\n#Điều này có nghĩa là mỗi trong mười nhãn trong mục tiêu có sự đại diện gần như như nhau trong dữ liệu.\nax = sns.countplot(x=y_train, color='#4895EE')\nax.set(\ntitle='Phân phối của mục tiêu có cân bằng không? Có!',\nxlabel='Label'\n)\n\n#Số lượng mẫu kỳ vọng cho một phân phối cân bằng của mỗi lớp là 42000/10 = 4200\n_ = ax.axhline(y=4200, color='#12086F', linestyle='--')\n\n#Vì các lớp mục tiêu là 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.","metadata":{"_uuid":"56cd7484-96d5-4052-a732-31184b3010be","_cell_guid":"31dd96d2-e9fc-4ec8-8027-6182b59bbb56","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:19:12.539016Z","iopub.execute_input":"2024-06-01T08:19:12.539443Z","iopub.status.idle":"2024-06-01T08:19:12.987176Z","shell.execute_reply.started":"2024-06-01T08:19:12.539408Z","shell.execute_reply":"2024-06-01T08:19:12.986107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tiếp theo, hãy hình dung 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\nfig, axs = plt.subplots(nrows=2, ncols=5, figsize=(10,5))\n\nfor i in range(10):\n    idx = np.where(y_train==i)[0][2]\n    img = axs[int(i >= 5), i%5].imshow(\n        X=X_train[idx].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'Label: {i}')","metadata":{"_uuid":"6e57247a-308f-4d8c-b860-3fcca06504e8","_cell_guid":"85fb1afc-211b-4d78-a121-385e208ece49","jupyter":{"outputs_hidden":false},"collapsed":false,"execution":{"iopub.status.busy":"2024-06-01T08:19:12.988778Z","iopub.execute_input":"2024-06-01T08:19:12.989236Z","iopub.status.idle":"2024-06-01T08:19:13.513751Z","shell.execute_reply.started":"2024-06-01T08:19:12.989197Z","shell.execute_reply":"2024-06-01T08:19:13.512649Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# transform data","metadata":{"_uuid":"9d4cd46f-3405-4ee3-a2e3-758b928024b1","_cell_guid":"abe3ee94-93c7-4941-9358-150e526414ec","trusted":true}},{"cell_type":"code","source":"# -------------------------------------------------- tuyên bố\nnumRotate_prior = 1\n\nangleOffset_prior = 0\nangleRange_prior = 90\n\nX_train_pick2rotate = {\n    'index': np.array([2300, 2301, 2302, 2303, 2304, 2305, 2306, 2307, 2308, 2309,2310,\n                       2311, 2312, 2313, 2314, 2315, 2316, 2317, 2318, 2319, 2320,\n                       2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330,\n                       2331, 2332, 2333, 2334, 2335, 2336, 2337,2338, 2339, 2340,\n                       2341, 2342, 2343, 2344, 2345, 2346, 2347, 2348, 2349, 2350,\n                       2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, 2360,\n                       2361, 2362, 2363, 2364, 2365,2366, 2367, 2368, 2369, 2370,\n                       2371, 2372, 2373, 2374, 2375, 2376, 2377, 2378, 2379, 2380,\n                       2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390,\n                       2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399]),\n    'offset': np.array([\n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n    \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n        [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ],\n        \n    [20, 20, -20, 11, 20, -11, -30, 10, 0, 10],\n    [60, 38, -2, 10, 5, -50, 45, 5, 0, 0],\n    [ -10, 5, 0, 5, 0, 10, -70, -8, 7, 0],\n    [ 7, 7, -7, -24, 15, 30, 0, -15, 0, 27],\n    [35, 18, 5, -8, -5, -3, 0, -20, 0, 40],\n    [ -5, 20, 0, -10, 45, -5, -15, -8 ,-45, 0],\n    [ 40, -20, 0, 15, 45, 45, 0 ,0, 45 ,45],\n    [ -115, 35, 0, -10, 0, 45, -25, 0, 0, 40],\n    [ 15, 10, 10, -10, 45, 5, -90, 0, 0, 30],\n    [ 135, 5, 0, -15, 0, -10, -80, 0, 20, 20 ]\n        \n])\n}\n\nnumPickRotate = len(X_train_pick2rotate['index'])\n\nprint('numPickRotate = ', numPickRotate)\n\n# -------------------------------------------------- xoay\n\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\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# Hãy chuẩn hóa các giá trị pixel thành khoảng [0,1]\n# Công thức: MinMaxscale(X) = (X - X_min) / (X_max - X_min)\n\nprint(\"Before applying MinMax Scaler:\")\nprint(\"  - Minimum value:\", X_sample_rotate.min())\nprint(\"  - Maximum value:\", 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(\"\\nAfter applying MinMax Scaler:\")\nprint(\"  - Minimum value:\", X_sample_rotate.min())\nprint(\"  - Maximum value:\", X_sample_rotate.max())\n\n\n\n# Tiếp theo, hãy hình dung 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\nfor k2 in range(numPickRotate):\n# for 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'Label: {k0}')\n        \nX_train_rotate = np.reshape(X_sample_rotate, (-1,784))\ny_train_rotate = np.reshape(y_sample_rotate, (-1,1)).flatten()","metadata":{"_uuid":"e87363e6-d051-4009-be4f-a825679930e7","_cell_guid":"db89b71d-4901-4716-9507-e9497e268d74","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:19:13.515794Z","iopub.execute_input":"2024-06-01T08:19:13.516262Z","iopub.status.idle":"2024-06-01T08:20:05.704866Z","shell.execute_reply.started":"2024-06-01T08:19:13.516221Z","shell.execute_reply":"2024-06-01T08:20:05.703523Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"---------\n Phần 3: Phân tích thành phần chính","metadata":{"_uuid":"8794e86b-0a44-46a2-8d06-c6186454767d","_cell_guid":"d404fd37-ba51-4a9f-9de3-878c4bb0ad21","trusted":true}},{"cell_type":"markdown","source":"<div style=\"border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left;\">\n    <b>Phân Tích Thành Phần Chính</b> (PCA) là một kỹ thuật thống kê để giảm chiều dữ liệu. Điều này được thực hiện bằng cách <b>biến đổi tuyến tính dữ liệu</b> thành một hệ tọa độ mới trong đó (hầu hết) biến thể của dữ liệu có thể được mô tả bằng ít chiều hơn so với dữ liệu ban đầu.<br><br>\nThành phần chính đầu tiên cũng có thể được định nghĩa tương đương như một <b>hướng mà tối đa hóa biến thể của dữ liệu được chiếu</b>. Thành phần chính thứ <i>i</i> có thể được xem như một hướng vuông góc với <i>i-1</i> thành phần chính đầu tiên mà tối đa hóa biến thể của dữ liệu được chiếu.<br><br>\n<code>sklearn.decomposition.PCA</code>: Giảm chiều dữ liệu tuyến tính sử dụng phân rã giá trị đơn của dữ liệu để chiếu nó vào một không gian chiều thấp hơn.<br><br>\nỞ đây, chúng ta sẽ sử dụng <b>10 thành phần chính hàng đầu</b>, giải thích <b>gần 50% biến thể</b> trong dữ liệu. Điều này có nghĩa là chúng ta có thể diễn đạt 50% biến thể của dữ liệu bằng một không gian đặc trưng <b>>98% nhỏ hơn so với ban đầu</b> (10 đặc trưng so với 784 đặc trưng).\n</div>","metadata":{"_uuid":"988c70b6-e9d9-4ae7-b127-921423ab0f0e","_cell_guid":"231a3cf2-1f44-475b-a745-a28f3eb35d1e","trusted":true}},{"cell_type":"code","source":"# Hãy chuẩn hóa các giá trị pixel thành khoảng [0,1]\n# Công thức: MinMaxscale(X) = (X - X_min) / (X_max - X_min)\n\nprint(\"Before applying MinMax Scaler:\")\nprint(\"  - Minimum value:\", X_train.min())\nprint(\"  - Maximum value:\", X_train.max())\n\nminmax_scaler = MinMaxScaler()\nX_train_rotate_scaled = minmax_scaler.fit_transform(X_train_rotate)\n\nprint(\"\\nAfter applying MinMax Scaler:\")\nprint(\"  - Minimum value:\", X_train_rotate_scaled.min())\nprint(\"  - Maximum value:\", X_train_rotate_scaled.max())","metadata":{"_uuid":"2f018749-1981-43d6-b1b8-b1716a72e3fb","_cell_guid":"7c68f163-b747-4392-9a1e-8792cb07ccde","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:05.706726Z","iopub.execute_input":"2024-06-01T08:20:05.707871Z","iopub.status.idle":"2024-06-01T08:20:05.777464Z","shell.execute_reply.started":"2024-06-01T08:20:05.707824Z","shell.execute_reply":"2024-06-01T08:20:05.776286Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Trên thực tế, chúng tôi sẽ nhúng MinMaxScaler và PCA\n# mô-đun vào Đường ống Sklearn.\n\npca = Pipeline(steps=[\n    ('scaler', MinMaxScaler()),\n    ('pca', PCA(n_components=10))\n])\n\nX_train_rotate_pca = pca.fit_transform(X=X_train_rotate)\n\nprint('X_train_pca shape:', X_train_rotate_pca.shape)","metadata":{"_uuid":"4bfc3273-16ce-4a37-93ae-9cf46373e2c4","_cell_guid":"2d2d1d92-8b2d-4b97-af06-40a19024b09e","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:05.778844Z","iopub.execute_input":"2024-06-01T08:20:05.779212Z","iopub.status.idle":"2024-06-01T08:20:05.913393Z","shell.execute_reply.started":"2024-06-01T08:20:05.779180Z","shell.execute_reply":"2024-06-01T08:20:05.911676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hãy trích xuất tỷ lệ phương sai được giải thích\n# Và hãy tính tổng tích lũy của chúng\n\npca_object = pca.named_steps['pca']\nvar_ratio = pd.Series(\n    data=pca_object.explained_variance_ratio_,\n    index=range(1, 11)\n)\n\ncumsum_var_ratio = var_ratio.cumsum()","metadata":{"_uuid":"e551b2e2-ce61-4b14-abca-472ab8f8fa6f","_cell_guid":"8fe76194-de6d-44ef-89b7-489a936a3a12","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:05.915868Z","iopub.execute_input":"2024-06-01T08:20:05.916504Z","iopub.status.idle":"2024-06-01T08:20:05.938260Z","shell.execute_reply.started":"2024-06-01T08:20:05.916443Z","shell.execute_reply":"2024-06-01T08:20:05.936601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Vẽ biểu đồ tỷ lệ phương sai giải thích cho từng thành phần (trái)\n# Và vẽ tổng tích lũy của chúng (phải)\n\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15,5))\n\nsns.barplot(\n    x=var_ratio.index, \n    y=var_ratio.values, \n    color='#4895EE', \n    ax=ax1\n)\nax1.set(\n    title='Explained Variance Ratio PCA',\n    xlabel='Component',\n    ylabel='Var Ratio',\n)\nax1.set_ylim(0, 0.5)\n\nsns.barplot(\n    x=cumsum_var_ratio.index, \n    y=cumsum_var_ratio.values, \n    color='#4895EE', \n    ax=ax2\n)\nax2.set(\n    title='Cumulative Explained Variance Ratio PCA',\n    xlabel='Component',\n    ylabel='Cumulative Var Ratio',\n)\n_ = ax2.set_ylim(0, 0.5)","metadata":{"_uuid":"34ab9680-e976-4c81-bf92-6e9d8a0236bb","_cell_guid":"5e8c9489-629b-45b3-a577-b855fe33869f","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:05.940541Z","iopub.execute_input":"2024-06-01T08:20:05.941160Z","iopub.status.idle":"2024-06-01T08:20:06.793602Z","shell.execute_reply.started":"2024-06-01T08:20:05.941102Z","shell.execute_reply":"2024-06-01T08:20:06.792425Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hãy đặt các thành phần ở định dạng Pandas DataFrame\n# Bằng cách này chúng ta có thể sử dụng thư viện seaborn và pploly\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\ndf_train_pca = pd.DataFrame(\n    data=X_train_rotate_pca[:,:3], \n    columns=['x', 'y', 'z']\n)\n# Hãy thêm mục tiêu để đặt màu\ndf_train_pca['target'] = y_train_rotate\n\nprint(df_train_pca.shape)\ndf_train_pca.head()","metadata":{"_uuid":"b779e0bc-6938-424a-9c08-3579404299da","_cell_guid":"8d64a105-5b85-4924-9d4c-d5bc95d474e8","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:06.795094Z","iopub.execute_input":"2024-06-01T08:20:06.795440Z","iopub.status.idle":"2024-06-01T08:20:06.812485Z","shell.execute_reply.started":"2024-06-01T08:20:06.795410Z","shell.execute_reply":"2024-06-01T08:20:06.811395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Trực quan hóa hai chiều của dữ liệu\n# Ở đây các thành phần chính của Top 2 được vẽ\n\nsns.set_style(\"dark\")\nax = sns.scatterplot(\n    data=df_train_pca,\n    x='x', \n    y='y', \n    hue='target', \n    palette='Blues',\n)\nax.set(\n    title='PCA Two-Dimensional Projection',\n    xlabel='1st Comp.',\n    ylabel='2nd Comp.'\n)\nax.axhline(y=0, linestyle='--', color='#FFBF00', zorder=-1)\nax.axvline(x=0, linestyle='--', color='#FFBF00', zorder=-1)","metadata":{"_uuid":"90d9d1e3-fbcb-400a-b8d5-68d2a5f4f646","_cell_guid":"c05ac3f3-ec1c-4fd0-8e88-cd0991afb146","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:06.813803Z","iopub.execute_input":"2024-06-01T08:20:06.814254Z","iopub.status.idle":"2024-06-01T08:20:07.785207Z","shell.execute_reply.started":"2024-06-01T08:20:06.814201Z","shell.execute_reply":"2024-06-01T08:20:07.784061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Trực quan hóa hai chiều của dữ liệu\n# Ở đây các thành phần chính của Top 3 được vẽ\n\nsns.set_style(\"white\")\nfig = px.scatter_3d(\n    data_frame=df_train_pca,\n    x='x',\n    y='y',\n    z='z',\n    labels={\n        'x': '1st Comp.',\n        'y': '2nd Comp.',\n        'z': '3rd Comp.'\n    },\n    color='target',\n    title='PCA Three-Dimensional Projection',\n    color_continuous_scale='blues',\n    template='ggplot2'\n)\n\nfig.show()","metadata":{"_uuid":"1fb19f51-6852-4f02-8660-90a8a537fa30","_cell_guid":"b76b9b11-2a7e-4cc9-983c-0ae311d92428","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:07.786664Z","iopub.execute_input":"2024-06-01T08:20:07.787018Z","iopub.status.idle":"2024-06-01T08:20:07.871653Z","shell.execute_reply.started":"2024-06-01T08:20:07.786987Z","shell.execute_reply":"2024-06-01T08:20:07.870385Z"},"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\n#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.\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_rotate==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}]:')","metadata":{"_uuid":"0d814515-0bec-4c78-af57-09ed84b2dfc4","_cell_guid":"7e42f02f-c973-4898-8c10-80b0ca2d625b","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:07.873392Z","iopub.execute_input":"2024-06-01T08:20:07.873847Z","iopub.status.idle":"2024-06-01T08:20:08.903452Z","shell.execute_reply.started":"2024-06-01T08:20:07.873805Z","shell.execute_reply":"2024-06-01T08:20:08.902347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chứng tỏ các thành phần PCA không tương quan\n# (Câu nói này đúng về mặt định nghĩa và cách 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)","metadata":{"_uuid":"8a21533b-a961-49b1-b24c-d4e7050cc806","_cell_guid":"32e1e369-193f-4071-9914-b2f03cce2893","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:08.904878Z","iopub.execute_input":"2024-06-01T08:20:08.905255Z","iopub.status.idle":"2024-06-01T08:20:09.769255Z","shell.execute_reply.started":"2024-06-01T08:20:08.905222Z","shell.execute_reply":"2024-06-01T08:20:09.768067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"----\nPhầ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.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":{"_uuid":"370f38cc-e5fb-4b4e-8dd3-6b9e303ee060","_cell_guid":"43a48481-ea7f-470e-b826-8fd2fcc9e37f","trusted":true}},{"cell_type":"markdown","source":"<div style=\"border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left;\">\nThuật toán <b>K-Nearest Neighbors</b> là một bộ phân loại học có giám sát, <i>không tham số</i>, sử dụng <b>sự gần gũi</b> để đưa ra các phân loại hoặc dự đoán về việc nhóm hóa 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 một <b>phần đa bầu cử</b>, nhãn xuất hiện phổ biến nhất xung quanh một điểm dữ liệu cụ thể.<br><br>\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.<br><br>\n<code>sklearn.neighbors.KNeighborsClassifier</code>: Bộ phân loại thực hiện bầu cử k láng giềng gần nhất. Chúng ta sẽ chạy một GridSearchCV để tối ưu hóa siêu tham số <code>n_neighbors</code>.\n</div>","metadata":{"_uuid":"db9e5ad7-aa3d-4606-9848-526bc3dc048f","_cell_guid":"efff5a1e-7ac3-41a4-b464-77ed4de056d3","trusted":true}},{"cell_type":"markdown","source":"---\n#  mô hình học máy trên tập dữ liệu Train","metadata":{"_uuid":"7c0a4822-eedd-4aa0-bac6-a2cf5a7b89bf","_cell_guid":"39e3a7f5-5363-4914-b78d-295251352529","trusted":true}},{"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_)","metadata":{"_uuid":"2d7cd392-e51f-420a-aad2-0eb5c72ff742","_cell_guid":"797ae756-d6fc-4456-83e5-447d3cc034e2","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:09.770890Z","iopub.execute_input":"2024-06-01T08:20:09.771335Z","iopub.status.idle":"2024-06-01T08:20:10.812636Z","shell.execute_reply.started":"2024-06-01T08:20:09.771301Z","shell.execute_reply":"2024-06-01T08:20:10.811045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Vẽ sơ đồ độ chính xác trong đào tạo và xác nhận\n# cho mỗi giá trị Siêu tham số được kiểm tra trong quá trình\n# Tìm kiếm lưới.\n# Biểu đồ này cho phép chúng ta kiểm tra xem có trang bị quá mức hay không.\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":{"_uuid":"2abf36ed-c1a2-4581-8a67-414fbfe556f5","_cell_guid":"38a69c14-7bf2-4328-abc9-45a9ef2c1b11","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:10.814828Z","iopub.execute_input":"2024-06-01T08:20:10.816066Z","iopub.status.idle":"2024-06-01T08:20:11.257418Z","shell.execute_reply.started":"2024-06-01T08:20:10.815981Z","shell.execute_reply":"2024-06-01T08:20:11.256241Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{"_uuid":"2254c327-a8ec-41ca-9fa2-c41a850f4c75","_cell_guid":"adcdd26f-00ba-479f-8e33-e1bc6d65eafb","trusted":true}},{"cell_type":"code","source":"# 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\nbest_cv_clf = knn_clf_gs.best_estimator_\ny_pred = best_cv_clf.predict(X=X_train_rotate_pca)\nconf_matrix = confusion_matrix(y_true=y_train_rotate, y_pred=y_pred)\n\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')","metadata":{"_uuid":"c3ac8535-57d6-4c40-9146-3b229c95284e","_cell_guid":"aa178b6c-9094-48f3-a36e-5cc5da6e3c50","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:11.258771Z","iopub.execute_input":"2024-06-01T08:20:11.259133Z","iopub.status.idle":"2024-06-01T08:20:12.093426Z","shell.execute_reply.started":"2024-06-01T08:20:11.259100Z","shell.execute_reply":"2024-06-01T08:20:12.092114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tính báo cáo phân loại bằng sklearn\n# Lưu ý: toàn bộ tập dữ liệu huấn luyện được sử dụng ở đây\n\nclf_report = classification_report(\n    y_true=y_train_rotate,\n    y_pred=y_pred,\n    labels=range(10),\n)\n\n# In báo cáo phân loại\nprint(clf_report)","metadata":{"_uuid":"80ad0fa3-460a-4b42-aaf6-52edfa03afe9","_cell_guid":"04c9e15a-0874-4e08-b997-858cbcc9a37e","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T08:20:12.099605Z","iopub.execute_input":"2024-06-01T08:20:12.100014Z","iopub.status.idle":"2024-06-01T08:20:12.120120Z","shell.execute_reply.started":"2024-06-01T08:20:12.099978Z","shell.execute_reply":"2024-06-01T08:20:12.118931Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 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\nbest_cv_clf = knn_clf_gs.best_estimator_\ny_pred = best_cv_clf.predict(X=pca_object.transform(X_train))\nconf_matrix = confusion_matrix(y_true=y_train, y_pred=y_pred)\n\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')\n\n# Tính báo cáo phân loại bằng sklearn\n# Lưu ý: toàn bộ tập dữ liệu huấn luyện được sử dụng ở đây\n\nclf_report = classification_report(\n    y_true=y_train,\n    y_pred=y_pred,\n    labels=range(10),\n)\n\n# In báo cáo phân loại\nprint(clf_report)","metadata":{"execution":{"iopub.status.busy":"2024-06-01T08:20:12.121942Z","iopub.execute_input":"2024-06-01T08:20:12.122423Z","iopub.status.idle":"2024-06-01T08:20:17.502142Z","shell.execute_reply.started":"2024-06-01T08:20:12.122381Z","shell.execute_reply":"2024-06-01T08:20:17.500930Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 📃 References 📃","metadata":{"_uuid":"0bf02057-e66b-4045-bf4b-b91fb25f67ba","_cell_guid":"b35209f1-8bed-48cd-986d-7c85a99a4519","trusted":true}},{"cell_type":"markdown","source":"<div style=\"border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left;\">\n    <ul>\n        <li>MNIST database. (2023, September 8). In Wikipedia. [<a href=https://en.wikipedia.org/wiki/MNIST_database>LINK</a>]</li>\n        <li>Principal component analysis. (2023, November 18). In Wikipedia. [<a href=https://en.wikipedia.org/wiki/Principal_component_analysis>LINK</a>]</li>\n        <li>PCA, scikit-learn documentation. [<a href=https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html>LINK</a>]\n        <li>K. (2023, September 27). In Wikipedia. [<a href=https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm>LINK</a>]</li>\n        <li>KNNeighborsClassifier, scikit-learn documentation. [<a href=https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html#sklearn.neighbors.KNeighborsClassifier>LINK</a>]</li>\n    </ul>\n</div>","metadata":{"_uuid":"ba488a1a-be81-42a7-8169-c106278b9f85","_cell_guid":"7d3e9231-c38d-42ac-ae5b-8c18407ec2ff","trusted":true}}]}