{ "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", "

\n", " \n", "

อินโฟกราฟิกโดย Jen Looper
\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", "
\n", "\n", "> โปรดทราบว่าเนื่องจากการสุ่มเลือกข้อมูลเริ่มต้น k ที่ใช้เป็น centroid การดำเนินการอาจให้ผลลัพธ์ที่แตกต่างกันเล็กน้อยในแต่ละครั้ง ด้วยเหตุนี้ อัลกอริทึมส่วนใหญ่จึงใช้การเริ่มต้นแบบสุ่มหลายครั้ง (*random starts*) และเลือกการวนซ้ำที่มีค่า WCSS ต่ำที่สุด ดังนั้นจึงแนะนำอย่างยิ่งให้รัน K-Means ด้วยค่าของ *nstart* หลายค่าเพื่อหลีกเลี่ยง *local optimum* ที่ไม่พึงประสงค์\n", "\n", "
\n", "\n", "แอนิเมชันสั้นๆ นี้ใช้ [งานศิลปะ](https://github.com/allisonhorst/stats-illustrations) ของ Allison Horst อธิบายกระบวนการจัดกลุ่ม:\n", "\n", "

\n", " \n", "

งานศิลปะโดย @allison_horst
\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", "

\n", " \n", "

อินโฟกราฟิกจาก Scikit-learn
\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", "

\n", " \n", "

ภาพประกอบโดย @allison_horst
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n---\n\n**ข้อจำกัดความรับผิดชอบ**: \nเอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI [Co-op Translator](https://github.com/Azure/co-op-translator) แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้\n" ] } ] }