{"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>Cơ sở dữ liệu đã được sửa đổi của Viện Tiêu chuẩn và Công nghệ Quốc gia</i>)  là một cơ sở dữ liệu lớn gồm <b>chữ số viết tay</b> thường được sử dụng để đào tạo các kỹ thuật xử lý hình ảnh khác nhau hệ thống.Khi các kỹ thuật học máy mới xuất hiện, MNIST vẫn là nguồn tài nguyên đáng tin cậy cho cả các nhà nghiên cứu và người học.<br><br>\nBản gốc <b>Hình ảnh đen trắng</b> ban đầu đã được chuẩn hóa để vừa với hộp giới hạn  <b>28x28 pixel</b> và khử răng cưa, giới thiệu <b>mức thang độ xám</b>.Cơ sở dữ liệu MNIST ban đầu chứa 60.000 hình ảnh đào tạo và 10.000 hình ảnh thử nghiệm, nhưng trong cuộc thi này, chúng tôi được cung cấp <b>42.000 hình ảnh đào tạo</b> (70%) và <b>18.000 hình ảnh thử nghiệm</b> (30%).<br><br>\nCác <i>kĩ thuật hiện đại</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 thần kinh chuyển đổi</i>. Kiểu kiến ​​trúc này thực sự 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 bài toán đơn giản nên trong cuốn sổ tay này chúng ta sẽ áp dụng  <i>các thuật toán học máy đơn giản</i> (và chúng ta sẽ có được độ chính xác khá cao!).<br><br>\nTrước tiên, chúng tôi sẽ thực hiện  <b>Principal Component Analysis</b> để giảm kích thước của dữ liệu (từ giá trị 784 pixel xuống còn 10 thành phần chính). Sau đó, chúng ta sẽ chạy thuật toán <i>lazy learner</i> thuật toán: <b>K-Nearest Neighbors</b>. Combinatino này đạt được  <b>TĐộ chính xác của thử nghiệm là 0,9271</b>,  một kết quả 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>.<br>\n    - Kiểm tra <i>phạm vi</i> của các giá trị pixel.<br>\n    - <i>Visualize</i> dữ liệu ảnh<br>\n    - Kiểm tra xem mục tiêu có <i>cân bằng</i>.</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>tỉ lệ</i>) dữ liệu.<br>\n        - Triển khai thuật toán PCA bằng cách sử dụng<code>Sklearn</code> thư viện.<br>\n        - Nhúng các mô-đun vào một <code>Sklearn Pipeline</code>.<br>\n        - Vẽ đồ thị <i>phương sai được giải thích</i> của các thành phần.<br>\n        - Two-dimensional visualization of the data using <code>seaborn</code>.<br>\n        - Trực quan hóa dữ liệu ba chiều bằng cách sử dụng <code>plotly</code>.<br>\n        - Trực quan hóa của <i>phép biến đổi nghịch đảo</i>.<br>\n        - Kiểm tra xem các thành phần có <i>không tương quan</i>.</li><br>\n    <li> 🔶 <b>KNN Trình phân loại</b> 🔶:<br>\n        - Xây dựng Trình 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ình trích xuất tính năng</i> cho KNN.<br>\n        - Chạy một <i>Grid-Search Cross-Validation</i> để tối ưu hóa<code>n_neighbors</code> siêu tham số.<br>\n        - So sánh số liệu đào tạo và xác nhận để kiểm tra<i>trang bị quá mức</i>.<br>\n        - Vẽ đồ thị <i>Confusion Matrix</i> tính toán.<br>\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":"## IMPORT THƯ VIỆN\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# Import thuật toán PCA\nfrom sklearn.decomposition import PCA\nfrom sklearn.pipeline import Pipeline\nimport plotly.express as px\n\n# Import thuật toán KNN\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.model_selection import GridSearchCV\nfrom sklearn.metrics import classification_report, \\\n    confusion_matrix, ConfusionMatrixDisplay \n\n# Import hàm rotate từ thư viện spicy\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-01T09:04:08.113008Z","iopub.execute_input":"2024-06-01T09:04:08.113596Z","iopub.status.idle":"2024-06-01T09:04:08.123376Z","shell.execute_reply.started":"2024-06-01T09:04:08.113552Z","shell.execute_reply":"2024-06-01T09:04:08.122078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def func_array1d_rotate2d(Narray1d,rotate_angle,sizePixel):\n#Hàm với ba đối số đầu vào: Narray1d, rotate_angle, sizePixel\n    N = Narray1d.shape[0] # Lấy số lượng hình ảnh\n\n    Narray1d_rotate = np.zeros(Narray1d.shape)# Khởi tạo mảng để lưu trữ hình ảnh sau khi xoay\n#Duyệt qua từng hình ảnh trong mảng Narray1d    \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# Trả về mảng chứa các hình ảnh đã được xoay","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-01T09:04:08.126004Z","iopub.execute_input":"2024-06-01T09:04:08.126637Z","iopub.status.idle":"2024-06-01T09:04:08.141509Z","shell.execute_reply.started":"2024-06-01T09:04:08.126602Z","shell.execute_reply":"2024-06-01T09:04:08.140586Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"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 các thuật ngữ nổi tiếng <code>Pandas</code> Thư viện Python để đọc dữ liệu từ<code>.csv</code> files.<br><br>\nChúng ta sẽ hình dung ra <b>phân phối giá trị pixel</b> (histogram)  bằng cách sử dụng <code>Seaborn</code> and <code>Matplotlib</code> thư viện Python. Chúng tôi cũng sẽ kiểm tra xem không có <b>giá trị bị mất</b>, \nvà đó là <b>các lớp mục tiêu được cân bằng</b>.<br><br>\nCuối cùng, chúng tôi sẽ <b>trực quan những hình ảnh</b>bằng cách vẽ một hình ảnh từ mỗi lớp.\n\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\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-01T09:04:08.142827Z","iopub.execute_input":"2024-06-01T09:04:08.143344Z","iopub.status.idle":"2024-06-01T09:04:13.134864Z","shell.execute_reply.started":"2024-06-01T09:04:08.143302Z","shell.execute_reply":"2024-06-01T09:04:13.133587Z"},"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-01T09:04:13.138050Z","iopub.execute_input":"2024-06-01T09:04:13.138558Z","iopub.status.idle":"2024-06-01T09:04:13.145733Z","shell.execute_reply.started":"2024-06-01T09:04:13.138517Z","shell.execute_reply":"2024-06-01T09:04:13.144521Z"},"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    \"Thiếu giá trị trong df_train: \", \\\n    df_train.isna().sum().sum()\n)\nprint(\n    \"Thiếu giá trị trong 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-01T09:04:13.147658Z","iopub.execute_input":"2024-06-01T09:04:13.148438Z","iopub.status.idle":"2024-06-01T09:04:13.208447Z","shell.execute_reply.started":"2024-06-01T09:04:13.148395Z","shell.execute_reply":"2024-06-01T09:04:13.207099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chúng ta hãy nhìn vào 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-01T09:04:13.210464Z","iopub.execute_input":"2024-06-01T09:04:13.211275Z","iopub.status.idle":"2024-06-01T09:04:13.233588Z","shell.execute_reply.started":"2024-06-01T09:04:13.211232Z","shell.execute_reply":"2024-06-01T09:04:13.232601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Ta thấy cột đầu tiên là nhãn,\n# và các cột còn lại là 784 pixel\n# đại diện cho hình ảnh thang độ xám 28x28.\n# Hãy tách X và Y\n\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-01T09:04:13.235105Z","iopub.execute_input":"2024-06-01T09:04:13.236056Z","iopub.status.idle":"2024-06-01T09:04:13.341849Z","shell.execute_reply.started":"2024-06-01T09:04:13.236021Z","shell.execute_reply":"2024-06-01T09:04:13.340786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hãy hình dung sự phân bố của các giá trị pixel\n\nax = sns.histplot(x=X_train.flatten(), color='#4895EE')\n_ = ax.set(title='Pixel Value Histogram', xlabel='Pixel Value')\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","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-01T09:04:13.343628Z","iopub.execute_input":"2024-06-01T09:04:13.344303Z","iopub.status.idle":"2024-06-01T09:04:32.155122Z","shell.execute_reply.started":"2024-06-01T09:04:13.344272Z","shell.execute_reply":"2024-06-01T09:04:32.153945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hãy kiểm tra xem mục tiêu có cân bằng không.\n# Nó có nghĩa là mười danh mục trong mục tiêu\n# Có sự biểu diễn gần giống nhau trong dữ liệu.\n\nax = sns.countplot(x=y_train, color='#4895EE')\nax.set(\n    title='Target distribution is balanced? Yes!',\n    xlabel='Label'\n)\n# Số lượng mẫu dự kiến\n# Phân bố 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à số dư nên chúng tôi không có\n# Lo lắng về phương pháp lấy mẫu và thành kiến trong mô hình của chúng tôi.","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-01T09:04:32.160368Z","iopub.execute_input":"2024-06-01T09:04:32.161366Z","iopub.status.idle":"2024-06-01T09:04:32.608977Z","shell.execute_reply.started":"2024-06-01T09:04:32.161300Z","shell.execute_reply":"2024-06-01T09:04:32.607863Z"},"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 tôi 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","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-01T09:04:32.610523Z","iopub.execute_input":"2024-06-01T09:04:32.610957Z","iopub.status.idle":"2024-06-01T09:04:33.141907Z","shell.execute_reply.started":"2024-06-01T09:04:32.610917Z","shell.execute_reply":"2024-06-01T09:04:33.140826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Chuyển đổi dữ liệu","metadata":{"_uuid":"9d4cd46f-3405-4ee3-a2e3-758b928024b1","_cell_guid":"abe3ee94-93c7-4941-9358-150e526414ec","trusted":true}},{"cell_type":"code","source":"\n# -------------------------------------------------- declare\nnumRotate_prior = 10\n\nangleOffset_prior = 0\nangleRange_prior = 90\n\nX_train_pick2rotate = {\n    'index': np.array([3100,3101,3102,3103,3104,3105,3106,3107,3108,\n                      3109,3110,3111,3112,3113,3114,3115,3116,3117,3118,3119,\n                       3120,3121,3122,3123,3124,3125,3126,3127,3128,3129,3130,3131,\n                    3132,3133,3134,3135,3136,3137,3138,3139]),\n    'offset': np.array([\n         [-5,35,-5,20,5, \n         0,20,-5,10,25],#3100\n        \n        [0,20,0,-5,5, \n         0,-10,0,10,10],#3101\n        [35,5,0,0,0, \n         10,10,0,20,-10],#3102\n        [15,5,10,-5,0, \n         20,20,0,-10,0],#3103\n        [25,10,0,0,0, \n         10,-30,0,-25,0],#3104\n        \n        [0,5,5,0,15, \n         20,-15,0,0,0],#3105\n        [0,30,25,0,25, \n         10,-30,0,10,30],#3106\n        [25,25,0,5,-5, \n         0,0,0,0,10],#3107\n        [0,0,0,0,15, \n         0,0,0,-5,10],#3108\n        [0,0,0,0,-5, \n         5,15,0,10,15],#3109\n        [0,-5,0,0,0, \n         0,0,0,-15,10],#3010\n        [25,15,0,0,20, \n         0,-60,-5,0,10],#3011\n        [45,0,10,20,30, \n         0,-25,0,-30,10],#3012\n        [15,0,0,-10,0, \n         0,-20,0,15,20],#3013\n        [0,25,-5,0,0, \n         15,-20,0,10,-10],#3014\n        [20,20,30,10,0, \n         0,-30,-5,0,10],#3015\n        [10,-5,0,-5,15, \n         0,0,0,0,-10],#3016\n        [0,0,20,0,0, \n         0,-25,0,20,-10],#3017\n        [0,20,15,0,0, \n         0,10,0,0,10],#3018\n        [0,40,-10,0,-5, \n         0,-25,0,0,-15],#3019\n       [30,25,25,10,20, \n         0,20,-5,20,20],#3120\n        \n        [0,-10,-5,0,15, \n         0,-35,-5,0,35],#3121\n        [35,5,0,5,0, \n         10,-20,20,-10,0],#3122\n        [15,0,10,5,5, \n         20,0,-5,-15,10],#3123\n        [25,5,0,10,10, \n         30,0,0,0,0],#3124\n        \n        [40,0,-20,0,40, \n         10,-15,-10,0,-10],#3125\n        [25,15,10,0,20, \n         20,-30,0,20,10],#3126\n        [25,5,15,5,5, \n         0,10,0,0,15],#3127\n        [75,0,0,0,5, \n         0,-20,0,25,35],#3128\n        [20,35,0,0,15, \n         15,-20,0,0,15],#3129\n        [15,-5,-5,-5,15, \n         10,-10,0,15,20],#3030\n        [25,15,0,0,-10, \n         0,-20,-5,0,20],#3031\n        [45,20,15,20,0, \n         15,-15,15,-20,10],#3032\n        [10,15,10,15,5, \n         0,-20,0,25,-5],#3033\n        [35,0,-25,0,15, \n         15,-25,0,10,10],#3034\n        [5,5,5,10,-10, \n         25,10,-5,15,10],#3035\n        [20,-10,0,5,20, \n         0,-30,0,-5,-10],#3036\n        [10,5,15,-25,10, \n         5,0,0,10,20],#3037\n        [0,20,15,0,0, \n         0,10,0,0,10],#3018\n        [0,40,-10,0,-5, \n         0,-25,0,0,-15],#3019\n        \n       \n        \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\n# Hãy bình thường 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(20):\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-01T09:04:33.143992Z","iopub.execute_input":"2024-06-01T09:04:33.144314Z","iopub.status.idle":"2024-06-01T09:04:46.551527Z","shell.execute_reply.started":"2024-06-01T09:04:33.144286Z","shell.execute_reply":"2024-06-01T09:04:46.550378Z"},"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>Principal Comopnent Analysis</b> (PCA) là một kỹ thuật thống kê để giảm tính chiều của tập dữ liệu. Điều này được thực hiện bởi <b>chuyển đổi tuyến tính dữ liệu</b> vào một hệ tọa độ mới trong đó (hầu hết) sự biến đổi trong dữ liệu có thể được mô tả với ít thứ nguyên hơn dữ liệu ban đầu.<br><br>\nThành phần chính đầu tiên có thể được định nghĩa tương đương là một<b>hướng tối đa hóa phương sai của dữ liệu dự kiến</b>. Các <i>i</i>-th thành phần chính thứ có thể được coi là hướng trực giao với thành phần đầu tiên <i>i-1</i> các thành phần chính giúp tối đa hóa phương sai của dữ liệu dự kiến.<br><br>\n<code>sklearn.decomposition.PCA</code>: Giảm kích thước tuyến tính bằng cách sử dụng Phân tách giá trị đơn lẻ của dữ liệu để chiếu dữ liệu đó vào không gian có chiều thấp hơn.<br><br>\n![](http://)Ở đây chúng ta sẽ sử dụng phần trên cùng<b>10 thành phần chính</b>, giải thích điều đó <b>gần 50% phương sai</b> trong dữ liệu. Điều này có nghĩa là chúng ta có thể biểu thị 50% phương sai của dữ liệu bằng một khoảng các đặc điểm <b>>98% nhỏ hơn cái ban đầu</b> (10 đặc trưng vs. 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 bình thường 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-01T09:04:46.553138Z","iopub.execute_input":"2024-06-01T09:04:46.553468Z","iopub.status.idle":"2024-06-01T09:04:46.635249Z","shell.execute_reply.started":"2024-06-01T09:04:46.553439Z","shell.execute_reply":"2024-06-01T09:04:46.634191Z"},"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-01T09:04:46.636663Z","iopub.execute_input":"2024-06-01T09:04:46.636975Z","iopub.status.idle":"2024-06-01T09:04:47.031059Z","shell.execute_reply.started":"2024-06-01T09:04:46.636949Z","shell.execute_reply":"2024-06-01T09:04:47.029361Z"},"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-01T09:04:47.034154Z","iopub.execute_input":"2024-06-01T09:04:47.036435Z","iopub.status.idle":"2024-06-01T09:04:47.052490Z","shell.execute_reply.started":"2024-06-01T09:04:47.036376Z","shell.execute_reply":"2024-06-01T09:04:47.050725Z"},"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 (trái)\n# And plot their cumulative sum (right)\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-01T09:04:47.055827Z","iopub.execute_input":"2024-06-01T09:04:47.058241Z","iopub.status.idle":"2024-06-01T09:04:47.951858Z","shell.execute_reply.started":"2024-06-01T09:04:47.058182Z","shell.execute_reply":"2024-06-01T09:04:47.950706Z"},"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# Chúng ta cũng 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-01T09:04:47.953188Z","iopub.execute_input":"2024-06-01T09:04:47.953562Z","iopub.status.idle":"2024-06-01T09:04:47.971994Z","shell.execute_reply.started":"2024-06-01T09:04:47.953519Z","shell.execute_reply":"2024-06-01T09:04:47.970690Z"},"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 Top2 đượ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-01T09:04:47.973916Z","iopub.execute_input":"2024-06-01T09:04:47.974950Z","iopub.status.idle":"2024-06-01T09:04:49.315622Z","shell.execute_reply.started":"2024-06-01T09:04:47.974907Z","shell.execute_reply":"2024-06-01T09:04:49.314425Z"},"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 Top3 đượ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-01T09:04:49.316899Z","iopub.execute_input":"2024-06-01T09:04:49.317225Z","iopub.status.idle":"2024-06-01T09:04:49.404832Z","shell.execute_reply.started":"2024-06-01T09:04:49.317197Z","shell.execute_reply":"2024-06-01T09:04:49.403658Z"},"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-01T09:04:49.406195Z","iopub.execute_input":"2024-06-01T09:04:49.406572Z","iopub.status.idle":"2024-06-01T09:04:50.403826Z","shell.execute_reply.started":"2024-06-01T09:04:49.406541Z","shell.execute_reply":"2024-06-01T09:04:50.402700Z"},"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)\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-01T09:04:50.405291Z","iopub.execute_input":"2024-06-01T09:04:50.406167Z","iopub.status.idle":"2024-06-01T09:04:51.271062Z","shell.execute_reply.started":"2024-06-01T09:04:50.406126Z","shell.execute_reply":"2024-06-01T09:04:51.269980Z"},"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;\">\nCác <b>K-Nearest Neighbors</b> là một thuật toán <i>phi tham số</i>, phân loại học có giám sát, sử dụng cái nào <b>proximity</b> để thực hiện phân loại hoặc dự đoán về việc nhóm một điểm dữ liệu riêng lẻ. Dự đoán: Một nhãn lớp được gán trên cơ sở một <b>đa số phiếu bầu</b>. nhãn được thể hiện thường xuyên nhất xung quanh một điểm dữ liệu nhất định.<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>: Trình phân loại thực hiện bỏ phiếu k hàng xóm gần nhất. Chúng tôi sẽ chạy GridSearchCV để tối ưu hóa <code>n_neighbors</code> siêu tham số.\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-01T09:04:51.272701Z","iopub.execute_input":"2024-06-01T09:04:51.273037Z","iopub.status.idle":"2024-06-01T09:04:57.050000Z","shell.execute_reply.started":"2024-06-01T09:04:51.273008Z","shell.execute_reply":"2024-06-01T09:04:57.047935Z"},"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-01T09:04:57.059733Z","iopub.execute_input":"2024-06-01T09:04:57.064470Z","iopub.status.idle":"2024-06-01T09:04:58.300819Z","shell.execute_reply.started":"2024-06-01T09:04:57.064394Z","shell.execute_reply":"2024-06-01T09:04:58.299640Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"---\n# 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":{"_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-01T09:04:58.302173Z","iopub.execute_input":"2024-06-01T09:04:58.302550Z","iopub.status.idle":"2024-06-01T09:04:59.495152Z","shell.execute_reply.started":"2024-06-01T09:04:58.302514Z","shell.execute_reply":"2024-06-01T09:04:59.494103Z"},"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\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-01T09:04:59.499771Z","iopub.execute_input":"2024-06-01T09:04:59.500138Z","iopub.status.idle":"2024-06-01T09:04:59.522517Z","shell.execute_reply.started":"2024-06-01T09:04:59.500107Z","shell.execute_reply":"2024-06-01T09:04:59.521395Z"},"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-01T09:04:59.523909Z","iopub.execute_input":"2024-06-01T09:04:59.524207Z","iopub.status.idle":"2024-06-01T09:05:06.529232Z","shell.execute_reply.started":"2024-06-01T09:04:59.524180Z","shell.execute_reply":"2024-06-01T09:05:06.528100Z"},"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}}]}