본문으로 바로가기

1. 엘보우(Elbow) 기법

- SSE(Sum of Squared Errors)의 값이 점점 줄어들다가 어느 순간 줄어드는 비율이 급격하게 작아지는 부분이 생긴다.
- 결과물인 그래프 모양을 보면 팔꿈치에 해당하는 바로 그 부분이 최적의 클러스터 개수가 된다.

- 간단하고, 이해하기 쉬워 사람들이 많이 사용하는 기법
- Cluster 내 응집도만 고려하고, Cluster 간 분리도는 고려하지 않는다.

- 보통, 엘보우 기법으로 그래프를 확인한 후, cluster의 개수를 2로 해야할지, 3으로 해야할지 헷갈리는 경우에 추가로 실루엣 기법을 사용한다.

 

def elbow(X):
    total_distance = []
    for i in range(1, 11):
        model = cluster.KMeans(n_clusters=i, random_state=0)
        model.fit(X)
        
        # inertia : Sum of squared distances of samples to their closest cluster center.
        total_distance.append(model.inertia_) 
        
    plt.plot(range(1, 11), total_distance, marker='o')
    plt.xlabel('# of clusters')
    plt.ylabel('Total distance (SSE)')
    plt.show()

 

 


2. 실루엣(Silhouette) 기법

- Cluster 내 응집도 뿐만 아니라, Cluster 간 분리도 또한 고려한다.

- 클러스터링의 품질을 정량적으로 계산해주는 방법 (K-means 뿐만 아니라 모든 클러스터링 기법에 적용 가능)

- a(i): 클러스터 내 데이터 응집도(cohesion)를 나타내는 값

 (데이터 x(i)와 동일한 클러스터 내의 나머지 데이터들과의 평균 거리)

- b(i): 클러스터 간 분리도(separation)를 나타내는 값

 (데이터 x(i)와 가장 가까운 클러스터 내의 모든 데이터들과의 평균 거리)

- 만약 클러스터 개수가 최적화 되어 있다면 b(i)의 값은 크고, a(i)의 값은 작아짐

   -> s(i)의 값은 1에 가까운 숫자가 됨
- 반대로 클러스터내 데이터 응집도와 클러스터간 분리도의 값이 같으면 실루엣 계수 s(i)는 0 (데이터들을 클러스터로 분리하는 것이 무의미)

 

from sklearn.metrics import silhouette_score
model = cluster.KMeans(n_clusters=2) # Change the number of clusters
y_fitted = model.fit_predict(X)
silhouette_avg = silhouette_score(X, y_fitted)
print("The average of silhouette coefficients is :", silhouette_avg)

 

 

c.f.) 실루엣 기법의 코드 작동원리와 시각화 그래프가 궁금하면 아래를 참고하면 된다.

import numpy as np
from sklearn.metrics import silhouette_samples
from matplotlib import cm

def plotSilhouette(X, y_fitted):
    cluster_labels = np.unique(y_fitted)
    n_clusters = cluster_labels.shape[0] # ex) (3,) -> 3
    silhouette_vals = silhouette_samples(X, y_fitted, metric='euclidean') # y_fitted 클러스터 라벨을 기준으로 한 X 데이터 각각이 가지는 실루엣 계수를 계산
    y_ax_lower, y_ax_upper = 0, 0
    yticks = []
    
    for index, label in enumerate(cluster_labels):
        cluster_silhouette_vals = silhouette_vals[y_fitted == label] # 각 라벨(center=3이면 0,1,2)에 해당하는 예측 데이터들의 실루엣 계수
        cluster_silhouette_vals.sort()
        
        # 라벨 순서대로 클러스터로 할당된 데이터 수만큼 y_ax_upper 에 더하여 y축 방향으로 쌓음
        y_ax_upper += len(cluster_silhouette_vals) 
        
        plt.barh(range(y_ax_lower, y_ax_upper), cluster_silhouette_vals, height=1.0) # barh(y, data), edge_color=None
        yticks.append((y_ax_lower + y_ax_upper) / 2) # 그래프에서 y축 위에 클러스터 번호 라벨링 적용
        
        # 라벨 순서대로 클러스터로 할당된 데이터 수만큼 y_ax_lower 에 더하여 y축 방향으로 쌓음
        y_ax_lower += len(cluster_silhouette_vals) 
        
    silhouette_avg = np.mean(silhouette_vals) # 전체 데이터에 대한 실루엣 계수의 평균
    plt.axvline(silhouette_avg, color='red', linestyle='--') # 전체 데이터에 대한 실루엣 계수의 평균을 수직선으로 표시
    print('The average silhouette value is', round(silhouette_avg, 2), '(near 0.7 or 0.7+ : desirable)')
    
    plt.yticks(yticks, cluster_labels+1)
    plt.ylabel('Cluster')
    plt.xlabel('Silhouette value')
    plt.show()