You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
639 lines
31 KiB
639 lines
31 KiB
{
|
|
"nbformat": 4,
|
|
"nbformat_minor": 0,
|
|
"metadata": {
|
|
"anaconda-cloud": "",
|
|
"kernelspec": {
|
|
"display_name": "R",
|
|
"language": "R",
|
|
"name": "ir"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": "r",
|
|
"file_extension": ".r",
|
|
"mimetype": "text/x-r-source",
|
|
"name": "R",
|
|
"pygments_lexer": "r",
|
|
"version": "3.4.1"
|
|
},
|
|
"colab": {
|
|
"name": "lesson_14.ipynb",
|
|
"provenance": [],
|
|
"collapsed_sections": [],
|
|
"toc_visible": true
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "ad65fb4aad0a156b42216e4929f490fc",
|
|
"translation_date": "2025-09-06T14:31:58+00:00",
|
|
"source_file": "5-Clustering/2-K-Means/solution/R/lesson_15-R.ipynb",
|
|
"language_code": "vi"
|
|
}
|
|
},
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "GULATlQXLXyR"
|
|
},
|
|
"source": [
|
|
"## Khám phá phân cụm K-Means bằng R và nguyên tắc dữ liệu Tidy.\n",
|
|
"\n",
|
|
"### [**Câu hỏi trước bài giảng**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/29/)\n",
|
|
"\n",
|
|
"Trong bài học này, bạn sẽ học cách tạo các cụm bằng gói Tidymodels và các gói khác trong hệ sinh thái R (chúng ta sẽ gọi chúng là bạn bè 🧑🤝🧑), cùng với bộ dữ liệu âm nhạc Nigeria mà bạn đã nhập trước đó. Chúng ta sẽ tìm hiểu những điều cơ bản về K-Means để phân cụm. Hãy nhớ rằng, như bạn đã học trong bài trước, có nhiều cách để làm việc với các cụm và phương pháp bạn sử dụng phụ thuộc vào dữ liệu của bạn. Chúng ta sẽ thử K-Means vì đây là kỹ thuật phân cụm phổ biến nhất. Bắt đầu nào!\n",
|
|
"\n",
|
|
"Các thuật ngữ bạn sẽ học:\n",
|
|
"\n",
|
|
"- Điểm số Silhouette\n",
|
|
"\n",
|
|
"- Phương pháp Elbow\n",
|
|
"\n",
|
|
"- Quán tính (Inertia)\n",
|
|
"\n",
|
|
"- Phương sai (Variance)\n",
|
|
"\n",
|
|
"### **Giới thiệu**\n",
|
|
"\n",
|
|
"[Phân cụm K-Means](https://wikipedia.org/wiki/K-means_clustering) là một phương pháp xuất phát từ lĩnh vực xử lý tín hiệu. Nó được sử dụng để chia và phân nhóm dữ liệu thành `k cụm` dựa trên sự tương đồng trong các đặc điểm của chúng.\n",
|
|
"\n",
|
|
"Các cụm có thể được hình dung dưới dạng [biểu đồ Voronoi](https://wikipedia.org/wiki/Voronoi_diagram), bao gồm một điểm (hoặc 'hạt giống') và vùng tương ứng của nó.\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/voronoi.png\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>Đồ họa thông tin bởi Jen Looper</figcaption>\n",
|
|
"\n",
|
|
"Phân cụm K-Means có các bước sau:\n",
|
|
"\n",
|
|
"1. Nhà khoa học dữ liệu bắt đầu bằng cách xác định số lượng cụm mong muốn sẽ được tạo.\n",
|
|
"\n",
|
|
"2. Tiếp theo, thuật toán chọn ngẫu nhiên K quan sát từ bộ dữ liệu để làm trung tâm ban đầu cho các cụm (tức là các centroid).\n",
|
|
"\n",
|
|
"3. Sau đó, mỗi quan sát còn lại được gán cho centroid gần nhất của nó.\n",
|
|
"\n",
|
|
"4. Tiếp theo, trung bình mới của mỗi cụm được tính toán và centroid được di chuyển đến vị trí trung bình.\n",
|
|
"\n",
|
|
"5. Bây giờ các trung tâm đã được tính toán lại, mỗi quan sát được kiểm tra lại để xem liệu nó có thể gần hơn với một cụm khác hay không. Tất cả các đối tượng được gán lại bằng cách sử dụng các trung bình cụm đã cập nhật. Các bước gán cụm và cập nhật centroid được lặp lại cho đến khi việc gán cụm không còn thay đổi (tức là khi đạt được sự hội tụ). Thông thường, thuật toán kết thúc khi mỗi lần lặp mới dẫn đến sự di chuyển không đáng kể của các centroid và các cụm trở nên ổn định.\n",
|
|
"\n",
|
|
"<div>\n",
|
|
"\n",
|
|
"> Lưu ý rằng do sự ngẫu nhiên của các quan sát k ban đầu được sử dụng làm centroid khởi đầu, chúng ta có thể nhận được kết quả hơi khác nhau mỗi lần áp dụng quy trình. Vì lý do này, hầu hết các thuật toán sử dụng nhiều *khởi đầu ngẫu nhiên* và chọn lần lặp có WCSS thấp nhất. Do đó, rất khuyến khích luôn chạy K-Means với nhiều giá trị *nstart* để tránh một *điểm cực tiểu cục bộ không mong muốn.*\n",
|
|
"\n",
|
|
"</div>\n",
|
|
"\n",
|
|
"Hình ảnh động ngắn này sử dụng [tác phẩm nghệ thuật](https://github.com/allisonhorst/stats-illustrations) của Allison Horst giải thích quá trình phân cụm:\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/kmeans.gif\"\n",
|
|
" width=\"550\"/>\n",
|
|
" <figcaption>Tác phẩm nghệ thuật bởi @allison_horst</figcaption>\n",
|
|
"\n",
|
|
"Một câu hỏi cơ bản nảy sinh trong phân cụm là: làm thế nào để bạn biết nên chia dữ liệu của mình thành bao nhiêu cụm? Một nhược điểm của việc sử dụng K-Means là bạn sẽ cần xác định `k`, tức là số lượng `centroid`. May mắn thay, `phương pháp elbow` giúp ước tính một giá trị khởi đầu tốt cho `k`. Bạn sẽ thử nó ngay bây giờ.\n",
|
|
"\n",
|
|
"### \n",
|
|
"\n",
|
|
"**Điều kiện tiên quyết**\n",
|
|
"\n",
|
|
"Chúng ta sẽ tiếp tục từ nơi đã dừng lại trong [bài học trước](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/1-Visualize/solution/R/lesson_14-R.ipynb), nơi chúng ta đã phân tích bộ dữ liệu, tạo nhiều hình ảnh trực quan và lọc bộ dữ liệu để lấy các quan sát quan trọng. Hãy chắc chắn kiểm tra nó!\n",
|
|
"\n",
|
|
"Chúng ta sẽ cần một số gói để hoàn thành module này. Bạn có thể cài đặt chúng bằng: `install.packages(c('tidyverse', 'tidymodels', 'cluster', 'summarytools', 'plotly', 'paletteer', 'factoextra', 'patchwork'))`\n",
|
|
"\n",
|
|
"Ngoài ra, đoạn mã dưới đây sẽ kiểm tra xem bạn đã có các gói cần thiết để hoàn thành module này chưa và cài đặt chúng nếu thiếu.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "ah_tBi58LXyi"
|
|
},
|
|
"source": [
|
|
"suppressWarnings(if(!require(\"pacman\")) install.packages(\"pacman\"))\n",
|
|
"\n",
|
|
"pacman::p_load('tidyverse', 'tidymodels', 'cluster', 'summarytools', 'plotly', 'paletteer', 'factoextra', 'patchwork')\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "7e--UCUTLXym"
|
|
},
|
|
"source": [
|
|
"Hãy bắt đầu ngay thôi nào!\n",
|
|
"\n",
|
|
"## 1. Một điệu nhảy với dữ liệu: Thu hẹp xuống 3 thể loại nhạc phổ biến nhất\n",
|
|
"\n",
|
|
"Đây là phần ôn lại những gì chúng ta đã làm trong bài học trước. Hãy cùng phân tích và xử lý dữ liệu nào!\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "Ycamx7GGLXyn"
|
|
},
|
|
"source": [
|
|
"# Load the core tidyverse and make it available in your current R session\n",
|
|
"library(tidyverse)\n",
|
|
"\n",
|
|
"# Import the data into a tibble\n",
|
|
"df <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/5-Clustering/data/nigerian-songs.csv\", show_col_types = FALSE)\n",
|
|
"\n",
|
|
"# Narrow down to top 3 popular genres\n",
|
|
"nigerian_songs <- df %>% \n",
|
|
" # Concentrate on top 3 genres\n",
|
|
" filter(artist_top_genre %in% c(\"afro dancehall\", \"afropop\",\"nigerian pop\")) %>% \n",
|
|
" # Remove unclassified observations\n",
|
|
" filter(popularity != 0)\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"# Visualize popular genres using bar plots\n",
|
|
"theme_set(theme_light())\n",
|
|
"nigerian_songs %>%\n",
|
|
" count(artist_top_genre) %>%\n",
|
|
" ggplot(mapping = aes(x = artist_top_genre, y = n,\n",
|
|
" fill = artist_top_genre)) +\n",
|
|
" geom_col(alpha = 0.8) +\n",
|
|
" paletteer::scale_fill_paletteer_d(\"ggsci::category10_d3\") +\n",
|
|
" ggtitle(\"Top genres\") +\n",
|
|
" theme(plot.title = element_text(hjust = 0.5))\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "b5h5zmkPLXyp"
|
|
},
|
|
"source": [
|
|
"🤩 Điều đó thật tuyệt!\n",
|
|
"\n",
|
|
"## 2. Khám phá dữ liệu thêm.\n",
|
|
"\n",
|
|
"Dữ liệu này sạch đến mức nào? Hãy kiểm tra các giá trị ngoại lai bằng cách sử dụng biểu đồ hộp. Chúng ta sẽ tập trung vào các cột số với ít giá trị ngoại lai hơn (mặc dù bạn có thể loại bỏ các giá trị ngoại lai). Biểu đồ hộp có thể hiển thị phạm vi của dữ liệu và sẽ giúp chọn những cột nào để sử dụng. Lưu ý rằng, biểu đồ hộp không hiển thị độ biến thiên, một yếu tố quan trọng của dữ liệu có thể phân cụm tốt. Vui lòng xem [thảo luận này](https://stats.stackexchange.com/questions/91536/deduce-variance-from-boxplot) để tìm hiểu thêm.\n",
|
|
"\n",
|
|
"[Biểu đồ hộp](https://en.wikipedia.org/wiki/Box_plot) được sử dụng để mô tả phân phối dữ liệu `số` một cách trực quan, vì vậy hãy bắt đầu bằng cách *chọn* tất cả các cột số cùng với các thể loại nhạc phổ biến.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "HhNreJKLLXyq"
|
|
},
|
|
"source": [
|
|
"# Select top genre column and all other numeric columns\n",
|
|
"df_numeric <- nigerian_songs %>% \n",
|
|
" select(artist_top_genre, where(is.numeric)) \n",
|
|
"\n",
|
|
"# Display the data\n",
|
|
"df_numeric %>% \n",
|
|
" slice_head(n = 5)\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "uYXrwJRaLXyq"
|
|
},
|
|
"source": [
|
|
"Hãy xem cách công cụ chọn `where` giúp việc này trở nên dễ dàng 💁? Khám phá các hàm khác tương tự [tại đây](https://tidyselect.r-lib.org/).\n",
|
|
"\n",
|
|
"Vì chúng ta sẽ tạo biểu đồ hộp cho từng đặc điểm số và muốn tránh sử dụng vòng lặp, hãy định dạng lại dữ liệu của chúng ta thành dạng *dài hơn* để có thể tận dụng `facets` - các biểu đồ con, mỗi biểu đồ hiển thị một tập hợp con của dữ liệu.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "gd5bR3f8LXys"
|
|
},
|
|
"source": [
|
|
"# Pivot data from wide to long\n",
|
|
"df_numeric_long <- df_numeric %>% \n",
|
|
" pivot_longer(!artist_top_genre, names_to = \"feature_names\", values_to = \"values\") \n",
|
|
"\n",
|
|
"# Print out data\n",
|
|
"df_numeric_long %>% \n",
|
|
" slice_head(n = 15)\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "-7tE1swnLXyv"
|
|
},
|
|
"source": [
|
|
"Lâu hơn nhiều! Bây giờ là lúc cho một số `ggplots`! Vậy chúng ta sẽ sử dụng `geom` nào?\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "r88bIsyuLXyy"
|
|
},
|
|
"source": [
|
|
"# Make a box plot\n",
|
|
"df_numeric_long %>% \n",
|
|
" ggplot(mapping = aes(x = feature_names, y = values, fill = feature_names)) +\n",
|
|
" geom_boxplot() +\n",
|
|
" facet_wrap(~ feature_names, ncol = 4, scales = \"free\") +\n",
|
|
" theme(legend.position = \"none\")\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "EYVyKIUELXyz"
|
|
},
|
|
"source": [
|
|
"Dễ dàng!\n",
|
|
"\n",
|
|
"Bây giờ chúng ta có thể thấy dữ liệu này hơi nhiễu: bằng cách quan sát từng cột dưới dạng biểu đồ hộp, bạn có thể thấy các giá trị ngoại lai. Bạn có thể duyệt qua tập dữ liệu và loại bỏ các giá trị ngoại lai này, nhưng điều đó sẽ làm cho dữ liệu trở nên khá tối giản.\n",
|
|
"\n",
|
|
"Hiện tại, hãy chọn những cột mà chúng ta sẽ sử dụng cho bài tập phân cụm. Hãy chọn các cột số với phạm vi tương tự. Chúng ta có thể mã hóa `artist_top_genre` dưới dạng số nhưng tạm thời sẽ bỏ qua nó.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "-wkpINyZLXy0"
|
|
},
|
|
"source": [
|
|
"# Select variables with similar ranges\n",
|
|
"df_numeric_select <- df_numeric %>% \n",
|
|
" select(popularity, danceability, acousticness, loudness, energy) \n",
|
|
"\n",
|
|
"# Normalize data\n",
|
|
"# df_numeric_select <- scale(df_numeric_select)\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "D7dLzgpqLXy1"
|
|
},
|
|
"source": [
|
|
"## 3. Tính toán phân cụm k-means trong R\n",
|
|
"\n",
|
|
"Chúng ta có thể tính toán k-means trong R bằng hàm tích hợp sẵn `kmeans`, xem `help(\"kmeans()\")`. Hàm `kmeans()` chấp nhận một khung dữ liệu (data frame) với tất cả các cột là số làm đối số chính.\n",
|
|
"\n",
|
|
"Bước đầu tiên khi sử dụng phân cụm k-means là xác định số cụm (k) sẽ được tạo ra trong giải pháp cuối cùng. Chúng ta biết có 3 thể loại bài hát được tách ra từ tập dữ liệu, vì vậy hãy thử với 3:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "uC4EQ5w7LXy5"
|
|
},
|
|
"source": [
|
|
"set.seed(2056)\n",
|
|
"# Kmeans clustering for 3 clusters\n",
|
|
"kclust <- kmeans(\n",
|
|
" df_numeric_select,\n",
|
|
" # Specify the number of clusters\n",
|
|
" centers = 3,\n",
|
|
" # How many random initial configurations\n",
|
|
" nstart = 25\n",
|
|
")\n",
|
|
"\n",
|
|
"# Display clustering object\n",
|
|
"kclust\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "hzfhscWrLXy-"
|
|
},
|
|
"source": [
|
|
"Đối tượng kmeans chứa nhiều thông tin được giải thích rõ trong `help(\"kmeans()\")`. Hiện tại, chúng ta hãy tập trung vào một vài điểm. Chúng ta thấy rằng dữ liệu đã được phân thành 3 cụm với kích thước lần lượt là 65, 110, 111. Kết quả cũng bao gồm các trung tâm cụm (giá trị trung bình) cho 3 nhóm trên 5 biến số.\n",
|
|
"\n",
|
|
"Vector phân cụm là sự phân bổ cụm cho từng quan sát. Hãy sử dụng hàm `augment` để thêm sự phân bổ cụm vào tập dữ liệu gốc.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "0XwwpFGQLXy_"
|
|
},
|
|
"source": [
|
|
"# Add predicted cluster assignment to data set\n",
|
|
"augment(kclust, df_numeric_select) %>% \n",
|
|
" relocate(.cluster) %>% \n",
|
|
" slice_head(n = 10)\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "NXIVXXACLXzA"
|
|
},
|
|
"source": [
|
|
"Hoàn hảo, chúng ta vừa phân chia tập dữ liệu thành 3 nhóm. Vậy, việc phân cụm của chúng ta tốt đến mức nào 🤷? Hãy cùng xem xét `Silhouette score`.\n",
|
|
"\n",
|
|
"### **Silhouette score**\n",
|
|
"\n",
|
|
"[Phân tích Silhouette](https://en.wikipedia.org/wiki/Silhouette_(clustering)) có thể được sử dụng để nghiên cứu khoảng cách phân tách giữa các cụm kết quả. Điểm số này dao động từ -1 đến 1, và nếu điểm số gần 1, cụm đó dày đặc và được phân tách tốt khỏi các cụm khác. Giá trị gần 0 biểu thị các cụm chồng lấn với các mẫu rất gần ranh giới quyết định của các cụm lân cận. [nguồn](https://dzone.com/articles/kmeans-silhouette-score-explained-with-python-exam).\n",
|
|
"\n",
|
|
"Phương pháp silhouette trung bình tính toán silhouette trung bình của các quan sát cho các giá trị khác nhau của *k*. Điểm silhouette trung bình cao cho thấy việc phân cụm tốt.\n",
|
|
"\n",
|
|
"Hàm `silhouette` trong gói cluster được sử dụng để tính toán độ rộng silhouette trung bình.\n",
|
|
"\n",
|
|
"> Silhouette có thể được tính toán với bất kỳ [khoảng cách](https://en.wikipedia.org/wiki/Distance \"Distance\") nào, chẳng hạn như [khoảng cách Euclid](https://en.wikipedia.org/wiki/Euclidean_distance \"Euclidean distance\") hoặc [khoảng cách Manhattan](https://en.wikipedia.org/wiki/Manhattan_distance \"Manhattan distance\") mà chúng ta đã thảo luận trong [bài học trước](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/1-Visualize/solution/R/lesson_14-R.ipynb).\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "Jn0McL28LXzB"
|
|
},
|
|
"source": [
|
|
"# Load cluster package\n",
|
|
"library(cluster)\n",
|
|
"\n",
|
|
"# Compute average silhouette score\n",
|
|
"ss <- silhouette(kclust$cluster,\n",
|
|
" # Compute euclidean distance\n",
|
|
" dist = dist(df_numeric_select))\n",
|
|
"mean(ss[, 3])\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "QyQRn97nLXzC"
|
|
},
|
|
"source": [
|
|
"Điểm số của chúng ta là **0.549**, nằm ở mức trung bình. Điều này cho thấy dữ liệu của chúng ta không thực sự phù hợp với loại phân cụm này. Hãy xem liệu chúng ta có thể xác nhận nhận định này một cách trực quan hay không. [Gói factoextra](https://rpkgs.datanovia.com/factoextra/index.html) cung cấp các hàm (`fviz_cluster()`) để trực quan hóa phân cụm.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "7a6Km1_FLXzD"
|
|
},
|
|
"source": [
|
|
"library(factoextra)\n",
|
|
"\n",
|
|
"# Visualize clustering results\n",
|
|
"fviz_cluster(kclust, df_numeric_select)\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "IBwCWt-0LXzD"
|
|
},
|
|
"source": [
|
|
"Sự chồng chéo giữa các cụm cho thấy rằng dữ liệu của chúng ta không thực sự phù hợp với loại phân cụm này, nhưng hãy tiếp tục.\n",
|
|
"\n",
|
|
"## 4. Xác định số cụm tối ưu\n",
|
|
"\n",
|
|
"Một câu hỏi cơ bản thường xuất hiện trong phân cụm K-Means là - khi không có nhãn lớp đã biết, làm thế nào để bạn biết nên chia dữ liệu thành bao nhiêu cụm?\n",
|
|
"\n",
|
|
"Một cách chúng ta có thể thử tìm ra là sử dụng một mẫu dữ liệu để `tạo một loạt các mô hình phân cụm` với số cụm tăng dần (ví dụ từ 1-10), và đánh giá các chỉ số phân cụm như **điểm Silhouette.**\n",
|
|
"\n",
|
|
"Hãy xác định số cụm tối ưu bằng cách tính toán thuật toán phân cụm với các giá trị khác nhau của *k* và đánh giá **Tổng bình phương khoảng cách trong cụm** (WCSS). Tổng bình phương khoảng cách trong cụm (WCSS) đo lường mức độ chặt chẽ của phân cụm và chúng ta muốn giá trị này càng nhỏ càng tốt, với các giá trị thấp hơn có nghĩa là các điểm dữ liệu gần nhau hơn.\n",
|
|
"\n",
|
|
"Hãy khám phá ảnh hưởng của các lựa chọn khác nhau về `k`, từ 1 đến 10, đối với phân cụm này.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "hSeIiylDLXzE"
|
|
},
|
|
"source": [
|
|
"# Create a series of clustering models\n",
|
|
"kclusts <- tibble(k = 1:10) %>% \n",
|
|
" # Perform kmeans clustering for 1,2,3 ... ,10 clusters\n",
|
|
" mutate(model = map(k, ~ kmeans(df_numeric_select, centers = .x, nstart = 25)),\n",
|
|
" # Farm out clustering metrics eg WCSS\n",
|
|
" glanced = map(model, ~ glance(.x))) %>% \n",
|
|
" unnest(cols = glanced)\n",
|
|
" \n",
|
|
"\n",
|
|
"# View clustering rsulsts\n",
|
|
"kclusts\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "m7rS2U1eLXzE"
|
|
},
|
|
"source": [
|
|
"Bây giờ chúng ta đã có tổng bình phương trong cụm (tot.withinss) cho mỗi thuật toán phân cụm với tâm *k*, chúng ta sử dụng [phương pháp khuỷu tay](https://en.wikipedia.org/wiki/Elbow_method_(clustering)) để tìm số lượng cụm tối ưu. Phương pháp này bao gồm việc vẽ biểu đồ WCSS như một hàm của số lượng cụm, và chọn [điểm khuỷu tay của đường cong](https://en.wikipedia.org/wiki/Elbow_of_the_curve \"Elbow of the curve\") làm số lượng cụm cần sử dụng.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "o_DjHGItLXzF"
|
|
},
|
|
"source": [
|
|
"set.seed(2056)\n",
|
|
"# Use elbow method to determine optimum number of clusters\n",
|
|
"kclusts %>% \n",
|
|
" ggplot(mapping = aes(x = k, y = tot.withinss)) +\n",
|
|
" geom_line(size = 1.2, alpha = 0.8, color = \"#FF7F0EFF\") +\n",
|
|
" geom_point(size = 2, color = \"#FF7F0EFF\")\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "pLYyt5XSLXzG"
|
|
},
|
|
"source": [
|
|
"Biểu đồ cho thấy sự giảm đáng kể trong WCSS (tức là *độ chặt chẽ* lớn hơn) khi số lượng cụm tăng từ một lên hai, và một sự giảm đáng chú ý khác từ hai lên ba cụm. Sau đó, mức giảm ít rõ rệt hơn, dẫn đến một điểm `khuỷu tay` 💪 trên biểu đồ ở khoảng ba cụm. Đây là một dấu hiệu tốt cho thấy có hai đến ba cụm dữ liệu được phân tách khá rõ ràng.\n",
|
|
"\n",
|
|
"Bây giờ chúng ta có thể tiếp tục và trích xuất mô hình phân cụm với `k = 3`:\n",
|
|
"\n",
|
|
"> `pull()`: được sử dụng để trích xuất một cột duy nhất \n",
|
|
">\n",
|
|
"> `pluck()`: được sử dụng để truy cập các cấu trúc dữ liệu như danh sách \n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "JP_JPKBILXzG"
|
|
},
|
|
"source": [
|
|
"# Extract k = 3 clustering\n",
|
|
"final_kmeans <- kclusts %>% \n",
|
|
" filter(k == 3) %>% \n",
|
|
" pull(model) %>% \n",
|
|
" pluck(1)\n",
|
|
"\n",
|
|
"\n",
|
|
"final_kmeans\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "l_PDTu8tLXzI"
|
|
},
|
|
"source": [
|
|
"Tuyệt vời! Hãy cùng xem qua các cụm đã thu được. Bạn có muốn thêm tính tương tác bằng cách sử dụng `plotly` không?\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "dNcleFe-LXzJ"
|
|
},
|
|
"source": [
|
|
"# Add predicted cluster assignment to data set\n",
|
|
"results <- augment(final_kmeans, df_numeric_select) %>% \n",
|
|
" bind_cols(df_numeric %>% select(artist_top_genre)) \n",
|
|
"\n",
|
|
"# Plot cluster assignments\n",
|
|
"clust_plt <- results %>% \n",
|
|
" ggplot(mapping = aes(x = popularity, y = danceability, color = .cluster, shape = artist_top_genre)) +\n",
|
|
" geom_point(size = 2, alpha = 0.8) +\n",
|
|
" paletteer::scale_color_paletteer_d(\"ggthemes::Tableau_10\")\n",
|
|
"\n",
|
|
"ggplotly(clust_plt)\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "6JUM_51VLXzK"
|
|
},
|
|
"source": [
|
|
"Có lẽ chúng ta đã kỳ vọng rằng mỗi cụm (được biểu thị bằng các màu sắc khác nhau) sẽ có các thể loại riêng biệt (được biểu thị bằng các hình dạng khác nhau).\n",
|
|
"\n",
|
|
"Hãy cùng xem xét độ chính xác của mô hình.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"metadata": {
|
|
"id": "HdIMUGq7LXzL"
|
|
},
|
|
"source": [
|
|
"# Assign genres to predefined integers\n",
|
|
"label_count <- results %>% \n",
|
|
" group_by(artist_top_genre) %>% \n",
|
|
" mutate(id = cur_group_id()) %>% \n",
|
|
" ungroup() %>% \n",
|
|
" summarise(correct_labels = sum(.cluster == id))\n",
|
|
"\n",
|
|
"\n",
|
|
"# Print results \n",
|
|
"cat(\"Result:\", label_count$correct_labels, \"out of\", nrow(results), \"samples were correctly labeled.\")\n",
|
|
"\n",
|
|
"cat(\"\\nAccuracy score:\", label_count$correct_labels/nrow(results))\n"
|
|
],
|
|
"execution_count": null,
|
|
"outputs": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "C50wvaAOLXzM"
|
|
},
|
|
"source": [
|
|
"Độ chính xác của mô hình này không tệ, nhưng cũng không quá tốt. Có thể là do dữ liệu không phù hợp để áp dụng K-Means Clustering. Dữ liệu này quá mất cân đối, ít tương quan và có quá nhiều sự biến đổi giữa các giá trị cột, khiến việc phân cụm trở nên khó khăn. Thực tế, các cụm được hình thành có lẽ bị ảnh hưởng hoặc lệch nhiều bởi ba danh mục thể loại mà chúng ta đã định nghĩa ở trên.\n",
|
|
"\n",
|
|
"Dù vậy, đây vẫn là một quá trình học hỏi rất thú vị!\n",
|
|
"\n",
|
|
"Trong tài liệu của Scikit-learn, bạn có thể thấy rằng một mô hình như thế này, với các cụm không được phân định rõ ràng, gặp phải vấn đề về 'phương sai':\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/problems.png\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>Infographic từ Scikit-learn</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"## **Phương sai**\n",
|
|
"\n",
|
|
"Phương sai được định nghĩa là \"trung bình của bình phương các độ lệch so với giá trị trung bình\" [nguồn](https://www.mathsisfun.com/data/standard-deviation.html). Trong bối cảnh của bài toán phân cụm này, nó ám chỉ việc các giá trị trong tập dữ liệu của chúng ta có xu hướng lệch quá nhiều so với giá trị trung bình.\n",
|
|
"\n",
|
|
"✅ Đây là thời điểm tuyệt vời để suy nghĩ về tất cả các cách bạn có thể khắc phục vấn đề này. Điều chỉnh dữ liệu thêm một chút? Sử dụng các cột khác? Dùng một thuật toán khác? Gợi ý: Hãy thử [chuẩn hóa dữ liệu của bạn](https://www.mygreatlearning.com/blog/learning-data-science-with-k-means-clustering/) để đưa nó về cùng một thang đo và kiểm tra các cột khác.\n",
|
|
"\n",
|
|
"> Hãy thử '[máy tính phương sai](https://www.calculatorsoup.com/calculators/statistics/variance-calculator.php)' để hiểu rõ hơn về khái niệm này.\n",
|
|
"\n",
|
|
"------------------------------------------------------------------------\n",
|
|
"\n",
|
|
"## **🚀Thử thách**\n",
|
|
"\n",
|
|
"Dành thời gian với notebook này, điều chỉnh các tham số. Bạn có thể cải thiện độ chính xác của mô hình bằng cách làm sạch dữ liệu hơn (ví dụ như loại bỏ các giá trị ngoại lai)? Bạn có thể sử dụng trọng số để tăng trọng số cho các mẫu dữ liệu nhất định. Còn cách nào khác để tạo ra các cụm tốt hơn?\n",
|
|
"\n",
|
|
"Gợi ý: Hãy thử chuẩn hóa dữ liệu của bạn. Có đoạn mã đã được chú thích trong notebook để thêm chuẩn hóa tiêu chuẩn, giúp các cột dữ liệu giống nhau hơn về mặt phạm vi. Bạn sẽ thấy rằng mặc dù điểm silhouette giảm xuống, nhưng 'gấp khúc' trong đồ thị khuỷu tay trở nên mượt mà hơn. Điều này là do việc để dữ liệu không được chuẩn hóa cho phép dữ liệu có ít phương sai hơn mang nhiều trọng số hơn. Đọc thêm về vấn đề này [tại đây](https://stats.stackexchange.com/questions/21222/are-mean-normalization-and-feature-scaling-needed-for-k-means-clustering/21226#21226).\n",
|
|
"\n",
|
|
"## [**Câu hỏi sau bài giảng**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/30/)\n",
|
|
"\n",
|
|
"## **Ôn tập & Tự học**\n",
|
|
"\n",
|
|
"- Xem qua một trình mô phỏng K-Means [như thế này](https://user.ceng.metu.edu.tr/~akifakkus/courses/ceng574/k-means/). Bạn có thể sử dụng công cụ này để trực quan hóa các điểm dữ liệu mẫu và xác định các tâm cụm. Bạn có thể chỉnh sửa độ ngẫu nhiên của dữ liệu, số lượng cụm và số lượng tâm cụm. Điều này có giúp bạn hình dung cách dữ liệu có thể được nhóm lại không?\n",
|
|
"\n",
|
|
"- Ngoài ra, hãy xem [tài liệu về K-Means](https://stanford.edu/~cpiech/cs221/handouts/kmeans.html) từ Stanford.\n",
|
|
"\n",
|
|
"Muốn thử áp dụng kỹ năng phân cụm mới học vào các tập dữ liệu phù hợp với K-Means clustering? Hãy xem:\n",
|
|
"\n",
|
|
"- [Huấn luyện và Đánh giá Mô hình Phân cụm](https://rpubs.com/eR_ic/clustering) sử dụng Tidymodels và các công cụ liên quan\n",
|
|
"\n",
|
|
"- [Phân tích Cụm K-means](https://uc-r.github.io/kmeans_clustering), Hướng dẫn Lập trình R của UC Business Analytics\n",
|
|
"\n",
|
|
"- [Phân cụm K-means với nguyên tắc dữ liệu gọn gàng](https://www.tidymodels.org/learn/statistics/k-means/)\n",
|
|
"\n",
|
|
"## **Bài tập**\n",
|
|
"\n",
|
|
"[Thử các phương pháp phân cụm khác nhau](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/2-K-Means/assignment.md)\n",
|
|
"\n",
|
|
"## CẢM ƠN ĐẾN:\n",
|
|
"\n",
|
|
"[Jen Looper](https://www.twitter.com/jenlooper) vì đã tạo phiên bản Python gốc của module này ♥️\n",
|
|
"\n",
|
|
"[`Allison Horst`](https://twitter.com/allison_horst/) vì đã tạo ra những hình minh họa tuyệt vời giúp R trở nên thân thiện và hấp dẫn hơn. Tìm thêm các hình minh họa tại [bộ sưu tập của cô ấy](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM).\n",
|
|
"\n",
|
|
"Chúc bạn học vui,\n",
|
|
"\n",
|
|
"[Eric](https://twitter.com/ericntay), Đại sứ Sinh viên Microsoft Learn Vàng.\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/r_learners_sm.jpeg\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>Tác phẩm nghệ thuật của @allison_horst</figcaption>\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Tuyên bố miễn trừ trách nhiệm**: \nTài liệu này đã được dịch bằng dịch vụ dịch thuật AI [Co-op Translator](https://github.com/Azure/co-op-translator). Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng các bản dịch tự động có thể chứa lỗi hoặc không chính xác. Tài liệu gốc bằng ngôn ngữ bản địa nên được coi là nguồn thông tin chính thức. Đối với các thông tin quan trọng, khuyến nghị sử dụng dịch vụ dịch thuật chuyên nghiệp bởi con người. Chúng tôi không chịu trách nhiệm cho bất kỳ sự hiểu lầm hoặc diễn giải sai nào phát sinh từ việc sử dụng bản dịch này.\n"
|
|
]
|
|
}
|
|
]
|
|
} |