Data Science/데이터 분석 📊

[데이터 분석] 21. 클러스터링 Ⅰ : K-Means

SLYK1D 2024. 10. 9. 08:25
728x90
반응형

1. 클러스터링?

이전까지 회귀나 분류는 정답이 존재하는, 정확히는 반응변수(혹은 결과변수)가 존재하는 지도학습의 과정이였다면, 클러스터링은 비지도학습에 해당하는 대표적인 기법으로 학습 데이터 내에 레이블(label)이 존재하지 않다는 특징이 있다. 
비지도학습의 장점은 데이터 셋 내에 사용자도 모르는 숨겨진 구조를 찾아낼 수 있다는 점이다. 이제부터 다뤄볼 클러스터링은 데이터를 각각의 군집으로 묶어서, 같은 군집의 요소들은 서로 비슷하게, 다른 군집에 속한 요소들과는 최대한 다르도록 하여 자연스러운 그룹을 찾는 기법이라고 할 수 있다. 

1.1 군집화

모델들을 소개하기에 앞서 군집화에 대한 정의를 이해할 필요가 있다. 군집이란 비슷한 객체로 이루어진 그룹을 찾는 기법이며, 앞서 설명한 것처럼, 한 그룹 안의 객체들은 다른 그룹에 있는 객체보다 더 관련이 많다. 대표적인 예시로 문서, 음악, 영화를 여러 주제의 그룹으로 모으는 경우가 있다. 

군집화의 종류에는 크게 프로토타입 기반, 계층적 군집, 밀집도 기반 군집으로 나눠볼 수 있으며, 이번 장에서 다룰 K-Means 알고리즘의 경우 프로토타입 기반의 군집화에 속한다. 

 

2. K-Means

이번 장에서 다루게 될 K-Means 알고리즘은 대표적인 클러스터링 기법이라고 할 수 있다. 또한 위에서 언급한 것처럼 군집화 종류 중에서는 프로토타입 기반의 군집화에 해당하는데, 각 클러스터가 하나의 프로토 타입으로 표현되며, 연속적인 특성에서는 비슷한 데이터 포인트가 평균으로, 범주형 특성에서는 가장 대표가 되는 포인트가 된다.
해당 알고리즘에서 가장 중요한 것은 군집의 개수를 의미하는 K 값을 적절히 설정해야한다는 것인데, 이 후에 모델 성능 평가부분에서 다루게 될 엘보우 기법이나 실루엣 기법을 이용해서 적절한 K 값을 찾을 수 있다.

그렇다면 K-Means 알고리즘이 어떻게 동작하는 지를 살펴보도록 하자. 알고리즘의 과정은 크게 아래와 같이 4단계로 나타낼 수 있으며, 적절한 K 값을 찾을 때까지 반복한다.

① 샘플 포인트에서 랜덤하게 k개의 평균 값을 초기 클러스터 중심으로 선택한다.
② 각 샘플을 가장 가까운 평균 값 $ {\mu}^{(j)} , j \in {{l, ..., k}} $ 에 할당한다.
③ 할당된 샘플들의 중심으로 평균을 이동한다.
④ 클러스터 할당이 변함 없거나, 사용자가 지정한 허용오차 또는 최대 반복횟수에 도달할 때까지 ②, ③ 과정을 반복한다.

이 때, 샘플 간의 유사도는 거리의 반대 개념으로, 일반적인 경우 m차원 공간에 있는 두 포인트의 유클리디안 거리의 제곱으로 계산된다. 구체적인 수식은 아래와 같다. 

 

$ {d(x, y)}^{2} = \sum_{j=1}^{m} {{x}_{j} - {y}_{j}}^{2} = {\Vert x-y \Vert}_{2}^{2} $

 

위의 식을 기반으로 클러스터 내 제곱 오차합은 아래와 같이 계산하게 된다. 

 

$ SSE = \sum_{i=1}^{n} \sum_{j=1}^{m} {w}^{(i, j)} = {\Vert {x}^{(i)} - {\mu}^{(j)} \Vert}_{2}^{2} $

 

위의 식에서 $\mu$ 는 클러스터의 대표 포인트(센트로이드, Centroid) 를 의미한다. 만약 샘플 x 가 클러스터 j 내에 존재한다면, $ {w}^{(i, j)} $ 는 1이 되고, 아니면 0이 된다. 

다음으로 K-Means 알고리즘의 사용법을 살펴보도록 하자. Python 및 R에서 사용 방법은 아래 코드와 같으니 사용 언어에 맞게 참고하기 바란다. 

from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs

from matplotlib import pyplot as plt

x, y = make_blobs(n_samples=150, n_features=2, centers=3, cluster_std=0.5, shuffle=True, random_state=0)

plt.scatter(x[:, 0], x[:, 1], c='white', marker='o', edgecolors='black', s=50)
plt.grid()
plt.tight_layout()
plt.show()

kmeans = KMeans(
	n_clusters=3,  # 클러스터 수 
	init='random', # 초기값 설정
 	n_init=10,     # 초기 포인트 수
 	max_iter=300,  # 최대 반복 횟수
	tol=1e-04,     # 사용자 지정 허용 오차
	random_state=0 # 난수 생성
)

y_pred = kmeans.fit_predict(x)

결과로 산출된 군집을 확인해보면 아래 그래프와 같이 나오게 된다. 

위의 산점도에서 K-Means 알고리즘이 원형 중심부에 3개의 센트로이드를 할당한 것을 확인할 수 있다. 보통 작은 데이터 셋에서는 잘 동작하지만, 사전에 군집의 개수인 k 를 잘 설정해줘야만 동작한다는 단점이 있다. 

3. K-Means ++

추가적으로 K-Means 와 관련된 알고리즘을 알아보도록 하자. 앞서 살펴본 K-Means 알고리즘의 경우, 초기 센트로이드 값을 랜덤하게 설정하기 때문에 초기 값이 나쁠 경우 군집 결과까지 나쁜 군집으로 만들 수 있다. 이를 개선하기 위해서 고안된 알고리즘이 지금 소개할 K-Means++ 알고리즘이다. K-Means 와의 차이점으로는 초기 센트로이드가 서로 떨어지지 않도록 위치시킨 후 K-Means 군집화가 진행된다. 자세한 알고리즘 과정은 아래와 같다.

① 선택한 k 개의 센트로이드를 저장할 빈 집합 M을 초기화한다. 
② 입력 샘플에서 첫 번째 센트로이드 $ {\mu}^{(i)} $ 를 랜덤하게 선택하고 M에 할당한다.
③ M에 있지 않은 각 샘플 $ {x}^{(i)} $ 에 대해 M에 있는 센트로이드까지 최소 제곱 거리를 찾는다.
④ 아래 식과 같은 가중치가 적용된 확률분포를 사용하여 센트로이드 $ {\mu}^{(i)} $ 를 랜덤하게 선택한다. 

$ \frac { { d({\mu}^{(p)}, M) }^{2} } { \sum_{i} {d({x}^{2}, M)}^{2} } $

⑤ k 개의 센트로이드가 나올 때까지 ② 와 ③을 반복한다.
⑥ 이 후 K-Means 를 수행한다.

위의 과정을 알고리즘으로 구현하면 다음과 같다. 추가적으로 파이썬의 경우, scikit-learn 의 K-Means 클래스로 K-Means++ 를 구현하기 위해서는 init 매개변수에 "k-means++" 로 설정하면 된다. 구체적인 코드는 다음과 같다.

kmeans_plus = KMeans(n_clusters=3, init="k-means++", n_init=10, max_iter=300, tol=1e-04, random_state=0)
pred_plus = kmeans_plus.fit_predict(x)
library(TreeDist)

set.seed(0)  # 파이썬의 random_state와 동일하게 설정
kmeans_result <- KMeansPP(x, k = 3, nstart = 10)

pred_plus <- kmeans_result$cluster
print(pred_plus)
728x90
반응형