{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":30588,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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":"b0093541-188e-43e9-acb2-d917ae3a5010","_cell_guid":"cadbfcb2-82c3-470f-bad6-eade54542125","trusted":true}},{"cell_type":"markdown","source":"# MNIST Handwritten Digits Recognition using PCA and KNN\nGiới thiệu\nThe MNIST database (Modified National Institute of Standards and Technology database) là một cơ sở dữ liệu lớn chứa các chữ số viết tay được sử dụng phổ biến để huấn luyện các hệ thống xử lý ả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.\n\nHình ảnh gốc màu đen trắng đã được chuẩn hóa để vừa với một ô giới hạn 28x28 pixel và được làm mờ cạnh (anti-aliased), điều này đã giới thiệu các mức xám. Cơ sở dữ liệu MNIST gốc chứa 60.000 hình ảnh huấn luyện và 10.000 hình ảnh kiểm tra, nhưng trong cuộc thi này, chúng ta được cung cấp 42.000 hình ảnh huấn luyện (70%) và 18.000 hình ảnh kiểm tra (30%).\n\nCác kỹ thuật tiên tiến nhất để tiếp cận bất kỳ vấn đề Phân loại Ảnh nào thường phụ thuộc vào Mạng Nơ-ron Tích chập (Convolutional Neural Networks). 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 tệp này chúng tôi sẽ áp dụng các Thuật toán Học Máy đơn giản (và chúng tôi sẽ có được độ chính xác khá!).\n\nĐầu tiên, chúng tôi sẽ thực hiện Phân tích thành phần chính (Principal Component Analysis - PCA) để giảm chiều dữ liệu (từ 784 giá trị pixel xuống còn 10 thành phần chính). Sau đó, chúng tôi sẽ chạy thuật toán Học Máy lười biếng yêu thích của chúng tôi: K-Nearest Neighbors. Kết hợp này đạt được Độ chính xác Kiểm tra là 0.9271, điều đó khá đáng chú ý!!!\n\nBảng nội dung:\n\n## Đọc & Khám phá Dữ liệu:\n\n1. Kiểm tra xem có giá trị thiếu không.\n1. Kiểm tra phạm vi của các giá trị pixel.\n1. Trực quan hóa dữ liệu (Hình ảnh).\n1. Kiểm tra xem mục tiêu (target) có cân bằng không.\n\n## PCA:\n\n* Giới thiệu thuật toán PCA.\n* Tiền xử lý (chuẩn hóa) dữ liệu.\n* Thực hiện thuật toán PCA bằng thư viện Sklearn.\n* Nhúng các module vào một Sklearn Pipeline.\n* Vẽ biểu đồ phương sai được giải thích của các thành phần.\n* Trực quan hóa hai chiều của dữ liệu bằng seaborn.\n* Trực quan hóa động ba chiều của dữ liệu bằng plotly.\n* Trực quan hóa phép biến đổi nghịch đảo.\n* Kiểm tra các thành phần có tương quan không.\n\n## KNN Classifier:\n* Xây dựng một Bộ phân loại KNN bằng Sklearn.\n* Sử dụng kỹ thuật PCA như một công cụ trích xuất đặc trưng cho KNN.\n* Chạy Grid-Search Cross-Validation để tối ưu hóa siêu tham số n_neighbors.\n* So sánh các chỉ số đo huấn luyện và kiểm tra để kiểm tra quá mức (overfitting).\n* Vẽ ma trận nhầm lẫn (Confusion Matrix).\n* Tính toán Báo cáo phân loại (Classification Report).","metadata":{"_uuid":"85d40f0d-34d6-4e02-90f3-7ef7a60e5573","_cell_guid":"ddf0a964-5157-492a-9699-253bc2caec79","trusted":true}},{"cell_type":"markdown","source":"\nĐầu tiên, chúng ta cần nhập các thư viện Python cần thiết để thực hiện phân tích và xây dựng mô hình. Dưới đây là các thư viện được sử dụng:\n\n1.  Đọc & Khám phá Dữ liệu:\n    - numpy: Thư viện Python cho tính toán khoa học.\n    - pandas: Thư viện Python cung cấp các cấu trúc dữ liệu và công cụ phân tích dữ liệu dễ sử dụng.\n    - seaborn: Thư viện Python dựa trên matplotlib cho trực quan hóa dữ liệu thống kê.\n2.  Principal Component Analysis (PCA):\n    - PCA: Thư viện PCA từ scikit-learn để thực hiện Phân tích thành phần chính.\n    - Pipeline: Chức năng Pipeline từ scikit-learn để kết hợp nhiều bước xử lý dữ liệu thành một quy trình.\n    - K-Nearest Neighbor Classifier (KNN):\n3.  KNeighborsClassifier: Bộ phân loại KNN từ scikit-learn để xây dựng mô hình K-Nearest Neighbors.\n    - GridSearchCV: Chức năng GridSearchCV từ scikit-learn để tìm kiếm siêu tham số tối ưu cho mô hình.\n    - classification_report: Chức năng từ scikit-learn để tạo báo cáo phân loại.\n    - confusion_matrix: Chức năng từ scikit-learn để tính ma trận nhầm lẫn.\n    - ConfusionMatrixDisplay: Chức năng từ scikit-learn để hiển thị ma trận nhầm lẫn.\n4.  VHT\n    - rotate: Hàm rotate từ scipy.ndimage để xoay hình ảnh.","metadata":{}},{"cell_type":"code","source":"## IMPORT THE NECESSARY PYTHON LIBRARIES\n\n# Read & Explore Data\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.preprocessing import MinMaxScaler\n\n# Principal Component Analysis (PCA)\nfrom sklearn.decomposition import PCA\nfrom sklearn.pipeline import Pipeline\nimport plotly.express as px\n\n# K-Nearest Neighbor Classifier (KNN)\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.model_selection import GridSearchCV\nfrom sklearn.metrics import classification_report, \\\n    confusion_matrix, ConfusionMatrixDisplay \n\n# VHT\nfrom scipy.ndimage import rotate","metadata":{"_uuid":"c382c9dc-2109-4ce6-b779-dc8c447da240","_cell_guid":"e169ec97-1f64-46cd-8099-1d09c757dccc","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:10.621717Z","iopub.execute_input":"2024-06-08T15:10:10.622455Z","iopub.status.idle":"2024-06-08T15:10:10.628952Z","shell.execute_reply.started":"2024-06-08T15:10:10.622422Z","shell.execute_reply":"2024-06-08T15:10:10.627932Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\nHàm func_array1d_rotate2d nhận vào một mảng 1D Narray1d, gồm N vectơ hình ảnh 1D được biểu diễn dưới dạng hàng, mỗi hàng đại diện cho một hình ảnh có kích thước sizePixel x sizePixel. Hàm sẽ xoay mỗi hình ảnh trong Narray1d một góc rotate_angle và trả về một mảng mới Narray1d_rotate có cùng kích thước như Narray1d, nhưng các hình ảnh trong đó đã được xoay.\n\n* Tham số:\n    * Narray1d: Mảng 1D chứa N vectơ hình ảnh 1D.\n* rotate_angle: Góc xoay hình ảnh (đơn vị: độ).\n    * sizePixel: Kích thước của mỗi hình ảnh (chiều dài và chiều rộng).\n* Trả về:\n    * Narray1d_rotate: Mảng 1D chứa các vectơ hình ảnh đã được xoay.\n* Lưu ý:\n    * Hàm này sử dụng hàm rotate từ thư viện scipy.ndimage để xoay hình ả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":"b362968d-9828-4257-92ec-b9e4e49a9cb0","_cell_guid":"8ace7936-3ee9-4fe9-b71e-8d7e3b7eee15","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:10.631185Z","iopub.execute_input":"2024-06-08T15:10:10.631483Z","iopub.status.idle":"2024-06-08T15:10:10.643972Z","shell.execute_reply.started":"2024-06-08T15:10:10.631458Z","shell.execute_reply":"2024-06-08T15:10:10.642912Z"},"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":"a7abf7cf-fbfb-44f8-a5eb-77885d1283fb","_cell_guid":"1e5c33e8-406f-4da5-929d-034f87443736","trusted":true}},{"cell_type":"markdown","source":"Trong phần này, chúng ta sẽ sử dụng thư viện Pandas Python nổi tiếng để đọc dữ liệu từ các file .csv.\n\nChúng ta sẽ trực quan hóa phân phối của các giá trị pixel (bằng histogram) bằng cách sử dụng các thư viện Seaborn và Matplotlib của Python. Chúng ta cũng sẽ kiểm tra xem có giá trị nào bị thiếu không, và các lớp mục tiêu có cân bằng không.\n\nCuối cùng, chúng ta sẽ trực quan hóa các hình ảnh bằng cách vẽ một hình ảnh từ mỗi lớp.\n\nPhần dưới đây sử dụng thư viện Pandas để đọc dữ liệu từ các tệp .csv.\n* df_train: Biến này lưu trữ dữ liệu huấn luyện được đọc từ tệp train.csv.\n* df_test: Biến này lưu trữ dữ liệu kiểm tra được đọc từ tệp test.csv.","metadata":{"_uuid":"d4c82fac-2a10-4c14-a95a-653bd16872cd","_cell_guid":"deaebcb3-115c-4e6b-b843-4522382a052d","trusted":true}},{"cell_type":"code","source":"# Read the data files using Pandas\ndf_train = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\ndf_test =  pd.read_csv('/kaggle/input/digit-recognizer/test.csv')","metadata":{"_uuid":"4da57b46-cc50-4ae9-9495-d0dbeb6be5ee","_cell_guid":"8967621a-0c55-49ff-b6e2-05d9efdeedf2","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:10.645079Z","iopub.execute_input":"2024-06-08T15:10:10.645367Z","iopub.status.idle":"2024-06-08T15:10:14.920631Z","shell.execute_reply.started":"2024-06-08T15:10:10.645342Z","shell.execute_reply":"2024-06-08T15:10:14.919781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Trong đoạn mã này, chúng ta kiểm tra kích thước của hai DataFrame chứa dữ liệu huấn luyện và kiểm tra bằng cách sử dụng phương thức shape của Pandas.\nKết quả của mã trên là:\n* DataFrame df_train chứa dữ liệu huấn luyện với kích thước là **42000** hàng và **785** cột.\n* DataFrame df_test chứa dữ liệu kiểm tra với kích thước là **28000** hàng và **784** cột.","metadata":{}},{"cell_type":"code","source":"# Check the size of the train and test DataFrames\nprint(\"df_train shape:\", df_train.shape)  # 70%\nprint(\"df_test shape: \", df_test.shape)  # 30%","metadata":{"_uuid":"5465a268-9ec4-4482-a0d7-e531d25f4917","_cell_guid":"da0e04b7-5ce3-48ec-be64-cee70c71af07","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:14.921921Z","iopub.execute_input":"2024-06-08T15:10:14.922247Z","iopub.status.idle":"2024-06-08T15:10:14.927619Z","shell.execute_reply.started":"2024-06-08T15:10:14.922219Z","shell.execute_reply":"2024-06-08T15:10:14.926591Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\nMay mắn thay, không có giá trị thiếu trong dữ liệu của chúng ta.\nKết quả:\n* Giá trị thiếu trong df_train:  0\n* Giá trị thiếu trong df_test:  0\n* Không có giá trị nào bị thiếu trong DataFrame df_train.\n* Không có giá trị nào bị thiếu trong DataFrame df_test.\n\nĐiều này có nghĩa là chúng ta có đủ dữ liệu để tiến hành huấn luyện và kiểm tra mô hình mà không cần phải xử lý các giá trị thiếu.","metadata":{}},{"cell_type":"code","source":"# Fortunately there are no missing values in our data\nprint(\n    \"Missing values in df_train: \", \\\n    df_train.isna().sum().sum()\n)\nprint(\n    \"Missing values in df_test: \", \\\n    df_test.isna().sum().sum()\n)","metadata":{"_uuid":"f8324800-d007-4d21-9eb6-39bba50ce548","_cell_guid":"758f94a2-69c7-4c9b-b1a3-22cb0f0b1a43","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:14.930760Z","iopub.execute_input":"2024-06-08T15:10:14.931080Z","iopub.status.idle":"2024-06-08T15:10:14.999018Z","shell.execute_reply.started":"2024-06-08T15:10:14.931053Z","shell.execute_reply":"2024-06-08T15:10:14.998039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's have a look at the first five training rows\ndf_train.head()","metadata":{"_uuid":"b1a7f2dc-6cec-4610-ba22-6ad5a7fb06ac","_cell_guid":"f1a8c252-31ba-46a1-b25c-c6ffff3fa2a9","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:15.000374Z","iopub.execute_input":"2024-06-08T15:10:15.000786Z","iopub.status.idle":"2024-06-08T15:10:15.020714Z","shell.execute_reply.started":"2024-06-08T15:10:15.000750Z","shell.execute_reply":"2024-06-08T15:10:15.019733Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Chúng ta có thể thấy rằng cột đầu tiên trong dữ liệu huấn luyện là nhãn (label), và các cột còn lại đại diện cho 784 pixel của mỗi hình ảnh xám có kích thước 28x28.\n\nĐể tiếp tục, chúng ta sẽ tách dữ liệu huấn luyện thành hai phần:\n* X_train: chứa các giá trị pixel của các hình ảnh.\n* y_train: chứa nhãn tương ứng với mỗi hình ảnh.","metadata":{}},{"cell_type":"code","source":"# We can see that the first colum is the label,\n# and the rest of the columns are the 784 pixels\n# that represent 28x28 gray-scale images.\n# Let's separate X and Y\n\nX_train = df_train.drop(columns=['label']).values\ny_train = df_train['label'].values","metadata":{"_uuid":"48085649-c225-4e39-824f-82474ea004ff","_cell_guid":"939c1008-c85e-4ee0-bdab-bcccc4a252e4","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:15.022372Z","iopub.execute_input":"2024-06-08T15:10:15.022833Z","iopub.status.idle":"2024-06-08T15:10:15.109223Z","shell.execute_reply.started":"2024-06-08T15:10:15.022795Z","shell.execute_reply":"2024-06-08T15:10:15.108328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Kiểm tra phân phối của các giá trị pixel\nChúng ta sẽ trực quan hóa phân phối của các giá trị pixel bằng một biểu đồ histogram.\n\nGiải thích kết quả\nTrong biểu đồ này, chúng ta có thể thấy rằng:\n - Giá trị 0 đại diện cho nền (background)\n - Các giá trị >0 đại diện cho các chữ số trong các hình ảnh\n \nsns.histplot(x=X_train.flatten(), color='#4895EE'): Tạo biểu đồ histogram của các giá trị pixel trong dữ liệu huấn luyện. X_train.flatten() được sử dụng để làm phẳng ma trận thành một mảng 1D để dễ dàng vẽ histogram. Màu của biểu đồ được đặt là #4895EE.\n \nax.set(title='Biểu đồ Phân phối Giá trị Pixel', xlabel='Giá trị Pixel'): Đặt tiêu đề cho biểu đồ histogram và gán nhãn cho trục x.\n\nBên dưới là một lời giải thích kết quả của biểu đồ:\nTrong biểu đồ này, chúng ta có thể thấy rằng:\n\nGiá trị 0 đại diện cho nền (background).\nCác giá trị lớn hơn 0 đại diện cho các chữ số trong các hình ảnh.","metadata":{}},{"cell_type":"code","source":"# Let's visualize the distribution of the pixel values\n\nax = sns.histplot(x=X_train.flatten(), color='#4895EE')\n_ = ax.set(title='Pixel Value Histogram', xlabel='Pixel Value')\n\n# As we can see, the 0 value represents the background\n# and the >0 values represent the digits in the images","metadata":{"_uuid":"f724afe4-d098-4f65-aaca-e97a096b8a32","_cell_guid":"36ec9986-72eb-4f04-abf8-84e2df3e3bfa","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:15.110711Z","iopub.execute_input":"2024-06-08T15:10:15.111139Z","iopub.status.idle":"2024-06-08T15:10:29.687416Z","shell.execute_reply.started":"2024-06-08T15:10:15.111100Z","shell.execute_reply":"2024-06-08T15:10:29.686105Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\nKiểm tra xem các nhãn mục tiêu có cân bằng không.\nĐiều này có nghĩa là mười danh mục trong nhãn mục tiêu\ncó sự đại diện gần như bằng nhau trong dữ liệu.\n\nsns.countplot(x=y_train, color='#4895EE'): Tạo một biểu đồ cột đếm số lượng mẫu cho mỗi nhãn mục tiêu trong y_train. Màu của cột được đặt là #4895EE.\n\nax.set(title='Phân phối của nhãn mục tiêu có cân bằng không? Có!', xlabel='Nhãn'): Đặt tiêu đề cho biểu đồ và gán nhãn cho trục x.\n\nax.axhline(y=4200, color='#12086F', linestyle='--'): Vẽ một đường ngang ở mức 4200 để biểu thị số lượng mẫu mong đợi cho mỗi lớp trong trường hợp phân phối cân bằng.\n\nDưới dòng này là một lời giải thích:\nVì các lớp mục tiêu được cân bằng, chúng ta không cần phải lo lắng về các phương pháp lấy mẫu và các yếu tố thiên vị trong các mô hình của chúng ta.","metadata":{}},{"cell_type":"code","source":"# Let's check that the target is balanced.\n# It means that the ten categories in the target\n# have roughly the same representation in the data.\n\nax = sns.countplot(x=y_train, color='#4895EE')\nax.set(\n    title='Target distribution is balanced? Yes!',\n    xlabel='Label'\n)\n# The expected number of samples for a balanced \n# class distribution is 42000/10 = 4200\n_ = ax.axhline(y=4200, color='#12086F', linestyle='--')\n\n# Since the target classes are balances, we don't have\n# worry about sampling methods and biases in our models.","metadata":{"_uuid":"cd488e69-2d9d-4209-ac52-f11e13ab443b","_cell_guid":"d681064d-2dbe-4c2b-8075-9ce2c254d755","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:29.689186Z","iopub.execute_input":"2024-06-08T15:10:29.689612Z","iopub.status.idle":"2024-06-08T15:10:30.133893Z","shell.execute_reply.started":"2024-06-08T15:10:29.689574Z","shell.execute_reply":"2024-06-08T15:10:30.132786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Next, let's visualize one image from each class.\n# We will use the matplotlib 'imshow' function.\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":"9d6c909c-609f-4b5d-a907-2d75a3c17401","_cell_guid":"1fa8f38f-f989-4007-86c5-590612877157","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:30.135362Z","iopub.execute_input":"2024-06-08T15:10:30.135735Z","iopub.status.idle":"2024-06-08T15:10:30.593265Z","shell.execute_reply.started":"2024-06-08T15:10:30.135705Z","shell.execute_reply":"2024-06-08T15:10:30.592293Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# transform data","metadata":{"_uuid":"dd9d9d94-7ea8-4f8d-aa91-866959d703e3","_cell_guid":"48cf8b51-1a3e-4d0e-94e5-da53b3aa7c90","trusted":true}},{"cell_type":"markdown","source":"\n## Phần này thực hiện việc xoay ảnh trong tập dữ liệu huấn luyện và tạo ra các phiên bản xoay khác nhau của từng ảnh.\n\n1. Khai báo thông số xoay:\n\nnumRotate_prior: Số lượng phép xoay được áp dụng cho mỗi ảnh.\nangleOffset_prior: Góc bắt đầu của phạm vi xoay.\nangleRange_prior: Phạm vi xoay.\n\n2. Xác định các ảnh cần được xoay:\n\nX_train_pick2rotate['index']: Chỉ mục của các ảnh trong tập dữ liệu huấn luyện mà chúng ta muốn xoay.\nX_train_pick2rotate['offset']: Số lượng độ xoay được áp dụng cho mỗi loại chữ số.\n\n3. Áp dụng xoay cho từng ảnh:\n\nChúng ta sẽ tạo một mảng X_sample_rotate để lưu trữ các phiên bản xoay của từng ảnh.\nSử dụng hàm func_array1d_rotate2d để xoay từng ảnh.\n\n4. Chuẩn hóa giá trị pixel về khoảng [0, 1]:\n\nSử dụng MinMaxScaler để chuẩn hóa giá trị pixel về khoảng [0, 255].\n\n5. Trực quan hóa một ảnh từ mỗi lớp sau khi xoay:\n\nDùng matplotlib để vẽ một ảnh từ mỗi lớp sau khi xoay.\n\n## Đoạn mã này thực hiện các bước sau:\n\nKhai báo thông số và tạo danh sách các ảnh cần xoay: \n\nĐầu tiên, chúng ta khai báo các thông số như số lượng phép xoay, phạm vi góc xoay, và chỉ mục của các ảnh cần xoay.\nÁp dụng xoay cho từng ảnh: Chúng ta sử dụng hàm func_array1d_rotate2d để xoay từng ảnh trong tập dữ liệu huấn luyện.\n\nChuẩn hóa giá trị pixel về khoảng [0, 1]: Sau khi xoay, chúng ta chuẩn hóa giá trị pixel của các ảnh xoay về khoảng [0, 255].\n\nTrực quan hóa một ảnh từ mỗi lớp sau khi xoay: Cuối cùng, chúng ta trực quan hóa một ảnh từ mỗi lớp sau khi xoay bằng cách sử dụng matplotlib.","metadata":{}},{"cell_type":"code","source":"# -------------------------------------------------- declare\nnumRotate_prior = 10\n\nangleOffset_prior = -40\nangleRange_prior = 80\n# numRotate_prior = 1\n\n# angleOffset_prior = 0\n# angleRange_prior = 90\n\n# Định nghĩa các chỉ số và góc quay cho từng nhãn\nX_train_pick2rotate = {\n    'index': np.array([i for i in range(2300, 2399 + 1)]),\n    'offset': np.array([\n\n        [-90, 40, 0, 30, 40, 5, -10, 37, 10, 30], \n    [60, 60, 10, 35, 30, 55, 0, 30, 30, 40],\n    [-100, 15, 10, 20, 20, 43, -30, 10, 30, 20],\n    [25, 30, 0, -10, 45, 45, 0, 20, 20, 45], \n    [65, 40, 38, 20, 7, 15, 10, 10, 25, 55],\n    [50, 30, 10, -5, 50, 20, -20, 20, -10, 40],\n    [45, 5, 23, 40, 60, 50, -10, 48, 45, 47],\n    [-78, 45, 40, 0, 15, 55, -5, 10, 10, 40], \n    [50, 30, 40, -8, 60, 20, 280, 30, 23, 50],\n    [65, 20, 20, 0, 20, 20, -50, 30, 42, 44],\n        \n    [67, 20, 20, 45, 40, 20, -10, 45, 5, 40], \n    [55, 20, 0, -10, 50, 45, 0, 50, 30, 30],\n    [55, 55, 45, 10, 20, 38, 0, 40, 45, 40],\n    [10, 45, 30, 30, 30, 45, -20, 20, 40, 45], \n    [80, 40, 18, 20, 60, 20, 0, 40, 45, 30],\n    [10, 45, -10, 20, 30, 25, 40, 20, 12, 35],\n    [-10, 53, 45, 35, 35, 45, -30, 30, 30, 30],\n    [20, 20, 30, 20, 20, 40, -10, 37, 10, 40], \n    [60, 45, 40, -5, 45, 28, -10, 45, 17, 48],\n    [80, 25, 26, 10, 50, 60, 0, 40, 0, 45],\n        \n    [-18, 45, 0, 20, 0, 40, 0, 25, 10, 45], \n    [-60, 49, 22, 45, 30, 40, -40, 45, 20, 32],\n    [45, 15, 20, 5, 53, 50, -50, 40, 20, 20],\n    [49, 43, 10, 0, 47, 20, -10, 30, 42, 50], \n    [20, 49, 20, 45, 44, 45, -10, 42, 48, 45],\n    [160, 30, 10, 10, 20, 20, -10, 40, 10, 45],\n    [49, 30, 27, 20, 50, 10, 0, 20, 30, 10],\n    [-45, 45, 20, 40, 42, 10, -10, 40, 18, 33], \n    [80, 50, 10, -10, 40, 30, 0, 40, 40, 30],\n    [45, 40, 40, -7, 40, 10, 0, 40, 47, 49],\n        \n    [50, 35, 35, 10, 30, 40, -10, 40, 45, 30], \n    [45, 45, 40, 40, 45, 30, 0, 0, 0, 30],\n    [20, 20, 30, 40, 40, 30, 0, 30, 20, 40],\n    [45, 20, 20, 40, 40, 10, 0, 30, 20, 40], \n    [45, 45, 0, 30, 40, 10, 0, 40, 20, 20],\n    [20, 28, 0, 20, 40, 30, 0, 40, 0, 40],\n    [37, 20, 40, 0, 40, 10, 0, 20, 20, 30],\n    [40, 40, 20, 20, 40, 40, 0, 0, 20, 30], \n    [50, 30, 20, 10, 20, 30, 0, 20, 10, 20],\n    [0, 40, 40, 40, 40, 40, 0, 0, 0, 40],\n        \n    [45, 10, 20, 20, 20, 40, -20, 45, 10, 40], \n    [45, 40, 40, 40, 30, 20, -10, 10, 40, 50],\n    [45, 20, 0, 30, 30, 40, 0, 40, 20, 50],\n    [40, 40, 0, 30, 40, 30, -10, 30, 30, 30], \n    [45, 20, 0, 40, 20, 20, 0, 10, 30, 30],\n    [20, 20, 30, 40, 30, 40, 0, 45, 40, 40],\n    [50, 45, 10, -10, 40, 30, -40, 20, 40, 30],\n    [20, 20, 20, 20, 30, 30, 0, 20, 40, 0], \n    [45, 10, 40, 40, 30, 40, 0, 40, 0, 20],\n    [0, 10, 0, 20, 30, 20, 0, 30, 30, 40],\n        \n    [90, 45, 20, 30, 40, 40, 0, 0, 10, 50], \n    [75, 30, 40, 10, 40, 40, 0, 40, 40, 40],\n    [45, 5, 10, 30, 40, 40, -10, 30, 40, 45],\n    [30, 40, 30, 20, 30, 20, -10, 40, 40, 50], \n    [90, 30, 10, 5, 40, 40, 0, 40, 40, 20],\n    [45, 55, 30, -20, 55, 40, 0, 45, 0, 40],\n    [75, 35, 20, 25, 40, 20, 0, 40, 20, 50],\n    [40, 45, 20, 40, 70, 20, 0, 35, 0, 25], \n    [70, 30, 30, 0, 30, 40, -10, 40, 45, 45],\n    [90, 45, 45, 45, 55, 30, 0, 30, 50, 30],\n        \n    [45, 45, 45, 45, 45, 45, 0, 30, 20, 45], \n    [45, 60, 45, 20, 35, 40, -20, 45, 0, 40],\n    [20, 20, 30, 30, 50, 0, 0, 40, 0, 20],\n    [40, 30, 20, 40, 20, 40, -20, 40, 40, 30], \n    [45, 30, 40, 10, 60, 40, -10, 20, 10, 30],\n    [90, 10, 30, 30, 40, 40, 0, 20, 40, 30],\n    [85, 20, 40, 0, 20, 40, -10, 40, 20, 30],\n    [30, 30, 30, 30, 20, 30, 0, 30, 50, 0], \n    [40, 40, 40, 20, 40, 40, -20, 30, 40, 0],\n    [30, 50, 10, 30, 40, 30, 0, 40, 50, 40],\n        \n    [45, 45, 35, 10, 60, 30, 0, 30, 20, 60], \n    [50, 30, 45, 45, 30, 40, 0, 45, 60, 60],\n    [90, 45, 20, 30, 30, 40, 0, 0, 40, 30],\n    [35, 30, 30, 40, 40, 40, 0, 40, 45, 30], \n    [65, 30, 20, 40, 35, 20, -20, 30, 35, 30],\n    [45, 0, 10, 40, 50, 20, 0, 40, 30, 15],\n    [0, 25, 10, -10, 40, 40, 0, 40, 40, 20],\n    [40, 60, 0, 40, 20, 30, 0, 40, 30, 30], \n    [0, 20, 10, 30, 40, 40, -30, 20, 40, 20],\n    [45, 55, 35, 10, 35, 30, 0, 30, 40, 50],\n    \n    [90, 35, 10, 40, 10, 30, -35, 40, 30, 30], \n    [90, 20, 5, 0, 10, 25, -10, 40, 30, 25],\n    [20, 20, 20, 30, 45, 40, -40, 40, 10, 30],\n    [-90, 45, 20, 15, 25, 40, 0, 30, 20, 30], \n    [70, 30, 40, 30, 30, 30, 0, 30, 20, 0],\n    [70, 20, 10, 20, 30, 45, 0, 40, 10, 20],\n    [35, 30, 40, 40, 40, 20, 0, 40, 40, 20],\n    [45, 20, 10, 10, 40, 20, 0, 40, 10, 20], \n    [20, 50, 40, 10, 15, 40, -10, 40, 30, 60],\n    [0, 25, 20, 20, 40, 40, -30, 20, 20, 50],\n    \n    [70, 20, 40, 10, 40, 30, -30, 40, 20, 0], \n    [75, 40, 40, 0, 5, 30, -20, 45, 30, 40],\n    [45, 40, 30, 30, 10, 30, -20, 40, 20, 40],\n    [10, 40, 20, 10, 20, 10, 0, 40, 20, 30], \n    [65, 40, 20, 30, 10, 40, -20, 40, 55, 20],\n    [20, 35, 40, 40, 50, 10, -20, 40, 20, 10],\n    [30, 40, 0, 30, 10, 30, 0, 40, 20, 0],\n    [70, 50, 40, 20, 20, 30, 0, 40, 20, 20], \n    [55, 40, 30, 0, 30, 30, 0, 40, 40, 30],\n    [50, 40, 30, 20, 40, 20, -40, 40, 50, 40],\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 * numRotate_prior + k1\n                \n                angleRotate = (k1/numRotate_prior * angleRange_prior) + angleOffset_prior + X_train_pick2rotate['offset'][k2][k0]\n                \n                X_sample_rotate[k0,k1,k2,:] = func_array1d_rotate2d(X_train[idx][np.newaxis,:],angleRotate, 28)\n\n                \nX_sample_rotate[X_sample_rotate < 10] = 0\n#X_sample_rotate[X_sample_rotate > 300] = 0\n\n# Let's normalize the pixel values to the interval [0,1]\n# Formula: 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# Next, let's visualize one image from each class.\n# We will use the matplotlib 'imshow' function.\n\n# for k2 in range(numPickRotate):\nfor k2 in range(5,10):\n    k1 = 0\n    # k1 = numRotate_prior - 1\n\n    fig, axs = plt.subplots(nrows=2, ncols=5, figsize=(10,5))\n\n    for k0 in range(10):\n        img = axs[int(k0 >= 5), k0%5].imshow(\n            X=X_sample_rotate[k0,k1,k2,:].reshape(28, 28),\n            cmap='Blues'\n        )\n        img.axes.get_yaxis().set_visible(False)\n        img.axes.get_xaxis().set_visible(False)\n        img.axes.set_title(f'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":"2fd8cd6b-8db8-45dc-b5f7-6721c80f11fd","_cell_guid":"6b163e1f-7fee-43a6-b6f6-40c0d402a4fc","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:30.595234Z","iopub.execute_input":"2024-06-08T15:10:30.595689Z","iopub.status.idle":"2024-06-08T15:10:37.407972Z","shell.execute_reply.started":"2024-06-08T15:10:30.595638Z","shell.execute_reply":"2024-06-08T15:10:37.406978Z"},"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":"f67ae6c7-11f8-4b88-a23e-bf62fb9fdb43","_cell_guid":"6e9093b4-f586-4545-b99e-b59d492b7958","trusted":true}},{"cell_type":"markdown","source":"Phân tích thành phần chính (PCA) là một kỹ thuật thống kê được sử dụng để giảm số chiều của một tập dữ liệu. Điều này được thực hiện bằng cách biến đổi tuyến tính dữ liệu thành một hệ tọa độ mới, trong đó (hầu hết) biến thiên của dữ liệu có thể được mô tả bằng ít chiều hơn so với dữ liệu ban đầu.\n\nTrong PCA, thành phần chính đầu tiên được xác định như một hướng mà tối đa hóa phương sai của dữ liệu được chiếu. Các thành phần chính tiếp theo được chọn là các hướng vuông góc với các thành phần chính trước đó, nhưng lại tối đa hóa phương sai của dữ liệu được chiếu.\n\nTrong thư viện sklearn, module sklearn.decomposition.PCA cung cấp các công cụ cho việc giảm chiều dữ liệu bằng cách sử dụng phương pháp phân tích giá trị riêng để chiếu dữ liệu vào một không gian chiều thấp hơn.\n\nỞ đây, chúng ta sẽ sử dụng 10 thành phần chính hàng đầu, giải thích gần 50% phương sai trong dữ liệu. Điều này có nghĩa là chúng ta có thể diễn giải 50% phương sai của dữ liệu với một không gian đặc trưng nhỏ hơn hầu hết gần như 98% so với ban đầu (10 đặc trưng so với 784 đặc trưng).","metadata":{"_uuid":"c9145741-6e7a-4419-bdb0-baca414ed4e7","_cell_guid":"54390ba7-75cb-4867-b577-97f23c85d6f2","trusted":true}},{"cell_type":"markdown","source":"\nTrước khi áp dụng MinMax Scaler:\n\nGiá trị tối thiểu: 0\nGiá trị tối đa: 255\n\nSau khi áp dụng MinMax Scaler:\n\nGiá trị tối thiểu:0.0\nGiá trị tối đa: 1.0\n\nỞ đây, chúng ta đã sử dụng MinMax Scaler để chuẩn hóa giá trị pixel vào khoảng [0,1]. Công thức được sử dụng là: MinMaxscale(X) = (X - X_min) / (X_max - X_min). Điều này giúp đảm bảo rằng tất cả các giá trị pixel nằm trong phạm vi này, giúp mô hình học tốt hơn và ổn định hơn.","metadata":{}},{"cell_type":"code","source":"# Let's normalize the pixel values to the interval [0,1]\n# Formula: 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":"aad53bf6-9b1f-4451-844d-ae51f509a68f","_cell_guid":"2f528db2-3b92-425b-a18c-2168dd8862d7","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:37.409516Z","iopub.execute_input":"2024-06-08T15:10:37.409831Z","iopub.status.idle":"2024-06-08T15:10:37.552692Z","shell.execute_reply.started":"2024-06-08T15:10:37.409804Z","shell.execute_reply":"2024-06-08T15:10:37.551576Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Trong phần này, chúng ta sẽ nhúng hai module MinMaxScaler và PCA vào một Sklearn Pipeline để chuẩn hóa dữ liệu và giảm chiều dữ liệu xuống còn 10 thành phần chính.\nKết quả:\n\nX_train_pca shape: (100, 10)\n\nTrong đoạn mã này, chúng ta đã thực hiện hai bước chính:\n\nChuẩn hóa dữ liệu: Sử dụng MinMaxScaler để chuẩn hóa dữ liệu vào khoảng [0,1].\n\nGiảm chiều dữ liệu: Sử dụng PCA để giảm số chiều dữ liệu xuống còn 10 thành phần chính, giữ lại 10 thành phần chính quan trọng nhất của dữ liệu.\nKết quả cuối cùng là X_train_rotate_pca có hình dạng là (100, 10), với mỗi mẫu dữ liệu được biểu diễn bằng 10 thành phần chính. Điều này giúp giảm chiều dữ liệu và tăng tốc độ tính toán trong quá trình huấn luyện mô hình.","metadata":{}},{"cell_type":"code","source":"# Actually, we will embed the MinMaxScaler and the PCA\n# modules into a Sklearn Pipeline.\n\n# pca = Pipeline(steps=[\n#     ('scaler', MinMaxScaler()),\n#     ('pca', PCA(n_components=40))\n# ])\n\npca = Pipeline(steps=[\n    ('scaler', MinMaxScaler()),\n    ('pca', PCA(n_components=23))\n])\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":"6cae5170-3af3-4ed9-a9d8-372ef224e9ba","_cell_guid":"e423b865-ef1d-4c48-9152-917f8b258060","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:37.554115Z","iopub.execute_input":"2024-06-08T15:10:37.554443Z","iopub.status.idle":"2024-06-08T15:10:39.075964Z","shell.execute_reply.started":"2024-06-08T15:10:37.554413Z","shell.execute_reply":"2024-06-08T15:10:39.074935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Trong phần này, chúng ta sẽ trích xuất tỷ lệ phương sai được giải thích bởi từng thành phần chính và tính tổng tích luỹ của chúng.","metadata":{}},{"cell_type":"code","source":"# Let's extract the explained variance ratio\n# And let's compute their cumulative sum\n# pca_object = pca.named_steps['pca']\n# var_ratio = pd.Series(\n#     data=pca_object.explained_variance_ratio_,\n#     index=range(1, 41)\n# )\npca_object = pca.named_steps['pca']\nvar_ratio = pd.Series(\n    data=pca_object.explained_variance_ratio_,\n    index=range(1, 24)\n)\n\ncumsum_var_ratio = var_ratio.cumsum()","metadata":{"_uuid":"68b1ffab-f2de-43fe-a655-e04c6060ec60","_cell_guid":"c3cdf97b-6136-45e0-94c6-bdf23e750bf0","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:39.090864Z","iopub.execute_input":"2024-06-08T15:10:39.093146Z","iopub.status.idle":"2024-06-08T15:10:39.106399Z","shell.execute_reply.started":"2024-06-08T15:10:39.093097Z","shell.execute_reply":"2024-06-08T15:10:39.104894Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Chúng ta sẽ vẽ biểu đồ tỷ lệ phương sai được giải thích cho mỗi thành phần chính (trái) và biểu đồ tổng tích luỹ của chúng (phải). Kết quả là hai biểu đồ, biểu thị tỷ lệ phương sai được giải thích và tổng tích luỹ của chúng cho mỗi thành phần chính. Điều này giúp chúng ta hiểu được phần trăm phương sai mà mỗi thành phần chính giải thích, và tổng phương sai giải thích bởi tất cả các thành phần chính.","metadata":{}},{"cell_type":"code","source":"# Plot the explained variance ratio for each component (left)\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":"9ea36679-af0c-40f1-bda2-641ef9c73a4e","_cell_guid":"6fe44c61-1ac9-4157-968d-022d42184ff6","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:39.109351Z","iopub.execute_input":"2024-06-08T15:10:39.111367Z","iopub.status.idle":"2024-06-08T15:10:40.428620Z","shell.execute_reply.started":"2024-06-08T15:10:39.111299Z","shell.execute_reply":"2024-06-08T15:10:40.427556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's put the components in Pandas DataFrame format\n# This way we can use the seaborn and plotly libraries\n# to visualize the data.\n# (these packages work better with Pandas DataFrames)\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":"9796e066-c6af-49c5-af8f-8ebde5e07430","_cell_guid":"4fb22220-cc87-410b-a637-55bc57791987","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:40.430270Z","iopub.execute_input":"2024-06-08T15:10:40.430718Z","iopub.status.idle":"2024-06-08T15:10:40.447402Z","shell.execute_reply.started":"2024-06-08T15:10:40.430680Z","shell.execute_reply":"2024-06-08T15:10:40.446386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Two-dimensional visualization of the data\n# Here the Top2 principal components are plotted\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":"30135dee-d7d0-4dc8-88f7-3a88a135ad5a","_cell_guid":"147fd5ee-e632-4611-b6de-35d64c6946e9","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:40.449071Z","iopub.execute_input":"2024-06-08T15:10:40.449453Z","iopub.status.idle":"2024-06-08T15:10:42.104266Z","shell.execute_reply.started":"2024-06-08T15:10:40.449417Z","shell.execute_reply":"2024-06-08T15:10:42.103121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now, let's apply the inverse_transform to compute\n# the approximation of images based on the 10 principal\n# compontents.\n# We can see that with just 10 components we convey a large\n# part of the information of each image.\n\npca_object = pca.named_steps['pca']\n\nfig, axs = plt.subplots(nrows=10, ncols=2, figsize=(6,20))\n\nfor i in range(10):\n    idx = np.where(y_train==i)[0][0]\n    original_img = axs[i, 0].imshow(\n        X=X_train_rotate[idx].reshape(28, 28),\n        cmap='Blues'\n    )\n    original_img.axes.get_yaxis().set_visible(False)\n    original_img.axes.get_xaxis().set_visible(False)\n    original_img.axes.set_title(f'Original [{i}]:')\n    \n    approx_img_ = pca_object.inverse_transform(X=X_train_rotate_pca[idx])\n    approx_img = axs[i, 1].imshow(\n        X=approx_img_.reshape(28, 28),\n        cmap='Blues'\n    )\n    approx_img.axes.get_yaxis().set_visible(False)\n    approx_img.axes.get_xaxis().set_visible(False)\n    approx_img.axes.set_title(f'Approximation [{i}]:')","metadata":{"_uuid":"77146579-275e-4359-8ec9-7d175da30220","_cell_guid":"329c95b3-2406-402c-b1f8-38487c4fa2f3","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:42.105500Z","iopub.execute_input":"2024-06-08T15:10:42.105828Z","iopub.status.idle":"2024-06-08T15:10:43.158083Z","shell.execute_reply.started":"2024-06-08T15:10:42.105801Z","shell.execute_reply":"2024-06-08T15:10:43.157176Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Show that the PCA components are uncorrelated\n# (This statement is true by definition and construction\n# of the Principal Component Analysis algorithm)\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":"8c3fa8bb-61ab-43dc-bc7e-e5fecc13e6e2","_cell_guid":"ab321270-3628-423b-8fd6-a7ebaf6ccfe5","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:43.159398Z","iopub.execute_input":"2024-06-08T15:10:43.159777Z","iopub.status.idle":"2024-06-08T15:10:45.293765Z","shell.execute_reply.started":"2024-06-08T15:10:43.159745Z","shell.execute_reply":"2024-06-08T15:10:45.292703Z"},"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":"3d69b1a4-faf3-4405-b740-9d5fc82b8424","_cell_guid":"16cefed3-22ad-4c84-ad87-230ed47df34d","trusted":true}},{"cell_type":"markdown","source":"Phân tích thành phần chính (PCA) là một kỹ thuật thống kê được sử dụng để giảm số chiều của một tập dữ liệu. Điều này được thực hiện bằng cách biến đổi tuyến tính dữ liệu thành một hệ tọa độ mới, trong đó (hầu hết) biến thiên của dữ liệu có thể được mô tả bằng ít chiều hơn so với dữ liệu ban đầu.\n\nTrong PCA, thành phần chính đầu tiên được xác định như một hướng mà tối đa hóa phương sai của dữ liệu được chiếu. Các thành phần chính tiếp theo được chọn là các hướng vuông góc với các thành phần chính trước đó, nhưng lại tối đa hóa phương sai của dữ liệu được chiếu.\n\nTrong thư viện `sklearn`, module `sklearn.decomposition.PCA` cung cấp các công cụ cho việc giảm chiều dữ liệu bằng cách sử dụng phương pháp phân tích giá trị riêng để chiếu dữ liệu vào một không gian chiều thấp hơn.\n\nỞ đây, chúng ta sẽ sử dụng 10 thành phần chính hàng đầu, giải thích gần 50% phương sai trong dữ liệu. Điều này có nghĩa là chúng ta có thể diễn giải 50% phương sai của dữ liệu với một không gian đặc trưng nhỏ hơn hầu hết gần như 98% so với ban đầu (10 đặc trưng so với 784 đặc trưng).\n\nTrong đoạn mã trên, chúng ta đã thực hiện việc xây dựng một mô hình phân loại sử dụng kết hợp giữa phân tích thành phần chính (PCA) và mô hình K-Nearest Neighbors (KNN).\n\nĐầu tiên, chúng ta đã tạo một pipeline bao gồm ba bước chính. Đầu tiên là chuẩn hóa dữ liệu sử dụng MinMaxScaler, sau đó là giảm chiều dữ liệu xuống còn 10 chiều bằng phương pháp PCA, và cuối cùng là sử dụng mô hình KNN để phân loại dữ liệu.\n\nSau đó, chúng ta đã thiết lập một grid search cross-validation để tìm kiếm các siêu tham số tốt nhất cho mô hình PCA + KNN. Trong trường hợp này, chúng ta tìm kiếm số lượng láng giềng tốt nhất cho mô hình KNN.\n\nCuối cùng, chúng ta đã huấn luyện mô hình trên tập dữ liệu huấn luyện sau khi đã được áp dụng kỹ thuật xoay và giảm chiều bằng PCA. Kết quả được in ra bao gồm các tham số tốt nhất và độ chính xác tốt nhất mà mô hình PCA + KNN đã đạt được trên tập cross-validation.","metadata":{"_uuid":"1abd82c5-f4e1-4142-acb2-bea45e42488e","_cell_guid":"b9e0603d-4dc8-4761-baad-a34cf6709975","trusted":true}},{"cell_type":"markdown","source":"---\n#  mô hình học máy trên tập dữ liệu Train","metadata":{"_uuid":"334a6d45-a6b2-499e-a95b-2477800b968f","_cell_guid":"e1b5a3d3-ced9-409c-b6f9-b07e7143bc66","trusted":true}},{"cell_type":"code","source":"# knn_clf = Pipeline(steps=[\n#     ('scaler', MinMaxScaler()),\n#     ('pca', PCA(n_components=10)),\n#     ('knn', KNeighborsClassifier())\n# ])\n\n# knn_clf_params = {\n#     'knn__n_neighbors': [2, 4, 8, 16, 32]\n# }\n\n# knn_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\n# knn_clf_gs.fit(X=X_train_rotate_pca, y=y_train_rotate)\n\n# print('best PCA+KNN params:', knn_clf_gs.best_params_)\n# print('best PCA+KNN cv accuracy:', knn_clf_gs.best_score_)\n\nknn_clf = Pipeline(steps=[\n    ('scaler', MinMaxScaler()),\n    ('pca', PCA(n_components=23)),\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":"7154a244-1406-4e55-9062-2a876ffcfb42","_cell_guid":"f8b23485-498b-446c-83a4-fdf1e50b8285","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:45.295097Z","iopub.execute_input":"2024-06-08T15:10:45.295399Z","iopub.status.idle":"2024-06-08T15:10:56.316416Z","shell.execute_reply.started":"2024-06-08T15:10:45.295374Z","shell.execute_reply":"2024-06-08T15:10:56.314490Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the Training and Validation Accuracies\n# for each Hyperparamter value tested during the\n# Grid Search.\n# This plot allows us to check for overfitting.\n\nfig, ax = plt.subplots()\n\nax.plot(\n    range(len(knn_clf_params['knn__n_neighbors'])), \n    knn_clf_gs.cv_results_['mean_train_score'],\n    label='train',\n    color='blue'\n)\nax.plot(\n    range(len(knn_clf_params['knn__n_neighbors'])),\n    knn_clf_gs.cv_results_['mean_test_score'],\n    label='test',\n    color='orange'\n)\nax.set_xticks(\n    ticks=range(len(knn_clf_params['knn__n_neighbors'])),\n    labels=knn_clf_params['knn__n_neighbors']\n)\n\nii = knn_clf_params['knn__n_neighbors'].index(\n    knn_clf_gs.best_params_['knn__n_neighbors']\n)\n\nplt.axvline(x=ii, linestyle='--', color='red', label='best', alpha=0.3)\nplt.legend()\nplt.xlabel(\"'n_neighbors' vals\")\nplt.ylabel('Accuracy')\nplt.title(\"PCA+KNN GridSearchCV Results\")\nplt.show()","metadata":{"_uuid":"43e1d701-a89a-463f-b860-3afbf14bbf36","_cell_guid":"b1316942-7e1c-4f06-8742-d3eb8f7918c3","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:56.325750Z","iopub.execute_input":"2024-06-08T15:10:56.330459Z","iopub.status.idle":"2024-06-08T15:10:56.773395Z","shell.execute_reply.started":"2024-06-08T15:10:56.330375Z","shell.execute_reply":"2024-06-08T15:10:56.772337Z"},"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":"b609f283-8a5c-477c-9c29-941acfedc9aa","_cell_guid":"5d9eb764-a90a-48b5-b962-be1b347e4925","trusted":true}},{"cell_type":"markdown","source":"\nTrong đoạn mã trên, chúng ta đã tính toán và hiển thị ma trận nhầm lẫn.\n\nĐầu tiên, chúng ta sử dụng mô hình tốt nhất được lựa chọn thông qua quá trình Grid Search Cross-Validation (best_cv_clf) để dự đoán nhãn cho toàn bộ tập dữ liệu huấn luyện đã được áp dụng kỹ thuật xoay và giảm chiều bằng PCA (X_train_rotate_pca).Sau đó, chúng ta tính toán ma trận nhầm lẫn bằng cách so sánh nhãn dự đoán với nhãn thực tế (y_train_rotate).Cuối cùng, chúng ta sử dụng ConfusionMatrixDisplay từ thư viện sklearn.metrics để hiển thị ma trận nhầm lẫn.\n","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score\n\n# Compute and display the Confusion Matrix\n# Note: the entire training dataset is used here\n\nX_val = X_train_rotate_pca\nY_val = y_train_rotate\n\nbest_cv_clf = knn_clf_gs.best_estimator_\ny_pred = best_cv_clf.predict(X=X_val)\n\ny_val_number = Y_val // numRotate_prior\ny_pred_number = y_pred // numRotate_prior\n\ny_val_angle = (Y_val % numRotate_prior)\ny_pred_angle = (y_pred % numRotate_prior)\n\nprint('KNN number + angle, Acc({:.2f}%)', accuracy_score(Y_val, y_pred))\nprint('KNN number, Acc({:.2f}%)', accuracy_score(y_val_number, y_pred_number))\nprint('KNN angle, Acc({:.2f}%)', accuracy_score(y_val_angle, y_pred_angle))\n\n# print('y_val_number =', y_val_number)\n# print('y_pred_number =', y_pred_number)\n\ny_val_angle_degree = y_val_angle/numRotate_prior * angleRange_prior + angleOffset_prior\ny_pred_angle_degree = y_pred_angle/numRotate_prior * angleRange_prior + angleOffset_prior\n\n# print('y_val_angle_degree =', y_val_angle_degree)\n# print('y_pred_angle_degree =', y_pred_angle_degree)\n\nconf_matrix = confusion_matrix(y_val_number, y_pred_number)\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')\n\nconf_matrix = confusion_matrix(y_val_angle, y_pred_angle)\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')","metadata":{"_uuid":"62d7493a-d5c0-42ca-ad56-df419c3367ee","_cell_guid":"fdfdc4ce-7aa6-4c8a-b7c9-8a069cb98054","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:56.775040Z","iopub.execute_input":"2024-06-08T15:10:56.775476Z","iopub.status.idle":"2024-06-08T15:10:58.950698Z","shell.execute_reply.started":"2024-06-08T15:10:56.775438Z","shell.execute_reply":"2024-06-08T15:10:58.949711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Trong phần này, chúng ta sẽ tính toán và hiển thị ma trận nhầm lẫn (Confusion Matrix) cùng với báo cáo phân loại (Classification Report) để đánh giá hiệu suất của mô hình phân loại. Để làm điều này, chúng ta sử dụng toàn bộ tập dữ liệu huấn luyện.\n\nTính và hiển thị ma trận nhầm lẫn:\nĐầu tiên, chúng ta sử dụng mô hình tốt nhất được lựa chọn thông qua Grid Search Cross-Validation để dự đoán nhãn cho toàn bộ tập dữ liệu huấn luyện sau khi áp dụng kỹ thuật giảm chiều bằng PCA. Sau đó, chúng ta tính toán ma trận nhầm lẫn bằng cách so sánh nhãn dự đoán với nhãn thực tế. Sử dụng ConfusionMatrixDisplay từ thư viện sklearn.metrics để hiển thị ma trận nhầm lẫn.\n\nTính toán và hiển thị báo cáo phân loại:\nSử dụng hàm classification_report từ thư viện sklearn.metrics để tính toán báo cáo phân loại dựa trên nhãn dự đoán và nhãn thực tế trên toàn bộ tập dữ liệu huấn luyện. In ra báo cáo phân loại để đánh giá hiệu suất của mô hình trên từng lớp.","metadata":{}},{"cell_type":"code","source":"X_val = pca.transform(X_train)\nY_val = y_train\n\nbest_cv_clf = knn_clf_gs.best_estimator_\ny_pred = best_cv_clf.predict(X=X_val)\n\ny_pred_number = y_pred\ny_val_number = Y_val\n\nprint('KNN number, Acc({:.2f}%)', accuracy_score(y_val_number, y_pred_number))\n\nconf_matrix = confusion_matrix(y_val_number, y_pred_number)\nconf_matrix_disp = ConfusionMatrixDisplay(conf_matrix)\nconf_matrix_disp.plot(cmap='Blues')\n\ny_pred_angle = (y_pred % numRotate_prior)\n\ny_pred_angle_degree = y_pred_angle/numRotate_prior * angleRange_prior + angleOffset_prior\n\nprint('y_pred_angle_degree =', y_pred_angle_degree)","metadata":{"_uuid":"da419e38-d545-49ee-a8b6-32f3fa9760c3","_cell_guid":"fa5652c2-d596-4880-8396-b6e77177f4ff","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:10:58.952175Z","iopub.execute_input":"2024-06-08T15:10:58.952496Z","iopub.status.idle":"2024-06-08T15:11:27.307484Z","shell.execute_reply.started":"2024-06-08T15:10:58.952468Z","shell.execute_reply":"2024-06-08T15:11:27.306425Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now, let's apply the inverse_transform to compute\n# the approximation of images based on the 10 principal\n# compontents.\n# We can see that with just 10 components we convey a large\n# part of the information of each image.\n\npca_object = pca.named_steps['pca']\n\nfig, axs = plt.subplots(nrows=10, ncols=2, figsize=(6,20))\n\nfor i in range(10):\n    idx = np.where(y_train == i )[0][0]\n    original_img = axs[i, 0].imshow(\n        X=X_train[idx].reshape(28, 28),\n        cmap='Blues'\n    )\n    original_img.axes.get_yaxis().set_visible(False)\n    original_img.axes.get_xaxis().set_visible(False)\n    original_img.axes.set_title(f'Original [{i}]:')\n    \n    approx_img_ = pca_object.inverse_transform(X=X_val[idx])\n    approx_img = axs[i, 1].imshow(\n        X=approx_img_.reshape(28, 28),\n        cmap='Blues'\n    )\n    approx_img.axes.get_yaxis().set_visible(False)\n    approx_img.axes.get_xaxis().set_visible(False)\n    approx_img.axes.set_title(f'Predicted angle [{y_pred_angle_degree[idx]}]:')","metadata":{"_uuid":"bc4e4c18-00b8-4441-900b-83786685d798","_cell_guid":"1393d26a-3a87-4a77-a642-c5b68c94fec3","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:11:27.308858Z","iopub.execute_input":"2024-06-08T15:11:27.309193Z","iopub.status.idle":"2024-06-08T15:11:28.307600Z","shell.execute_reply.started":"2024-06-08T15:11:27.309166Z","shell.execute_reply":"2024-06-08T15:11:28.306517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"text_cell_render border-box-sizing rendered_html\">\n<div style=\"color:white;\n           display:fill;\n           border-radius:5px;\n           background-color:gray;\n           font-size:110%;\n           font-family:Verdana;\n           letter-spacing:0.5px\">\n<h1 style=\"text-align: center;\n           padding: 10px;\n              color:white\">\nLight CNN Classifier\n</h1>\n</div>\n</div>","metadata":{"_uuid":"a1446365-443d-48e8-aa24-514805f2df61","_cell_guid":"f6dca012-b643-484b-8f63-b0e218c6f1e2","trusted":true}},{"cell_type":"markdown","source":"<img src=\"https://taewanmerepo.github.io/2018/01/cnn/head.png\">","metadata":{"_uuid":"5a238d9f-7fbe-4d2a-85c7-b38f1793fe47","_cell_guid":"d606a8da-01ba-4650-b234-842037898d97","trusted":true}},{"cell_type":"markdown","source":"### CNN is a very efficient ways for computer vision tasks. \na convolutional neural network (CNN, or ConvNet) is a class of Artificial Neural Network (ANN), most commonly applied to analyze visual imagery. They are also known as Shift Invariant or Space Invariant Artificial Neural Networks (SIANN), based on the shared-weight architecture of the convolution kernels or filters that slide along input features and provide translation equivariant responses known as feature maps. Counter-intuitively, most convolutional neural networks are only equivariant, as opposed to invariant, to translation. They have applications in image and video recognition, recommender systems, image classification, image segmentation, medical image analysis, natural language processing, brain-computer interfaces, and financial time series.","metadata":{"_uuid":"3f62053b-0364-4ff6-bb7b-5deba2a02280","_cell_guid":"1c349770-f9d8-4f7f-8efb-d87b402f2f10","trusted":true}},{"cell_type":"code","source":"import plotly.express as px\nfrom plotly.offline import init_notebook_mode, iplot\nimport plotly.graph_objects as go\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2024-06-08T15:11:28.308833Z","iopub.execute_input":"2024-06-08T15:11:28.309127Z","iopub.status.idle":"2024-06-08T15:11:28.314319Z","shell.execute_reply.started":"2024-06-08T15:11:28.309102Z","shell.execute_reply":"2024-06-08T15:11:28.313266Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_confusion_matrix(cm, labels, title):\n    # cm : confusion matrix list(list)\n    # labels : name of the data list(str)\n    # title : title for the heatmap\n    data = go.Heatmap(z=cm, y=labels, x=labels)\n    annotations = []\n    for i, row in enumerate(cm):\n        for j, value in enumerate(row):\n            annotations.append(\n                {\n                    \"x\": labels[i],\n                    \"y\": labels[j],\n                    \"font\": {\"color\": \"white\"},\n                    \"text\": str(value),\n                    \"xref\": \"x1\",\n                    \"yref\": \"y1\",\n                    \"showarrow\": False\n                }\n            )\n    layout = {\n        \"title\": title,\n        \"xaxis\": {\"title\": \"Predicted value\"},\n        \"yaxis\": {\"title\": \"Real value\"},\n        \"annotations\": annotations\n    }\n    fig = go.Figure(data=data, layout=layout)\n    return fig","metadata":{"execution":{"iopub.status.busy":"2024-06-08T15:11:28.315627Z","iopub.execute_input":"2024-06-08T15:11:28.315998Z","iopub.status.idle":"2024-06-08T15:11:28.326650Z","shell.execute_reply.started":"2024-06-08T15:11:28.315969Z","shell.execute_reply":"2024-06-08T15:11:28.325727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from tensorflow.keras.utils import np_utils\nfrom tensorflow.keras import utils\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping\nfrom tensorflow.keras import datasets, layers, models","metadata":{"_uuid":"17fc4707-d986-4950-b0fe-ac152932ac00","_cell_guid":"0d0efe69-4e03-4204-86d8-3ee2e5125830","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:11:28.327996Z","iopub.execute_input":"2024-06-08T15:11:28.328357Z","iopub.status.idle":"2024-06-08T15:11:28.341962Z","shell.execute_reply.started":"2024-06-08T15:11:28.328293Z","shell.execute_reply":"2024-06-08T15:11:28.341045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train_cnn = X_train_rotate.reshape(-1,28,28,1)\n# X_val = np.array(pca.transform(X_train)).reshape(-1,28,28,1)\ny_train_cnn = utils.to_categorical(np.array(y_train_rotate// numRotate_prior))\nX_val = X_train.reshape(-1,28,28,1)\nY_val = utils.to_categorical(np.array(y_train))","metadata":{"_uuid":"598e24f0-4665-4486-ba5d-fb550022bc96","_cell_guid":"b20ca6f3-b288-42fe-8210-9e804c27268e","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:11:28.343261Z","iopub.execute_input":"2024-06-08T15:11:28.343595Z","iopub.status.idle":"2024-06-08T15:11:28.355682Z","shell.execute_reply.started":"2024-06-08T15:11:28.343568Z","shell.execute_reply":"2024-06-08T15:11:28.354614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train_cnn.shape","metadata":{"execution":{"iopub.status.busy":"2024-06-08T15:11:28.356987Z","iopub.execute_input":"2024-06-08T15:11:28.357778Z","iopub.status.idle":"2024-06-08T15:11:28.367472Z","shell.execute_reply.started":"2024-06-08T15:11:28.357749Z","shell.execute_reply":"2024-06-08T15:11:28.366556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# y_train_rotate// numRotate_prior","metadata":{"execution":{"iopub.status.busy":"2024-06-08T15:11:28.368760Z","iopub.execute_input":"2024-06-08T15:11:28.369100Z","iopub.status.idle":"2024-06-08T15:11:28.382523Z","shell.execute_reply.started":"2024-06-08T15:11:28.369064Z","shell.execute_reply":"2024-06-08T15:11:28.381333Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# X_train_rotate = np.array(X_train_rotate).reshape(-1,28,28,1)\n# # X_val = np.array(pca.transform(X_train)).reshape(-1,28,28,1)\n# X_val = np.array(X_test_crop).reshape(-1,28,28,1)\n# y_train_rotate = utils.to_categorical(np.array(y_train_rotate))\n# Y_val = utils.to_categorical(np.array(y_test_crop))","metadata":{"_uuid":"f978abbd-867f-46a5-b6ec-35f64e740f64","_cell_guid":"9d27fd4b-f9a8-4c46-a856-7e8970246998","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:11:28.384111Z","iopub.execute_input":"2024-06-08T15:11:28.384531Z","iopub.status.idle":"2024-06-08T15:11:28.393758Z","shell.execute_reply.started":"2024-06-08T15:11:28.384490Z","shell.execute_reply":"2024-06-08T15:11:28.392865Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model = models.Sequential()\n# model.add(layers.Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', \n#                  activation ='relu', input_shape = (28,28,1)))\n# model.add(layers.Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', \n#                  activation ='relu'))\n# model.add(layers.MaxPool2D(pool_size=(2,2)))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Dropout(0.25))\n# model.add(layers.Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', \n#                  activation ='relu'))\n# model.add(layers.Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', \n#                  activation ='relu'))\n# model.add(layers.MaxPool2D(pool_size=(2,2), strides=(2,2)))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Dropout(0.25))\n# model.add(layers.Flatten())\n# model.add(layers.Dense(256, activation = \"relu\"))\n# model.add(layers.Dropout(0.5))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Dense(10, activation = \"softmax\"))\n\n# model.summary()\nmodel = models.Sequential()\n\nmodel.add(layers.Conv2D(filters = 16, kernel_size = (3, 3), activation='relu',\n                 input_shape = (28, 28, 1)))\nmodel.add(layers.BatchNormalization())\nmodel.add(layers.Conv2D(filters = 16, kernel_size = (3, 3), activation='relu'))\nmodel.add(layers.BatchNormalization())\n#model.add(Conv2D(filters = 16, kernel_size = (3, 3), activation='relu'))\n#model.add(BatchNormalization())\nmodel.add(layers.MaxPool2D(strides=(2,2)))\nmodel.add(layers.Dropout(0.25))\n\nmodel.add(layers.Conv2D(filters = 32, kernel_size = (3, 3), activation='relu'))\nmodel.add(layers.BatchNormalization())\nmodel.add(layers.Conv2D(filters = 32, kernel_size = (3, 3), activation='relu'))\nmodel.add(layers.BatchNormalization())\n#model.add(Conv2D(filters = 32, kernel_size = (3, 3), activation='relu'))\n#model.add(BatchNormalization())\nmodel.add(layers.MaxPool2D(strides=(2,2)))\nmodel.add(layers.Dropout(0.25))\n\nmodel.add(layers.Flatten())\nmodel.add(layers.Dense(512, activation='relu'))\nmodel.add(layers.Dropout(0.25))\nmodel.add(layers.Dense(1024, activation='relu'))\nmodel.add(layers.Dropout(0.5))\nmodel.add(layers.Dense(10, activation='softmax'))\nmodel.summary()\n# model = models.Sequential()\n\n# model.add(layers.Conv2D(32, kernel_size = 3, activation='relu', input_shape = (28, 28, 1)))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Conv2D(32, kernel_size = 3, activation='relu'))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Conv2D(32, kernel_size = 5, strides=2, padding='same', activation='relu'))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Dropout(0.4))\n\n# model.add(layers.Conv2D(64, kernel_size = 3, activation='relu'))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Conv2D(64, kernel_size = 3, activation='relu'))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Conv2D(64, kernel_size = 5, strides=2, padding='same', activation='relu'))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Dropout(0.4))\n\n# model.add(layers.Conv2D(128, kernel_size = 4, activation='relu'))\n# model.add(layers.BatchNormalization())\n# model.add(layers.Flatten())\n# model.add(layers.Dropout(0.4))\n# model.add(layers.Dense(10, activation='softmax'))\n# model.summary()\n","metadata":{"_uuid":"d30da279-3261-4e97-9b79-6e2a01d703d4","_cell_guid":"43ead369-05c5-4e2d-960c-d5773b8a7f7d","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:11:28.395221Z","iopub.execute_input":"2024-06-08T15:11:28.395561Z","iopub.status.idle":"2024-06-08T15:11:28.626036Z","shell.execute_reply.started":"2024-06-08T15:11:28.395533Z","shell.execute_reply":"2024-06-08T15:11:28.623721Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator\nfrom keras.callbacks import LearningRateScheduler\nfrom keras.optimizers import Adam\nimport tensorflow as tf\nmodel_path = './best_model.h5'\n\ncheckpointer = ModelCheckpoint(\n    filepath=model_path,\n    monitor='val_accuracy',\n    verbose=1,\n    mode='max',\n    save_best_only=True)\n\ndatagen = ImageDataGenerator(\n        rotation_range=10,  \n        zoom_range = 0.10,  \n        width_shift_range=0.1, \n        height_shift_range=0.1)\n\n# # model.optimizer.get_config()\n# datagen = ImageDataGenerator(zoom_range = 0.1,\n#                             height_shift_range = 0.1,\n#                             width_shift_range = 0.1,\n#                             rotation_range = 10)\nannealer = LearningRateScheduler(lambda x: 1e-3 * 0.9 ** x)\n\n\nmodel.compile(loss='categorical_crossentropy', optimizer = Adam(lr=1e-4), metrics=[\"accuracy\"])\nhistory = model.fit(\n    datagen.flow(X_train_cnn, y_train_cnn, batch_size=32),\n    steps_per_epoch=len(X_train_cnn) // 32,\n    epochs=50,  # Increase this when not on Kaggle kernel\n    verbose=1,  # 1 for ETA, 0 for silent\n    validation_data=(X_val, Y_val),  # For speed\n    callbacks=[annealer, checkpointer]\n)\n\n# opt =  tf.keras.optimizers.RMSprop(learning_rate=0.001)\n# model.compile(\n#     loss='categorical_crossentropy',\n#     optimizer=opt,\n#     metrics=['accuracy']\n# )\n# history = model.fit(X_train_cnn, y_train_cnn, validation_data = (X_val, Y_val), epochs=50, batch_size = 128, callbacks=[checkpointer])\n","metadata":{"execution":{"iopub.status.busy":"2024-06-08T15:11:28.627314Z","iopub.execute_input":"2024-06-08T15:11:28.627602Z","iopub.status.idle":"2024-06-08T15:18:12.986825Z","shell.execute_reply.started":"2024-06-08T15:11:28.627577Z","shell.execute_reply":"2024-06-08T15:18:12.985789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model_path = './best_model.h5'\n# checkpointer = ModelCheckpoint(\n#     filepath=model_path,\n#     monitor='val_accuracy',\n#     verbose=1,\n#     mode='max',\n#     save_best_only=True)\n\n# LRP = callbacks.ReduceLROnPlateau(\n#     monitor = 'val_loss',\n#     factor = 0.1,\n#     patience = 5,\n#     verbose = 1\n# )\n# # opt = optimizers.Adam(learning_rate = 0.0001)\n# opt =  tf.keras.optimizers.RMSprop(learning_rate=0.001)\n# model.compile(\n#     loss='categorical_crossentropy',\n#     optimizer=opt,\n#     metrics=['accuracy']\n# )\n","metadata":{"_uuid":"d131aa49-918d-43dd-be19-62d3110ca17a","_cell_guid":"db889d42-b8bb-4667-8f07-b831a3fbcf38","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:18:12.988268Z","iopub.execute_input":"2024-06-08T15:18:12.988538Z","iopub.status.idle":"2024-06-08T15:18:12.993629Z","shell.execute_reply.started":"2024-06-08T15:18:12.988516Z","shell.execute_reply":"2024-06-08T15:18:12.992504Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # # history = model.fit(X_train_cnn, y_train_cnn, validation_data = (X_val, Y_val), epochs=50, batch_size = 128, callbacks=[checkpointer])\n# batch_size = 128\n# history = model.fit(\n#     X_train_cnn, y_train_cnn,\n#     validation_data=(X_val, Y_val),\n#     steps_per_epoch=len(X_train_cnn)//batch_size,\n#     batch_size = 128,\n#     epochs = 70,\n#     callbacks=[checkpointer, early, LRP]\n# )","metadata":{"_uuid":"620687bc-89d9-40dc-b133-006fef4e7c78","_cell_guid":"cd8420f8-3c35-407f-93de-b1691d1b534f","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:18:12.995103Z","iopub.execute_input":"2024-06-08T15:18:12.995518Z","iopub.status.idle":"2024-06-08T15:18:13.010871Z","shell.execute_reply.started":"2024-06-08T15:18:12.995481Z","shell.execute_reply":"2024-06-08T15:18:13.009890Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import plotly.graph_objects as go\nfrom plotly.subplots import make_subplots\nimport numpy as np\n\nfig = go.Figure()\n\nlabels = ['Loss', 'Accuracy']\ncolors = ['rgb(67,67,67)', 'rgb(49,130,189)']\n\nmode_size = [8, 8]\nline_size = [2, 2]\n\nx_data = np.vstack((np.arange(1, 51),)*2)\n\ny_data = np.array([\n    np.array(history.history['loss']), \n                      np.array(history.history['accuracy'])\n])\n\nfor i in range(0, 2):\n    fig.add_trace(go.Scatter(x=x_data[i], y=y_data[i], mode='lines',\n        name=labels[i],\n        line=dict(color=colors[i], width=line_size[i]),\n        connectgaps=True,\n    ))\n\n    # endpoints\n    fig.add_trace(go.Scatter(\n        x=[x_data[i][0], x_data[i][-1]],\n        y=[y_data[i][0], y_data[i][-1]],\n        mode='markers',\n        marker=dict(color=colors[i], size=mode_size[i])\n    ))\n\nfig.update_layout(\n    xaxis=dict(\n        showline=True,\n        showgrid=False,\n        showticklabels=True,\n        linecolor='rgb(204, 204, 204)',\n        linewidth=2,\n        ticks='outside',\n        tickfont=dict(\n            family='Arial',\n            size=12,\n            color='rgb(82, 82, 82)',\n        ),\n    ),\n    yaxis=dict(\n        showgrid=False,\n        zeroline=False,\n        showline=False,\n        showticklabels=False,\n    ),\n    autosize=False,\n    margin=dict(\n        autoexpand=False,\n        l=100,\n        r=20,\n        t=110,\n    ),\n    showlegend=False,\n    plot_bgcolor='white'\n)\n\n# annotations = []\n\n# # Adding labels\n# for y_trace, label, color in zip(y_data, labels, colors):\n#     # labeling the left_side of the plot\n#     annotations.append(dict(xref='paper', x=0.05, y=y_trace[0],\n#                                   xanchor='right', yanchor='middle',\n#                                   text=label + ' {:.2f}'.format(y_trace[0]),\n#                                   font=dict(family='Arial',\n#                                             size=16),\n#                                   showarrow=False))\n#     # labeling the right_side of the plot\n#     annotations.append(dict(xref='paper', x=0.95, y=y_trace[31],\n#                                   xanchor='left', yanchor='middle',\n#                                   text='{:.4f}%'.format(y_trace[31]),\n#                                   font=dict(family='Arial',\n#                                             size=16),\n#                                   showarrow=False))\n# # Title\n# annotations.append(dict(xref='paper', yref='paper', x=0.0, y=1.05,\n#                               xanchor='left', yanchor='bottom',\n#                               text='Training Loss & Accuracy',\n#                               font=dict(family='Arial',\n#                                         size=30,\n#                                         color='rgb(37,37,37)'),\n#                               showarrow=False))\n# # Source\n# annotations.append(dict(xref='paper', yref='paper', x=0.5, y=-0.1,\n#                               xanchor='center', yanchor='top',\n#                               text='Epochs',\n#                               font=dict(family='Arial',\n#                                         size=12,\n#                                         color='rgb(150,150,150)'),\n#                               showarrow=False))\n\n# fig.update_layout(annotations=annotations,height=600, width=1000)\n\n# fig.show()\nannotations = []\n\n# Adding labels\nfor y_trace, label, color in zip(y_data, labels, colors):\n    # labeling the left_side of the plot\n    annotations.append(dict(xref='paper', x=0.05, y=y_trace[0],\n                                  xanchor='right', yanchor='middle',\n                                  text=label + ' {:.2f}'.format(y_trace[0]),\n                                  font=dict(family='Arial',\n                                            size=16),\n                                  showarrow=False))\n    # labeling the right_side of the plot\n    annotations.append(dict(xref='paper', x=0.95, y=y_trace[-1],  # Use -1 to get the last element\n                                  xanchor='left', yanchor='middle',\n                                  text='{:.4f}%'.format(y_trace[-1]),  # Use -1 here too\n                                  font=dict(family='Arial',\n                                            size=16),\n                                  showarrow=False))\n# Title\nannotations.append(dict(xref='paper', yref='paper', x=0.0, y=1.05,\n                              xanchor='left', yanchor='bottom',\n                              text='Training Loss & Accuracy',\n                              font=dict(family='Arial',\n                                        size=30,\n                                        color='rgb(37,37,37)'),\n                              showarrow=False))\n# Source\nannotations.append(dict(xref='paper', yref='paper', x=0.5, y=-0.1,\n                              xanchor='center', yanchor='top',\n                              text='Epochs',\n                              font=dict(family='Arial',\n                                        size=12,\n                                        color='rgb(150,150,150)'),\n                              showarrow=False))\n\nfig.update_layout(annotations=annotations, height=600, width=1000)\n\nfig.show()\n","metadata":{"_uuid":"b2a98a55-6032-40d1-8bbf-bf6b35840730","_cell_guid":"7fcbf806-1db3-45b9-8bc0-3abf08dfabd9","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:18:13.012574Z","iopub.execute_input":"2024-06-08T15:18:13.012944Z","iopub.status.idle":"2024-06-08T15:18:13.058264Z","shell.execute_reply.started":"2024-06-08T15:18:13.012910Z","shell.execute_reply":"2024-06-08T15:18:13.057196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.models import load_model\nmodel = load_model('./best_model.h5')\ny_pred = model.predict(X_val)\nM = confusion_matrix(np.argmax(Y_val,1), np.argmax(y_pred,1))\nplot_confusion_matrix(M, sorted(np.unique(np.argmax(Y_val,1))), 'Simple CNN Confusion Matrix, Acc({:.2f}%)'.format(accuracy_score(np.argmax(Y_val,1), np.argmax(y_pred,1))*100))\n","metadata":{"_uuid":"57a8380a-11c2-45fb-8190-4e9da5bce1ed","_cell_guid":"bdeca8dc-2a69-4598-b508-873212a964dd","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:18:13.059592Z","iopub.execute_input":"2024-06-08T15:18:13.059947Z","iopub.status.idle":"2024-06-08T15:18:17.628013Z","shell.execute_reply.started":"2024-06-08T15:18:13.059920Z","shell.execute_reply":"2024-06-08T15:18:17.626914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"accuracy = accuracy_score(np.argmax(Y_val, 1), np.argmax(y_pred, 1)) * 100\nprint(accuracy)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T15:18:17.629602Z","iopub.execute_input":"2024-06-08T15:18:17.630069Z","iopub.status.idle":"2024-06-08T15:18:17.643313Z","shell.execute_reply.started":"2024-06-08T15:18:17.630028Z","shell.execute_reply":"2024-06-08T15:18:17.642195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"text_cell_render border-box-sizing rendered_html\">\n<div style=\"color:white;\n           display:fill;\n           border-radius:5px;\n           background-color:gray;\n           font-size:110%;\n           font-family:Verdana;\n           letter-spacing:0.5px\">\n<h1 style=\"text-align: center;\n           padding: 10px;\n              color:white\">\nChecking Failure Cases\n</h1>\n</div>\n</div>","metadata":{"_uuid":"8f029ba0-23b9-4fa0-83a3-eae4a5e4b39b","_cell_guid":"d955f566-e62b-4f51-a801-3de790bca12a","trusted":true}},{"cell_type":"code","source":"n = np.where(np.argmax(y_pred,1)!=np.argmax(Y_val,1))\nn = np.array(n).reshape(-1)\nn","metadata":{"_uuid":"042a5e34-2598-420b-bb05-79a34d48eac3","_cell_guid":"3cea1159-64d0-4e09-88e9-7b2f3a4fc1fb","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:18:17.644977Z","iopub.execute_input":"2024-06-08T15:18:17.645441Z","iopub.status.idle":"2024-06-08T15:18:17.661014Z","shell.execute_reply.started":"2024-06-08T15:18:17.645396Z","shell.execute_reply":"2024-06-08T15:18:17.659889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# X_val_vis = np.array(X_val).reshape(X_val.shape[0], 28, 28)\n# X_val_vis = X_val_vis\n# fig, axis = plt.subplots(int(len(n)/8), 8, figsize=(35, 15))\n# for i, ax in enumerate(axis.flat):\n#     ax.imshow(X_val_vis[n[i]], cmap='binary')\n#     digit = np.argmax(y_pred,1)[n[i]]\n#     digit2 = np.argmax(Y_val,1)[n[i]]\n#     ax.set(title = f\"Pred Number is {digit}\\n Real Number is {digit2}\");","metadata":{"_uuid":"470a60fd-078e-4a6b-99bf-206ffd71cfb8","_cell_guid":"33ee870d-1eaa-4702-ac0b-7d41a9333040","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-06-08T15:18:17.662473Z","iopub.execute_input":"2024-06-08T15:18:17.663968Z","iopub.status.idle":"2024-06-08T15:18:17.668869Z","shell.execute_reply.started":"2024-06-08T15:18:17.663928Z","shell.execute_reply":"2024-06-08T15:18:17.667814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### It looks like even human can't get the right answers.😅","metadata":{"_uuid":"7b0368af-6a8a-44d9-8dee-e994819269d4","_cell_guid":"11579e1a-7889-4904-ab0f-b96007a7069b","trusted":true}},{"cell_type":"markdown","source":"<div class=\"text_cell_render border-box-sizing rendered_html\">\n<div style=\"color:white;\n           display:fill;\n           border-radius:5px;\n           background-color:gray;\n           font-size:110%;\n           font-family:Verdana;\n           letter-spacing:0.5px\">\n<h1 style=\"text-align: center;\n           padding: 10px;\n              color:white\">\nSubmit to Leaderboard\n</h1>\n</div>\n</div>","metadata":{"_uuid":"b052fb30-6e45-4c66-87af-ce25efdbe87e","_cell_guid":"739fd7c4-6753-4052-8204-a0038c866f87","trusted":true}},{"cell_type":"code","source":"X_test = np.array(df_test).reshape(-1,28,28,1)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T15:18:17.677427Z","iopub.execute_input":"2024-06-08T15:18:17.677901Z","iopub.status.idle":"2024-06-08T15:18:17.747351Z","shell.execute_reply.started":"2024-06-08T15:18:17.677869Z","shell.execute_reply":"2024-06-08T15:18:17.746209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = load_model('./best_model.h5')\ny_pred = model.predict(X_test)\ny_pred = np.argmax(y_pred,1)\npd_submit = pd.read_csv('../input/digit-recognizer/sample_submission.csv')\npd_submit['Label'] = y_pred\npd_submit.to_csv('submission.csv', header=True, index=False)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T15:18:17.749058Z","iopub.execute_input":"2024-06-08T15:18:17.749411Z","iopub.status.idle":"2024-06-08T15:18:20.932445Z","shell.execute_reply.started":"2024-06-08T15:18:17.749383Z","shell.execute_reply":"2024-06-08T15:18:20.931582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 📃 References 📃","metadata":{"_uuid":"dc3e2507-cb68-47fb-b3e6-3a0c9c7f84f7","_cell_guid":"e301b5a4-ce01-4ba9-ac3b-40983ec40265","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":"a9ad3adf-fc21-4ebc-9eb2-d83f80966629","_cell_guid":"f861cd0f-8e0d-48d3-95f9-029e6cf80e84","trusted":true}}]}