Developer's Development
3.2.13 [머신러닝] 비지도 학습 본문
군집 (Clustering)
비지도 학습 알고리즘의 한 종류로, 레이블이 없는 데이터를 유사성에 따라 그룹(클러스터)로 나눈 데 사용된다. 군집은 데이터의 내재된 구조를 파악하거나, 탐색적 데이터 분석(EDA)에 유용하다.
각 클러스터는 내부적으로는 데이터 간의 유사성이 높고, 다른 클러스터와는 유사성이 낮도록 구성된다. 군집은 데이터의 숨겨진 패턴이나 구조를 발견하는 데 중점을 둔다.
https://scikit-learn.org/stable/modules/clustering.html
2.3. Clustering
Clustering of unlabeled data can be performed with the module sklearn.cluster. Each clustering algorithm comes in two variants: a class, that implements the fit method to learn the clusters on trai...
scikit-learn.org
- 군집의 목적
데이터의 그룹화, 데이터의 분포 이해, 노이즈 제거, 새로운 데이터의 레이블 생성
- 군집과 분류의 차이
군집 : 레이블이 없는 데이터를 그룹화한다. (비지도 학습)
분류 : 이미 정의된 레이블에 데이터를 매핑한다. (지도 학습)
- 군집 알고리즘
1. K-평균 군집 (K-Means Clustering)
👉🏻 데이터를 k개의 클러스터로 나누는 가장 널리 사용되는 군집 알고리즘이다.
👉🏻 알고리즘 작동 방식
1) 클러스터 중심(centroid)을 초기화한다.
2) 각 데이터 포인트를 가장 가까운 클러스터 중심에 할당한다.
3) 클러스터 중심을 재계산한다.
4) 중심이 수렴할 때까지 반복한다.
👉🏻 장점 : 간단하고 빠르므로 대규모 데이터셋에 적합하다.
👉🏻 단점 : K값을 미리 설정해야 하고, 데이터 분포가 구형(Gaussian)에 가까워야 잘 작동한다.
2. 계층적 군집 (Hierarchical Clustrering)
👉🏻 데이터를 계층 구조로 그룹화하는 알고리즘이다.
👉🏻 계층적 군집의 두 가지 유형
- 병합형(Agnes) : 각 데이터를 개별 클러스터로 시작하여 점차 병합
- 분할형(Diana) : 하나의 클러스터로 시작하여 점차 분할
👉🏻 결과는 덴드로그램(Dendrogram)으로 시각화된다.
3. DBSCAN (Destiny-Based Spatial Clustrering of Applications with Noise)
👉🏻 밀도 기반 군집 알고리즘으로, 밀도가 높은 지역을 클러스터로 식별한다.
👉🏻 장점 : 클러스터의 모양이 비구형이어도 잘 작동하고, 노이즈를 효과적으로 처리한다.
👉🏻 단점 : 하이퍼파라미터 ϵ(epsilon)과 최소 데이터 포인트 설정에 민감하다.
가우시안 혼합 (Gaussian Mixture Model, GMM)
데이터가 여러 개의 가우시안 분포로 구성된다고 가정하고, 이를 기반으로 군집을 수행하는 비지도 학습 알고리즘이다. 각 데이터 포인트는 특정 가우시안 분포에 속할 확률로 표현된다.
- 혼합 분포
GMM은 각 데이터 포인트가 K개의 가우시안 분포 중 하나에서 생성된 것으로 본다.
- GMM과 K-평균의 차이
K-평균 : 각 데이터가 특정 클러스터에 완전히 속한다고 가정한다.
GMM : 각 데이터가 클러스터에 속할 확률을 기반으로 군집화한다.
실루엣 분석 (Silhouette Analysis)
- 실루엣 계수 (Silhouette Coefficient)
데이터 포인트가 얼마나 잘 군집화되었는지 평가하는 지표다.
DBSCAN 군집화
밀도 기반 군집화 알고리즘이다. 군집은 데이터 포인트 주변의 밀집도를 기준으로 형성된다.
핵심 파라미터는 ϵ(거리 임계값)과 minPts(최소 이웃 데이터 수)이다.
👉🏻 핵심 포인트(Core Point) : ϵ 거리 내에 최소 minPts 이상의 이웃이 있는 데이터
👉🏻 경계 포인트(Border Point) : 핵심 포인트 주변에 있지만, minPts에는 미치지 못하는 데이터
👉🏻 노이즈 포인트(Noise Point) : 어느 군집에도 속하지 않는 데이터
실습 (KMeans)
- KMeans
K개의 그룹(중심점을 기준)으로 데이터 포인트를 나눔
👉🏻 작동 단계
1. K개의 중심점 임의 선택
2. 각 데이터 포인트를 가장 가까운 중심점에 할당 > 군집 형성
3. 각 군집의 데이터 포인트 기반으로 새로운 중심점 계산
4. 2~3단계를 중심점의 변화가 거의 없을 때까지 반복 실행
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
iris = load_iris()
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
iris_df[iris.target_names[0]] = iris.target
# KMeans
from sklearn.cluster import KMeans
kmeans = KMeans(
n_clusters=3, # 클러스터 개수 (= 중심정 개수)
init='k-means++', # 초기 중심점 설정 방식
max_iter=300, # 최대 반복 횟수
random_state=0
)
kmeans.fit(iris.data) # 중심점 찾기
kmeans.transform(iris.data) # 중심점과의 거리 계산 및 반환 /(150, 3) 배열 반환
kmeans.labels_ # 군집화 결과
# 군집화 결과 확인
iris_df['cluster'] = kmeans.labels_
iris_df.groupby('species')['cluster'].value_counts()
# 시각화를 위한 PCA (2개의 주성분으로 변환)
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca_transformed = pca.fit_transform(iris.data)
iris_df['pca1'] = pca_transformed[:, 0]
iris_df['pca2'] = pca_transformed[:, 1]
print(kmeans.cluster_centers_) # 클러스터의 중심점
# 시각화를 위해 중심점도 차원 축소
centers = pca.transform(kmeans.cluster_centers_)
# species(라벨 데이터) 기준 시각화
plt.scatter(x=iris_df['pca1'], y=iris_df['pca2'], c=iris_df['species'])
plt.title('Species')
plt.show()
# cluster(kmeans 군집 데이터) 기준 시각화
plt.scatter(x=iris_df['pca1'], y=iris_df['pca2'], c=iris_df['cluster'])
plt.title('Cluster')
plt.show()


- 과일 데이터 군집
흑백 이미지 데이터 픽셀값 0(흑) ~ 255(백)
# 이미지 시각화 함수
def draw_fruits(arr, ratio=1):
N = len(arr)
rows = int(np.ceil(N / 10))
cols = N if rows < 2 else 10
fig, ax = plt.subplots(rows, cols, figsize=(cols * ratio, rows * ratio), squeeze=False)
for i in range(rows):
for j in range(cols):
if i * 10 + j < N:
ax[i, j].imshow(arr[i * 10 + j], cmap='gray_r')
ax[i, j].axis('off')
plt.show()
fruits = np.load('./data/fruits_300.npy')
fruits.shape # (300, 100, 100)
# KMeans 군집 적용을 위한 reshape
fruits_1d = fruits.reshape(-1, 100 * 100)
# KMeans 적용
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(fruits_1d)
kmeans.transform(fruits_1d)
np.unique(kmeans.labels_, return_counts=True) # (array([0, 1, 2], dtype=int32), array([112, 98, 90]))
# 각 클러스터별 이미지 시각화
draw_fruits(fruits[kmeans.labels_ == 0])
draw_fruits(fruits[kmeans.labels_ == 1])
draw_fruits(fruits[kmeans.labels_ == 2])



# PCA 적용 후 clustering
pca = PCA(n_components=2)
fruits_pca = pca.fit_transform(fruits_1d)
fruits_pca.shape
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(fruits_pca)
np.unique(fruits[kmeans.labels_ == 0])
np.unique(fruits[kmeans.labels_ == 1])
np.unique(fruits[kmeans.labels_ == 2])
draw_fruits(fruits[kmeans.labels_ == 0])
draw_fruits(fruits[kmeans.labels_ == 1])
draw_fruits(fruits[kmeans.labels_ == 2])



# 중심점 시각화
draw_fruits(kmeans.cluster_centers_.reshape(-1, 100, 100))

pred = kmeans.predict(fruits_pca[100:101])
print(pred) # [0]
draw_fruits(fruits[100:101]) # 시각화 결과 파인애플 (파인애플 == 0 클러스터)

- 최적의 k값 찾기
inertia : 중심점으로 각 데이터포인트의 분산값 ▶️ 이너셔 값이 작을수록 군집이 잘 되어 있다고 볼 수 있음
Elbow 기법 : inertia 값이 급격히 감소하는 k값을 최적의 k값으로 판단
inertias = []
for k in range(2, 12):
kmeans = KMeans(n_clusters=k, random_state=42)
kmeans.fit(fruits_pca)
inertias.append(kmeans.inertia_) # inertia 속성 확인
inertias
plt.plot(range(2, 12), inertias)
plt.xlabel('K')
plt.ylabel('inertia')
plt.show()

실습 (Silhouette Score: 군집 평가)
- Silhouette Score : 군집 평가
- 실루엣 계수를 통해 군집화의 품질 평가
- 실루엣 계수는 -1에서 1 사이의 값을 가짐
- 1에 가까울수록 군집화 잘됨 (다른 군집과 잘 분리되어 있음)
- 0은 군집의 경계에 위치함
- -1은다른 군집과 겹치거나 잘못 분류된 경우
👉🏻 주요 함수
- silhouette_samples : 개별 데이터 포인트의 점수
- silhouette_score : 전체 데이터 포인트의 평균값
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
iris_data = load_iris()
iris_df = pd.DataFrame(iris_data.data, columns=iris_data.feature_names)
iris_df['species'] = iris_data.target
# KMeans 군집화
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=100, random_state=0)
kmeans.fit(iris_data.data)
iris_df['cluster'] = kmeans.labels_
# 실루엣 계수 측정
sil_samples = silhouette_samples(iris_data.data, kmeans.labels_)
sil_samples.shape # (150,)
# 전체 클러스터의 실루엣 계수 == 개별 데이터 포인트의 실루엣 계수 평균
sil_score = silhouette_score(iris_data.data, kmeans.labels_)
sil_score # (0.551191604619592, np.float64(0.551191604619592))
실습 (GMM: Gaussian Mixture Model)
# 데이터 생성 및 시각화
from sklearn.datasets import make_classification
import matplotlib.pyplot as plt
X, _ = make_classification(
n_samples=300, # 데이터 개수
n_features=2, # 특성 개수
n_informative=2, # 유의미한 특성 개수
n_redundant=0, # 중복 특성 개수
n_clusters_per_class=1, # 클래스 당 클러스터 개수
n_classes=2, # 클래스(레이블) 개수
random_state=42
)
plt.scatter(X[:, 0], X[:, 1], s=50, c='gray', marker='o', edgecolors='k')
plt.title('scikit-learn making data')
plt.show()

# GNM 적용 및 군집화 결과 시각화
from sklearn.mixture import GaussianMixture
gnm = GaussianMixture(n_components=2, random_state=42)
gnm.fit(X)
labels = gnm.predict(X)
plt.scatter(X[:, 0], X[:, 1], c=labels, s=50, edgecolors='k')
plt.title('GNM Cluster result')
plt.show()

# 군집 포함 확률 시각화
probs = gnm.predict_proba(X)
plt.scatter(X[:, 0], X[:, 1], c=probs.max(axis=1), s=50, edgecolors='k')
plt.title('GNM Cluster rate')
plt.colorbar(label='rate')
plt.show()

실습 (DBSCAN, Destiny-Based Special Clustering of Application with Noise)
밀도(데이터포인트의 간격) 기반 군집 알고리즘
👉🏻 장점
비구형 클러스터 방지
노이즈 데이터 처리
비지도 학습 (클러스터 개수를 사전에 알 필요가 없음)
👉🏻 단점
데이터 밀도가 자주 변하거나 아예 변하지 않으면 군집화 성능 저하
특성 개수가 많으면 군집화 성능 저하 (고차원 데이터에서의 밀도 불균형)
매개변수 민감성
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN
import matplotlib.pyplot as plt
X, _ = make_moons(n_samples=300, noise=0.1, random_state=42)
dbscan = DBSCAN(eps=0.2, min_samples=6) # eps(이웃 점의 거리 반지름, 0.5), min_samples(minPts, 5)
dbscan.fit(X) # 클러스터링 계산
plt.scatter(X[:, 0], X[:, 1], c=dbscan.labels_)
plt.show()

- iris 데이터셋에 DBSCAN 적용
from sklearn.datasets import load_iris
import pandas as pd
from sklearn.decomposition import PCA
iris = load_iris()
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
iris_df['species'] = iris.target
# DBSCAN 적용
dbscan = DBSCAN(eps=0.6, min_samples=4)
dbscan.fit_predict(iris.data) # dbscan은 transform, predict가 없음
iris_df['cluster'] = dbscan.labels_
iris_df.groupby('species')['cluster'].value_counts()
# 시각화를 위한 PCA
pca = PCA(n_components=2)
pca_transformed = pca.fit_transform(iris.data)
iris_df['pca1'] = pca_transformed[:, 0]
iris_df['pca2'] = pca_transformed[:, 1]
# species(라벨 데이터) 기준 시각화 + cluster(dbscan 군집 데이터) 기준 시각화
fit, ax = plt.subplots(1, 2, figsize=(12, 6))
ax[0].scatter(x=iris_df['pca1'], y=iris_df['pca2'], c=iris_df['species'])
ax[1].scatter(x=iris_df['pca1'], y=iris_df['pca2'], c=iris_df['cluster'])
plt.show()

'데이터 분석과 머신러닝, 딥러닝 > 머신러닝' 카테고리의 다른 글
| 3.2.17 [머신러닝] etc (5) | 2025.08.03 |
|---|---|
| 3.2.12 [머신러닝] 차원 축소 (3) | 2025.08.03 |
| 3.2.11 [머신러닝] 앙상블 (2) | 2025.08.03 |
| 3.2.10 [머신러닝] 서포트 벡터 머신 (0) | 2025.07.24 |
| 3.2.9 [머신러닝] 결정 트리 (6) | 2025.07.24 |