Notice
Recent Posts
Recent Comments
Link
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

Silver bullet

Clustering & K-means Algorithm / 클러스터 수 결정 기법 - Elbow method & Silhouette score 본문

AI/AI

Clustering & K-means Algorithm / 클러스터 수 결정 기법 - Elbow method & Silhouette score

밀크쌀과자 2024. 7. 11. 15:08

K-means Algorithm - 비지도 학습

  1. K개의 임의의 중심값을 고른다. (보통 데이터 샘플 중의 하나를 선택)
  2. 각 데이터마다 중심값까지의 거리를 계산하여 가까운 중심값의 클러스터에 할당한다.
  3. 클러스터에 속한 데이터들의 평균값으로 각 중심값을 이동시킨다.
  4. 데이터에 대한 클러스터 할당이 변하지 않을 때까지 2와 3을 반복한다.

x 데이터 열이 많아질수록, 클러스터 개수가 많아질수록 계산 시간이 오래걸린다.


 

from sklearn import cluster
import numpy as np

import warnings
warnings.filterwarnings("ignore") # 불필요한 Warning 메시지를 꺼줍니다.

# 데이터 준비
X = np.array([[2, 4], [2, 8], [2, 0],
              [8, 9], [4, 0], [0, 4]])
X

kmeans = cluster.KMeans(n_clusters=2, random_state=0).fit(X) 
kmeans

print("Clusters : ", kmeans.labels_)

print("Cluster centroids: ", kmeans.cluster_centers_) # 학습이 끝난 중심값을 확인하려면?
Clusters :  [0 1 0 1 0 0]
Cluster centroids:  [[2.  2. ]
 [5.  8.5]]
# 학습이 끝난 모델 테스트 하기 (on test data)
print("Prediction cluster of [0, 0], [8, 4]: ", (kmeans.predict([[0, 0], [8, 4]])))
Prediction cluster of [0, 0], [8, 4]:  [0 1]

K-means for Iris data

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

from sklearn import cluster
from sklearn import datasets
from sklearn import metrics

# 붓꽃 데이터 읽어들이기
iris = datasets.load_iris()

X = iris.data
y = iris.target
print(X)
print()
print(y)

# 비어있는 모델 객체 만들기 + 모델 학습시키기
estimators = [('k=8', cluster.KMeans(n_clusters=8)),
              ('k=3', cluster.KMeans(n_clusters=3)),
              ('k=3(r)', cluster.KMeans(n_clusters=3, n_init=1, init='random'))] # random init

print(estimators[0]), print()
print(estimators[1]), print()
print(estimators[2])

# K-Means++ 알고리즘 : 최초의 중심값을 설정하기 위한 개선된 알고리즘
# 참고 : https://goo.gl/1ghahK & https://goo.gl/69zSB2

기본적으로 default로는 init이 ' k-means++ '로 설정되어 안정적인 클러스터를 고를 수 있게 되나, init을 cluster.KMeans(n_clusters=3, n_init=1, init='random') 다음과 같이 random으로 설정하면 클러스터 분석이 잘 이루어지지 않을 수도 있다.

 

3D 모델 시각화 코드

fignum = 1
titles = ['8 clusters', '3 clusters', '3 clusters, bad initialization']

for name, est in estimators: # estimators : ('k=8', cluster.KMeans(n_clusters=8))
    fig = plt.figure(fignum, figsize=(7, 7))
    ax = Axes3D(fig, elev=48, azim=134) # Set the elevation and azimuth of the axes. (축의 고도와 방위각)
    est.fit(X)
    labels = est.labels_ # 150행 x데이터 행 각각에 대한 클러스터 번호(150개의 숫자)

    # X = iris.data
    ax.scatter(X[:, 3], X[:, 0], X[:, 2], c=labels.astype(np.float64), edgecolor='w', s=100)

    ax.w_xaxis.set_ticklabels([])
    ax.w_yaxis.set_ticklabels([])
    ax.w_zaxis.set_ticklabels([])
    ax.set_xlabel('Petal width')
    ax.set_ylabel('Sepal length')
    ax.set_zlabel('Petal length')
    ax.set_title(titles[fignum - 1])
    ax.dist = 12 # 값이 커지면 전체 plot 이 작아짐
    
    fignum = fignum + 1

plt.show()

 

- 정답이 존재하지 않는 경우, 클러스터를 몇개로 설정할 지 애매한 경우가 있다. → 클러스터링 모델 품질 평가


최적 클러스터 개수 찾기

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

from sklearn import cluster
from sklearn import datasets
from sklearn import metrics

import warnings
warnings.filterwarnings("ignore") # 불필요한 Warning 메시지를 꺼줍니다.

iris = datasets.load_iris()

X = iris.data
y = iris.target

 

(1) 엘보우(elbow) 기법 :

  • SSE(Sum of Squared Errors)의 값이 점점 줄어들다가 어느 순간 줄어드는 비율이 급격하게 작아지는 부분이 생기는데,
  • 결과물인 그래프 모양을 보면 팔꿈치에 해당하는 바로 그 부분이 최적의 클러스터 개수가 됨.
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.
        # inertia : 각 샘플마다 자신이 속한 클러스터의 센터까지의 거리를 계산한 후, 모든 거리 값들을 제곱해서 합친 값
        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()

elbow(X) # Iris case : 2

엘보우 기법

 

위의 경우 2 - 3 정도가 적당함

 

 

(2) 실루엣(silhouette) 기법 :

  • 클러스터링의 품질을 정량적으로 계산해주는 방법 (K-means 뿐만 아니라 모든 클러스터링 기법에 적용 가능)
  • i번째 데이터 x(i)에 대한 실루엣 계수(silhouette coefficient) s(i) 값은 아래의 식으로 정의 
  • a(i)는 클러스터 내 데이터 응집도(cohesion)를 나타내는 값 == 데이터 x(i)와 동일한 클러스터 내의 나머지 데이터들과의 평균 거리
  • b(i)는 클러스터 간 분리도(separation)를 나타내는 값 == 데이터 x(i)와 가장 가까운 클러스터 내의 모든 데이터들과의 평균 거리

  • 만약 클러스터 개수가 최적화 되어 있다면 b(i)의 값은 크고, a(i)의 값은 작아짐 -> s(i)의 값은 1에 가까운 숫자가 됨
  • 반대로 클러스터내 데이터 응집도와 클러스터간 분리도의 값이 같으면 실루엣 계수 s(i)는 0 (데이터들을 클러스터로 분리하는 것이 무의미)
  • 요약) 클러스터의 개수가 최적화되어 있으면 실루엣 계수의 값은 1에 가까운 값이 됨
# 활용 x
# 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()

# model = cluster.KMeans(n_clusters=2) # Change the number of clusters
# y_fitted = model.fit_predict(X)
# plotSilhouette(X, y_fitted)
The average silhouette value is 0.68 (near 0.7 or 0.7+ : desirable)
# 간단하게 실루엣 계수의 평균만을 바로 구해 확인해 볼 수 있습니다 (for 문을 활용하여 여러 cluster 수를 기준으로 한 모델을 동시 비교 가능)

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)
The average of silhouette coefficients is : 0.6810461692117462

실루엣 값의 평균값이 0.7 이상이면 클러스터링이 잘 되었다고 말할 수 있다.