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.
637 lines
30 KiB
637 lines
30 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-04T02:16:38+00:00",
|
|
"source_file": "5-Clustering/2-K-Means/solution/R/lesson_15-R.ipynb",
|
|
"language_code": "ko"
|
|
}
|
|
},
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"id": "GULATlQXLXyR"
|
|
},
|
|
"source": [
|
|
"## R과 Tidy 데이터 원칙을 활용한 K-Means 클러스터링 탐구\n",
|
|
"\n",
|
|
"### [**강의 전 퀴즈**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/29/)\n",
|
|
"\n",
|
|
"이 강의에서는 Tidymodels 패키지와 R 생태계의 다른 패키지들(우리는 이들을 친구 🧑🤝🧑라고 부를 거예요), 그리고 이전에 가져온 나이지리아 음악 데이터셋을 사용하여 클러스터를 생성하는 방법을 배웁니다. 우리는 클러스터링을 위한 K-Means의 기본 개념을 다룰 것입니다. 이전 강의에서 배운 것처럼, 클러스터를 다루는 방법은 여러 가지가 있으며, 사용하는 방법은 데이터에 따라 달라집니다. 우리는 가장 일반적인 클러스터링 기법인 K-Means를 시도해볼 것입니다. 시작해봅시다!\n",
|
|
"\n",
|
|
"이 강의에서 배우게 될 용어:\n",
|
|
"\n",
|
|
"- 실루엣 점수(Silhouette scoring)\n",
|
|
"\n",
|
|
"- 엘보우 방법(Elbow method)\n",
|
|
"\n",
|
|
"- 관성(Inertia)\n",
|
|
"\n",
|
|
"- 분산(Variance)\n",
|
|
"\n",
|
|
"### **소개**\n",
|
|
"\n",
|
|
"[K-Means 클러스터링](https://wikipedia.org/wiki/K-means_clustering)은 신호 처리 분야에서 유래한 방법입니다. 이 방법은 데이터의 특징 간 유사성을 기반으로 `k개의 클러스터`로 데이터를 나누고 분할하는 데 사용됩니다.\n",
|
|
"\n",
|
|
"클러스터는 [보로노이 다이어그램](https://wikipedia.org/wiki/Voronoi_diagram)으로 시각화할 수 있으며, 여기에는 점(또는 '시드')과 해당 영역이 포함됩니다.\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/voronoi.png\"\n",
|
|
" width=\"500\"/>\n",
|
|
" <figcaption>Jen Looper의 인포그래픽</figcaption>\n",
|
|
"\n",
|
|
"K-Means 클러스터링의 단계는 다음과 같습니다:\n",
|
|
"\n",
|
|
"1. 데이터 과학자는 생성할 클러스터의 원하는 개수를 지정합니다.\n",
|
|
"\n",
|
|
"2. 알고리즘은 데이터셋에서 K개의 관측값을 무작위로 선택하여 클러스터의 초기 중심(즉, 중심점)으로 사용합니다.\n",
|
|
"\n",
|
|
"3. 나머지 관측값 각각을 가장 가까운 중심점에 할당합니다.\n",
|
|
"\n",
|
|
"4. 각 클러스터의 새로운 평균을 계산하고 중심점을 평균 위치로 이동시킵니다.\n",
|
|
"\n",
|
|
"5. 중심점이 재계산되었으므로, 모든 관측값이 다른 클러스터에 더 가까운지 다시 확인합니다. 모든 객체는 업데이트된 클러스터 평균을 사용하여 다시 할당됩니다. 클러스터 할당 및 중심점 업데이트 단계는 클러스터 할당이 더 이상 변경되지 않을 때까지(즉, 수렴이 이루어질 때까지) 반복됩니다. 일반적으로 알고리즘은 각 새로운 반복에서 중심점의 이동이 미미해지고 클러스터가 고정될 때 종료됩니다.\n",
|
|
"\n",
|
|
"<div>\n",
|
|
"\n",
|
|
"> 초기 중심점으로 사용되는 k개의 관측값이 무작위로 선택되기 때문에, 절차를 적용할 때마다 약간 다른 결과를 얻을 수 있습니다. 이러한 이유로 대부분의 알고리즘은 여러 번의 *무작위 시작(random starts)*을 사용하고, 가장 낮은 WCSS(클러스터 내 제곱합)를 가진 반복을 선택합니다. 따라서 *nstart* 값을 여러 번 설정하여 K-Means를 실행하는 것이 권장되며, 이는 *원치 않는 지역 최적화(local optimum)*를 피하는 데 도움이 됩니다.\n",
|
|
"\n",
|
|
"</div>\n",
|
|
"\n",
|
|
"Allison Horst의 [작품](https://github.com/allisonhorst/stats-illustrations)을 활용한 이 짧은 애니메이션은 클러스터링 과정을 설명합니다:\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/kmeans.gif\"\n",
|
|
" width=\"550\"/>\n",
|
|
" <figcaption>@allison_horst의 작품</figcaption>\n",
|
|
"\n",
|
|
"클러스터링에서 근본적으로 제기되는 질문은 다음과 같습니다: 데이터를 몇 개의 클러스터로 나누어야 할까요? K-Means의 단점 중 하나는 `k`, 즉 `중심점`의 개수를 설정해야 한다는 점입니다. 다행히도 `엘보우 방법`은 `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`가 얼마나 쉽게 만들어주는지 보이시죠 💁? 이런 다른 함수들도 [여기](https://tidyselect.r-lib.org/)에서 확인해보세요.\n",
|
|
"\n",
|
|
"이제 각 숫자형 특징에 대해 박스플롯을 만들 예정인데, 반복문 사용을 피하고 싶다면 데이터를 *더 긴* 형식으로 재구성해야 합니다. 이렇게 하면 `facets`를 활용할 수 있는데, 이는 데이터의 각 하위 집합을 별도의 서브플롯으로 표시할 수 있게 해줍니다.\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",
|
|
"우선, 클러스터링 연습에 사용할 열을 선택해 봅시다. 비슷한 범위를 가진 숫자형 열을 선택해 보겠습니다. `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. R에서 k-평균 클러스터링 계산하기\n",
|
|
"\n",
|
|
"R에서는 내장된 `kmeans` 함수를 사용하여 k-평균 클러스터링을 계산할 수 있습니다. 자세한 내용은 `help(\"kmeans()\")`를 참조하세요. `kmeans()` 함수는 모든 열이 숫자로 구성된 데이터 프레임을 주요 인수로 받습니다.\n",
|
|
"\n",
|
|
"k-평균 클러스터링을 사용할 때 첫 번째 단계는 최종 결과에서 생성될 클러스터(군집)의 수(k)를 지정하는 것입니다. 데이터셋에서 추출한 3개의 노래 장르가 있다는 것을 알고 있으니, 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()\")`에서 잘 설명된 여러 정보를 포함하고 있습니다. 지금은 몇 가지에만 집중해 봅시다. 데이터를 65, 110, 111 크기의 3개의 클러스터로 그룹화한 것을 확인할 수 있습니다. 출력에는 또한 5개의 변수에 걸친 3개 그룹의 클러스터 중심(평균)도 포함되어 있습니다.\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 방법은 *k*의 다양한 값에 대해 관측치의 평균 Silhouette을 계산합니다. 높은 평균 Silhouette 점수는 좋은 클러스터링을 나타냅니다.\n",
|
|
"\n",
|
|
"클러스터 패키지의 `silhouette` 함수를 사용하여 평균 Silhouette 폭을 계산할 수 있습니다.\n",
|
|
"\n",
|
|
"> Silhouette은 [유클리드 거리](https://en.wikipedia.org/wiki/Euclidean_distance \"Euclidean distance\")나 [맨해튼 거리](https://en.wikipedia.org/wiki/Manhattan_distance \"Manhattan distance\")와 같은 [거리](https://en.wikipedia.org/wiki/Distance \"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": [
|
|
"우리의 점수는 **0.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 클러스터링에서 자주 제기되는 근본적인 질문은 다음과 같습니다. 알려진 클래스 레이블이 없는 상태에서 데이터를 몇 개의 클러스터로 나누어야 할지 어떻게 알 수 있을까요?\n",
|
|
"\n",
|
|
"이를 알아내는 한 가지 방법은 데이터 샘플을 사용하여 `클러스터 수를 점진적으로 증가시키며` (예: 1-10) 일련의 클러스터링 모델을 생성하고, **Silhouette 점수**와 같은 클러스터링 지표를 평가하는 것입니다.\n",
|
|
"\n",
|
|
"*클러스터 수*의 다양한 값에 대해 클러스터링 알고리즘을 계산하고 **클러스터 내 제곱합 (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": [
|
|
"이제 각 클러스터링 알고리즘에서 중심 *k*에 대한 총 클러스터 내 제곱합 (tot.withinss)을 구했으므로, [엘보우 방법](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 클러스터링에 적합하지 않을 가능성이 있습니다. 이 데이터는 너무 불균형적이고, 상관관계가 적으며, 열 값 간의 분산이 너무 커서 클러스터링이 잘 이루어지지 않습니다. 사실, 형성된 클러스터는 우리가 위에서 정의한 세 가지 장르 카테고리에 의해 크게 영향을 받거나 왜곡될 가능성이 높습니다.\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",
|
|
"힌트: 데이터를 스케일링해보세요. 노트북에는 데이터 열이 범위 면에서 서로 더 비슷하게 보이도록 표준 스케일링을 추가하는 주석 처리된 코드가 있습니다. 실루엣 점수가 낮아지긴 하지만, 엘보 그래프의 '꺾임'이 부드러워집니다. 이는 데이터를 스케일링하지 않으면 분산이 적은 데이터가 더 많은 영향을 미치게 되기 때문입니다. 이 문제에 대해 더 읽어보세요 [여기](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",
|
|
"- 또한 Stanford에서 제공하는 [K-Means 핸드아웃](https://stanford.edu/~cpiech/cs221/handouts/kmeans.html)을 살펴보세요.\n",
|
|
"\n",
|
|
"새로 습득한 클러스터링 기술을 K-Means 클러스터링에 적합한 데이터셋에 적용해보고 싶으신가요? 다음을 확인해보세요:\n",
|
|
"\n",
|
|
"- [클러스터링 모델 학습 및 평가](https://rpubs.com/eR_ic/clustering) (Tidymodels 및 관련 도구 사용)\n",
|
|
"\n",
|
|
"- [K-Means 클러스터 분석](https://uc-r.github.io/kmeans_clustering), UC 비즈니스 분석 R 프로그래밍 가이드\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"
|
|
]
|
|
}
|
|
]
|
|
} |