본문 바로가기
데이터 청년 캠퍼스/SQL | 군집분석 | 소셜분석

[군집분석] 군집평가 및 비교 - 응집도, 분리도, 유사도 매트릭스, 엔트로피 / 최적 K 찾기 / 2차원 클러스터 / K-mean의 장단점과 한계

by 뚱뚜루뚱 2022. 7. 12.

엔트로피 Entropy 기반 군집 평가

군집결과의 엔트로피를 측정하여 군집평가에 사용

엔트로피 값이 낮을수록 안정적이고 변화가 적어 좋은 군집으로 평가한다

 

 

군집평가 실습

1. 필요한 패키지, 라이브러리, 데이터 불러오기

from __future__ import print_function
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
from pandas import DataFrame
from scipy.stats import entropy
from math import log, e
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
import pandas as pd
import math
import timeit

X = np.array([[2, 0], [3, 0], [4, 0], [10, 0], [11, 0], [12, 0],[20, 0], [25, 0], [30, 0]])
plt.scatter(X[:, 0], X[:, 1], s=100)
plt.show()

 

2. 군집 모델 생성하기

model1 = KMeans(n_clusters = 2, init = np.array([[3,0], [4,0]]), n_init = 1, max_iter =1, random_state = 1 ).fit(X) 
c0, c1 = model1.cluster_centers_
print(model1.labels_)
print(c0, c1)
print(abs(model1.score(X)))

model2 = KMeans(n_clusters = 2, init = np.array([[2.5,0], [16,0]]), n_init = 1, max_iter =1, random_state = 1 ).fit(X) 
c0, c1 = model2.cluster_centers_
print(model2.labels_)
print(c0, c1)
print(abs(model2.score(X)))

 

3. 분리도 구하기

def separation(model):
  total_mean = np.mean(X[:,0])
  mean1 = np.mean(X[model.labels_==0,0])
  mean2 = np.mean(X[model.labels_==1,0])
  count1 = np.count_nonzero(X[model.labels_==0,0])
  count2 = np.count_nonzero(X[model.labels_==1,0])

  separation_value = count1*(total_mean - mean1)**2 + count2*(total_mean - mean2)**2
  print(separation_value)
  
separation(model1)
separation(model2)

 

4. 유사도 매트릭스 구하기

list=[]

def s_matrix(model):
    for i in range(0,np.count_nonzero(X)):
        list.append([])
        for j in range(0, np.count_nonzero(X)):
            if model.labels_[i]==model.labels_[j]:
                list[i].append(1)
            else:
                list[i].append(0)
                
s_matrix(model1)
list

s_matrix(model2)
list

 

5. 엔트로피 구하기

def entropy(labels):
  n_labels = len(labels)
  counts = np.bincount(labels)
  probs = counts[np.nonzero(counts)] / n_labels
  n_classes = len(probs)

  return - np.sum(probs*np.log(probs)) / np.log(n_classes)
  
entropy(model1.labels_)
entropy(model2.labels_)

 

 

 

군집 비교

  • Cohesion(SSE) / Separation: 값이 작을수록 군집의 비율이 잘 분리됨
  • 유사도 매트릭스: 1 상자의 영역이 작을수록 군집의 비율이 잘 분리됨
  • Entropy: 값이 작을수록 군집의 비율이 잘 분리됨

 

 

군집비교 실습

1. 다양한 모델 생성

# 1회차
model1 = KMeans(n_clusters = 2, init = np.array([[3,0], [4,0]]), n_init = 1, max_iter =1, random_state = 1 ).fit(X) 
c1, c1 = model1.cluster_centers_
print(model1.labels_)
print(c0, c1)

# 2회차
model2 = KMeans(n_clusters=2, init=np.array([[3,0],[4,0]]), n_init=1, max_iter=2).fit(X)
c0, c1 = model2.cluster_centers_
print(model2.labels_)
print(c0, c1)

# 10회차
model10 = KMeans(n_clusters = 2, init = np.array([[3,0], [4,0]]), n_init = 1, max_iter =10, random_state = 1 ).fit(X) 
c0, c1 = model10.cluster_centers_
print(model10.labels_)
print(c0, c1)


# 군집이 3개인 경우
model3 = KMeans(n_clusters=3, init='random', n_init=10, max_iter=100).fit(X)
c0, c1 = model3.cluster_centers_
print(model3.labels_)
print(c0, c1)

 

2. 모델 비교

# SSE 비교
model1.inertia_, model2.inertia_, model10.inertia_, model3.inertia_


# Separation 비교
separation(model1), separation(model2), separation(model10), separation(model3)


# Matrix 비교
list = []
s_matrix(model1)
list

list = []
s_matrix(mode2)
list

list = []
s_matrix(model10)
list

list = []
s_matrix(model3)
list


# Entropy 비교
entropy(model1.labels_), entropy(model2.labels_),entropy(model10.labels_), entropy(model3.labels_)

 

 

Internal Measures: SSE 

 클러스터링 방법이나 외부조건과 관계없이 분석 결과 자체 정보만으로 클러스터의 우수성을 평가할 수 있다. 두 개 이상의 클러스터링 분석 결과를 비교하거나 다른 클러스터링 방법의 결과를 서로 비교하는데 좋은 기준이 된다.

 SSE의 감소가 클 때의 k를 구하면 좋은 군집의 수 k를 찾을 수 있다. 즉, SSE의 감소폭이 줄어들기 직전의 k를 가장 좋은 k로 본다. 수학적으로 정확한 k값을 구할 수는 없다.

 

 

SSE로 k 찾기 실습

ks = range(1, 10)
SSE = []

for k in ks: 
  model = KMeans(n_clusters=k) #init을 지정하지 않으면 클러스터의 중심
  model.fit(X)
  SSE.append(model.inertia_) #inertia_: 클러스터별 SSE 저장

plt.plot(ks, SSE, '-o')
plt.xlabel('number of clusters, k')
plt.ylabel('SSE')
plt.xticks(ks)

 

 

 

2차원 데이터

초기 중심값 갱신 n_init과 최대 반복 횟수 max_iter가 늘어나면 더 좋은 군집을 찾을 가능성이 높다(SSE 값의 개선)

군집을 여러 개로 나누는 경우 더 좋은 군집을 찾을 가능성이 높다

 

3차원 이상의 데이터는 시각화가 어려우므로 특정 축을 기준으로 시각화하기 등의 방법을 이용한다

 

1. 2차원 데이터 X 만들고 살펴보기

X = np.array([[7,5], [5,7], [7,7], [4,4], [4,6], [1,4],
             [0,0], [2,2], [8,7], [6,8], [5,5], [3,7]])
plt.scatter(X[:,0], X[:,1], s=100)
plt.show()

 

2. 군집을 2개로 나누는 경우

model2 = KMeans(n_clusters=2, init='random', n_init=100, max_iter=10000, random_state=1).fit(X)
c0, c1 =model2.cluster_centers_
c0, c1

def plot_cluster2(model, c0, c1):
  plt.scatter(X[model.labels_==0,0],
              X[model.labels_==0,1], s=100, marker='v', c='r')
  plt.scatter(X[model.labels_==1,0],
              X[model.labels_==1,1], s=100, marker='^', c='b')
  plt.scatter(c0[0], c0[1], s=200, c='r')
  plt.scatter(c1[0], c1[1], s=200, c='b')
  plt.show()
  
  plot_cluster2(model2, c0, c1)

 

3. 군집을 3개로 나누는 경우

model3 = KMeans(n_clusters=3, init='random', n_init=100, max_iter=10000, random_state=1).fit(X)
c0, c1, c2 =model3.cluster_centers_
c0, c1, c2

def plot_cluster3(model, c0, c1, c2):
  plt.scatter(X[model.labels_==0,0],
              X[model.labels_==0,1], s=100, marker='v', c='r')
  plt.scatter(X[model.labels_==1,0],
              X[model.labels_==1,1], s=100, marker='^', c='b')
  plt.scatter(X[model.labels_==2,0],
              X[model.labels_==2,1], s=100, marker='^', c='g')
  plt.scatter(c0[0], c0[1], s=200, c='r')
  plt.scatter(c1[0], c1[1], s=200, c='b')
  plt.scatter(c2[0], c2[1], s=200, c='g')
  plt.show()
  
  plot_cluster3(model3, c0, c1, c2)

 

4. 군집을 4개로 나누는 경우

model4 = KMeans(n_clusters=4, init='random', n_init=100, max_iter=10000, random_state=1).fit(X)
c0, c1, c2, c3 =model4.cluster_centers_
c0, c1, c2, c3

def plot_cluster4(model, c0, c1, c2, c3):
  plt.scatter(X[model.labels_==0,0],
              X[model.labels_==0,1], s=100, marker='v', c='r')
  plt.scatter(X[model.labels_==1,0],
              X[model.labels_==1,1], s=100, marker='^', c='b')
  plt.scatter(X[model.labels_==2,0],
              X[model.labels_==2,1], s=100, marker='^', c='g')
  plt.scatter(X[model.labels_==3,0],
              X[model.labels_==3,1], s=100, marker='v', c='y')
  plt.scatter(c0[0], c0[1], s=200, c='r')
  plt.scatter(c1[0], c1[1], s=200, c='b')
  plt.scatter(c2[0], c2[1], s=200, c='g')
  plt.scatter(c3[0], c3[1], s=200, c='y')
  plt.show()
  
  plot_cluster4(model4, c0, c1, c2, c3)

 

5. 최적 k 찾기: k=4 선택

ks = range(1,10)
SSE = []

for k in ks:
  model = KMeans(n_clusters=k)
  model.fit(X)
  SSE.append(model.inertia_)

print(SSE)

plt.plot(ks, SSE, '-o')
plt.xlabel('number of clusters, k')
plt.ylabel('SSE')
plt.xticks(ks)

 

6. Separation 비교하기: 군집3이 더 적절한 모델

# 군집2
def separation2(model):
  total_mean = np.mean(X[:,0])
  mean1 = np.mean(X[model.labels_==0,0])
  mean2 = np.mean(X[model.labels_==1,0])
  count1 = np.count_nonzero(model.labels_==0)
  count2 = np.count_nonzero(model.labels_==1)
  separation_value = count1*(total_mean - mean1)**2 + count2*(total_mean - mean2)**2
  print(separation_value)
  
# 군집3
def separation3(model):
  total_mean = np.mean(X[:,0])
  mean1 = np.mean(X[model.labels_==0,0])
  mean2 = np.mean(X[model.labels_==1,0])
  mean3 = np.mean(X[model.labels_==2,0])
  count1 = np.count_nonzero(model.labels_==0)
  count2 = np.count_nonzero(model.labels_==1)
  count3 = np.count_nonzero(model.labels_==2)
  separation_value = count1*(total_mean - mean1)**2 + count2*(total_mean - mean2)**2 + count3*(total_mean - mean3)**2
  print(separation_value)
  
# 군집4
def separation4(model):
  total_mean = np.mean(X[:,0])
  mean1 = np.mean(X[model.labels_==0,0])
  mean2 = np.mean(X[model.labels_==1,0])
  mean3 = np.mean(X[model.labels_==2,0])
  mean4 = np.mean(X[model.labels_==3,0])
  count1 = np.count_nonzero(model.labels_==0)
  count2 = np.count_nonzero(model.labels_==1)
  count3 = np.count_nonzero(model.labels_==2)
  count4 = np.count_nonzero(model.labels_==3)
  separation_value = count1*(total_mean - mean1)**2 + count2*(total_mean - mean2)**2 + count3*(total_mean - mean3)**2 + count4*(total_mean - mean4)**2
  print(separation_value)
  
  
separation2(model2), separation3(model3), separation4(model4)

 

7. Matrix 비교하기

list=[]

def s_matrix(model):
  for i in range(0,len(X)):
        list.append([])
        for j in range(0, len(X)):
            if model.labels_[i]==model.labels_[j]:
                list[i].append(1)
            else:
                list[i].append(0)
                
s_matrix(model2)
list

list=[]
s_matrix(model3)
list

list=[]
s_matrix(model4)
list

 

8. Entropy 비교하기

def entropy(labels):
  n_labels = len(labels)
  counts = np.bincount(labels)
  probs = counts[np.nonzero(counts)] / n_labels
  n_classes = len(probs)
  return - np.sum(probs*np.log(probs)) / np.log(n_classes)
  
  
entropy(model2.labels_), entropy(model3.labels_), entropy(model4.labels_)

 

 

 

K-Means 장단점

장점

  • 상대적 효율성
  • 주로 local optimum 클러스터 군집을 찾는다. The Global Optimum 군집의 경우, 다수의 초기 seed 선택이나 진화적 seed 선택 등과 같은 개선 기법이 필요하다.

 

단점

  • 중심값을 구할 수 있는 경우에만 적용가능하고, 아이템 형식의 데이터 등은 적용하기 어렵다
  • k, the Number of Clusters가 적절히 정의되어야한다
  • 잡음데이터나 이상치데이터에 영향을 받는다
  • 임의 모양의 클러스터 군집을 찾는데 적합하지 않다

 

K-Means의 한계점

  • 군집들이 다양성을 가지는 경우 효율적이지 못하다 - 군집의 크기, 군집의 밀도, 비원형 형태의 군집
  • K-means 이상치를 포함한 데이터 처리에 부적합하다

→ 많은 수의 작은 군집들을 1차적으로 탐색한 뒤, 2차적으로 큰 군집을 찾는 과정을 수행한다 (Hierarchical Clustering)

 

DBSCAN, GRID, Optic, 등의 수단도 존재

 

 

실습) Iris 데이터 군집분석