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
44 KiB
639 lines
44 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:29:51+00:00",
|
|
"source_file": "5-Clustering/2-K-Means/solution/R/lesson_15-R.ipynb",
|
|
"language_code": "th"
|
|
}
|
|
},
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "GULATlQXLXyR"
|
|
},
|
|
"source": [
|
|
"## สำรวจการจัดกลุ่ม K-Means ด้วย R และหลักการข้อมูลแบบ Tidy\n",
|
|
"\n",
|
|
"### [**แบบทดสอบก่อนเรียน**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/29/)\n",
|
|
"\n",
|
|
"ในบทเรียนนี้ คุณจะได้เรียนรู้วิธีสร้างกลุ่มโดยใช้แพ็กเกจ Tidymodels และแพ็กเกจอื่นๆ ในระบบนิเวศของ R (เราจะเรียกพวกมันว่าเพื่อน 🧑🤝🧑) รวมถึงชุดข้อมูลเพลงไนจีเรียที่คุณนำเข้าไว้ก่อนหน้านี้ เราจะครอบคลุมพื้นฐานของ K-Means สำหรับการจัดกลุ่ม โปรดจำไว้ว่า ตามที่คุณได้เรียนรู้ในบทเรียนก่อนหน้า มีหลายวิธีในการทำงานกับการจัดกลุ่ม และวิธีที่คุณใช้ขึ้นอยู่กับข้อมูลของคุณ เราจะลองใช้ K-Means เนื่องจากเป็นเทคนิคการจัดกลุ่มที่พบได้บ่อยที่สุด มาเริ่มกันเลย!\n",
|
|
"\n",
|
|
"คำศัพท์ที่คุณจะได้เรียนรู้:\n",
|
|
"\n",
|
|
"- การให้คะแนน Silhouette\n",
|
|
"\n",
|
|
"- วิธี Elbow\n",
|
|
"\n",
|
|
"- ความเฉื่อย (Inertia)\n",
|
|
"\n",
|
|
"- ความแปรปรวน (Variance)\n",
|
|
"\n",
|
|
"### **บทนำ**\n",
|
|
"\n",
|
|
"[K-Means Clustering](https://wikipedia.org/wiki/K-means_clustering) เป็นวิธีที่มาจากสาขาการประมวลผลสัญญาณ ใช้ในการแบ่งและจัดกลุ่มข้อมูลออกเป็น `k clusters` โดยอิงจากความคล้ายคลึงกันของคุณลักษณะ\n",
|
|
"\n",
|
|
"กลุ่มเหล่านี้สามารถแสดงผลเป็น [แผนภาพ Voronoi](https://wikipedia.org/wiki/Voronoi_diagram) ซึ่งประกอบด้วยจุด (หรือ 'seed') และพื้นที่ที่เกี่ยวข้อง\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/voronoi.png\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>อินโฟกราฟิกโดย Jen Looper</figcaption>\n",
|
|
"\n",
|
|
"ขั้นตอนของ K-Means clustering มีดังนี้:\n",
|
|
"\n",
|
|
"1. นักวิทยาศาสตร์ข้อมูลเริ่มต้นโดยกำหนดจำนวนกลุ่มที่ต้องการสร้าง\n",
|
|
"\n",
|
|
"2. จากนั้น อัลกอริทึมจะสุ่มเลือก K ข้อมูลจากชุดข้อมูลเพื่อใช้เป็นจุดศูนย์กลางเริ่มต้นสำหรับกลุ่ม (หรือที่เรียกว่า centroids)\n",
|
|
"\n",
|
|
"3. ต่อมา ข้อมูลที่เหลือทั้งหมดจะถูกจัดกลุ่มไปยัง centroid ที่ใกล้ที่สุด\n",
|
|
"\n",
|
|
"4. จากนั้น คำนวณค่าเฉลี่ยใหม่ของแต่ละกลุ่ม และ centroid จะถูกย้ายไปยังตำแหน่งค่าเฉลี่ยนั้น\n",
|
|
"\n",
|
|
"5. เมื่อจุดศูนย์กลางถูกคำนวณใหม่แล้ว ข้อมูลทุกชิ้นจะถูกตรวจสอบอีกครั้งเพื่อดูว่ามันอาจใกล้กับกลุ่มอื่นมากกว่า ข้อมูลทั้งหมดจะถูกจัดกลุ่มใหม่โดยใช้ค่าเฉลี่ยของกลุ่มที่อัปเดต ขั้นตอนการจัดกลุ่มและการอัปเดต centroid จะถูกทำซ้ำจนกว่าการจัดกลุ่มจะหยุดเปลี่ยนแปลง (หรือเมื่อเกิดการลู่เข้า) โดยทั่วไป อัลกอริทึมจะหยุดเมื่อการเคลื่อนที่ของ centroid ในแต่ละรอบใหม่มีน้อยมาก และกลุ่มกลายเป็นคงที่\n",
|
|
"\n",
|
|
"<div>\n",
|
|
"\n",
|
|
"> โปรดทราบว่าเนื่องจากการสุ่มเลือกข้อมูลเริ่มต้น k ที่ใช้เป็น centroid การดำเนินการอาจให้ผลลัพธ์ที่แตกต่างกันเล็กน้อยในแต่ละครั้ง ด้วยเหตุนี้ อัลกอริทึมส่วนใหญ่จึงใช้การเริ่มต้นแบบสุ่มหลายครั้ง (*random starts*) และเลือกการวนซ้ำที่มีค่า WCSS ต่ำที่สุด ดังนั้นจึงแนะนำอย่างยิ่งให้รัน K-Means ด้วยค่าของ *nstart* หลายค่าเพื่อหลีกเลี่ยง *local optimum* ที่ไม่พึงประสงค์\n",
|
|
"\n",
|
|
"</div>\n",
|
|
"\n",
|
|
"แอนิเมชันสั้นๆ นี้ใช้ [งานศิลปะ](https://github.com/allisonhorst/stats-illustrations) ของ Allison Horst อธิบายกระบวนการจัดกลุ่ม:\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/kmeans.gif\"\n",
|
|
" width=\"550\"/>\n",
|
|
" <figcaption>งานศิลปะโดย @allison_horst</figcaption>\n",
|
|
"\n",
|
|
"คำถามพื้นฐานที่เกิดขึ้นในการจัดกลุ่มคือ: คุณจะรู้ได้อย่างไรว่าควรแยกข้อมูลออกเป็นกี่กลุ่ม? ข้อเสียของการใช้ K-Means คือคุณจะต้องกำหนด `k` ซึ่งก็คือจำนวน `centroids` โชคดีที่ `elbow method` ช่วยประมาณค่าที่ดีสำหรับการเริ่มต้น `k` คุณจะได้ลองใช้ในอีกสักครู่\n",
|
|
"\n",
|
|
"### \n",
|
|
"\n",
|
|
"**ข้อกำหนดเบื้องต้น**\n",
|
|
"\n",
|
|
"เราจะเริ่มต้นจากจุดที่เราหยุดไว้ใน [บทเรียนก่อนหน้า](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/1-Visualize/solution/R/lesson_14-R.ipynb) ซึ่งเราได้วิเคราะห์ชุดข้อมูล สร้างภาพจำนวนมาก และกรองชุดข้อมูลไปยังข้อมูลที่น่าสนใจ อย่าลืมตรวจสอบ!\n",
|
|
"\n",
|
|
"เราจะต้องใช้แพ็กเกจบางตัวเพื่อดำเนินการในโมดูลนี้ คุณสามารถติดตั้งได้โดยใช้: `install.packages(c('tidyverse', 'tidymodels', 'cluster', 'summarytools', 'plotly', 'paletteer', 'factoextra', 'patchwork'))`\n",
|
|
"\n",
|
|
"หรือใช้สคริปต์ด้านล่างเพื่อตรวจสอบว่าคุณมีแพ็กเกจที่จำเป็นสำหรับโมดูลนี้หรือไม่ และติดตั้งให้ในกรณีที่ขาดหาย\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": [
|
|
"มาลุยกันเลย!\n",
|
|
"\n",
|
|
"## 1. เต้นรำกับข้อมูล: คัดเลือก 3 แนวเพลงที่ได้รับความนิยมมากที่สุด\n",
|
|
"\n",
|
|
"นี่คือการทบทวนสิ่งที่เราได้ทำในบทเรียนก่อนหน้า มาลองจัดการข้อมูลกันเถอะ!\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": [
|
|
"🤩 นั่นไปได้ดีมาก!\n",
|
|
"\n",
|
|
"## 2. การสำรวจข้อมูลเพิ่มเติม\n",
|
|
"\n",
|
|
"ข้อมูลนี้สะอาดแค่ไหน? มาลองตรวจสอบค่าผิดปกติด้วยการใช้กล่องแสดงค่ากัน เราจะเน้นไปที่คอลัมน์ตัวเลขที่มีค่าผิดปกติน้อยกว่า (แม้ว่าคุณจะสามารถลบค่าผิดปกติออกได้) กล่องแสดงค่าจะช่วยแสดงช่วงของข้อมูลและช่วยเลือกว่าคอลัมน์ใดควรนำมาใช้ อย่างไรก็ตาม กล่องแสดงค่าไม่ได้แสดงค่าความแปรปรวน ซึ่งเป็นองค์ประกอบสำคัญของข้อมูลที่สามารถจัดกลุ่มได้ดี กรุณาดู [การอภิปรายนี้](https://stats.stackexchange.com/questions/91536/deduce-variance-from-boxplot) เพื่ออ่านเพิ่มเติม\n",
|
|
"\n",
|
|
"[กล่องแสดงค่า](https://en.wikipedia.org/wiki/Box_plot) ถูกใช้เพื่อแสดงการกระจายของข้อมูล `ตัวเลข` ในรูปแบบกราฟิก ดังนั้นเรามาเริ่มต้นด้วยการ *เลือก* คอลัมน์ตัวเลขทั้งหมดควบคู่ไปกับแนวเพลงยอดนิยมกัน\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": [
|
|
"ดูว่า `where` ใน selection helper ทำให้เรื่องนี้ง่ายขึ้นแค่ไหน 💁? ลองสำรวจฟังก์ชันอื่นๆ ได้ [ที่นี่](https://tidyselect.r-lib.org/) \n",
|
|
"\n",
|
|
"เนื่องจากเราจะสร้าง boxplot สำหรับแต่ละคุณสมบัติที่เป็นตัวเลข และเราต้องการหลีกเลี่ยงการใช้ loops ลองปรับรูปแบบข้อมูลของเราให้เป็น *longer* format ซึ่งจะช่วยให้เราใช้ `facets` ได้ - subplots ที่แสดงข้อมูลแต่ละชุดแยกกัน\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": [
|
|
"ถึงเวลาสนุกกับ `ggplots` กันแล้ว! แล้วเราจะใช้ `geom` อะไรดี?\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": [
|
|
"ง่ายมาก!\n",
|
|
"\n",
|
|
"ตอนนี้เราสามารถเห็นได้ว่าข้อมูลนี้ค่อนข้างมีความแปรปรวน: เมื่อดูแต่ละคอลัมน์ในรูปแบบกล่องแสดงค่ากระจาย คุณจะเห็นค่าผิดปกติ คุณอาจจะไล่ดูชุดข้อมูลและลบค่าผิดปกติเหล่านี้ออกไป แต่การทำเช่นนั้นจะทำให้ข้อมูลเหลือน้อยลงมาก\n",
|
|
"\n",
|
|
"สำหรับตอนนี้ เรามาเลือกคอลัมน์ที่เราจะใช้สำหรับการฝึกการจัดกลุ่มกันดีกว่า เราจะเลือกคอลัมน์ตัวเลขที่มีช่วงค่าคล้ายกัน เราสามารถเข้ารหัส `artist_top_genre` เป็นตัวเลขได้ แต่ตอนนี้เราจะตัดมันออกไปก่อน\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. การคำนวณ k-means clustering ใน R\n",
|
|
"\n",
|
|
"เราสามารถคำนวณ k-means ใน R ได้โดยใช้ฟังก์ชัน `kmeans` ที่มีอยู่แล้ว ดูเพิ่มเติมได้ที่ `help(\"kmeans()\")` ฟังก์ชัน `kmeans()` รับข้อมูลในรูปแบบ data frame ที่มีคอลัมน์เป็นตัวเลขทั้งหมดเป็นอาร์กิวเมนต์หลัก\n",
|
|
"\n",
|
|
"ขั้นตอนแรกในการใช้ k-means clustering คือการกำหนดจำนวนคลัสเตอร์ (k) ที่จะสร้างขึ้นในผลลัพธ์สุดท้าย เราทราบว่ามี 3 ประเภทของเพลงที่เราแบ่งออกมาจากชุดข้อมูล ดังนั้นลองใช้ค่า k เป็น 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": [
|
|
"วัตถุ kmeans มีข้อมูลหลายส่วนที่อธิบายได้ดีใน `help(\"kmeans()\")` สำหรับตอนนี้ เรามาเน้นที่บางส่วนกัน เราเห็นว่าข้อมูลถูกจัดกลุ่มเป็น 3 กลุ่ม โดยมีขนาด 65, 110, 111 ผลลัพธ์ยังมีจุดศูนย์กลางของกลุ่ม (ค่าเฉลี่ย) สำหรับ 3 กลุ่มใน 5 ตัวแปร\n",
|
|
"\n",
|
|
"เวกเตอร์การจัดกลุ่มคือการกำหนดกลุ่มสำหรับแต่ละการสังเกตการณ์ ลองใช้ฟังก์ชัน `augment` เพื่อเพิ่มการกำหนดกลุ่มลงในชุดข้อมูลเดิม\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": [
|
|
"เยี่ยมเลย! ตอนนี้เราได้แบ่งชุดข้อมูลของเราออกเป็น 3 กลุ่มแล้ว ทีนี้คำถามคือ การจัดกลุ่มของเราดีแค่ไหน 🤷? มาดูที่ `Silhouette score` กันเถอะ\n",
|
|
"\n",
|
|
"### **Silhouette score**\n",
|
|
"\n",
|
|
"[การวิเคราะห์ Silhouette](https://en.wikipedia.org/wiki/Silhouette_(clustering)) สามารถใช้เพื่อศึกษาระยะห่างระหว่างกลุ่มที่ได้จากการจัดกลุ่ม คะแนนนี้มีค่าตั้งแต่ -1 ถึง 1 โดยถ้าคะแนนใกล้ 1 หมายความว่ากลุ่มนั้นมีความหนาแน่นและแยกออกจากกลุ่มอื่นได้ดี ค่าใกล้ 0 หมายถึงกลุ่มที่มีการทับซ้อนกัน โดยตัวอย่างอยู่ใกล้กับเส้นแบ่งเขตของกลุ่มข้างเคียง [แหล่งที่มา](https://dzone.com/articles/kmeans-silhouette-score-explained-with-python-exam)\n",
|
|
"\n",
|
|
"วิธีการเฉลี่ย Silhouette จะคำนวณค่าเฉลี่ยของ Silhouette ของตัวอย่างสำหรับค่าต่าง ๆ ของ *k* คะแนน Silhouette เฉลี่ยที่สูงบ่งบอกถึงการจัดกลุ่มที่ดี\n",
|
|
"\n",
|
|
"ฟังก์ชัน `silhouette` ในแพ็กเกจ cluster ใช้สำหรับคำนวณค่าเฉลี่ยความกว้างของ Silhouette\n",
|
|
"\n",
|
|
"> Silhouette สามารถคำนวณได้ด้วย [ระยะทาง](https://en.wikipedia.org/wiki/Distance \"Distance\") เมตริกใด ๆ เช่น [ระยะทางแบบยุคลิด](https://en.wikipedia.org/wiki/Euclidean_distance \"Euclidean distance\") หรือ [ระยะทางแบบแมนฮัตตัน](https://en.wikipedia.org/wiki/Manhattan_distance \"Manhattan distance\") ซึ่งเราได้พูดถึงใน [บทเรียนก่อนหน้า](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": [
|
|
"คะแนนของเราคือ **.549** ซึ่งอยู่ตรงกลางพอดี นี่บ่งชี้ว่าข้อมูลของเราไม่ได้เหมาะสมกับการจัดกลุ่มประเภทนี้มากนัก ลองมาดูกันว่าเราสามารถยืนยันข้อสันนิษฐานนี้ด้วยการมองเห็นได้หรือไม่ [แพ็กเกจ factoextra](https://rpkgs.datanovia.com/factoextra/index.html) มีฟังก์ชัน (`fviz_cluster()`) สำหรับการแสดงผลการจัดกลุ่มแบบภาพ\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": [
|
|
"การที่กลุ่มข้อมูลมีการทับซ้อนกันแสดงให้เห็นว่าข้อมูลของเราอาจไม่เหมาะสมกับการจัดกลุ่มประเภทนี้นัก แต่เรามาลองทำต่อกันเถอะ\n",
|
|
"\n",
|
|
"## 4. การกำหนดจำนวนกลุ่มที่เหมาะสม\n",
|
|
"\n",
|
|
"คำถามพื้นฐานที่มักเกิดขึ้นใน K-Means clustering คือ - หากเราไม่มีป้ายกำกับคลาสที่รู้ล่วงหน้า เราจะทราบได้อย่างไรว่าควรแบ่งข้อมูลออกเป็นกี่กลุ่ม?\n",
|
|
"\n",
|
|
"วิธีหนึ่งที่เราสามารถลองหาคำตอบได้คือการใช้ตัวอย่างข้อมูลเพื่อ `สร้างโมเดลการจัดกลุ่มหลายชุด` โดยเพิ่มจำนวนกลุ่มทีละขั้น (เช่น จาก 1-10) และประเมินเมตริกการจัดกลุ่ม เช่น **Silhouette score**\n",
|
|
"\n",
|
|
"เรามาลองกำหนดจำนวนกลุ่มที่เหมาะสมโดยการคำนวณอัลกอริธึมการจัดกลุ่มสำหรับค่าต่าง ๆ ของ *k* และประเมิน **Within Cluster Sum of Squares** (WCSS) กันดีกว่า โดย WCSS หรือผลรวมของกำลังสองภายในกลุ่มทั้งหมดนั้นใช้วัดความกระชับของการจัดกลุ่ม ซึ่งเราต้องการให้ค่าต่ำที่สุดเท่าที่จะเป็นไปได้ เพราะค่าที่ต่ำกว่าหมายความว่าจุดข้อมูลอยู่ใกล้กันมากขึ้น\n",
|
|
"\n",
|
|
"เรามาสำรวจผลกระทบของการเลือกค่า `k` ที่แตกต่างกัน ตั้งแต่ 1 ถึง 10 ต่อการจัดกลุ่มนี้กันเถอะ\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": [
|
|
"ตอนนี้เรามีผลรวมของภายในคลัสเตอร์ทั้งหมด (tot.withinss) สำหรับแต่ละอัลกอริทึมการจัดกลุ่มที่มีศูนย์ *k* เราใช้ [วิธีข้อศอก](https://en.wikipedia.org/wiki/Elbow_method_(clustering)) เพื่อหาจำนวนคลัสเตอร์ที่เหมาะสมที่สุด วิธีนี้ประกอบด้วยการวาดกราฟ WCSS เป็นฟังก์ชันของจำนวนคลัสเตอร์ และเลือก [ข้อศอกของกราฟ](https://en.wikipedia.org/wiki/Elbow_of_the_curve \"Elbow of the curve\") เป็นจำนวนคลัสเตอร์ที่ใช้\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": [
|
|
"กราฟแสดงการลดลงอย่างมากของ WCSS (ซึ่งหมายถึง *ความกระชับ* ที่มากขึ้น) เมื่อจำนวนคลัสเตอร์เพิ่มขึ้นจากหนึ่งเป็นสอง และมีการลดลงที่สังเกตได้ชัดเจนอีกครั้งจากสองเป็นสามคลัสเตอร์ หลังจากนั้น การลดลงจะไม่เด่นชัดเท่าเดิม ทำให้เกิด `elbow` 💪ในกราฟประมาณที่สามคลัสเตอร์ นี่เป็นสัญญาณที่ดีที่บ่งบอกว่ามีคลัสเตอร์ของจุดข้อมูลที่แยกกันได้ดีประมาณสองถึงสามคลัสเตอร์\n",
|
|
"\n",
|
|
"ตอนนี้เราสามารถดำเนินการและดึงโมเดลการจัดกลุ่มที่ `k = 3` ได้แล้ว:\n",
|
|
"\n",
|
|
"> `pull()`: ใช้สำหรับดึงคอลัมน์เดียว\n",
|
|
">\n",
|
|
"> `pluck()`: ใช้สำหรับเข้าถึงโครงสร้างข้อมูล เช่น ลิสต์\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": [
|
|
"เยี่ยมเลย! มาลองดูการจัดกลุ่มที่ได้กันดีกว่า สนใจเพิ่มความโต้ตอบด้วย `plotly` ไหม?\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": [
|
|
"บางทีเราอาจคาดหวังว่ากลุ่มแต่ละกลุ่ม (แสดงด้วยสีต่างๆ) จะมีประเภทที่แตกต่างกัน (แสดงด้วยรูปร่างต่างๆ)\n",
|
|
"\n",
|
|
"ลองมาดูความแม่นยำของโมเดลกัน\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": [
|
|
"ความแม่นยำของโมเดลนี้ไม่แย่ แต่ก็ไม่ได้ดีมากนัก อาจเป็นเพราะข้อมูลไม่เหมาะสมกับการใช้ K-Means Clustering ข้อมูลนี้มีความไม่สมดุลกันมาก มีความสัมพันธ์ระหว่างคอลัมน์น้อย และมีความแปรปรวนระหว่างค่าของคอลัมน์สูงเกินไปที่จะจัดกลุ่มได้ดี ในความเป็นจริง กลุ่มที่เกิดขึ้นอาจได้รับอิทธิพลหรือถูกบิดเบือนอย่างมากจากสามหมวดหมู่ของประเภทที่เรากำหนดไว้ข้างต้น\n",
|
|
"\n",
|
|
"อย่างไรก็ตาม นี่เป็นกระบวนการเรียนรู้ที่น่าสนใจมาก!\n",
|
|
"\n",
|
|
"ในเอกสารของ Scikit-learn คุณจะเห็นว่าโมเดลแบบนี้ ที่มีการจัดกลุ่มที่ไม่ชัดเจน มีปัญหาเรื่อง 'ความแปรปรวน':\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/problems.png\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>อินโฟกราฟิกจาก Scikit-learn</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"## **ความแปรปรวน**\n",
|
|
"\n",
|
|
"ความแปรปรวนถูกนิยามว่าเป็น \"ค่าเฉลี่ยของผลต่างกำลังสองจากค่าเฉลี่ย\" [แหล่งข้อมูล](https://www.mathsisfun.com/data/standard-deviation.html) ในบริบทของปัญหาการจัดกลุ่มนี้ หมายถึงข้อมูลที่ค่าต่างๆ ในชุดข้อมูลมีแนวโน้มที่จะเบี่ยงเบนจากค่าเฉลี่ยมากเกินไป\n",
|
|
"\n",
|
|
"✅ นี่เป็นช่วงเวลาที่ดีในการคิดถึงวิธีต่างๆ ที่คุณสามารถแก้ไขปัญหานี้ได้ ลองปรับข้อมูลอีกเล็กน้อย? ใช้คอลัมน์ที่แตกต่างกัน? ใช้อัลกอริทึมที่แตกต่างกัน? เคล็ดลับ: ลอง [ปรับขนาดข้อมูลของคุณ](https://www.mygreatlearning.com/blog/learning-data-science-with-k-means-clustering/) เพื่อทำให้ข้อมูลเป็นมาตรฐานและทดสอบคอลัมน์อื่นๆ\n",
|
|
"\n",
|
|
"> ลองใช้ '[เครื่องคำนวณความแปรปรวน](https://www.calculatorsoup.com/calculators/statistics/variance-calculator.php)' เพื่อทำความเข้าใจแนวคิดนี้เพิ่มเติม\n",
|
|
"\n",
|
|
"------------------------------------------------------------------------\n",
|
|
"\n",
|
|
"## **🚀ความท้าทาย**\n",
|
|
"\n",
|
|
"ใช้เวลาสักครู่กับโน้ตบุ๊กนี้ ปรับพารามิเตอร์ต่างๆ คุณสามารถปรับปรุงความแม่นยำของโมเดลได้โดยการทำความสะอาดข้อมูลเพิ่มเติม (เช่น การลบค่าผิดปกติ)? คุณสามารถใช้น้ำหนักเพื่อให้ความสำคัญกับตัวอย่างข้อมูลบางตัวมากขึ้น คุณสามารถทำอะไรอีกเพื่อสร้างกลุ่มที่ดีกว่า?\n",
|
|
"\n",
|
|
"เคล็ดลับ: ลองปรับขนาดข้อมูลของคุณ มีโค้ดที่ถูกคอมเมนต์ไว้ในโน้ตบุ๊กที่เพิ่มการปรับขนาดมาตรฐานเพื่อทำให้คอลัมน์ข้อมูลมีลักษณะคล้ายกันมากขึ้นในแง่ของช่วง คุณจะพบว่าแม้คะแนน silhouette จะลดลง แต่ 'จุดหัก' ในกราฟข้อศอกจะเรียบขึ้น นี่เป็นเพราะการปล่อยให้ข้อมูลไม่ได้ปรับขนาดทำให้ข้อมูลที่มีความแปรปรวนน้อยมีน้ำหนักมากขึ้น อ่านเพิ่มเติมเกี่ยวกับปัญหานี้ [ที่นี่](https://stats.stackexchange.com/questions/21222/are-mean-normalization-and-feature-scaling-needed-for-k-means-clustering/21226#21226)\n",
|
|
"\n",
|
|
"## [**แบบทดสอบหลังการบรรยาย**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/30/)\n",
|
|
"\n",
|
|
"## **การทบทวนและการศึกษาด้วยตนเอง**\n",
|
|
"\n",
|
|
"- ลองดูตัวจำลอง K-Means [เช่นนี้](https://user.ceng.metu.edu.tr/~akifakkus/courses/ceng574/k-means/) คุณสามารถใช้เครื่องมือนี้เพื่อแสดงภาพจุดข้อมูลตัวอย่างและกำหนดจุดศูนย์กลางของมัน คุณสามารถแก้ไขความสุ่มของข้อมูล จำนวนกลุ่ม และจำนวนจุดศูนย์กลาง สิ่งนี้ช่วยให้คุณเข้าใจวิธีการจัดกลุ่มข้อมูลได้หรือไม่?\n",
|
|
"\n",
|
|
"- นอกจากนี้ ลองดู [เอกสารประกอบเกี่ยวกับ K-Means](https://stanford.edu/~cpiech/cs221/handouts/kmeans.html) จาก Stanford\n",
|
|
"\n",
|
|
"ต้องการลองใช้ทักษะการจัดกลุ่มที่คุณเพิ่งเรียนรู้กับชุดข้อมูลที่เหมาะสมกับ K-Means clustering? โปรดดู:\n",
|
|
"\n",
|
|
"- [การฝึกและประเมินโมเดลการจัดกลุ่ม](https://rpubs.com/eR_ic/clustering) โดยใช้ Tidymodels และเพื่อนๆ\n",
|
|
"\n",
|
|
"- [การวิเคราะห์กลุ่มด้วย K-Means](https://uc-r.github.io/kmeans_clustering), UC Business Analytics R Programming Guide\n",
|
|
"\n",
|
|
"- [การจัดกลุ่มด้วย K-Means โดยใช้หลักการข้อมูลที่เป็นระเบียบ](https://www.tidymodels.org/learn/statistics/k-means/)\n",
|
|
"\n",
|
|
"## **งานที่ได้รับมอบหมาย**\n",
|
|
"\n",
|
|
"[ลองใช้วิธีการจัดกลุ่มที่แตกต่างกัน](https://github.com/microsoft/ML-For-Beginners/blob/main/5-Clustering/2-K-Means/assignment.md)\n",
|
|
"\n",
|
|
"## ขอขอบคุณ:\n",
|
|
"\n",
|
|
"[Jen Looper](https://www.twitter.com/jenlooper) สำหรับการสร้างเวอร์ชัน Python ดั้งเดิมของโมดูลนี้ ♥️\n",
|
|
"\n",
|
|
"[`Allison Horst`](https://twitter.com/allison_horst/) สำหรับการสร้างภาพประกอบที่น่าทึ่งซึ่งทำให้ R น่าสนใจและเข้าถึงได้มากขึ้น ค้นหาภาพประกอบเพิ่มเติมได้ที่ [แกลเลอรีของเธอ](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM)\n",
|
|
"\n",
|
|
"เรียนรู้อย่างมีความสุข,\n",
|
|
"\n",
|
|
"[Eric](https://twitter.com/ericntay), Gold Microsoft Learn Student Ambassador.\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/r_learners_sm.jpeg\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>ภาพประกอบโดย @allison_horst</figcaption>\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**ข้อจำกัดความรับผิดชอบ**: \nเอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI [Co-op Translator](https://github.com/Azure/co-op-translator) แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้\n"
|
|
]
|
|
}
|
|
]
|
|
} |