클립보드에 복사되었습니다
Post

Clustering(cont.)

Clustering(cont.)

Introduction

데이터를 “비슷한 것끼리” 묶는 비지도 학습(unsupervised learning)

  • 목적
    • 데이터의 집단[cluster, 어려운 말로 차원성(dimensionality)] 파악
    • 이상치(outlier) 탐지
    • 패턴파악

분류(classification) vs 클러스터링(clutering)

supervised(정답 있음, 그룹(레이블, 정답)이 미리 주어짐) vs unsupervised(그룹을 찾아냄, 비슷한것끼리 묶어줌)

종류

  • 계층적(Hierarchical)
  • 분할적(Partioning)
  • Mixture Models

Similarity & Dissimilarity

유사도란 “비슷한 것 끼리 묶자”를 수치로 표현하는 방법

  • row(행)을 묶을 때 : 거리(distance)
  • col(열)을 묶을 때 : 상관계수(correlation)와 같은 연관도(association measure) 사용

대표적인 거리

유클리드(euclidean, L2 norm)

피타고라스 거리 두 점을 잇는 가장 짧은 거리

범위 : $[0,∞)$, 0에가까울 수록 유사함

맨해튼 거리(manhattan, L1 norm)

택시 거리 격자를 따라 이동하는 가장 짧은 거리

[!NOTE]

민코프스키 거리

q=1 : 맨해튼 거리

q=2 : 유클리드 거리

q 값이 커질 수록, 큰 차이의 좌표에 압도된다. 즉 가장 큰 좌표의 차이가 모든것을 압도하여 그 값만 남게된다. 즉 체스판에서 킹의 최소 움직임횟수와 동일하다. 따라서 체스판거리라고도 불리운다.

범위 : $[0,∞)$, 0에가까울 수록 유사함

마할라노비스 거리(mahalanobis)

공분산을 고려하여 변수 스케일, 상관성까지 반영한 거리로 다차원 데이터에서 실제 분포 모양까지 고려

**범위 : $[0,∞)$, 0에가까울 수록 통계적으로 유사함

캔버라(canberra)

값이 0 근처일때 민감하게 반응하는 거리이며, 양의 값에서 주로 사용한다. 즉 작은값에 민감하게 반응하는 거리이다.

**범위 : $[0,∞)$, 0에가까울 수록 유사함

체카노프스키(Czekanowski)

두 대상이 얼마나 겹치는지에 대한 척도

**범위 : $[0,1]$, 1에가까울 수록 유사함

이진 변수 유사도

  • 0-0을 유사하게 볼지, 유사하지않게 볼지에 따라 가중을 다르게 줄 수 있다. 즉 아니다-아니다가 같지 않을수도 같을수도 있다.
  • e.g. 1 : 콜라를 사다, 0 = 콜라를 사지 않다. 0-0은 콜라를 사지 않는 경우에는 다른것을 사서 안 산 것일 수도 있다. 사이다-닥터페퍼 이런형식이다.

**범위 : $[0,1]$, 1에가까울 수록 유사함

Gower

혼합변수에 사용한다. 변수마다 $s_{ijk}$를 구한 후 한번에 계산한다.

**범위 : $[0,1]$, 1에가까울 수록 유사함

Example

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
29
# L2 norm
import numpy as np

from scipy.spatial import distance

  

def pairwise_l2(P):

P = np.atleast_2d(P)

diff = P[:, None, :] - P[None, :, :] # (n,n,3)

return np.sqrt(np.sum(diff**2, axis=2)) # (n,n)

  

D_l2 = pairwise_l2(points)

  

# 검증

D_l2_sp = distance.squareform(distance.pdist(points, metric='euclidean'))

print("euclidean allclose:", np.allclose(D_l2, D_l2_sp))

D_l2[:3, :3]

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
29
# L1 norm
import numpy as np

from scipy.spatial import distance

  

def pairwise_l1(P):

P = np.atleast_2d(P)

diff = np.abs(P[:, None, :] - P[None, :, :])

return np.sum(diff, axis=2)

  

D_l1 = pairwise_l1(points)

  

# 검증

D_l1_sp = distance.squareform(distance.pdist(points, metric='cityblock'))

print("manhattan allclose:", np.allclose(D_l1, D_l1_sp))

D_l1[:3, :3]

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
29
30
31
# minkowski : q=3
import numpy as np

from scipy.spatial import distance

  

def pairwise_minkowski(P, q=3):

P = np.atleast_2d(P)

diff_q = np.abs(P[:, None, :] - P[None, :, :]) ** q

return np.sum(diff_q, axis=2) ** (1.0 / q)

  

q = 3

D_mink = pairwise_minkowski(points, q=q)

  

# 검증

D_mink_sp = distance.squareform(distance.pdist(points, metric='minkowski', p=q))

print(f"minkowski(q={q}) allclose:", np.allclose(D_mink, D_mink_sp))

D_mink[:3, :3]

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
29
# chebyshev
import numpy as np

from scipy.spatial import distance

  

def pairwise_chebyshev(P):

P = np.atleast_2d(P)

diff = np.abs(P[:, None, :] - P[None, :, :])

return np.max(diff, axis=2)

  

D_ch = pairwise_chebyshev(points)

  

# 검증

D_ch_sp = distance.squareform(distance.pdist(points, metric='chebyshev'))

print("chebyshev allclose:", np.allclose(D_ch, D_ch_sp))

D_ch[:3, :3]

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
29
30
31
32
33
34
35
36
37
# canberra
import numpy as np

from scipy.spatial import distance

  

def pairwise_canberra(P):

P = np.atleast_2d(P)

X = P[:, None, :] # (n,1,3)

Y = P[None, :, :] # (1,n,3)

num = np.abs(X - Y)

den = np.abs(X) + np.abs(Y)

frac = np.divide(num, den, out=np.zeros_like(num), where=(den != 0))

return np.sum(frac, axis=2)

  

D_can = pairwise_canberra(points)

  

# 검증

D_can_sp = distance.squareform(distance.pdist(points, metric='canberra'))

print("canberra allclose:", np.allclose(D_can, D_can_sp))

D_can[:3, :3]

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# mahalanobis
import numpy as np

from scipy.spatial import distance

  

def pairwise_mahalanobis(P, S_inv):

P = np.atleast_2d(P)

X = P[:, None, :] # (n,1,3)

Y = P[None, :, :] # (1,n,3)

Δ = Y - X # (n,n,3)

d2 = np.einsum('ijk,kl,ijl->ij', Δ, S_inv, Δ) # 각 쌍의 제곱거리

return np.sqrt(np.maximum(d2, 0.0))

  

# 공분산 및 역행렬

S = np.cov(points.T, bias=False)

S_inv = np.linalg.pinv(S) # 정칙화/안정성 목적

  

D_mah = pairwise_mahalanobis(points, S_inv)

  

# 검증

D_mah_sp = distance.squareform(distance.pdist(points, metric='mahalanobis', VI=S_inv))

print("mahalanobis allclose:", np.allclose(D_mah, D_mah_sp))

D_mah[:3, :3]

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
29
30
31
32
33
34
35
36
37
38
39
# binary
import numpy as np

from scipy.spatial import distance

  

# 예시 이진 데이터 (3D 대신 0/1 피처로 시범)

np.random.seed(0)

bin_points = np.random.randint(0, 2, size=(10, 8)) # 10개 샘플, 8개 이진 피처

  

def pairwise_smc_distance(B):

B = np.atleast_2d(B).astype(int)

eq = (B[:, None, :] == B[None, :, :]).astype(float) # (n,n,p)

smc = np.mean(eq, axis=2) # (a+d)/p

return 1.0 - smc # distance

  

D_bin = pairwise_smc_distance(bin_points)

  

# 검증 (Hamming = (b+c)/p = 1 - (a+d)/p)

D_bin_sp = distance.squareform(distance.pdist(bin_points, metric='hamming'))

print("binary (SMC) allclose:", np.allclose(D_bin, D_bin_sp))

D_bin[:3, :3]

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
29
30
31
32
33
34
35
36
37
38
39
# czekanowski
import numpy as np

  

def pairwise_czekanowski(P):

P = np.atleast_2d(P)

X = P[:, None, :] # (n,1,p)

Y = P[None, :, :] # (1,n,p)

num = 2.0 * np.sum(np.minimum(X, Y), axis=2)

den = np.sum(X + Y, axis=2)

S = np.divide(num, den, out=np.zeros_like(num), where=(den != 0))

return 1.0 - S

  

# 테스트용: 비음수 데이터로 변환하여 사용(예: 절댓값)

nonneg_points = np.abs(points.copy())

D_cz = pairwise_czekanowski(nonneg_points)

  

# 자체 성질 체크

print("czekanowski symmetric:", np.allclose(D_cz, D_cz.T))

print("czekanowski zero diag:", np.allclose(np.diag(D_cz), 0.0))

D_cz[:3, :3]

계층적 클러스터링

종류

  • Agglomerative(bottom-up) : 모든 데이터 포인트가 독립된 클러스터로 시작하여 가장 가까운 두 클러스터를 합쳐간다. 최종적으로 하나가 될 때 까지 반복한다
    1. 모든 점을 독립된 n개의 클러스터로 시작(데이터 수 만큼 클러스터)
    2. 현재 클러스터 집합에서 가장 가까운 두 집합(A, B)의 linkage를 찾음
    3. A와 B집합을 합침
    4. 클러스터가 특정 threshold 혹은 1이 될때까지 반복
  • Divisve(top-down) : 모든 점을 하나의 클러스터로 시작하여 계속 쪼개간다.

Linkage(거리)

  • Single linkage : 두 클러스터안의 데이터 포인트에 대하여 가장 가까운 거리

  • complete linkage : 두 클러스터안의 데이터 포인트에 대하여 가장 먼 거리

  • average linkage : 두 클러스터 간 평균 거리

Dendrogram(덴드로그램)

계층적 클러스터링 다운로드

This post is licensed under CC BY 4.0 by the author.