군집 평가(Cluster Evaluation)

  • 대부분의 군집화 데이터 셋은 비교할만한 데이터 셋을 가지고 있지 않다. 또한 군집화는 분류와 유사해 보일 수 있으나 성격이 많이 다르다.
  • 데이터 내에 숨어 있는 별도의 그룹을 찾아서 의미를 부여하거나 동일한 분류 값에 속하더라도 그 안에서 더 세분화된 군집화를 추구하거나 서로 다른 분류 값의 데이터도 더 넓은 군집화 레벨하 등의 영역을 가지고 있다.
  • 비지도 학습의 특성상 어떠한 지표라도 정확하게 성능을 평가하기는 어렵다. 그럼에도 불구하고 군집화의 성능을 평가하는 대표적인 방법으로 실루엣 분석을 이용한다.

 

실루엣 분석 이란

실루엣 분석은 각 군집 간의 거리가 얼마나 효율적으로 분리 돼있는지를 나타낸다. 효율적으로 잘 분리 됐다는 것은 다른 군집과의 거리는 떨어져 있고 동일 군집끼리의 데이터는 서로 가깝게 잘 뭉쳐 있다는 의미이다.

군집화가 잘 될 수록 개별 군집은 비슷한 정도의 여우 공간을 가지고 떨어져 있을 것이다.

실루엣 분석은 실루엣 계수를 기반으로 한다. 실루엣 계수는 개별 데이터가 가지는 군집화 지표이다.

개별 데이터가 가지는 실루엣 계수는 해당 데이터가 같은 군집 내의 데이터와 얼마나 가깝게 군집화 돼있고, 다른 군집에 있는 데이터와는 어얼마나 멀리 분리돼 있는지를 나타내는 지표이다 .

특정 데이터 포인트의 실루엣 계수 값은 해당 데이터 포인트와 같은 군집 내에 있는 다른 데이터 포인트와의 거리를 평균한 값 a(i), 해당 데이터 포인트가 속하지 않은 군집 중 가장 가까운 군집과의 평균 거리 b(i)를 기반으로 계산된다.

두 군집 간의 거리 값은 b(i) - a(i) 이며 이 값을 정규화 하기 위해 Max(a(i),b(i)) 값으로 나눈다. 따라서 i 번째 데이터 포인트의 실루엣 계수 값 s(i) 는 다음과 같이 정의된다.

실루엣 계수는 -1 에서 1사이의 값을 가지며, 1로 가까워 질수록 근처의 군집과 더 멀리 떨어져 있다는 것이고, 0에 가까울 수록 근처의 군집과 가까워 진다는 것이다.
- 값은 아예 다른 군집에 데이터 포인트가 할당 됐음을 뜻한다.

좋은 군집화가 되려면 다음 기준 조건을 만족해야 한다.

  1. 전체 실루엣 계수의 평균값, 즉 사이킷런읜 silhouette_score() 값은 0~1 사이의 값을 가지며, 1에 가까울 수록 좋다.
  2. 전체 실루엣 계수의 평균값과 더불어 개별 군집의 평균값의 편차가 작아야한다.
    즉 개별 군집의 실루엣 계수 평균값이 전체 실루엣 계수의 평균값에서 크게 벗어나지 않는 것이 중요하다.
    만약 전체 실루엣 계수의 평균값은 높지만, 특정 군집의 실루엣 계수 평균값만 유난히 높고 다른 군집들의 실루엣 계수 평균값은 낮으면 좋은 군집화가 아니다.

사이킷런은 이러한 실루엣 분석을 위해 다음과 같은 메서드를 제공한다.

  • sklearn.metrics.silhouette_samples(X, labels, metric=’euclidean’, **kwds) : 인자로 X feature 데이터 셋과 각 피처 데이터 셋이 속한 군집 레이블 값인 labels 데이터를 입력해주면 각 데이터 포인트의 실루엣 계수를 계산해 반환한다.
  • sklearn.metrics.silhouette_score(X, labels, metric=’euclidean’, smaple_szie = None, **kwds) : 인자로 X feature 데이터 셋과 각 피처 데이터 셋이 속한 군집 레이블 값인 labels 데이터를 입력해 주면 전체 데이터의 실루엣 계수 값을 평균해 반환한다. 즉 np.mead(silhouette_samples()) 이다. 이 값이 높을수록 군집화가 어느정도 잘 됐다고 판단할 수 있다. (절대적인 기준이 될 수는 없다.)

 

붓꽃(Iris) 데이터 셋을 이용한 클러스터 평가


붓꽃 데이터 셋의 군집화 결과를 실루엣 분석으로 평가해 보자. sklearn.metrics 모듈의
silhouette_samples() 와 silhouette_score()를 이용 함.

from sklearn.preprocessing import scale
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
# 실루엣 분석 metric 값을 구하기 위한 API 추가
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

%matplotlib inline

iris = load_iris()
feature_names = ['sepal_length','sepal_width','petal_length','petal_width']
irisDF = pd.DataFrame(data=iris.data, columns=feature_names)
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0).fit(irisDF)

irisDF['cluster'] = kmeans.labels_

# iris 의 모든 개별 데이터에 실루엣 계수값을 구함. 
score_samples = silhouette_samples(iris.data, irisDF['cluster'])
print('silhouette_samples( ) return 값의 shape' , score_samples.shape)

# irisDF에 실루엣 계수 컬럼 추가
irisDF['silhouette_coeff'] = score_samples

# 모든 데이터의 평균 실루엣 계수값을 구함. 
average_score = silhouette_score(iris.data, irisDF['cluster'])
print('붓꽃 데이터셋 Silhouette Analysis Score:'.format(average_score))

irisDF.head(3)
silhouette_samples( ) return 값의 shape (150,)
붓꽃 데이터셋 Silhouette Analysis Score:0.553
  sepal_length sepal_width petal_length petal_width cluster silhouette_coeff
0 5.1 3.5 1.4 0.2 1 0.851573
1 4.9 3.0 1.4 0.2 1 0.817887
2 4.7 3.2 1.3 0.2 1 0.830087

평균 실루엣 계수 값이 약 0.533이다. 1번 군집의 경우 0.8 이상의 높은 실루엣 계수 값을 나타내고 있다. 1번 군집이 아닌 다른 군집의 경우 실루엣 계수 값이 평균 보다 낮기 때문일 것이다.

군집별 평균 실루엣 계수 값을 알아보자.

irisDF.groupby('cluster')['silhouette_coeff'].mean()
cluster
0    0.417182
1    0.797630
2    0.451105
Name: silhouette_coeff, dtype: float64

1번 군집은 실루엣 계수 평균 값이 약 0.79인데 반해, 0번은 약 0.41, 2번은 0.45로 상대적으로 평균값이 1번에 비해 낮다.

 

클러스터별 평균 실루엣 계수의 시각화를 통한 클러스터 개수 최적화 방법


전체 데이터의 평균 실루엣 계수 값이 높다고 해서 반드시 최적의 군집 개수로 군집화가 잘 됐다고 볼수는 없다.

별 군집별로 적당히 분리된 거리를 유지하면서도 군집 내의 데이터가 서로 뭉쳐 있는 경우에 K-평균의 적절한 군집 개수가 설정됐다고 판단 할 수 있다.

여러개의 군집 개수가 주어졌을 때 이를 분석한 도표를 참고해 평균 실루엣 계수로 군집 개수를 최적화 하는방법을 알아보자.

첫 번째 경우는 다음 그림과 같이 주어진 데이터에 대해서 군집의 개수 2개를 정했을 때다.
이때 평균 실루엣 계수, 즉 silhouette_score 는 약 0.704로 매우 높다.

다음 그림에서 왼쪽 부분은 개별 군집에 속하는 데이터의 실루엣 계수를 2차원으로 나타낸 것이다. X축은 실루엣 계수 값이고, Y 축은 개별 군집과 이에 속하는 데이터 이다.

개별 군집은 Y축에 숫자 값으로 0, 1로 표시돼 있다. 이에 해당하는 데이터는 일일이 숫자 값으로 표시되지 않았지만, Y축 높이로 추측할 수 있다.
그리고 점선으로 표시된 선은 전체 평균 실루엣 계수 값을 나타낸다.

이로 판단해 볼 때 1번 군집의 모든 데이터는 평균 실루엣 계수 값 이상이지만, 2번 군집의 경우는 평균보다 적은 데이터 값이 매우 많다.

오른쪽에 있는 그림으로 그 이유를 보충해서 설명할 수 있다. 1번 군집의 경우는 0번 군집과 멀리 떨어져 있고, 내부 데이터끼리도 잘 뭉쳐 있다.

하지만 1번 군집의 경우는 내부 데이터 끼리 많이 떨어져 있는 모습이다.

다음 그림은 군집 개수가 3개일 경우이다. 전체 데이터의 평균 실루엣 계수 값은 약 0.588이다 . 1번, 2번 군집의 경우 평균보다 높은 실루엣 계수 값을 가지고 있지만, 0번의 경우 모두 평균보다 낮다.

오른쪽 그림을 보면 0번의 경우 내부 데이터 간의 거리도 멀지만, 2번 군집과도 거리가 가깝게 위치하고 있기 때문이다.

사이킷런에 있는 소스코드 중 왼쪽 그림의 군빕별 평균 실루엣 계수 값을 구하는 부분만 별도의 함수로 만들어 이를 시각화 해보자.

visualize_silhouette() 함수는 군집 개수를 변화시키면서 K – 평균 군집을 수행했을 때 개별 군집별 평균 실루엣 계수 값을 시각화해서 군집의 개수를 정하는데 도움을 준다.

### 여러개의 클러스터링 갯수를 List로 입력 받아 각각의 실루엣 계수를 면적으로 시각화한 함수 작성
def visualize_silhouette(cluster_lists, X_features): 

    from sklearn.datasets import make_blobs
    from sklearn.cluster import KMeans
    from sklearn.metrics import silhouette_samples, silhouette_score

    import matplotlib.pyplot as plt
    import matplotlib.cm as cm
    import math

    # 입력값으로 클러스터링 갯수들을 리스트로 받아서, 각 갯수별로 클러스터링을 적용하고 실루엣 개수를 구함
    n_cols = len(cluster_lists)

    # plt.subplots()으로 리스트에 기재된 클러스터링 수만큼의 sub figures를 가지는 axs 생성 
    fig, axs = plt.subplots(figsize=(4*n_cols, 4), nrows=1, ncols=n_cols)

    # 리스트에 기재된 클러스터링 갯수들을 차례로 iteration 수행하면서 실루엣 개수 시각화
    for ind, n_cluster in enumerate(cluster_lists):

        # KMeans 클러스터링 수행하고, 실루엣 스코어와 개별 데이터의 실루엣 값 계산. 
        clusterer = KMeans(n_clusters = n_cluster, max_iter=500, random_state=0)
        cluster_labels = clusterer.fit_predict(X_features)

        sil_avg = silhouette_score(X_features, cluster_labels)
        sil_values = silhouette_samples(X_features, cluster_labels)

        y_lower = 10
        axs[ind].set_title('Number of Cluster : '+ str(n_cluster)+'\n' \
                          'Silhouette Score :' + str(round(sil_avg,3)) )
        axs[ind].set_xlabel("The silhouette coefficient values")
        axs[ind].set_ylabel("Cluster label")
        axs[ind].set_xlim([-0.1, 1])
        axs[ind].set_ylim([0, len(X_features) + (n_cluster + 1) * 10])
        axs[ind].set_yticks([])  # Clear the yaxis labels / ticks
        axs[ind].set_xticks([0, 0.2, 0.4, 0.6, 0.8, 1])

        # 클러스터링 갯수별로 fill_betweenx( )형태의 막대 그래프 표현. 
        for i in range(n_cluster):
            ith_cluster_sil_values = sil_values[cluster_labels==i]
            ith_cluster_sil_values.sort()

            size_cluster_i = ith_cluster_sil_values.shape[0]
            y_upper = y_lower + size_cluster_i

            color = cm.nipy_spectral(float(i) / n_cluster)
            axs[ind].fill_betweenx(np.arange(y_lower, y_upper), 0, ith_cluster_sil_values, \
                                facecolor=color, edgecolor=color, alpha=0.7)
            axs[ind].text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
            y_lower = y_upper + 10

        axs[ind].axvline(x=sil_avg, color="red", linestyle="--")

make_blobs() 함수를 통해 4개 군집 중심의 500개 2차원 데이터 셋을 만들고 이를 K-budrbsdmfh 군집화할 때 2개, 3개, 4개, 5개 중 최적의 군집 개수를 시각화로 알아보자.

import numpy as np
# make_blobs 을 통해 clustering 을 위한 4개의 클러스터 중심의 500개 2차원 데이터 셋 생성  
from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples=500, n_features=2, centers=4, cluster_std=1, \
                  center_box=(-10.0, 10.0), shuffle=True, random_state=1)  

# cluster 개수를 2개, 3개, 4개, 5개 일때의 클러스터별 실루엣 계수 평균값을 시각화 
visualize_silhouette([ 2, 3, 4, 5], X)

앞에서 소개한 바와 마찬가지로 4개의 군집일 때 가장 최적이 됨을 알 수 있다. 이번에는 붓꽃 데이터를 이용해 K-평균 수행 시 최적의 군집 개수를 알아 보자.

from sklearn.datasets import load_iris

iris=load_iris()
visualize_silhouette([ 2, 3, 4,5 ], iris.data)

붓꽃 데이터를 K-평균으로 군집화할 경우에는 군집 개수를 2개로 하는 것이 가장 좋아보인다.

3개의 경우 평균 실루엣 계수 값도 2개보다 작을뿐더러 1번 군집과 다른 0번, 2번 군집과의 실루엣 계수의 편차가 크다. 4개 ,5개의 경우도 마찬가지다.

 

본문 내용은 파이썬 머신러닝 완벽 가이드 (권철민 저)을 요약정리한 내용입니다.

'머신러닝' 카테고리의 다른 글

GNM(Gaussian Mixture Model)  (0) 2020.08.24
평균이동(Mean Shift)  (0) 2020.08.24
군집화 - K-Means  (0) 2020.08.24
NMF(Non - Negative Matrix Factorization)  (0) 2020.08.23
Truncated SVD  (0) 2020.08.23

+ Recent posts