Programming

K-Means 군집 성능 평가를 위한 실루엣(Silhouette) 분석 완전 이해

Lucas.Kim 2026. 1. 12. 14:27
반응형

1. 개요

군집화(Clustering)는 정답(label)이 없는 비지도 학습이기 때문에
모델 성능을 단순 정확도로 평가할 수 없습니다.

따라서 군집이 얼마나 잘 분리되었는지를 정량적으로 판단할 수 있는 지표가 필요하며,
그중 가장 널리 사용되는 방법이 실루엣(Silhouette) 분석입니다.

이번 글에서는

  • 실루엣 계수의 개념과 수식
  • 붓꽃(Iris) 데이터셋을 이용한 실루엣 분석 실습
  • 클러스터 개수(K) 최적화를 위한 실루엣 시각화
  • 다양한 K값에 따른 실루엣 점수 비교

입문자도 이해할 수 있도록 단계별로 정리합니다.


2. 실루엣 분석 개념 정리

2-1. 실루엣 분석이란?

실루엣 분석은 다음 조건을 동시에 만족하는지를 평가합니다.

  • 같은 군집 내 데이터끼리는 서로 가깝고
  • 다른 군집과는 멀리 떨어져 있는지

즉, 군집 간 분리도(separation)
군집 내 응집도(cohesion) 를 함께 측정합니다.


2-2. 실루엣 계수 수식

개별 데이터 ii에 대한 실루엣 계수는 다음과 같습니다.

  • a(i)
    → 데이터 i가 같은 군집 내 다른 데이터들과의 평균 거리
  • b(i)
    → 데이터 i가 가장 가까운 다른 군집과의 평균 거리

값 해석

  • 1에 가까울수록 → 매우 잘 군집화됨
  • 0에 가까울수록 → 군집 경계에 위치
  • 음수 → 잘못된 군집에 할당되었을 가능성 높음

3. 붓꽃(Iris) 데이터 셋으로 실루엣 분석 실습

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

👉

  • silhouette_samples() : 개별 데이터의 실루엣 계수
  • silhouette_score() : 전체 평균 실루엣 계수

3-1. 붓꽃 데이터 DataFrame 변환

iris = load_iris()
feature_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']

irisDF = pd.DataFrame(
    data=iris.data,
    columns=feature_names
)

👉

  • 붓꽃 데이터의 4개 피처를 DataFrame으로 변환합니다.
  • 이후 군집 결과와 실루엣 계수를 함께 관리하기 위함입니다.

3-2. K-Means 군집화 수행

kmeans = KMeans(
    n_clusters=3,
    init='k-means++',
    max_iter=300,
    random_state=0
)
kmeans.fit(irisDF)

👉

  • 군집 개수는 3개로 지정합니다.
  • 이는 붓꽃 데이터의 실제 클래스 개수와 동일하지만,
    군집화는 클래스 정보를 사용하지 않습니다.

3-3. 군집 결과 저장

irisDF['cluster'] = kmeans.labels_

print(irisDF.shape)
display(irisDF.head(5))

👉

  • 각 데이터가 어느 군집에 속했는지를 cluster 컬럼에 저장합니다.


4. 개별 데이터 실루엣 계수 계산

score_samples = silhouette_samples(
    iris.data,
    irisDF['cluster']
)
print(f'silhouette_samples return 값 : {score_samples.shape}')

👉

  • 붓꽃 데이터 150개 각각에 대한 실루엣 계수가 계산됩니다.
  • 결과 shape은 (150,) 입니다.
irisDF['silhouette_coef'] = score_samples
irisDF.head(10)

 

  • 각 데이터가 얼마나 잘 군집화되었는지를 수치로 확인할 수 있습니다.
  • Setosa 군집은 매우 높은 실루엣 계수를 가짐을 확인할 수 있습니다.

4-1. 전체 평균 실루엣 계수

average_score = silhouette_score(
    iris.data,
    irisDF['cluster']
)
print(f'붓꽃 데이터 셋 : {average_score}')

 

👉

  • 붓꽃 데이터의 전체 평균 실루엣 계수입니다.
  • 일반적으로 0.5 이상이면 군집 품질이 양호하다고 판단합니다.


4-2. 군집별 평균 실루엣 계수

irisDF.groupby('cluster')['silhouette_coef'].mean()

👉

  • 특정 군집이 다른 군집보다 더 응집도가 높은지 확인할 수 있습니다.
  • Setosa 군집이 가장 안정적인 군집임을 확인할 수 있습니다.

4-3. 실루엣 계수 분포 확인

irisDF['silhouette_coef'].hist()

irisDF['silhouette_coef'].mean()

👉

  • 실루엣 계수 분포를 통해
    음수 값이 존재하는지, 경계 데이터가 많은지를 시각적으로 확인합니다.

5. 실루엣 시각화를 통한 클러스터 개수(K) 최적화

5-1. 실루엣 시각화 함수 정의

# 여러개의 클러스터링 개수를 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="--")

👉

  • 여러 개의 K값을 동시에 비교하여
  • 어떤 K가 가장 안정적인 군집 구조를 만드는지 시각적으로 확인하기 위한 함수입니다.

5-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
)

visualize_silhouette([2, 3, 4, 5], X)

👉

  • 실제 군집 개수가 4개인 데이터에서
  • K=4일 때 평균 실루엣 점수가 가장 높게 나타남을 확인할 수 있습니다.

5-3. 붓꽃 데이터에 대한 K 비교

from sklearn.datasets import load_iris

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

👉

  • 붓꽃 데이터에서는 K=3이 가장 합리적인 선택임을 확인할 수 있습니다.

5-4. 공식 예제 기반 상세 실루엣 분석

(아래 코드는 K값 증가에 따른 실루엣 점수 출력 및 시각화)

range_n_clusters = [2, 3, 4, 5, 6]

#결과
For n_clusters = 2 → 0.7049
For n_clusters = 3 → 0.5882
For n_clusters = 4 → 0.6505
For n_clusters = 5 → 0.5614
For n_clusters = 6 → 0.4857

👉

  • 평균 실루엣 점수만 보면 K=2가 가장 높지만
  • 데이터 구조상 과도한 단순화일 수 있음
  • 실무에서는 점수 + 시각화 + 도메인 이해를 함께 고려해야 합니다.


6. 결론 정리

  • 실루엣 분석은 군집화 모델의 품질을 정량적으로 평가하는 핵심 도구입니다.
  • 평균 실루엣 계수는 군집 개수(K) 선택의 중요한 기준이 됩니다.
  • 단, 실루엣 점수 하나만으로 K를 결정해서는 안 되며
    • 데이터 분포
    • 시각화 결과
    • 비즈니스/도메인 해석
      을 함께 고려해야 합니다.
  • K-Means를 사용할 때는
    👉 차원 축소(PCA)
    👉 스케일링
    👉 실루엣 분석
    이 하나의 세트처럼 사용되는 것이 바람직합니다.
반응형