{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":30698,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 🔶 PCA & KNN 🔶\n### 🔸 Principal Component Analysis <b>&</b> K-Nearest Neighbors 🔸","metadata":{}},{"cell_type":"markdown","source":"<div style=\"border-radius: 10px; border: #4361EE solid; padding: 15px; background-color: #ffffff00; font-size: 100%; text-align: left;\">\nCơ sở dữ liệu <b>MNIST</b> (<i>Cơ sở dữ liệu của Viện Tiêu chuẩn và Công nghệ Quốc gia được Sửa đổi</i>) là một cơ sở dữ liệu lớn chứa các <b>chữ số viết tay</b> thường được sử dụng để huấn luyện các hệ thống xử lý hình ảnh khác nhau. Khi các kỹ thuật học máy mới xuất hiện, MNIST vẫn là một nguồn tài nguyên đáng tin cậy cho các nhà nghiên cứu và người học.<br><br>\nCác <b>hình ảnh đen trắng</b> gốc đã được chuẩn hóa để vừa với một <b>hộp giới hạn 28x28 điểm ảnh</b> và được làm mịn, điều này đã giới thiệu các <b>mức xám</b>. Cơ sở dữ liệu MNIST gốc chứa 60,000 hình ảnh huấn luyện và 10,000 hình ảnh kiểm tra, nhưng trong cuộc thi này, chúng tôi được cung cấp <b>42,000 hình ảnh huấn luyện</b> (70%) và <b>18,000 hình ảnh kiểm tra</b> (30%).<br><br>\nCác kỹ thuật <i>tân tiến nhất</i> để tiếp cận bất kỳ vấn đề Phân loại Hình ảnh nào thường phụ thuộc vào <i>Mạng nơ-ron tích chập</i>. Loại kiến trúc này rất tốt trong việc trích xuất các mẫu từ hình ảnh. Tuy nhiên, vì MNIST là một vấn đề đơn giản, trong sổ ghi chú này, chúng tôi sẽ áp dụng <i>Các Thuật toán Học máy đơn giản</i> (và chúng tôi sẽ đạt được độ chính xác tốt!).<br><br>\nTrước tiên, chúng tôi sẽ thực hiện một <b>Phân tích thành phần chính (PCA)</b> để giảm chiều dữ liệu (từ 784 giá trị điểm ảnh xuống còn 10 thành phần chính). Sau đó, chúng tôi sẽ chạy thuật toán <i>lười biếng yêu thích</i> của chúng tôi: <b>Người láng giềng gần nhất (K-Nearest Neighbors)</b>. Kết hợp này đạt được <b>Độ chính xác Kiểm tra là 0.9271</b>, điều này khá đáng nể!!!<br><br>\n<b>Mục lục:</b>\n<ul>\n    <li>🔎 <b>Đọc & Khám phá Dữ liệu</b> 🔍:<br>\n    - Kiểm tra xem có <i>giá trị còn thiếu</i> không.<br>\n    - Kiểm tra <i>phạm vi</i> của các giá trị điểm ảnh.<br>\n    - <i>Hiển thị</i> dữ liệu (Hình ảnh).<br>\n    - Kiểm tra xem mục tiêu có <i>cân bằng</i> không.</li><br>\n    <li> 🔶 <b>PCA</b> 🔶:<br>\n        - Giới thiệu thuật toán PCA.<br>\n        - Tiền xử lý (<i>chuẩn hóa</i>) dữ liệu.<br>\n        - Thực hiện thuật toán PCA bằng thư viện <code>Sklearn</code>.<br>\n        - Nhúng các mô-đun vào một <code>Ống dẫn Sklearn</code>.<br>\n        - Vẽ biểu đồ <i>phương sai được giải thích</i> của các thành phần.<br>\n        - Hiển thị hai chiều của dữ liệu bằng cách sử dụng <code>seaborn</code>.<br>\n        - Hiển thị ba chiều động của dữ liệu bằng cách sử dụng <code>plotly</code>.<br>\n        - Hiển thị <i>phản chiếu nghịch đảo</i> của dữ liệu.<br>\n        - Kiểm tra xem các thành phần có <i>không tương quan</i> không.</li><br>\n    <li> 🔶 <b>Bộ phân loại KNN</b> 🔶:<br>\n        - Xây dựng một Bộ phân loại KNN bằng cách sử dụng <code>Sklearn</code>.<br>\n        - Sử dụng kỹ thuật PCA như một <i>trích xuất đặc trưng</i> cho KNN.<br>\n        - Chạy một <i>Lưới tìm kiếm Chéo</i> để tối ưu hóa các siêu tham số <code>n_neighbors</code>.<br>\n        - So sánh các chỉ số đo cho tập huấn luyện và tập kiểm tra để kiểm tra xem có <i>quá mức</i> không.<br>\n        - Vẽ <i>Ma trận nhầm lẫn</i>.<br>\n        - Tính toán <i>Báo cáo Phân loại</i>.<br>\n    </ul>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# **Phần 1 : Import Python Module**","metadata":{}},{"cell_type":"code","source":"import numpy as np # đại số tuyến tính\nimport pandas as pd # xử lý dữ liệu, CSV file I/O (ví dụ: pd.read_csv)\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:50:44.632471Z","iopub.execute_input":"2024-04-23T02:50:44.633463Z","iopub.status.idle":"2024-04-23T02:50:44.645250Z","shell.execute_reply.started":"2024-04-23T02:50:44.633420Z","shell.execute_reply":"2024-04-23T02:50:44.643633Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## NHẬP CÁC THƯ VIỆN PYTHON CẦN THIẾT\n\n# Đọc và Khám phá Dữ liệu\nimport numpy as np  # Thư viện cho các phép toán số học và ma trận\nimport pandas as pd  # Thư viện ch\nimport matplotlib.pyplot as plt  # Tho xử lý dữ liệu dạng bảng\nimport seaborn as sns  # Thư viện để vẽ đồ thị thống kêư viện để vẽ đồ thị\nfrom sklearn.preprocessing import MinMaxScaler  # Thư viện cho việc chuẩn hóa dữ liệu\n\n# Phân tích thành phần chính (PCA)\nfrom sklearn.decomposition import PCA  # Thư viện cho phân tích thành phần chính\nfrom sklearn.pipeline import Pipeline  # Thư viện cho việc xây dựng các công đoạn xử lý dữ liệu tuần tự\nimport plotly.express as px  # Thư viện để vẽ đồ thị tương tác\n\n# Bộ phân loại K-Nearest Neighbor (KNN)\nfrom sklearn.neighbors import KNeighborsClassifier  # Thư viện cho mô hình phân loại K-Nearest Neighbor\nfrom sklearn.model_selection import GridSearchCV  # Thư viện cho tìm kiếm siêu tham số trên lưới\nfrom sklearn.metrics import classification_report, \\\n    confusion_matrix, ConfusionMatrixDisplay  # Thư viện cho báo cáo phân loại và ma trận nhầm lẫn\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:54:32.113593Z","iopub.execute_input":"2024-04-23T02:54:32.114335Z","iopub.status.idle":"2024-04-23T02:54:32.123232Z","shell.execute_reply.started":"2024-04-23T02:54:32.114298Z","shell.execute_reply":"2024-04-23T02:54:32.121789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Phần 2 : Data**","metadata":{}},{"cell_type":"markdown","source":"Trong phần này, chúng ta sẽ sử dụng thư viện Python nổi tiếng <code>Pandas</code> để đọc dữ liệu từ các tập tin <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ị điểm ảnh</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ị còn thiếu</b> không và các <b>lớp mục tiêu có cân bằng</b> không.<br><br>\nCuối cùng, chúng ta sẽ <b>hiển thị hình ảnh</b> bằng cách vẽ một hình ảnh từ mỗi lớp.","metadata":{}},{"cell_type":"code","source":"# Đọc các tập tin dữ liệu bằng Pandas\ndf_train = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')  # Đọc tập tin dữ liệu huấn luyện\ndf_test = pd.read_csv('/kaggle/input/digit-recognizer/test.csv')  # Đọc tập tin dữ liệu kiểm tra\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:54:35.766114Z","iopub.execute_input":"2024-04-23T02:54:35.767119Z","iopub.status.idle":"2024-04-23T02:54:41.174445Z","shell.execute_reply.started":"2024-04-23T02:54:35.767074Z","shell.execute_reply":"2024-04-23T02:54:41.173090Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Kiểm tra kích thước của DataFrame huấn luyện và kiểm tra\nprint(\"Kích thước df_train:\", df_train.shape)  # 70%\nprint(\"Kích thước df_test: \", df_test.shape)  # 30%","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:54:44.870506Z","iopub.execute_input":"2024-04-23T02:54:44.871567Z","iopub.status.idle":"2024-04-23T02:54:44.879714Z","shell.execute_reply.started":"2024-04-23T02:54:44.871514Z","shell.execute_reply":"2024-04-23T02:54:44.878070Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Giá trị thiếu trong dữ liệu\nprint(\n    \"Giá trị thiếu trong df_train: \", \\\n    df_train.isna().sum().sum()\n)\nprint(\n    \"Giá trị thiếu trong df_test: \", \\\n    df_test.isna().sum().sum()\n)\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.263938Z","iopub.status.idle":"2024-04-23T02:48:50.264875Z","shell.execute_reply.started":"2024-04-23T02:48:50.264644Z","shell.execute_reply":"2024-04-23T02:48:50.264667Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Xem năm dòng đầu tiên của dữ liệu huấn luyện\ndf_train.head()","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.266184Z","iopub.status.idle":"2024-04-23T02:48:50.266630Z","shell.execute_reply.started":"2024-04-23T02:48:50.266428Z","shell.execute_reply":"2024-04-23T02:48:50.266445Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chúng ta có thể 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 xám 28x28.\n# Hãy tách X và Y\n\nX_train = df_train.drop(columns=['label']).values  # Dữ liệu đầu vào\ny_train = df_train['label'].values  # Nhãn","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.268876Z","iopub.status.idle":"2024-04-23T02:48:50.269326Z","shell.execute_reply.started":"2024-04-23T02:48:50.269117Z","shell.execute_reply":"2024-04-23T02:48:50.269134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hãy trực quan hóa phân phối của các giá trị pixel\n\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 nền\n# và các giá trị > 0 đại diện cho các chữ số trong hình ảnh","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.270732Z","iopub.status.idle":"2024-04-23T02:48:50.271194Z","shell.execute_reply.started":"2024-04-23T02:48:50.270966Z","shell.execute_reply":"2024-04-23T02:48:50.270984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hãy kiểm tra xem nhãn có cân bằng không.\n# Điều này có nghĩa là mười danh mục trong nhãn\n# có sự đại diện gần như như nhau trong dữ liệu.\n\nax = sns.countplot(x=y_train, color='#4895EE')\nax.set(\n    title='Phân phối nhãn có cân bằng không? Có!',\n    xlabel='Nhãn'\n)\n# Số lượng mẫu kỳ vọng cho một phân phối cân bằng\n# là 42000/10 = 4200\n_ = ax.axhline(y=4200, color='#12086F', linestyle='--')\n\n# Vì các lớp nhãn được cân bằng, chúng ta không cần\n# lo lắng về các phương pháp lấy mẫu và độ chệch trong các mô hình của chúng ta.","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.272474Z","iopub.status.idle":"2024-04-23T02:48:50.272895Z","shell.execute_reply.started":"2024-04-23T02:48:50.272710Z","shell.execute_reply":"2024-04-23T02:48:50.272726Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tiếp theo, hãy trực quan hóa một hình ảnh từ mỗi lớp.\n# Sử dụng hàm 'imshow' của matplotlib.\n\n# Tạo một hình mới với 2 hàng và 5 cột\nfig, axs = plt.subplots(nrows=2, ncols=5, figsize=(10,5))\n\n# Lặp qua mỗi lớp (từ 0 đến 9)\nfor i in range(10):\n    # Lấy chỉ mục của một mẫu từ lớp hiện tại\n    idx = np.where(y_train==i)[0][0]\n    # Trực quan hóa hình ảnh với hàm 'imshow' của matplotlib\n    img = axs[int(i >= 5), i%5].imshow(\n        X=X_train[idx].reshape(28, 28),  # Định dạng lại hình ảnh về kích thước 28x28\n        cmap='Blues'  # Sử dụng màu xanh lam cho hình ảnh\n    )\n    # Ẩn trục y\n    img.axes.get_yaxis().set_visible(False)\n    # Ẩn trục x\n    img.axes.get_xaxis().set_visible(False)\n    # Đặt tiêu đề cho hình ảnh với nhãn tương ứng\n    img.axes.set_title(f'Nhãn: {i}')\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.273909Z","iopub.status.idle":"2024-04-23T02:48:50.274336Z","shell.execute_reply.started":"2024-04-23T02:48:50.274142Z","shell.execute_reply":"2024-04-23T02:48:50.274159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Phần 3 : PCA**","metadata":{}},{"cell_type":"markdown","source":"<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 thiên trong dữ liệu có thể được mô tả với í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ó thể được định nghĩa tương đương như là một <b>hướng mà làm tăng phương sai của dữ liệu chiếu</b>. Thành phần chính thứ <i>i</i> có thể được xem như là một hướng vuông góc với <i>i-1</i> thành phần chính đầu tiên làm tăng phương sai của dữ liệu chiếu.<br><br>\n<code>sklearn.decomposition.PCA</code>: Giảm chiều dữ liệu tuyến tính bằng cách 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</b> hàng đầu, giải thích <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 diễn 50% phương sai của dữ liệu với một không gian đặc trưng <b>lớn hơn 98% so với ban đầu</b> (10 đặc trưng so với 784 đặc trưng).\n\n\n\n\n\n","metadata":{}},{"cell_type":"code","source":"# Hãy chuẩn hóa các giá trị pixel vào khoảng [0,1]\n# Công thức: MinMaxscale(X) = (X - X_min) / (X_max - X_min)\n\n# In thông tin trước khi áp dụng MinMax Scaler\nprint(\"Trước khi áp dụng MinMax Scaler:\")\nprint(\"  - Giá trị nhỏ nhất:\", X_train.min())\nprint(\"  - Giá trị lớn nhất:\", X_train.max())\n\n# Tạo một bộ chuẩn hóa MinMaxScaler\nminmax_scaler = MinMaxScaler()\n# Áp dụng MinMax Scaler vào dữ liệu huấn luyện\nX_train_scaled = minmax_scaler.fit_transform(X_train)\n\n# In thông tin sau khi áp dụng MinMax Scaler\nprint(\"\\nSau khi áp dụng MinMax Scaler:\")\nprint(\"  - Giá trị nhỏ nhất:\", X_train_scaled.min())\nprint(\"  - Giá trị lớn nhất:\", X_train_scaled.max())\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.276359Z","iopub.status.idle":"2024-04-23T02:48:50.276754Z","shell.execute_reply.started":"2024-04-23T02:48:50.276562Z","shell.execute_reply":"2024-04-23T02:48:50.276578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Thực tế, nhúng các module MinMaxScaler và PCA\n# vào một ống dẫn Sklearn.\n\n# Tạo một pipeline gồm các bước:\n# 1. 'scaler': chuẩn hóa MinMaxScaler\n# 2. 'pca': phân tích thành phần chính với 10 thành phần chính được giữ lại\npca = Pipeline(steps=[\n    ('scaler', MinMaxScaler()),  # Bước 1: Chuẩn hóa MinMaxScaler\n    ('pca', PCA(n_components=10))  # Bước 2: Phân tích thành phần chính với 10 thành phần chính\n])\n\n# Áp dụng pipeline vào dữ liệu huấn luyện\nX_train_pca = pca.fit_transform(X=X_train)\n\n# In kích thước của dữ liệu huấn luyện sau khi được giảm chiều bởi PCA\nprint('Kích thước của X_train_pca:', X_train_pca.shape)\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.278224Z","iopub.status.idle":"2024-04-23T02:48:50.278651Z","shell.execute_reply.started":"2024-04-23T02:48:50.278446Z","shell.execute_reply":"2024-04-23T02:48:50.278462Z"},"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à tính tổng tích lũy của chúng\n\n# Trích xuất đối tượng PCA từ pipeline\npca_object = pca.named_steps['pca']\n# Tính tỷ lệ phương sai giải thích được và lưu vào Series với chỉ mục từ 1 đến 10\nvar_ratio = pd.Series(\n    data=pca_object.explained_variance_ratio_,  # Tỷ lệ phương sai giải thích được\n    index=range(1, 11)  # Chỉ mục từ 1 đến 10\n)\n\n# Tính tổng tích lũy của tỷ lệ phương sai giải thích được\ncumsum_var_ratio = var_ratio.cumsum()\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.280074Z","iopub.status.idle":"2024-04-23T02:48:50.280604Z","shell.execute_reply.started":"2024-04-23T02:48:50.280323Z","shell.execute_reply":"2024-04-23T02:48:50.280347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Vẽ biểu đồ tỷ lệ phương sai giải thích được cho mỗi thành phần (bên trái)\n# Và vẽ tổng tích lũy của chúng (bên phải)\n\n# Tạo một hình với 2 subplot\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15,5))\n\n# Biểu đồ cột cho tỷ lệ phương sai giải thích được\nsns.barplot(\n    x=var_ratio.index, \n    y=var_ratio.values, \n    color='#4895EE', \n    ax=ax1\n)\nax1.set(\n    title='Tỷ Lệ Phương Sai Giải Thích Được PCA',\n    xlabel='Thành Phần',\n    ylabel='Tỷ lệ Phương Sai',\n)\nax1.set_ylim(0, 0.5)  # Đặt giới hạn trục y\n\n# Biểu đồ cột cho tổng tích lũy của tỷ lệ phương sai giải thích được\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='Tổng Tích Lũy Tỷ Lệ Phương Sai Giải Thích Được PCA',\n    xlabel='Thành Phần',\n    ylabel='Tổng Tỷ lệ Phương Sai',\n)\n_ = ax2.set_ylim(0, 0.5)  # Đặt giới hạn trục y\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.281936Z","iopub.status.idle":"2024-04-23T02:48:50.282383Z","shell.execute_reply.started":"2024-04-23T02:48:50.282189Z","shell.execute_reply":"2024-04-23T02:48:50.282206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hãy đưa các thành phần vào định dạng DataFrame của Pandas\n# Điều này cho phép chúng tôi sử dụng các thư viện seaborn và plotly\n# để trực quan hóa dữ liệu.\n# (các gói này hoạt động tốt hơn với DataFrame của Pandas)\n\n# Tạo DataFrame từ dữ liệu các thành phần PCA\ndf_train_pca = pd.DataFrame(\n    data=X_train_pca[:,:3],  # Chỉ lấy 3 thành phần đầu tiên\n    columns=['x', 'y', 'z']  # Đặt tên cột là 'x', 'y', 'z'\n)\n# Thêm cột 'target' là nhãn để đặt màu\ndf_train_pca['target'] = y_train\n\n# In kích thước của DataFrame và các dòng đầu tiên\nprint(df_train_pca.shape)\ndf_train_pca.head()\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.284653Z","iopub.status.idle":"2024-04-23T02:48:50.288467Z","shell.execute_reply.started":"2024-04-23T02:48:50.288221Z","shell.execute_reply":"2024-04-23T02:48:50.288247Z"},"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, hai thành phần chính được trực quan hóa\n\n# Đặt kiểu mẫu cho biểu đồ\nsns.set_style(\"dark\")\n# Vẽ biểu đồ phân tán 2 chiều\nax = sns.scatterplot(\n    data=df_train_pca,\n    x='x',  # Trục x là thành phần chính thứ nhất\n    y='y',  # Trục y là thành phần chính thứ hai\n    hue='target',  # Đặt màu sắc dựa trên nhãn\n    palette='Blues',  # Sử dụng bảng màu xanh lam\n)\n# Đặt tiêu đề và nhãn cho biểu đồ\nax.set(\n    title='Chiếu 2 chiều của PCA',\n    xlabel='Thành phần 1',\n    ylabel='Thành phần 2'\n)\n# Vẽ đường đứng và ngang qua gốc tọa độ\nax.axhline(y=0, linestyle='--', color='#FFBF00', zorder=-1)\nax.axvline(x=0, linestyle='--', color='#FFBF00', zorder=-1)\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.290129Z","iopub.status.idle":"2024-04-23T02:48:50.290960Z","shell.execute_reply.started":"2024-04-23T02:48:50.290702Z","shell.execute_reply":"2024-04-23T02:48:50.290738Z"},"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, ba thành phần chính được trực quan hóa\n\n# Đặt kiểu mẫu cho biểu đồ\nsns.set_style(\"white\")\n# Vẽ biểu đồ phân tán 3 chiều bằng plotly\nfig = px.scatter_3d(\n    data_frame=df_train_pca,\n    x='x',  # Trục x là thành phần chính thứ nhất\n    y='y',  # Trục y là thành phần chính thứ hai\n    z='z',  # Trục z là thành phần chính thứ ba\n    labels={\n        'x': 'Thành phần 1',\n        'y': 'Thành phần 2',\n        'z': 'Thành phần 3'\n    },\n    color='target',  # Đặt màu sắc dựa trên nhãn\n    title='Chiếu Ba Chiều của PCA',  # Đặt tiêu đề\n    color_continuous_scale='blues',  # Sử dụng bảng màu xanh lam\n    template='ggplot2'  # Sử dụng mẫu ggplot2\n)\n\n# Hiển thị biểu đồ\nfig.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.292771Z","iopub.status.idle":"2024-04-23T02:48:50.293229Z","shell.execute_reply.started":"2024-04-23T02:48:50.293011Z","shell.execute_reply":"2024-04-23T02:48:50.293042Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Bây giờ, hãy áp dụng inverse_transform để tính toán\n# ước lượng của các hình ảnh dựa trên 10 thành phần chính.\n# Chúng ta có thể thấy rằng chỉ với 10 thành phần chính, chúng ta\n# truyền tải một phần lớn thông tin của mỗi hình ảnh.\n\n# Trích xuất đối tượng PCA từ pipeline\npca_object = pca.named_steps['pca']\n\n# Tạo một hình với 10 hàng và 2 cột\nfig, axs = plt.subplots(nrows=10, ncols=2, figsize=(6,20))\n\n# Lặp qua mỗi lớp (từ 0 đến 9)\nfor i in range(10):\n    # Lấy chỉ mục của một mẫu từ lớp hiện tại\n    idx = np.where(y_train==i)[0][0]\n    \n    # Trực quan hóa hình ảnh gốc\n    original_img = axs[i, 0].imshow(\n        X=X_train[idx].reshape(28, 28),\n        cmap='Blues'\n    )\n    original_img.axes.get_yaxis().set_visible(False)\n    original_img.axes.get_xaxis().set_visible(False)\n    original_img.axes.set_title(f'Gốc [{i}]:')\n    \n    # Tính toán và trực quan hóa hình ảnh ước lượng\n    approx_img_ = pca_object.inverse_transform(X=X_train_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'Ước lượng [{i}]:')\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.294639Z","iopub.status.idle":"2024-04-23T02:48:50.295094Z","shell.execute_reply.started":"2024-04-23T02:48:50.294854Z","shell.execute_reply":"2024-04-23T02:48:50.294871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chứng minh rằng các thành phần PCA không tương quan\n# (Tuyên bố này đúng theo định nghĩa và xây dựng\n# của thuật toán Phân Tích Thành Phần Chính)\n\n# Tính ma trận tương quan giữa các thành phần PCA\ncorr = np.corrcoef(x=X_train_pca, rowvar=False)\ncorr = np.around(corr, decimals=2)\n\n# Vẽ biểu đồ heatmap của ma trận tương quan\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# Đặt tiêu đề và nhãn cho biểu đồ\n_ = ax.set(\n    title='Tương Quan Các Thành Phần PCA [Không Tương Quan]',\n    xlabel='Các Thành Phần',\n    ylabel='Các Thành Phần'\n)\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.296450Z","iopub.status.idle":"2024-04-23T02:48:50.296940Z","shell.execute_reply.started":"2024-04-23T02:48:50.296732Z","shell.execute_reply":"2024-04-23T02:48:50.296750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Phần 4 : KNN**","metadata":{}},{"cell_type":"markdown","source":"Thuậ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>gần gũi</b> để thực hiện 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ụ thể. Dự đoán: Một nhãn lớp được gán dựa trên một <b>phiếu bầu đa số</b>, nhãn mà được đại diện nhiều 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ình chọn kết quả từ 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\n\n\n\n\n","metadata":{}},{"cell_type":"code","source":"# Tạo pipeline cho KNN với MinMaxScaler, PCA và KNeighborsClassifier\nknn_clf = Pipeline(steps=[\n    ('scaler', MinMaxScaler()),  # Chuẩn hóa dữ liệu\n    ('pca', PCA(n_components=10)),  # Phân tích thành phần chính với 10 thành phần chính\n    ('knn', KNeighborsClassifier())  # Mô hình phân loại K-Nearest Neighbors\n])\n\n# Đặt các tham số mô hình KNN để tinh chỉnh\nknn_clf_params = {\n    'knn__n_neighbors': [2, 4, 8, 16, 32]  # Số lượng láng giềng gần nhất\n}\n\n# Tạo GridSearchCV để tìm kiếm siêu tham số tốt nhất cho mô hình\nknn_clf_gs = GridSearchCV(\n    estimator=knn_clf,  # Mô hình KNN đã xây dựng\n    param_grid=knn_clf_params,  # Siêu tham số cần tìm kiếm\n    scoring='accuracy',  # Sử dụng độ chính xác để đánh giá mô hình\n    n_jobs=-1,  # Sử dụng tất cả các lõi CPU để tính toán\n    cv=5,  # Sử dụng phương pháp cross-validation 5-fold\n    verbose=1,  # In thông tin về quá trình tìm kiếm siêu tham số\n    return_train_score=True  # Trả về kết quả độ chính xác trên tập huấn luyện\n)\n\n# Huấn luyện mô hình trên dữ liệu huấn luyện\nknn_clf_gs.fit(X=X_train, y=y_train)\n\n# In ra các siêu tham số tốt nhất và độ chính xác tương ứng\nprint('Các siêu tham số tốt nhất cho PCA+KNN:', knn_clf_gs.best_params_)\nprint('Độ chính xác tốt nhất của PCA+KNN trên tập validation:', knn_clf_gs.best_score_)\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.298028Z","iopub.status.idle":"2024-04-23T02:48:50.298439Z","shell.execute_reply.started":"2024-04-23T02:48:50.298247Z","shell.execute_reply":"2024-04-23T02:48:50.298264Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Vẽ đồ thị độ chính xác trên tập huấn luyện và tập validation\n# cho mỗi giá trị siêu tham số được kiểm tra trong Grid Search.\n# Đồ thị này cho phép chúng ta kiểm tra xem có quá mức của mô hình không.\n\n# Tạo một hình mới và trục để vẽ đồ thị\nfig, ax = plt.subplots()\n\n# Vẽ đường cong độ chính xác trên tập huấn luyện\nax.plot(\n    range(len(knn_clf_params['knn__n_neighbors'])), \n    knn_clf_gs.cv_results_['mean_train_score'],\n    label='Huấn luyện',\n    color='blue'\n)\n# Vẽ đường cong độ chính xác trên tập validation\nax.plot(\n    range(len(knn_clf_params['knn__n_neighbors'])),\n    knn_clf_gs.cv_results_['mean_test_score'],\n    label='Kiểm tra',\n    color='orange'\n)\n# Đặt các chỉ mục của trục x và nhãn của chúng\nax.set_xticks(\n    ticks=range(len(knn_clf_params['knn__n_neighbors'])),\n    labels=knn_clf_params['knn__n_neighbors']\n)\n\n# Đánh dấu dọc dòng đứng ở vị trí của siêu tham số tốt nhất\nii = knn_clf_params['knn__n_neighbors'].index(\n    knn_clf_gs.best_params_['knn__n_neighbors']\n)\nplt.axvline(x=ii, linestyle='--', color='red', label='Tốt nhất', alpha=0.3)\n\n# Thêm chú thích và đặt tiêu đề của đồ thị\nplt.legend()\nplt.xlabel(\"Giá trị 'n_neighbors'\")\nplt.ylabel('Độ chính xác')\nplt.title(\"Kết quả GridSearchCV PCA+KNN\")\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.299759Z","iopub.status.idle":"2024-04-23T02:48:50.300209Z","shell.execute_reply.started":"2024-04-23T02:48:50.299966Z","shell.execute_reply":"2024-04-23T02:48:50.299982Z"},"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\n# Lấy mô hình tốt nhất từ kết quả GridSearchCV\nbest_cv_clf = knn_clf_gs.best_estimator_\n# Dự đoán nhãn của dữ liệu huấn luyện\ny_pred = best_cv_clf.predict(X=X_train)\n# Tính ma trận nhầm lẫn\nconf_matrix = confusion_matrix(y_true=y_train, y_pred=y_pred)\n\n# Hiển thị ma trận nhầm lẫn\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.302101Z","iopub.status.idle":"2024-04-23T02:48:50.302504Z","shell.execute_reply.started":"2024-04-23T02:48:50.302310Z","shell.execute_reply":"2024-04-23T02:48:50.302327Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tính toán 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\n# Sử dụng hàm classification_report để tính toán báo cáo phân loại\nclf_report = classification_report(\n    y_true=y_train,  # Nhãn thực tế\n    y_pred=y_pred,   # Nhãn dự đoán\n    labels=range(10),  # Các nhãn của dữ liệu\n)\n\n# In ra báo cáo phân loại\nprint(clf_report)\n","metadata":{"execution":{"iopub.status.busy":"2024-04-23T02:48:50.304245Z","iopub.status.idle":"2024-04-23T02:48:50.304666Z","shell.execute_reply.started":"2024-04-23T02:48:50.304454Z","shell.execute_reply":"2024-04-23T02:48:50.304471Z"},"trusted":true},"execution_count":null,"outputs":[]}]}