본문 바로가기
AI/Machine Learning

[ML] 군집분석(계층 군집, 밀집도 기반 군집)

by 안녕나는현서 2021. 5. 12.
728x90

- 계층 군집(Hierarchical clustering)

  • 덴드로그램(Dendrogram)을 그릴 수 있음
    • 의미 있는 분류 체계를 만들어줌 → 군집 결과 이해, 설명에 적합
  • 클러스터의 수를 미리 정하지 않아도 됨
  • 병합 계층 군집(Agglomerative hierarchical clustering)
    • 클러스터 당 하나의 데이터에서 시작하여, 모든 데이터가 하나의 클러스터에 속할 때까지 가장 가까운 클러스터를 병합해 나감
    • 단일 연결
      1. 클러스터 쌍에서 가장 비슷한, 즉 가장 가까운 데이터 간의 거리를 계산
      2. 거리의 값이 가장 작은 두 클러스터를 하나로 합침
    • 완전 연결
      1. 클러스터 쌍에서 가장 비슷하지 않은, 즉 가장 멀리 있는 데이터를 찾아 거리를 계산
      2. 가장 가까운 두 클러스터를 합침
    • 평균 연결 : 두 클러스터에 있는 모든 샘플 사이의 평균 거리가 가장 작은 클러스터 쌍을 합침
    • 와드 연결 : 클러스터 내 SSE가 가장 작게 증가하는 두 클러스터를 합침
  • 분할 계층 군집(Divisive hierarchical clustering)
    • 전체 데이터를 포함하는 하나의 클러스터에서 시작해, 클러스터 속 데이터가 하나가 남을 때까지 반복적으로 클러스터를 나눔

 

 

- 병합 계층 군집 : 완전 연결

  • 진행 과정
    1. 모든 데이터의 거리행렬을 계산
    2. 모든 데이터 포인트를 단일 클러스터로 표현
    3. 가장 비슷하지 않은, 즉 멀리 떨어진 데이터 간 거리에 기초하여 가장 가까운 두 클러스터를 하나로 합침
    4. 유사도 행렬 업데이트
    5. 하나의 클러스터가 남을 때 까지 2~4단계 반복
  • 파이썬으로 구현
# 1. 모든 데이터의 거리행렬 계산

import numpy as np
import pandas as pd

np.random.seed(123)
X = np.random.random_sample([5, 3]) * 10 # 무작위 함수를 통해 5*3 행렬 생성

variables = ['X', 'Y', 'Z']
labels = ['ID_0', 'ID_1', 'ID_2', 'ID_3', 'ID_4']
df = pd.DataFrame(X, columns = variables, index = labels) # 데이터프레임으로 저장
df

 

from scipy.spatial.distance import pdist, squareform

# pdist() : 축약된 거리행렬

Y = pdist(df)
Y

# squareform() : 거리행렬 벡터를 행렬형식으로 변환

row_dist = pd.DataFrame(squareform(pdist(df, metric = 'euclidean')),
                       columns = labels, index = labels)
row_dist

# 2~5. 계층 군집 시행

from scipy.cluster.hierarchy import linkage

# linkage : pdist함수에서 축약된 거리행렬을 입력 속성으로 사용

row_clusters = linkage(pdist(df, metric = 'euclidean'),
                       method = 'complete')

# linkage함수에서 초기 데이터 배열을 전달하고, euclidean 지표를 매개변수로 사용

row_clusters = linkage(df.values, metric = 'euclidean', method = 'complete')


# 군집 결과를 데이터프레임 형태로 반환

pd.DataFrame(row_clusters,
            columns = ['row label 1', 'row label 2', 'distance', 'no. of items in clust.'],
            index = ['cluster %d' %(i+1) for i in range(row_clusters.shape[0])])

  • 군집 결과 해석
    • 무작위 생성 데이터 5개 → 4개의 클러스터로 군집화됨
    • row label 1, 2는 각 클러스터에서 완전 연결방식으로 병합된 클러스터를 나타냄
      • 클러스터1의 경우, ID_0과 ID_4가 병합됨 → 이게 ID_5가 되는 것
      • 클러스터2의 경우, ID_1과 ID_2가 병합됨 → 이게 ID_6이 되는 것
      • 클러스터3의 경우, ID_3과 ID_5(ID_0 + ID_4)가 병합됨 → ID_7
      • 클러스터4의 경우, ID_6(ID_1 + ID_2)과 ID_7(ID_3 + (ID_0 + ID_4))가 병합됨
    • distance는 클러스터 간의 거리
    • no. of items in clust는 군집에 속한 데이터의 수
      • 클러스터1 : ID_0, ID_4 → 2개
      • 클러스터2 : ID_1, ID_2 → 2개
      • 클러스터3 : ID_0, ID_4, ID_3 → 3개
      • 클러스터1 : ID_1, ID_2, ID_3, ID_0, ID_4 → 5개
  • 덴드로그램 그리기
import scipy.cluster.hierarchy as sch
import matplotlib.pyplot as plt

row_dendr = sch.dendrogram(row_clusters, labels = labels)

plt.tight_layout()
plt.ylabel('Euclidean distance')
plt.show()

 

  • 히트맵(Heat map)
    • 열을 뜻하는 '히트' + 지도를 뜻하는 '맵'
    • 이미지의 위 또는 측면에 열분포 형태로 데이터를 표현하는 시각화 기법

 

 

- 밀집도 기반 군집(DBSCAN)

  • 원형 클러스터를 가정하지 않음
  • 밀집도 : 특정 반경(ε; 엡실론)안에 있는 샘플의 개수(MinPts)로 정의
  • 어떤 데이터의 특정 반경 안에 있는 이웃점이 우리가 임의로 지정한 개수 이상이면 해당 데이터는 중심점
    • 중심점을 중심으로 군집이 됨
    • 중심점이 서로 다른 중심점의 군집의 일부가 되면 그 군집은 서로 연결되어 있다고 하고 하나의 군집으로 연결

  • 가장 가까운 점 기준, 특정 반경 이내에 지정된 개수보다 이웃은 적지만, 다른 중심점의 반경 안에 있으면 경계점
    • 군집에는 속하지만 스스로 중심점이 되지 못하는 점
    • 클러스터의 외각을 이루는 점

  • 이러한 방식으로 점을 할당하고 나서 그 어떠한 점에도 속하지 않는 모든 점들은 이상치(noise point)
    • 그 어떠한 클러스터에도 속하지 못한 점
  • 모든 데이터들을 꼭 클러스터에 할당하지 않아도 되기에, 자연스럽게 이상치 데이터들을 구분할 수 있음
  • 데이터의 특성이 늘어남에 따라 차원의 저주로 인한 역효과가 증가(유클리디안 거리 측정 방식을 사용하는 다른 군집 알고리즘도 역시 영향 받음) → 차원 축소, 표준화 등으로 해결
  • 밀집도 기반 군집으로 좋은 군집 결과를 만들기 위해서 하이퍼 파라미터 최적화 필요
    • 엡실론, 최소 이웃수

 

 

- k-means군집, 계층 군집, 밀집도 기반 군집 비교

  • 데이터셋 불러오기
from sklearn.datasets import make_moons
import matplotlib.pyplot as plt

X, y = make_moons(n_samples = 200, noise = 0.05, random_state = 0)
# X, y에 각각 100개씩 할당

plt.scatter(X[:, 0], X[:, 1])
plt.tight_layout()
plt.show()

  • K-means 군집, 병합 계층 군집
f, (ax1, ax2) = plt.subplots(1, 2, figsize = (8, 3))


# K-means 군집

from sklearn.cluster import KMeans

km = KMeans(n_clusters = 2, random_state = 0)
y_km = km.fit_predict(X)

ax1.scatter(X[y_km == 0, 0], X[y_km == 0, 1],
           s = 40, c = 'lightblue', marker = 'o', edgecolor = 'black', label = 'cluster 1')

ax1.scatter(X[y_km == 1, 0], X[y_km == 1, 1],
           s = 40, c = 'red', marker = 's', edgecolor = 'black', label = 'cluster 2')

ax1.set_title('K-means clustering')


# 계층 군집

from sklearn.cluster import AgglomerativeClustering

ac = AgglomerativeClustering(n_clusters = 2,
                            affinity = 'euclidean',
                            linkage = 'complete')
y_ac = ac.fit_predict(X)

ax2.scatter(X[y_ac == 0, 0], X[y_ac == 0, 1],
           s = 40, c = 'lightblue', marker = 'o', edgecolor = 'black', label = 'cluster 1')

ax2.scatter(X[y_ac == 1, 0], X[y_ac == 1, 1],
           s = 40, c = 'red', marker = 's', edgecolor = 'black', label = 'cluster 2')

ax2.set_title('Agglomerative clustering')

plt.legend()
plt.tight_layout()
plt.show()

*구분 불가*

  • 밀집도 기반 군집
from sklearn.cluster import DBSCAN

db = DBSCAN(eps = 0.2,             # 엡실론 값
           min_samples = 5,        # 최소 이웃 수
           metric = 'euclidean')   # 거리 측정 기법
y_db = db.fit_predict(X)

plt.scatter(X[y_db == 0, 0], X[y_db == 0, 1],
           s = 40, c = 'lightblue', marker = 'o', edgecolor = 'black', label = 'cluster 1')

plt.scatter(X[y_db == 1, 0], X[y_db == 1, 1],
           s = 40, c = 'red', marker = 's', edgecolor = 'black', label = 'cluster 2')

plt.legend()
plt.tight_layout()
plt.show()

성공적으로 반달모양 감지, 복잡한 구조를 가진 데이터 셋을 구분하는 능력이 출중하다는 것을 알 수 있음

728x90

댓글