소소하지만 소소하지 않은 개발 공부/머신 러닝 교과서

[머신러닝교과서] 사이킷런을 사용한 k-평균 군집, KMeans, python

still..epochs 2023. 1. 6. 14:41

* 본 포스팅은 머신러닝교과서를 참조하여 작성되었습니다.

* https://github.com/rickiepark/python-machine-learning-book-3rd-edition

 

GitHub - rickiepark/python-machine-learning-book-3rd-edition: <머신 러닝 교과서 3판>의 코드 저장소

<머신 러닝 교과서 3판>의 코드 저장소. Contribute to rickiepark/python-machine-learning-book-3rd-edition development by creating an account on GitHub.

github.com

 

사이킷런을 사용한 k-평균 군집

k-평균 알고리즘은 프로토타입 기반 군집(prototype-based clustering)에 속한다. 이 장에서는 이외에도 계층적 군집(hierachical clustering)과 밀집도 기반 군집(density-based clustering)을 소개한다.

 

프로토타입 기반 군집은 각 클러스터가 하나의 프로토타입으로 표현된다는 의미이다. 프로토타입은 연속적인 특성에서는 비슷한 데이터 포인트의 센트로이드(centroid)(평균)이거나, 범주형 특성에서는 메도이드(medoid)가 된다. k-평균 알고리즘이 원형 클러스터를 구분하는 데 뛰어나지만, 이 알고리즘 단점은 사전에 클러스터 개수 k를 지정해야 한다는 것이다.

 

적절하지 않은 k를 고르면 군집 성능이 좋지 않다. 군집 품질을 평가하는 기법인 엘보우 방법(elbow method)과 실루엣 그래프(silhouette plot)를 통해 최적의 k를 결정하는데 도움을 받을 수 있다.

 

k-평균 군집의 시각화를 위해 간단한 2차원 데이터셋을 사용하여 진행해보자.

from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples=150,
                  n_features=2,
                  centers=3,
                  cluster_std=0.5,
                  shuffle=True,
                  random_state=0)


import matplotlib.pyplot as plt
plt.scatter(X[:,0], X[:, 1], c='white', marker='o', edgecolor='black', s=50)
plt.grid()
plt.tight_layout()
plt.show()

랜덤하게 생성된 데이터셋

실제 군집 애플리케이션에서는 샘플에 대한 진짜 카테고리 정보(추론이 아니라 실증적인 정보)가 전혀 없다. 클래스 레이블이 있다면 이 작업은 지도 학습에 해당한다. 그러므로 여기서 목표는 특성의 유사도에 기초하여 샘플을 그룹으로 모으는 것이다.

 

k-평균 알고리즘의 네 단계

  1. 샘플 포인트에서 랜덤하게 k개의 센트로이드(대표포인트)를 초기 클러스터 중심으로 선택한다.
  2. 각 샘플을 가장 가까운 센트로이드에 할당한다.
  3. 할당된 샘플들의 중심으로 센트로이드를 이동한다.
  4. 클러스터 할당이 변하지 않거나 사용자가 지정한 허용 오차나 최대 반복 횟수에 도달할 때까지 2,3 단계를 반복한다.

 

샘플 간의 유사도는 가장 널리 사용되는 거리인 유클리디안 거리 지표를 기반으로 연속적인 특성을 가진 샘플을 클러스터로 묶는다. 클러스터 내 제곱 오차합(SSE) 또는 클러스터 관성(Cluster inertia)을 반복적으로 최소화 한다.

 

준비된 샘플 데이터셋에 사이킷런 cluster 모듈의 KMeans 클래스를 적용해 보자.

from sklearn.cluster import KMeans
km = KMeans(n_clusters=3,
            init='random',
            n_init=10,
            max_iter=300,
            tol=1e-04,
            random_state=0)
y_km = km.fit_predict(X)

'n_init=10 으로 설정하면 k-평균 군집 알고리즘을 각기 다른 랜덤한 센트로이드(대표포인트)에서 독립적으로 열 번 실행하여 가장 낮은 SSE를 만드는 하나를 최종 모델로 선택한다.'

'max_iter 매개변수는 한 번의 실행에서 수행할 최대 반복 횟수를 지정한다'

 

k-평균의 한 가지 문제는 하나 이상의 클러스터가 비어 있을 수 있다는 것인데, 사이킷런의 k-평균에는 이 문제가 고려되어 있다. 한 클러스터가 비어 있다면 알고리즘이 빈 클러스터의 센트로이드에서 가장 멀리 떨어진 샘플을 찾고, 그런 다음 가장 먼 포인트에 센트로이드를 다시 할당한다.

 

이제 데이터셋에서 k-평균이 식별한 클러스터와 클러스터 센트로이드를 시각화해 보자.

lt.scatter(X[y_km == 0, 0],
            X[y_km == 0, 1],
            s=50, c='lightgreen',
            marker='s', edgecolor='black',
            label='Cluster 1')
plt.scatter(X[y_km == 1, 0],
            X[y_km == 1, 1],
            s=50, c='orange',
            marker='o', edgecolor='black',
            label='Cluster 2')
plt.scatter(X[y_km == 2, 0],
            X[y_km == 2, 1],
            s=50, c='lightblue',
            marker='v', edgecolor='black',
            label='Cluster 3')
plt.scatter(km.cluster_centers_[:, 0],
            km.cluster_centers_[:, 1],
            s=250, c='red',
            marker='*', edgecolor='black',
            label='Centroids')
plt.legend(scatterpoints=1)
plt.grid()

k-평균 알고리즘으로 찾은 클러스터

k-평균이 이런 작은 데이터셋에서 잘 동작하지만 k-평균 알고리즘은 클러스터 개수 k를 사전에 지정해야 하는 중요한 단점이 있다. 클러스터 개수를 얼마로 선택할지는 실제 애플리케이션에서는 명확하지 않을 수 있다. 특히 시각화할 수 없는 고차원 데이터셋에서 그렇다.