{"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>\n<b>Cơ sở dữ liệu MNIST</b> chứa các ảnh về chữ số viết tay được sử dụng rộng rãi trong việc huấn luyện các hệ thống xử lý ảnh. Dữ liệu này đã được chuẩn hóa để phù hợp với ô bao 28x28 pixel và được biến đổi thành mức độ xám. Cơ sở dữ liệu gốc có 60,000 ảnh huấn luyện và 10,000 ảnh kiểm tra. Trong cuộc thi này, chúng ta chỉ sử dụng 42,000 ảnh train (70%) và 18,000 ảnh test (30%).\n\n- Trong nhiều trường hợp, để giải quyết vấn đề Phân loại Ảnh, ta thường sử dụng Mạng Nơ-ron Tích chập (CNN), nhưng với MNIST, một bài toán đơn giản, ta có thể dùng các thuật toán Học Máy cơ bản và vẫn đạt được độ chính xác tốt.\n<ul>\n    Cụ thể, ta sẽ thực hiện hai bước chính:\n    <li> 🔶 <b>Phân tích thành phần chính (PCA):</b> 🔶:<br>\n        - Thu nhỏ dữ liệu từ 784 giá trị pixel xuống chỉ còn 10 thành phần chính.<br>\n        - Giảm chiều dữ liệu giúp tăng tốc quá trình huấn luyện mà không mất đi nhiều thông tin quan trọng.<br>\n    <li> 🔶 <b>Thuật toán K-Nearest Neighbors (KNN):</b> 🔶:<br>\n        - Sử dụng KNN là một thuật toán lazy learner tức là không cần huấn luyện một mô hình phức tạp.<br>\n        - KNN dựa trên việc so sánh điểm gần nhất để dự đoán nhãn của một điểm mới.<br>\n        - Kết hợp KNN với PCA giúp tăng hiệu suất của thuật toán.<br>\n</ul>\n\n==> Sau khi kết hợp PCA và KNN, chúng ta sẽ có một mô hình có độ chính xác kiểm tra đáng kinh ngạc, xấp xỉ 92.71%. Điều này chứng tỏ rằng, dù sử dụng các phương pháp đơn giản, ta vẫn có thể đạt được kết quả tốt trên bài toán MNIST.\n</div>","metadata":{}},{"cell_type":"markdown","source":"## Import thư viện ","metadata":{}},{"cell_type":"code","source":"# Đọc,xử lý 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# Gọi thư viện PCA\nfrom sklearn.decomposition import PCA\nfrom sklearn.pipeline import Pipeline\nimport plotly.express as px\n\n# KNN\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.model_selection import GridSearchCV\nfrom sklearn.metrics import classification_report, \\\n    confusion_matrix, ConfusionMatrixDisplay \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-05-25T05:51:20.982454Z","iopub.execute_input":"2024-05-25T05:51:20.982970Z","iopub.status.idle":"2024-05-25T05:51:24.967793Z","shell.execute_reply.started":"2024-05-25T05:51:20.982923Z","shell.execute_reply":"2024-05-25T05:51:24.966364Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Hàm Xoay ảnh","metadata":{}},{"cell_type":"code","source":"def func_array1d_rotate2d(Narray1d,rotate_angle,sizePixel):\n\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","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-05-25T05:51:24.970611Z","iopub.execute_input":"2024-05-25T05:51:24.971560Z","iopub.status.idle":"2024-05-25T05:51:24.981120Z","shell.execute_reply.started":"2024-05-25T05:51:24.971500Z","shell.execute_reply":"2024-05-25T05:51:24.979810Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- Hàm func_array1d_rotate2d thực hiện quay từng ảnh trong một danh sách các ảnh. Đầu vào là danh sách các ảnh (biểu diễn dưới dạng mảng 1 chiều), góc quay mà bạn muốn áp dụng cho mỗi ảnh, và kích thước của mỗi ảnh vuông.\n> rotate_angle: góc quay","metadata":{}},{"cell_type":"markdown","source":"----\n# Phầ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":"code","source":"# đọc dữ liệu từ tệp csv\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-05-25T05:51:24.983298Z","iopub.execute_input":"2024-05-25T05:51:24.984155Z","iopub.status.idle":"2024-05-25T05:51:32.823423Z","shell.execute_reply.started":"2024-05-25T05:51:24.984100Z","shell.execute_reply":"2024-05-25T05:51:32.822027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# chia tỉ lệ tập dữ liệu 7/3\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-05-25T05:51:32.827083Z","iopub.execute_input":"2024-05-25T05:51:32.827702Z","iopub.status.idle":"2024-05-25T05:51:32.835816Z","shell.execute_reply.started":"2024-05-25T05:51:32.827651Z","shell.execute_reply":"2024-05-25T05:51:32.834055Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# kiểm tra dữ liệu\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)\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-05-25T05:51:32.837672Z","iopub.execute_input":"2024-05-25T05:51:32.838157Z","iopub.status.idle":"2024-05-25T05:51:32.904366Z","shell.execute_reply.started":"2024-05-25T05:51:32.838107Z","shell.execute_reply":"2024-05-25T05:51:32.902727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Trực quan 5 hàng đầu dữ liệu\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-05-25T05:51:32.906241Z","iopub.execute_input":"2024-05-25T05:51:32.906672Z","iopub.status.idle":"2024-05-25T05:51:32.943109Z","shell.execute_reply.started":"2024-05-25T05:51:32.906635Z","shell.execute_reply":"2024-05-25T05:51:32.941720Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- cột đầu tiên là label còn tất cả 785 cột còn lại là pixel\n- tiếp tục thực hiện chia thành 2 cột x và y(để nhìn bao quát về dữ liệu)","metadata":{}},{"cell_type":"code","source":"X_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-05-25T05:51:32.944914Z","iopub.execute_input":"2024-05-25T05:51:32.945469Z","iopub.status.idle":"2024-05-25T05:51:33.138931Z","shell.execute_reply.started":"2024-05-25T05:51:32.945411Z","shell.execute_reply":"2024-05-25T05:51:33.137510Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# thực hiện trực quan hóa dữ liệu của các giá trị pixel\nax = sns.histplot(x=X_train.flatten(), color='#4895EE')\n_ = ax.set(title='Pixel Value Histogram', xlabel='Pixel Value')","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-05-25T05:51:33.140859Z","iopub.execute_input":"2024-05-25T05:51:33.141429Z","iopub.status.idle":"2024-05-25T05:51:53.620927Z","shell.execute_reply.started":"2024-05-25T05:51:33.141378Z","shell.execute_reply":"2024-05-25T05:51:53.619484Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#kiểm tra trực quan để đảm bảo rằng các danh mục của biến mục tiêu được cân bằng, điều này rất quan trọng để xây dựng các mô hình học máy.\n\nax = sns.countplot(x=y_train, color='#4895EE')\nax.set(\n    title='Target distribution is balanced? Yes!',\n    xlabel='Label'\n)\n_ = ax.axhline(y=4200, color='#12086F', linestyle='--')","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-05-25T05:51:53.622816Z","iopub.execute_input":"2024-05-25T05:51:53.623226Z","iopub.status.idle":"2024-05-25T05:51:53.999795Z","shell.execute_reply.started":"2024-05-25T05:51:53.623189Z","shell.execute_reply":"2024-05-25T05:51:53.998352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- Dựa vào biểu đồ sau khi trực quan ta có thể thấy rằng các chúng được phân chia 1 cách khác cân bằng điều này cho ta thấy bộ dữ liệu khá tốt và có thể tiến hành các bước xử lý dữ liệu tiếp theo,điều này giúp chúng ta yên tâm hơn về phương pháp lấy mẫu và sai lệch của mô hình khi ta dự đoán.","metadata":{}},{"cell_type":"code","source":"#thực hiện trực quan hóa một hình ảnh từ mỗi lớp trong tập dữ liệu(0-9),mỗi hình có ô 2x5,mỗi ô hiển thị một hình ảnh từ một lớp khác, cùng với nhãn lớp tương ứng.\n#Hình ảnh được định hình lại thành 28x28 pixel\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","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-05-25T05:51:54.001548Z","iopub.execute_input":"2024-05-25T05:51:54.002021Z","iopub.status.idle":"2024-05-25T05:51:54.884209Z","shell.execute_reply.started":"2024-05-25T05:51:54.001980Z","shell.execute_reply":"2024-05-25T05:51:54.882683Z"},"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":"# -------------------------------------------------- Khai báo\nnumRotate_prior = 1\nangleOffset_prior = 0\nangleRange_prior = 90\n\nX_train_pick2rotate = {\n    'index': np.array([2200,2201,2202,2203,2204,2205,2206,2207,2208,2209,2210,2211,2212,2213,2214,2215,2216,2217,2218,2219,2220,2221,\n                      2222,2223,2224,2225,2226,2227,2228,2229,2230,2231]),\n    'offset': np.array([\n        [0,8,0,-15,0,25,-35,0,17,25],\n        [0,12,-20,32,24,4,-45,0,5,10],\n        [10,18,0,-20,0,0,10,0,25,0],\n        [45,10,-25,0,0,0,-15,0,-20,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,10,0,-15,0,25,-35,0,20,25],\n        [0,12,-20,32,24,0,-45,0,10,10],\n        [10,5,0,0,0,0,10,0,20,0],\n        [45,10,-20,0,0,0,-15,0,-35,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,12,-20,32,24,0,-45,0,10,10],\n        [10,5,0,0,0,0,10,0,20,0],\n        [45,10,-20,0,0,0,-15,0,-35,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,12,-20,32,24,0,-45,0,10,10],\n        [10,5,0,0,0,0,10,0,20,0],\n        [45,10,-20,0,0,0,-15,0,-35,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,12,-20,32,24,0,-45,0,10,10],\n        [10,5,0,0,0,0,10,0,20,0],\n        [45,10,-20,0,0,0,-15,0,-35,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,12,-20,32,24,0,-45,0,10,10],\n        [10,5,0,0,0,0,10,0,20,0],\n        [45,10,-20,0,0,0,-15,0,-35,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n        [0,-5,0,0,10,10,-25,0,20,0],\n    ])\n}\n\nnumPickRotate = len(X_train_pick2rotate['index'])\n\nprint('numPickRotate = ', numPickRotate)\n\n# -------------------------------------------------- rotate\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\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\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-05-25T05:58:34.963457Z","iopub.execute_input":"2024-05-25T05:58:34.964007Z","iopub.status.idle":"2024-05-25T05:58:55.085023Z","shell.execute_reply.started":"2024-05-25T05:58:34.963964Z","shell.execute_reply":"2024-05-25T05:58:55.083635Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- Tới bước này chúng ta sẽ thực hiện xoay ảnh,mỗi ảnh sẽ được định nghĩa số lần xoay tình chỉnh dựa theo numRotate_prior còn góc quay sẽ được tính dựa trên angleOffset_prior, angleRange_prior, và các offset trong X_train_pick2rotate.\n- Tiếp theo ta sẽ chuẩn hóa giá trị pixel của ảnh và ở đây thì nó được chuẩn hóa về khoảng[0,1],sau khi chuẩn hóa và đưa giá trị pixel về 255 thì ta sẽ sử dụng matplotlib để hiển thị ảnh sau khi đã xoay.\n","metadata":{}},{"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    - PCA là một kỹ thuật thống kê giúp giảm số chiều của tập dữ liệu bằng cách biến đổi tuyến tính dữ liệu vào một hệ tọa độ mới mà sự biến thiên trong dữ liệu có thể được mô tả bằng ít chiều hơn so với dữ liệu ban đầu.<br> - PCA tìm hướng tối đa hóa phương sai của dữ liệu chiếu.<br><i>Trong trường hợp này, chúng ta 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, giảm kích thước đặc trưng từ 784 xuống còn 10.</i>\n</div>","metadata":{"_uuid":"988c70b6-e9d9-4ae7-b127-921423ab0f0e","_cell_guid":"231a3cf2-1f44-475b-a745-a28f3eb35d1e","trusted":true}},{"cell_type":"code","source":"# chuẩn hóa các giá trị pixel trong tập X_train_rotate ở khoảng [0, 1] bằng cách sử dụng MinMax Scaler\n# sau đó in ra giá trị trước và sau khi áp dụng MinMax Scaler \n# vieecj sử dụng MinMax Scaler sẽ giúp ta đưa các giá trị pixel về một giá trị chuẩn giúp huấn luyện mô hình hiệu quả hơn \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-05-25T05:52:14.670065Z","iopub.execute_input":"2024-05-25T05:52:14.670503Z","iopub.status.idle":"2024-05-25T05:52:14.756727Z","shell.execute_reply.started":"2024-05-25T05:52:14.670463Z","shell.execute_reply":"2024-05-25T05:52:14.755428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# bây giờ sẽ hiện thực hiện kết hợp MinMaxScaler và PCA vào một luồng xử lý liên tục bằng Sklearn Pipeline.\n# và PCA với số lượng thành phần chính được chỉnh định là 10.\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-05-25T05:52:14.758321Z","iopub.execute_input":"2024-05-25T05:52:14.758732Z","iopub.status.idle":"2024-05-25T05:52:14.835184Z","shell.execute_reply.started":"2024-05-25T05:52:14.758699Z","shell.execute_reply":"2024-05-25T05:52:14.833188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# sau khi đã thực hiện Sklearn Pipeline ở trên thì giờ sẽ tiến hình rút trích tỉ lệ phương sai trong các thành chính đã được xây dựng trước đó.\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-05-25T05:52:14.837572Z","iopub.execute_input":"2024-05-25T05:52:14.838372Z","iopub.status.idle":"2024-05-25T05:52:14.852716Z","shell.execute_reply.started":"2024-05-25T05:52:14.838304Z","shell.execute_reply":"2024-05-25T05:52:14.850027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Giờ chúng ta sẽ thực hiện trực quan hóa hai biểu đồ:\n# bên trái sẽ là tỉ lệ phương sai được giải thích cho từng thành phần.\n# bên phải sẽ là tổng tích lũy tỉ lệ phương sai.\n# biểu đồ trực quan thể hiện được mức độ biến thiên trên từng thành phần chính và mức độ tổng hợp của 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-05-25T05:52:14.854963Z","iopub.execute_input":"2024-05-25T05:52:14.856865Z","iopub.status.idle":"2024-05-25T05:52:15.565977Z","shell.execute_reply.started":"2024-05-25T05:52:14.856789Z","shell.execute_reply":"2024-05-25T05:52:15.564678Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Thực hiện chuyển đổi từ ma trận sang DataFrame với mục đích là sử dụng cho việc trực quan hóa và nó được biểu diễn bởi 3 cột x y z\n\ndf_train_pca = pd.DataFrame(\n    data=X_train_rotate_pca[:,:3], \n    columns=['x', 'y', 'z']\n)\n# Let's also add the target to set the colors\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-05-25T05:52:15.567696Z","iopub.execute_input":"2024-05-25T05:52:15.568106Z","iopub.status.idle":"2024-05-25T05:52:15.587636Z","shell.execute_reply.started":"2024-05-25T05:52:15.568071Z","shell.execute_reply":"2024-05-25T05:52:15.586331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# sử dụng seaborn để trực quan biểu đồ phân tán 2 chiều với mục đích hiển thị dữ liệu sau khi đã giảm chiều bằng PCA\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-05-25T05:52:15.589321Z","iopub.execute_input":"2024-05-25T05:52:15.589752Z","iopub.status.idle":"2024-05-25T05:52:16.576127Z","shell.execute_reply.started":"2024-05-25T05:52:15.589716Z","shell.execute_reply":"2024-05-25T05:52:16.574683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hiển thị các thành phần chính Top3(máy em chạy không hiển thị gì hết @_@)\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-05-25T05:52:16.578363Z","iopub.execute_input":"2024-05-25T05:52:16.579219Z","iopub.status.idle":"2024-05-25T05:52:19.907137Z","shell.execute_reply.started":"2024-05-25T05:52:16.579161Z","shell.execute_reply":"2024-05-25T05:52:19.905661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Sử dụng inverse_transform từ đối tượng PCA để tính toán xấp xỉ của các hình ảnh dựa trên 10 thành phần chí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-05-25T05:52:19.909994Z","iopub.execute_input":"2024-05-25T05:52:19.910607Z","iopub.status.idle":"2024-05-25T05:52:21.049172Z","shell.execute_reply.started":"2024-05-25T05:52:19.910542Z","shell.execute_reply":"2024-05-25T05:52:21.047786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# tính toán và hiển thị ma trận tương quan giữa các thành phần chính đã được giảm chiều bởi PCA\n# giúp chúng ta kiểm tra và xác nhận rằng các thành phần chính từ PCA là không tương quan.\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-05-25T05:52:21.051108Z","iopub.execute_input":"2024-05-25T05:52:21.051624Z","iopub.status.idle":"2024-05-25T05:52:21.977162Z","shell.execute_reply.started":"2024-05-25T05:52:21.051574Z","shell.execute_reply":"2024-05-25T05:52:21.975785Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"----\n# 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":{"_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;\">\n<li>Thuật toán K-Nearest Neighbors (KNN) là một thuật toán phân loại phi tham số, có giám sát, sử dụng sự gần gũi để thực hiện việ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 việc bỏ phiếu đa số, tức là nhãn lớp xuất hiện nhiều nhất xung quanh một điểm dữ liệu đã cho.</li>\n<li>Trong phần này, đầu vào của Bộ phân loại K-Nearest Neighbors (KNN) sẽ là 10 thành phần chính hàng đầu từ PCA.</li>\nsklearn.neighbors.KNeighborsClassifier: Bộ phân loại thực hiện bỏ phiếu của k láng giềng gần nhất. Ta sẽ chạy một GridSearchCV để tối ưu hóa siêu tham số n_neighbors.\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-05-25T05:52:21.979231Z","iopub.execute_input":"2024-05-25T05:52:21.979797Z","iopub.status.idle":"2024-05-25T05:52:24.315858Z","shell.execute_reply.started":"2024-05-25T05:52:21.979747Z","shell.execute_reply":"2024-05-25T05:52:24.314148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- Xây dựng mô hình tìm ra tham số tốt nhất cho KNN sau khi đã giảm chiều dữ liệu bằng PCA và chuẩn hóa MinMaxScaler\n- sau khi tìm kiếm sẽ trả về cho ta tham số và độ chính xác tốt nhất,giúp tối ưu hóa mô hình KNN có thể đạt tới khả năng phân loại tốt nhất.","metadata":{}},{"cell_type":"code","source":"# Tiếp tục trực quan hóa ẽ biểu đồ thể hiện độ chính xác của mô hình trên tập huấn luyện và tập validation cho mỗi giá trị tham số n_neighbors đã thử nghiệm trong quá trình Grid Search.\n# Biểu đồ này giúp chúng ta xác định liệu có sự chênh lệch đáng kể giữa độ chính xác trên tập huấn luyện và tập validation không, từ đó đánh giá xem mô hình có bị overfitting không,để có thể chọn được n_neighbors tối ưu nhất\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-05-25T05:52:24.318304Z","iopub.execute_input":"2024-05-25T05:52:24.319666Z","iopub.status.idle":"2024-05-25T05:52:24.756736Z","shell.execute_reply.started":"2024-05-25T05:52:24.319603Z","shell.execute_reply":"2024-05-25T05:52:24.755152Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**END**","metadata":{}}]}