반응형

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)
👉 스케일링
👉 실루엣 분석
이 하나의 세트처럼 사용되는 것이 바람직합니다.
반응형
'Programming' 카테고리의 다른 글
| Gaussian Mixture Model(GMM) 군집화– K-Means의 한계를 극복하는 확률 기반 군집 알고리즘 (0) | 2026.01.12 |
|---|---|
| Mean Shift 군집화 완전 이해– KDE 기반 자동 군집 개수 결정 알고리즘 (0) | 2026.01.12 |
| K-Means 군집화 실험을 위한 인공 데이터 생성과 중심점 시각화 (0) | 2026.01.12 |
| 군집화(Clustering) 개념과 K-Means 알고리즘 실습 정리 (0) | 2026.01.12 |
| 차원 축소(Dimension Reduction) 완전 정리 요약PCA · LDA · SVD · Truncated SVD · NMF (0) | 2026.01.11 |