본문 바로가기
머신러닝/미니프로젝트

[미니프로젝트] KNN Classification, K-Means Clustering 이해 및 구현

by doyou1 2021. 10. 8.
반응형

K-NN classification과 K-means Clustering는"거리"를 기준으로 알고리즘이 구현된다는 점에서 공통점 가진다.

이 두 알고리즘에 대해 병렬로 포스팅을 진행함으로써, 분류(classification)와 군집화(Clustering)를 구분해보려 한다.

이와 함께 "거리"를 기준으로 한 이 두 알고리즘에 대해서 이해해보고, 실제 어떻게 구현되는지 확인해보겠다.

 

목차

1. Classification과 Clustering

1.1 Classification

1.2 Clustering

2. KNN Classification

3. K-Means Clustering

- Centroid

4. Algorism Class화

4.1 KNN Classification Class화

- 코드 및 시각화 결과

4.2 K-Means Clustring Class화

- 코드 및 시각화 결과

 


 

1. Classification과 Clustering

1.1 Classification

Classification는 "분류"를 의미한다.

말 그대로, 기준에 따라 어떤 "그룹", "집합"에 속하는지를 판단하는 것을 말한다.

그렇다면 기준이 될 그룹과 집합이 있어야겠다

 

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

np.random.seed(np.random.randint(0, 1000))
# np.random.seed(3)

fig, ax = plt.subplots(2,2,figsize=(40, 40))

# -10 ~ 10 사이의 random value 추출
# shape:(5, 2) / 5개의 x, y
a = np.random.uniform(-10, 10, (5, 2))
cmap = cm.get_cmap('tab10', lut=a.shape[0])

# top left
ax[0, 0].scatter(a[:, 0], a[:, 1], color=cmap(np.arange(a.shape[0])), alpha=0.5, s=200)

b = np.random.uniform(-10, 10, (3, 2))
cmap = cm.get_cmap('tab10', lut=b.shape[0])
# top right
ax[0, 1].scatter(b[:, 0], b[:, 1], color=cmap(np.arange(b.shape[0])), alpha=0.5, s=200)

cmap = cm.get_cmap('tab10', lut=a.shape[0])
# bottom left
for class_idx in range(a.shape[0]):
    a_data = np.random.normal(loc=a[class_idx], scale=1, size=(100, 2))
    ax[1, 0].scatter(a_data[:, 0], a_data[:, 1], color=cmap(class_idx), alpha=0.5, s=10)
ax[1, 0].scatter(a[:, 0], a[:, 1], color=cmap(np.arange(a.shape[0])), alpha=0.5, s=200)

cmap = cm.get_cmap('tab10', lut=b.shape[0])
# bottom right
for class_idx in range(b.shape[0]):
    b_data = np.random.normal(loc=b[class_idx], scale=1, size=(100, 2))
    ax[1, 1].scatter(b_data[:, 0], b_data[:, 1], color=cmap(class_idx), alpha=0.5, s=10)
ax[1, 1].scatter(b[:, 0], b[:, 1], color=cmap(np.arange(b.shape[0])), alpha=0.5, s=200)

plt.xlim(-10,10)
plt.ylim(-10,10)

plt.show()

위와 같이 코드를 작성했고, 작성한 코드의 결과는 위의 그림과 같이 나온다.

- 시각화 그림에서 x축과 y축을 고정해놓고 비교했어야 더 잘 보였을 텐데

 

먼저 -10 ~ 10 사이의 random value를 추출한다.

- a는 shape이 (5, 2)로 -10 ~ 10 사이인, 5개의 임의의 x, y (그림 상단 왼쪽 출력)

- b는 shape이 (3, 2)로 -10 ~ 10사이인, 3개의 임의의 x, y (그림 상단 오른쪽 출력)

 

이후 a, b를 기준으로 sample data를 추출한다.

- a_data는 a의 요소들을 평균으로 갖고, 표준편차가 1인, 각각의 크기가 100인 집단(data) (그림 하단 왼쪽 출력)

- b_data는 b의 요소들을 평균으로 갖고, 표준편차가 1인, 각각의 크기가 100인 집단(data)  (그림 하단 오른쪽 출력)

 

그림 하단을 보면 a, b의 요소들을 기준으로 군집해있는 sample data가 추출됐음을 확인할 수 있다.

 

이 상황에서 분류할 대상인 x를 추가한다고 해보자.

 

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

np.random.seed(np.random.randint(0, 1000))
# np.random.seed(3)

fig, ax = plt.subplots(2,2,figsize=(40, 40))

a = np.random.uniform(-10, 10, (5, 2))
cmap = cm.get_cmap('tab10', lut=a.shape[0])
ax[0, 0].scatter(a[:, 0], a[:, 1], color=cmap(np.arange(a.shape[0])), alpha=0.5, s=200)

b = np.random.uniform(-10, 10, (3, 2))
cmap = cm.get_cmap('tab10', lut=b.shape[0])
ax[0, 1].scatter(b[:, 0], b[:, 1], color=cmap(np.arange(b.shape[0])), alpha=0.5, s=200)

cmap = cm.get_cmap('tab10', lut=a.shape[0])
for class_idx in range(a.shape[0]):
    a_data = np.random.normal(loc=a[class_idx], scale=1, size=(100, 2))
    ax[1, 0].scatter(a_data[:, 0], a_data[:, 1], color=cmap(class_idx), alpha=0.5, s=10)
ax[1, 0].scatter(a[:, 0], a[:, 1], color=cmap(np.arange(a.shape[0])), alpha=0.5, s=200)

x = np.random.uniform(-10, 10, (2, ))
ax[1, 0].scatter(x[0], x[1], color='black', alpha=1, s=400)

cmap = cm.get_cmap('tab10', lut=b.shape[0])
for class_idx in range(b.shape[0]):
    b_data = np.random.normal(loc=b[class_idx], scale=1, size=(100, 2))
    ax[1, 1].scatter(b_data[:, 0], b_data[:, 1], color=cmap(class_idx), alpha=0.5, s=10)
ax[1, 1].scatter(b[:, 0], b[:, 1], color=cmap(np.arange(b.shape[0])), alpha=0.5, s=200)

x = np.random.uniform(-10, 10, (2, ))
ax[1, 1].scatter(x[0], x[1], color='black', alpha=1, s=400)

plt.xlim(-10,10)
plt.ylim(-10,10)

plt.show()

위 코드 및 그림과 같이 임의의 x를 지정했을 때,

 

x가 어떤 군집에 포함되는 것이 적절한가 = 분류

관련한 결과를 도출하기 위한 알고리즘이 Classification 알고리즘이 되는 것이다.

 

다시 한번 정리하면,

Classification은 분류를 의미하며, 임의의 데이터가 이미 군집화돼있는 여러 개의 군집들 중 어느 군집에 속하는지 판단하는 것을 의미한다.

예를 들어, 개, 고양이, 돼지, 소로 분류된 데이터가 있을 때, "임의의 동물 데이터"를 분류된 데이터와 비교하고, "임의의 동물"이 어떤 분류에 속하는지를 판단하는 것을 Classification(분류)라고 하고 관련한 알고리즘을 Classiciation algorism(분류 알고리즘)이라 한다.

 

오늘 포스팅에서는 이러한 Classification algorism(분류 알고리즘) 중 K-NN 알고리즘을 알아볼 것이다.

 


 

1.2 Clustering

Clustering은 "군집화"를 의미한다.

말 그대로, 임의의 데이터들을 "군집화"시키는 것을 의미한다.

Classification이 임의의 데이터를 이미 군집화된 그룹과 비교해 특정 그룹으로 분류하는 것을 의미했다면,

Clustering은 임의의 데이터들을 분석해 각각 군집화된 그룹들로 나누는 것을 의미한다.

그러면 먼저 임의의 데이터가 필요하겠다.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

np.random.seed(np.random.randint(0, 1000))
# np.random.seed(3)

fig, ax = plt.subplots(1,2,figsize=(40, 40))

a = np.random.uniform(-10, 10, (100, 2))

ax[0].scatter(a[:, 0], a[:, 1], alpha=0.5, s=100)

b = np.random.uniform(-10, 10, (300, 2))

ax[1].scatter(b[:, 0], b[:, 1], alpha=0.5, s=100)

plt.show()

plt.show()

그림에서 왼쪽은 -10 ~ 10 사이의 임의의 x, y을 100개 추출한 a이고,

그림에서 오른쪽은 -10 ~ 10 사이의 임의의 x, y을 100개 추출한 b이다.

 

이렇게 임의의 데이터가 있다고 했을 때, 특정한 조건 및 기준으로 데이터들을 분류하고, 각각끼리 군집화시킨다.

 

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(np.random.randint(0, 1000))
# np.random.seed(3)

fig, ax = plt.subplots(2,2,figsize=(40, 40))

a = np.random.uniform(-10, 10, (100, 2))

ax[0, 0].scatter(a[:, 0], a[:, 1], alpha=0.5, s=100)

# 하단 좌측
# x: -10 ~ 0, y: -10 ~ 0
bottom_left = a[a[:,0]<=0]
bottom_left = bottom_left[bottom_left[:,1]<=0]

# 하단 우측
# x: 0 ~ 10, y: -10 ~ 0 
bottom_right = a[a[:,0]>0]
bottom_right = bottom_right[bottom_right[:,1]<=0]

# 상단 좌측
# x: -10 ~ 0, y: 0 ~ 10
top_left = a[a[:,0]<=0]
top_left = top_left[top_left[:,1]>0]

# 상단 우측
# x: 0 ~ 10, y: 0 ~ 10
top_right = a[a[:,0]>0]
top_right = top_right[top_right[:,1]>0]

ax[1, 0].scatter(bottom_left[:, 0], bottom_left[:, 1], color='red',alpha=0.5, s=100)
ax[1, 0].scatter(bottom_right[:, 0], bottom_right[:, 1], color='blue',alpha=0.5, s=100)
ax[1, 0].scatter(top_left[:, 0], top_left[:, 1], color='green',alpha=0.5, s=100)
ax[1, 0].scatter(top_right[:, 0], top_right[:, 1], color='yellow',alpha=0.5, s=100)

b = np.random.uniform(-10, 10, (300, 2))

ax[0, 1].scatter(b[:, 0], b[:, 1], alpha=0.5, s=100)

# 하단 좌측
# x: -10 ~ 0, y: -10 ~ 0
bottom_left = b[b[:,0]<=0]
bottom_left = bottom_left[bottom_left[:,1]<=0]

# 하단 우측
# x: 0 ~ 10, y: -10 ~ 0 
bottom_right = b[b[:,0]>0]
bottom_right = bottom_right[bottom_right[:,1]<=0]

# 상단 좌측
# x: -10 ~ 0, y: 0 ~ 10
top_left = b[b[:,0]<=0]
top_left = top_left[top_left[:,1]>0]

# 상단 우측
# x: 0 ~ 10, y: 0 ~ 10
top_right = b[b[:,0]>0]
top_right = top_right[top_right[:,1]>0]

ax[1, 1].scatter(bottom_left[:, 0], bottom_left[:, 1], color='red',alpha=0.5, s=100)
ax[1, 1].scatter(bottom_right[:, 0], bottom_right[:, 1], color='blue',alpha=0.5, s=100)
ax[1, 1].scatter(top_left[:, 0], top_left[:, 1], color='green',alpha=0.5, s=100)
ax[1, 1].scatter(top_right[:, 0], top_right[:, 1], color='yellow',alpha=0.5, s=100)
plt.show()

하드코딩이지만, 위의 데이터를 양수, 음수를 기준으로 4개의 군집으로 나눠보았다.

다양한 조건, 방식이 있겠지만, 위의 코드 및 그림처럼 임의의 데이터들을 특정한 공식이나 조건을 이용해 N개의 군집으로 나누는 과정을 Clustering(군집화)이라고 한다. 

실제 데이터들은 위의 자료들처럼 딱 떨어지게 군집화가 되지 않는다. 그래서 Clustering algorism(군집화 알고리즘)에는 반복을 통해 최적화를 찾아내는 노력이 필요하다.

 

오늘 포스팅에서는 이러한 Clustering algorism(군집화 알고리즘) 중 K-Means 알고리즘을 알아볼 것이다.

 


2. KNN Classification Algorism

KNN의 풀네임은 K-Nearest Neighbor으로, 최근접 이웃 알고리즘, 최근접 이웃법으로 표현된다.

알고리즘 이름에서 알 수 있듯, 가장 가까운 이웃을 찾는 알고리즘이다.

여기서 K는 개수, 제일 가까운 이웃을 몇 개나 뽑아낼 것인지를 나타내는 변수이다. 

임의의 데이터가 각각의 데이터들과 얼마만큼의 거리를 가지는지를 구하고, 

거리가 가장 가까운 K개의 데이터들을 뽑고, 그 데이터들이 어떤 분류에 해당하는지 찾아내는 알고리즘이다.

"K개의 데이터들 중 어떤 분류의 데이터가 가장 많은가"를 기준으로 임의의 데이터가 속하는 군집을 알아낸다.

 

관련한 구현을 위해 Classification챕터에서 작성한 샘플 데이터를 가져오도록 하겠다.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

# np.random.seed(np.random.randint(0, 1000))
np.random.seed(3)

fig, ax = plt.subplots(figsize=(40, 40))

a = np.random.uniform(-10, 10, (5, 2))
cmap = cm.get_cmap('tab10', lut=a.shape[0])
ax.scatter(a[:, 0], a[:, 1], color=cmap(np.arange(a.shape[0])), alpha=0.5, s=200)

for class_idx in range(a.shape[0]):
    a_data = np.random.normal(loc=a[class_idx], scale=1, size=(100, 2))
    ax.scatter(a_data[:, 0], a_data[:, 1], color=cmap(class_idx), alpha=0.5, s=100)

x = np.random.uniform(-10, 10, (2, ))
ax.scatter(x[0], x[1], color='black', alpha=1, s=400)

plt.xlim(-10,10)
plt.ylim(-10,10)

plt.show()

위의 코드 및 그림처럼 분류된 data set이 있고, 임의의 값 x가 있을 때,

x는 어디에 속해야 할까? x의 nearest nieghbor은 어디일까?

사람들이라면, 시각화된 자료가 있기 때문에

아래와 같이 확인해 분류된 값을 찾을 수 있을 것이다.

 

하지만, 아래 그림처럼 애매한 경우가 있을 수 있다. 그리고, 이러한 과정들을 어떻게 컴퓨터가 이해할 수 있게 만들까 하는 문제가 생긴다. 때문에 K-NN Classification Algorism이 필요해진다.

K-NN Classification Algorism의 구현 과정은 아주 간단하게 2가지로 나누어질 수 있다.

구현과정 1 : 임의의 데이터 x와 각각의 데이터들 간의 거리를 구하기

구현과정 2 : 임의의 데이터 x와 거리가 가까운 데이터의 군집 확인

 

두 과정을 거치면, 임의의 데이터 x를 분류할 수 있다.

그런데, 머릿속으로 생각을 해봤을 때, 구현과정 2에서 조심해야 할 상황이 있다.

극단적인 상황일 수 있지만, 실제 데이터에서 표준편차가 다른 집합들보다 큰 경우, 예기치 못한 튀는 값이 있는 경우에 감안하지 않으면, 컴퓨터는 위와 같이 "가장 가까운 값이니까"라는 이유로 올바르지 못한 선택을 할 수 있다.

그래서 우리는 "K"-NN 분류 알고리즘을 사용한다. 그래서 구현과정을 조금 더 구체화하면,

 

 

구현과정 1 : 임의의 데이터 x와 각각의 데이터들 간의 거리를 구하기

구현과정 2 : 임의의 데이터 x와의 거리 정렬, 가장 가까운 K개의 데이터 추출

구현과정 3 : K개의 데이터가 가장 많이 속한 군집 추출

 

정리하면,

가장 가까운 데이터의 군집만을 확인할 경우, 위 그림의 경우와 같이 오류가 생길 수 있다.

그래서 가장 가까운 "K"개의 데이터를 추출해 그들이 가장 많이 속한 군집을 확인하는 것이다.

* 이 상황에서도 가장 많이 속한 군집이 복수로 존재할 수 있다., 그러한 경우를 최소화하기 위해 "K"개는  홀수로 설정하도록 한다.

 

관련한 내용을 구현해 보도록 하자

 

- Dataset과 임의의 값 x 추출

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

# np.random.seed(np.random.randint(0, 1000))
# np.random.seed(3)

# 군집의 개수, 한 군집당 요소의 개수, 군집들의 차원 (5개, 100개, 2차원:(x,y))
n_class, n_class_feature, n_class_dim = 2, 5, 2

a = np.random.uniform(-10, 10, (n_class, n_class_dim))

# 실제 데이터(2차원: (x,y)의 좌표값)
dataset_data = list()
# 데이터가 속한 군집의 index
dataset_label = list()

for class_idx in range(n_class):
    class_data = np.random.normal(loc=a[class_idx], scale=1, size=(n_class_feature, 2))
    class_label = np.full(fill_value=class_idx, shape=(n_class_feature,1))
    
    # print(class_data.shape, class_label.shape)  # (100, 2) (100, 2)
    dataset_data.append(class_data)
    dataset_label.append(class_label)

dataset_data = np.vstack(dataset_data)
# print(dataset_data.shape)   # (500, 2) = (군집의개수*요소의개수, 차원수) (n_class*n_class_feature, n_dim)
dataset_label = np.vstack(dataset_label)
# print(dataset_data.shape)   # (500, 1) = (군집의 개수*요소의 개수, 군집의인덱스) (n_class*n_class_feature, 1)

# 임의의 값 x (2차원:(x,y)의 좌표값)
x = np.random.uniform(-10, 10, (2, ))
# print(x.shape)	# (2, )

 

- 구현과정 1 : 임의의 데이터 x와 각각의 데이터들 간의 거리를 구하기

* 유클리드 거리(Euclidean distance) 활용

# 구현과정 1 : 임의의 데이터 x와 각각의 데이터들간의 거리를 구하기
# L2 Euclidean distance 활용
e_distances = np.sqrt(np.sum((dataset_data-x)**2, axis=-1))
# print(e_distances.shape)    # (500, ) = x와 각각의 데이터과의 거리

 

- 구현과정 2 : 임의의 데이터 x와의 거리 정렬, 가장 가까운 K개의 데이터 추출

# 구현과정 2 : 임의의 데이터 x와의 거리 정렬, 가장 가까운 K개의 데이터 추출
# K 설정
K = 21
# e_distances 오름차순 정렬, 가장 가까운 데이터 값 K개 추출
close_data = np.sort(e_distances)[:K]
# e_distances 오름차순 정렬, 가장 가까운 데이터의 index K개 추출
close_data_indices = np.argsort(e_distances)[:K]

# print(close_data.shape)   # (21,)
print(close_data, '\n')
# [3.32531152 4.16703653 4.24207119 4.25166127 4.36663157 4.54661366
#  4.55492473 4.56316286 4.6251108  4.69994645 4.72868838 4.79193492
#  4.81734593 4.85133254 4.85331954 4.85865025 4.9150941  4.94491401
#  4.94757038 4.95426564 5.01020725] 

# print(close_data_indices.shape)    # (21,)
print(close_data_indices, '\n')
# [227 271 285 232 241 247 276 256 208 250 213 120 186 231 255 210 246 262
#  169 106 239]

print(e_distances[close_data_indices], '\n')
# [3.32531152 4.16703653 4.24207119 4.25166127 4.36663157 4.54661366
#  4.55492473 4.56316286 4.6251108  4.69994645 4.72868838 4.79193492
#  4.81734593 4.85133254 4.85331954 4.85865025 4.9150941  4.94491401
#  4.94757038 4.95426564 5.01020725]

 

- 구현과정 3 : K개의 데이터가 가장 많이 속한 군집 추출

# 구현과정 3 : K개의 데이터가 가장 많이 속한 군집 추출
# 가장 가까운 데이터가 속한 군집의 index값
close_data_class = dataset_label[close_data_indices]
print(close_data_class, '\n')
# [[2]
#  [2]
#  [2]
#  [2]
#  [2]
#  [2]
#  [2]
#  [2]
#  [2]
#  [2]
#  [2]
#  [1]
#  [1]
#  [2]
#  [2]
#  [2]
#  [2]
#  [2]
#  [1]
#  [1]
#  [2]]

# index들의 count 확인
uniques, counts = np.unique(close_data_class, return_counts=True)
M_idx = np.argmax(counts)
M_class = uniques[M_idx]

print(uniques)  # [1 2]
print(counts)   # [ 4 17]
print("M_idx: ", M_idx, '\n')   # M_idx:  1

print("M_class: ", M_class) # M_class:  2 / 적절한 군집의 index

 

- 시각화 코드

# 시각화
cmap  = cm.get_cmap('tab10', lut=n_class)
fig, ax = plt.subplots(figsize=(20, 20))

# dataset
ax.scatter(dataset_data[..., 0], dataset_data[..., 1], color=cmap(dataset_label), s=100)

# x
ax.scatter(x[0], x[1], color='black', s=200)

# x와 close_data의 거리(red line)
for i in close_data_indices:
    ax.plot([x[0], dataset_data[i ,0]], 
            [x[1], dataset_data[i, 1]], color='red')

plt.show()

 

- 시각화 결과

위와 같은 결과가 나온다. 

1. 유클리드 거리를 구한다.

2. 가장 가까운 K개의 요소들을 찾는다.

3. K개의 요소들 중 가장 많은 군집을 확인한다. 

4. 임의의 값 X은 가장 많은 군집에 포함된다.(분류 완료!)

 

위와 같은 방식으로 K-NN Classification Algorism이 구현된다. 

 

처음 다뤄본 머신러닝 알고리즘인데, 재밌네에, 뿌듯하네

 


 

3. K-Means Clustering Algorism

K-Means ClusteringK-평균 군집화로 표현된다.

알고리즘 이름에서 알 수 있듯, 평균값을 기준으로 군집화시키는 알고리즘이다.

여기서  K는 개수, 군집화시킬 군집의 개수를 나타내는 변수이다.

K개의 임의의 중심점(centroid)을 기준으로 각각의 데이터들이 얼마만큼의 거리를 가지는지를 구하고,

이를 통해 각각의 데이터가 어떤 임의의 중심점과 가까운가를 기준으로 군집으로 형성한다.

헌데, 기본적으로 최초의 중심점은 임의로 설정되는 것이기에, 곧바로 이들을 군집들의 중심이라 보기는 어렵다. 때문에 반복해서 중심점을 갱신하는 작업을 통해, 최적의 K개의 군집으로 나눈 결과를 추출한다.

 

- Centroid

Centroid는 K-Means Clustering Algorism에서 활용되는 중요한 개념 중의 하나이다.

Centroid는 "중심, 기하학적 중심"을 의미한다.

K-Means Clustering Algorism에서 해당 개념은 Cluster(군집)의 중심을 의미하게 된다. 최초의 Centroid는 임의로 설정된다.* K-NN Classification에서의 "임의의 값"은 비교를 위한 대상이지만, K-Means Clustering에서의 "임의의 값"은 "군집화"를 위한 대상이라고 생각하자.

 

* 참조 : https://en.wikipedia.org/wiki/Centroid

 

Centroid - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Mean ("average") position of all the points in a shape In mathematics and physics, the centroid or geometric center of a plane figure is the arithmetic mean position of all the points

en.wikipedia.org

 

관련한 구현을 위해 Clustering챕터에서 작성한 샘플 데이터를 가져오도록 하겠다.

 

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

np.random.seed(np.random.randint(0, 1000))
# np.random.seed(3)

fig, ax = plt.subplots(1, 2, figsize=(40, 40))

# 임의의 값의 개수, 임의의 값의 차원    = (300, 2)
n_feature, n_feature_dim = 300, 2
a = np.random.uniform(-10, 10, (n_feature, n_feature_dim))

# 4개의 군집으로 군집화하겠다
K = 4
# 임의의 군집의 K개의 centroid(중심)
# dataset(a)에서 임의의 K개의 값을 centroid로 설정
shuffled = np.arange(n_feature)
np.random.shuffle(shuffled)
centroid_indicies = shuffled[:K]

cmap = cm.get_cmap('tab10', lut=K)
ax[0].scatter(a[:, 0], a[:, 1], alpha=0.5, s=100)

ax[1].scatter(a[:, 0], a[:, 1], alpha=0.5, s=100)
ax[1].scatter(a[centroid_indicies, 0], a[centroid_indicies, 1], color=cmap(np.arange(K)), alpha=1, s=200)

plt.xlim(-10,10)
plt.ylim(-10,10)

plt.show()

위의 그림은 왼쪽, 오른쪽 모두 같은 dataset(a)를 그렸다. 그리고, 그중에서 임의의 K개의 값을 centroid로 설정했다.

이 임의의 centroid를 중심으로 Clustering(군집화)를 진행한다.

 

군집화는 L2 Euclidean distance(유클리드 거리)를 활용해, 

각각의 data들이 어떤 centroid와 가까운지를 계산 및 판단한다.

그리하면 임의의 군집이 형성된다.

 

실제 계산을 해봐야겠지만, 위의 그림처럼 임의로 군집화가 진행될 것이다.

그리고, 이 임의의 군집화는 임의로 진행되었기에 최적화된(정확한) 군집화의 상태가 아니므로, 추가적인 작업이 필요하다. 우리는 그 임의의 군집을 통해 새로운 centroid를 찾을 것이다.

이때의 과정에서 "means(평균)" 개념이 활용된다.

K = 4로 설정했기에

- centroid1의 군집, centroid2의 군집, centroid3의 군집, centroid4의 군집에 해당하는 데이터들이 있을 것이다.

이들 데이터의 평균을 찾아 새로운 중심점을 만드는 것이다.

 

$$ new\ centroid(x, y) =  (\frac{1}{N} \times \Sigma_{i=1}^{n}X_{i}, \frac{1}{N} \times \Sigma_{i=1}^{n}Y_{i})\ (단,\ 2차원일 경우)$$

 

위의 수식과 같이 new centroid를 구하고, 이 new centroid를 기준으로 다시 군집화를 진행하는 것이다.

평균을 이용한 new centroid -> 군집화 -> 평균을 이용한 new centroid -> 군집화가 반복적으로 진행되면, 임의의 데이터가 최적화된 군집들을 갖게 될 것이다. 이 처리 과정을 "K-means Clustering algorism"이라고 한다.

 

관련한 구현을 실시한 건데, 실시하기 전에 구현과정을 확인해보자!

 

Dataset과 임의의 centroids 추출 

구현과정 1 : 각각의 데이터와 모든 centroid와의 거리 구하기

구현과정 2 : 거리 데이터 정렬을 통해 가장 가까운 centroid 확인 및 가장 가까운 centroid로 군집화(labeling)

구현과정 3 : 군집마다의 평균을 구해 새로운 centroid 추출

구현과정 4 :  새로운 centroid를 기준으로 새로운 군집화

구현과정 5: 최적화될 때까지 구현과정 3, 4 반복

 

구현과정은 위와 같은 정도가 될 것이다. 실제 결과가 나오는지 구현해보자

 

- Dataset과 임의의 centroids 추출

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

np.random.seed(np.random.randint(0, 1000))
# np.random.seed(3)

# Dataset 추출
# 임의의 값의 개수, 임의의 값의 차원    = (300, 2)
n_feature, n_feature_dim = 300, 2
dataset = np.random.uniform(-10, 10, (n_feature, n_feature_dim))

# K: 군집의 개수 설정
K = 4
shuffled = np.arange(n_feature)
np.random.shuffle(shuffled)

# dataset 중 임의의 centroid K개 추출
centroid_indicies = shuffled[:K]

 

구현과정 1 : 각각의 데이터와 모든 centroid와의 거리 구하기

# 구현과정 1 : 각각의 데이터와 모든 centroid와의 거리 구하기
# Euclidean distance(유클리드 거리) 이용
e_distances = list()
for data_idx in range(n_feature):
    # 각각의 data
    data = dataset[data_idx]
    # centroids data
    centroids_data = dataset[centroid_indicies]

    e_distance = np.sqrt(np.sum((data-centroids_data)**2, axis=-1))
    e_distances.append(e_distance)

e_distances = np.vstack(e_distances)
# print(e_distances.shape)    # (300, 4) / 각각의 data와 모든 centroid와의 거리 추출

 

 

구현과정 2 : 거리 데이터 정렬을 통해 가장 가까운 centroid 확인 및 가장 가까운 centroid로 군집화(labeling)

# 구현과정 2 : 거리 데이터 정렬을 통해 가장 가까운 centroid 확인 및 가장 가까운 centroid로 군집화(labeling)
sort_e_distances = np.sort(e_distances, axis=-1)
closest_e_distances_data = sort_e_distances[..., 0]

argsort_e_distances = np.argsort(e_distances, axis=-1)
closest_centroid_indices = argsort_e_distances[..., 0]

# 각각의 데이터와 모든 centroids와의 e distance 오름차순 정렬
# print(sort_e_distances.shape)   # (300, 4)
# 각각의 데이터에 가까운 centroid index 오름차순 정렬
# print(argsort_e_distances.shape)    # (300, 4)
# print(closest_e_distances_data.shape)   # (300, )
# print(closest_centroid_indices.shape)   # (300, )

 

 

구현과정 3 : 군집마다의 평균을 구해 새로운 centroid 추출

# 구현과정 3 : 군집마다의 평균을 구해 새로운 centroid 추출
new_centroides = list()

for centroid_idx in range(K):
    # closest_centroid_indices가 for문 내의 현재 centroid_idx와 일치한 dataset 추출
    closest_dataset = dataset[closest_centroid_indices == centroid_idx]
    # print(closest_data_indices.shape)   # (??, )
    # print(closest_dataset.shape)    # (??, 2): (x, y)의 값 단, 2차원일 경우
    new_centroid = np.sum(closest_dataset, axis=0) / closest_dataset.shape[0]

    new_centroides.append(new_centroid)

new_centroides = np.vstack(new_centroides)
# print(new_centroides.shape) # (4, 2)

 

구현과정 4 :  새로운 centroid를 기준으로 새로운 군집화

# 구현과정 4 :  새로운 centroid를 기준으로 새로운 군집화
e_distances = list()
for data_idx in range(n_feature):
    # 각각의 data
    data = dataset[data_idx]
    # new centroides data
    '''new_centroides'''

    e_distance = np.sqrt(np.sum((data-new_centroides)**2, axis=-1))
    e_distances.append(e_distance)

e_distances = np.vstack(e_distances)
# print(e_distances.shape)    # (300, 4)

new_sort_e_distances = np.sort(e_distances, axis=-1)
new_closest_e_distances_data = sort_e_distances[..., 0]

new_argsort_e_distances = np.argsort(e_distances, axis=-1)
new_closest_centroid_indices = argsort_e_distances[..., 0]

# 각각의 데이터와 모든 centroids와의 e distance 오름차순 정렬
# print(new_sort_e_distances.shape)   # (300, 4)
# 각각의 데이터에 가까운 centroid index 오름차순 정렬
# print(new_argsort_e_distances.shape)    # (300, 4)
# print(new_closest_e_distances_data.shape)   # (300, )
# print(new_closest_centroid_indices.shape)   # (300, )

 

구현과정 5: 최적화될 때까지 구현과정 3, 4 반복

# 구현과정 5: 최적화될때까지 구현과정 반복
# new_centroid > 군집화 > new_centroid > 군집화 > ...
result_centroides = None
result_cluster_indices = None

# 임의로 10회 반복
for i in range(10):
    for_new_centroides = list()

    for centroid_idx in range(K):
        # new_closest_centroid_indices가 for문 내의 현재 centroid_idx와 일치한 dataset 추출
        new_closest_dataset = dataset[new_closest_centroid_indices == centroid_idx]
        # print(new_closest_data_indices.shape)   # (??, )
        # print(new_closest_dataset.shape)    # (??, 2): (x, y)의 값 단, 2차원일 경우
        for_new_centroid = np.sum(new_closest_dataset, axis=0) / new_closest_dataset.shape[0]
        for_new_centroides.append(for_new_centroid)

    for_new_centroides = np.vstack(for_new_centroides)
    # print(for_new_centroides.shape) # (4, 2)

    e_distances = list()
    for data_idx in range(n_feature):
        # 각각의 data
        data = dataset[data_idx]
        # new centroides data
        '''new_centroides'''

        e_distance = np.sqrt(np.sum((data-for_new_centroides)**2, axis=-1))
        e_distances.append(e_distance)

    e_distances = np.vstack(e_distances)
    # print(e_distances.shape)    # (300, 4)

    new_sort_e_distances = np.sort(e_distances, axis=-1)
    new_closest_e_distances_data = new_sort_e_distances[..., 0]

    new_argsort_e_distances = np.argsort(e_distances, axis=-1)
    new_closest_centroid_indices = new_argsort_e_distances[..., 0]

    result_centroides = for_new_centroides
    result_cluster_indices = new_closest_centroid_indices

    # 각각의 데이터와 모든 centroids와의 e distance 오름차순 정렬
    # print(new_sort_e_distances.shape)   # (300, 4)
    # 각각의 데이터에 가까운 centroid index 오름차순 정렬
    # print(new_argsort_e_distances.shape)    # (300, 4)
    # print(new_closest_e_distances_data.shape)   # (300, )

 

- 시각화

# 시각화
fig, ax = plt.subplots(3, 2, figsize=(20, 20))
cmap  = cm.get_cmap('tab10', lut=K)

# 0, 0
ax[0, 0].scatter(dataset[..., 0], dataset[..., 1], alpha=0.5, s=100)

# 0, 1
# 임의의 centroides 설정
ax[0, 1].scatter(dataset[..., 0], dataset[..., 1], alpha=0.5, s=100)
ax[0, 1].scatter(dataset[centroid_indicies, 0], dataset[centroid_indicies, 1], color=cmap(np.arange(K)),alpha=1, s=200)

# 1, 0
# 최초 군집화 결과 시각화
ax[1, 0].scatter(dataset[..., 0], dataset[..., 1], color=cmap(closest_centroid_indices), alpha=1, s=100)

# 1, 1
# 새로운 centroid 시각화
# 군집에 따른 dataset
ax[1, 1].scatter(dataset[..., 0], dataset[..., 1], color=cmap(closest_centroid_indices), alpha=0.5, s=100)
# 기존 centroid
ax[1, 1].scatter(dataset[centroid_indicies, 0], dataset[centroid_indicies, 1], color=cmap(np.arange(K)),alpha=0.8, s=200)
# new centroid
ax[1, 1].scatter(new_centroides[:, 0], new_centroides[:, 1], color=cmap(np.arange(K)),alpha=1, s=500)

# 2, 0
# new_centroid > 군집화 10회 반복
ax[2,0].scatter(dataset[..., 0], dataset[..., 1], color=cmap(result_cluster_indices), alpha=0.5, s=100)
ax[2,0].scatter(result_centroides[:, 0], result_centroides[:, 1], color=cmap(np.arange(K)),alpha=1, s=500)

result_centroides = None
result_cluster_indices = None

# 임의로 10회 반복
for i in range(10):
    for_new_centroides = list()

    for centroid_idx in range(K):
        # new_closest_centroid_indices가 for문 내의 현재 centroid_idx와 일치한 dataset 추출
        new_closest_dataset = dataset[new_closest_centroid_indices == centroid_idx]
        # print(new_closest_data_indices.shape)   # (??, )
        # print(new_closest_dataset.shape)    # (??, 2): (x, y)의 값 단, 2차원일 경우
        for_new_centroid = np.sum(new_closest_dataset, axis=0) / new_closest_dataset.shape[0]
        for_new_centroides.append(for_new_centroid)

    for_new_centroides = np.vstack(for_new_centroides)
    # print(for_new_centroides.shape) # (4, 2)

    e_distances = list()
    for data_idx in range(n_feature):
        # 각각의 data
        data = dataset[data_idx]
        # new centroides data
        '''new_centroides'''

        e_distance = np.sqrt(np.sum((data-for_new_centroides)**2, axis=-1))
        e_distances.append(e_distance)

    e_distances = np.vstack(e_distances)
    # print(e_distances.shape)    # (300, 4)

    new_sort_e_distances = np.sort(e_distances, axis=-1)
    new_closest_e_distances_data = new_sort_e_distances[..., 0]

    new_argsort_e_distances = np.argsort(e_distances, axis=-1)
    new_closest_centroid_indices = new_argsort_e_distances[..., 0]

    result_centroides = for_new_centroides
    result_cluster_indices = new_closest_centroid_indices

    # 각각의 데이터와 모든 centroids와의 e distance 오름차순 정렬
    # print(new_sort_e_distances.shape)   # (300, 4)
    # 각각의 데이터에 가까운 centroid index 오름차순 정렬
    # print(new_argsort_e_distances.shape)    # (300, 4)
    # print(new_closest_e_distances_data.shape)   # (300, )

# 2, 1
# 추가로 10회 더 반복
ax[2,1].scatter(dataset[..., 0], dataset[..., 1], color=cmap(result_cluster_indices), alpha=0.5, s=100)
# new centroid
ax[2,1].scatter(result_centroides[:, 0], result_centroides[:, 1], color=cmap(np.arange(K)),alpha=1, s=500)

plt.show()

위 그림과 같은 결과가 나온다.

 

위 그림을 분석해 보면

위의 그림처럼 new centroid > 군집화를 반복해 점점 최적화된 군집이 형성된다.

 

관련해서 K의 값(군집의 개수)을 바꿔보면서도 실시해보았다.

 

- K = 2

K = 2

- K = 5

K = 5

- K = 7

K = 7

임의의 데이터로 구현을 실시했음에도 K = 2 혹은 K = 4일 경우는 자연스럽게 2 분할, 4 분할이 진행됐다.

마지막으로 K-Means Clustering Algorism을 정리해보면,

1. 임의의 Centroid 설정

2. 각각의 데이터와 Centroid들과의 유클리드 거리 연산

3. 유클리드 거리를 기준으로 각각의 데이터가 속하는 "Cluster(군집)" 분류

4. Cluster(군집)에 따른 새로운 Centroid 추출

5. 새로운 Centroid를 기준으로 각각의 데이터와 유클리드 거리 연산

6. 5번 연산 결과의 유클리드 거리를 기준으로 "Cluster(군집)" 분류

7. 이 과정 최적화될 때까지 반복

 

위와 같은 방식으로 K-Means Clustering Algorism이 구현된다. 

 

np.random.uniform()의 임의의 dataset을 이용했기에, 유의미한 결과를 가져오지 못한 것 같다. 

추후 포스팅에는 실제 데이터에 대해서 다뤄보도록 하겠다.

 


 

4. Algorism Class화

결국 위의 구현 코드들처럼, 길게 늘여서 코드를 작성하면 알고리즘의 핵심을 파악하기 어렵고, 가독성이 낮아진다.

그러한 문제를 해결하기 위해 관련 알고리즘의 핵심을 잡아 Class화하는 과정을 진행하도록 하겠다.

 

4.1 KNN Classification Class화

 

- main.py

from Dataset_Generator import Dataset_KNNClassification
from Classification_Factory import KNN_Classification
import numpy as np

n_class, n_class_feature, class_dim = 5, 100, 2
value_min, value_Max = -10, 10

dg_knn = Dataset_KNNClassification(
            n_class=n_class, n_class_feature=n_class_feature, class_dim=class_dim,
            value_min=value_min, value_Max=value_Max)

# 분류된 dataset, dataset의 label
dataset_data, dataset_label = dg_knn.get_dataset()
print(dataset_data.shape, dataset_label.shape)
# (500, 2)  (500, 1)

# 시각화 코드
# dg_knn.vis_dataset()

# 분류할 임의의 x
x = np.random.uniform(value_min, value_Max, (class_dim, ))
K = 21

knn_classification = KNN_Classification(dataset_data=dataset_data, dataset_label=dataset_label, x=x, K=K)
knn_classification.classification()
knn_classification.vis_result()

 

- Dataset_Generator.py

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

class Dataset_KNNClassification():

    def __init__(self, n_class, n_class_feature, class_dim,
                value_min, value_Max):
        self.n_class = n_class
        self.n_class_feature = n_class_feature
        self.class_dim = class_dim

        self.value_min = value_min
        self.value_Max = value_Max

        self.dataset_data = list()
        self.dataset_label = list()

        # 시각화를 위한 center array
        self.centers = list()

        self._make_dataset()

    def _make_dataset(self):
        
        for class_idx in range(self.n_class):
            center = np.random.uniform(self.value_min, self.value_Max, size=(self.class_dim, ))
            class_data = np.random.normal(loc=center, scale=1, size=(self.n_class_feature, self.class_dim))
            class_label = np.full(fill_value=class_idx, shape=(self.n_class_feature, 1))

            self.centers.append(center)
            self.dataset_data.append(class_data)
            self.dataset_label.append(class_label)

        self.centers = np.vstack(self.centers)
        self.dataset_data = np.vstack(self.dataset_data)
        self.dataset_label = np.vstack(self.dataset_label)

    def get_dataset(self):
        if len(self.dataset_data) == 0 or len(self.dataset_label) == 0:
            print("empty dataset")
            self._make_dataset()
            self.get_dataset()

        return self.dataset_data, self.dataset_label

    def vis_dataset(self):
        if len(self.dataset_data) == 0 or len(self.dataset_label) == 0:
            print("empty dataset")
            self._make_dataset()

        fig, ax = plt.subplots(figsize=(10, 10))
        cmap = cm.get_cmap(name='tab10',lut=self.n_class)

        # dataset
        ax.scatter(self.dataset_data[..., 0], self.dataset_data[..., 1], color=cmap(self.dataset_label), alpha=0.5, s=100)
        # center    뒤에 코드를 집어넣어서 위에 덮어씀
        ax.scatter(self.centers[..., 0], self.centers[..., 1], color=cmap(np.arange(self.n_class)), alpha=1, s=300)

        plt.show()

 

- Classification_Factory.py

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

class KNN_Classification:
    def __init__(self, dataset_data, dataset_label, x, K):
        self.dataset_data = dataset_data
        self.dataset_label = dataset_label 
        self.x = x
        self.K = K
        
        self.close_data_indices = None
        self.result = None

    def classification(self):
        # 구현과정 1
        e_distances = np.sqrt(np.sum((self.dataset_data-self.x)**2, axis=-1))

        # 구현과정 2
        close_data = np.sort(e_distances)[:self.K]
        close_data_indices = np.argsort(e_distances)[:self.K]

        # 구현과정 3
        close_data_class = self.dataset_label[close_data_indices]

        uniques, counts = np.unique(close_data_class, return_counts=True)
        print(f"x와 가장 가까운 {self.K}개의 데이터들의 class index count")
        print(uniques)
        print(counts)

        M_idx = np.argmax(counts)
        result = uniques[M_idx]
        
        print(f"가장 많은 class index : {result}")

        self.close_data_indices = close_data_indices
        self.result = result

    def vis_result(self):

        fig, ax = plt.subplots(figsize=(10, 10))
        cmap = cm.get_cmap(name='tab10')

        # dataset
        ax.scatter(self.dataset_data[..., 0], self.dataset_data[..., 1], color=cmap(self.dataset_label), alpha=0.5, s=100)

        # x
        ax.scatter(self.x[0], self.x[1], color=cmap(self.result), alpha=1, s=300)
        arrowprops = dict(color='black',arrowstyle="-|>")

        ax.annotate("x", xy=(self.x[0], self.x[1]),xytext=(self.x[0]+0.5, self.x[1]+0.5),arrowprops=arrowprops, fontsize=15)

        for i in range(self.close_data_indices.shape[0]):
            close_data = self.dataset_data[self.close_data_indices[i]]

            # close data와의 거리 시각화(red line)
            ax.plot([self.x[0], close_data[0]],
                    [self.x[1], close_data[1]], color='red')

        plt.show()

 

 

- 실행 시 결과 시각화

knn_classification.classification()

knn_classification.vis_result()

 

 

위와 같이 Dataset_Generator의 Dataset_KNNClassification, Classification_Factory의 KNN_Classification으로 Class화 하였다. 이 Class들을 이용해 main.py에서 KNN Classification Algorism을 구현해봤다.

 

4.2 K-Means Clustring Class화

 

- main.py

from Dataset_Generator import Dataset_KMeansClustering
from Clustering_Factory import KMeans_Clustering
import numpy as np

n_feature, n_feature_dim = 300, 2
value_min, value_Max = -10, 10

dg_kmeans = Dataset_KMeansClustering(n_feature=n_feature, n_feature_dim=n_feature_dim, 
                            value_min=value_min, value_Max=value_Max)

dataset = dg_kmeans.get_dataset()
# print(dataset.shape)    # (300, 2)

# 시각화 코드
# dg_kmeans.vis_dataset()

# K: 군집의 개수 설정
K = 4

kmeans_clustering = KMeans_Clustering(dataset=dataset, K=K)

kmeans_clustering.make_centroides()
kmeans_clustering.vis_result(value="make_centroides")

kmeans_clustering.clustering()
kmeans_clustering.vis_result(value="clustering")

kmeans_clustering.make_new_centroid()
kmeans_clustering.vis_result(value="make_new_centroid")

kmeans_clustering.new_clustering()
kmeans_clustering.vis_result(value="new_clustering")

repeat_count = 10
kmeans_clustering.repeat(repeat_count)
kmeans_clustering.vis_result()  # value="repeat"

 

- Dataset_Generator.py

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

class Dataset_KMeansClustering():

    def __init__(self, n_feature, n_feature_dim, value_min, value_Max):
        self.n_feature = n_feature
        self.n_feature_dim = n_feature_dim

        self.value_min = value_min
        self.value_Max = value_Max

        self.dataset = list()

        self._make_dataset()

    def _make_dataset(self):
        self.dataset = np.random.uniform(self.value_min, self.value_Max, (self.n_feature, self.n_feature_dim))

    def get_dataset(self):
        if len(self.dataset) == 0:
            print("empty dataset")
            self._make_dataset()
            self.get_dataset()

        return self.dataset

    def vis_dataset(self):
        if len(self.dataset) == 0:
            print("empty dataset")
            self._make_dataset()

        fig, ax = plt.subplots(figsize=(10, 10))

        # dataset
        ax.scatter(self.dataset[..., 0], self.dataset[..., 1], alpha=0.5, s=100)

        plt.show()

 

- Clustering_Factory.py

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

class KMeans_Clustering:
    def __init__(self, dataset, K):
        self.dataset = dataset
        self.K = K

        self.centroid_data = list()
        self.centroid_indicies = list()
        
        self.closest_centroid_indices = list()
        self.new_centroides = list()

    def make_centroides(self):
        shuffled = np.arange(self.dataset.shape[0])
        np.random.shuffle(shuffled)
        # dataset 중 임의의 centroid를 K개 추출
        self.centroid_indicies = shuffled[:self.K]
        self.centroid_data = self.dataset[self.centroid_indicies]

    def clustering(self):
        if len(self.centroid_indicies) == 0 or len(self.centroid_data) == 0:
            print("empty centroid. make centroides")
            self.make_centroides()
            self.clustering()
        
        # 구현과정 1
        e_distances = list()
        for data_idx in range(self.dataset.shape[0]):
            # 각각의 data
            data = self.dataset[data_idx]
            # centroids data
            centroids_data = self.dataset[self.centroid_indicies]

            e_distance = np.sqrt(np.sum((data-centroids_data)**2, axis=-1))
            e_distances.append(e_distance)

        e_distances = np.vstack(e_distances)

        # 구현과정 2
        sort_e_distances = np.sort(e_distances, axis=-1)
        closest_e_distances_data = sort_e_distances[..., 0]

        argsort_e_distances = np.argsort(e_distances, axis=-1)
        closest_centroid_indices = argsort_e_distances[..., 0]

        self.closest_centroid_indices = closest_centroid_indices

    def make_new_centroid(self):
        # 구현과정 3 : 군집마다의 평균을 구해 새로운 centroid 추출
        self.new_centroides = list()        

        for centroid_idx in range(self.K):
            # closest_centroid_indices가 for문 내의 현재 centroid_idx와 일치한 dataset 추출
            closest_dataset = self.dataset[self.closest_centroid_indices == centroid_idx]
            # print(closest_data_indices.shape)   # (??, )
            # print(closest_dataset.shape)    # (??, 2): (x, y)의 값 단, 2차원일 경우
            new_centroid = np.sum(closest_dataset, axis=0) / closest_dataset.shape[0]

            self.new_centroides.append(new_centroid)

        self.new_centroides = np.vstack(self.new_centroides)

    def new_clustering(self):
        e_distances = list()
        for data_idx in range(self.dataset.shape[0]):
            data = self.dataset[data_idx]

            e_distance = np.sqrt(np.sum((data-self.new_centroides)**2, axis=-1))
            e_distances.append(e_distance)

        e_distances = np.vstack(e_distances)

        sort_e_distances = np.sort(e_distances, axis=-1)
        closest_e_distances_data = sort_e_distances[..., 0]

        argsort_e_distances = np.argsort(e_distances, axis=-1)
        closest_centroid_indices = argsort_e_distances[..., 0]

        self.closest_centroid_indices = closest_centroid_indices

    def repeat(self, repeat_count):
        for repeat in range(repeat_count):
            self.make_new_centroid()
            self.new_clustering()

    def vis_result(self, value="repeat"):
        if value == "make_centroides":
            self.vis_make_centroides()

        elif value == "clustering":
            self.vis_clustering()
        
        elif value == "make_new_centroid":
            self.vis_make_new_centroid()
        
        elif value == "new_clustering":
            self.vis_new_clustering()
        
        elif value == "repeat":
            self.vis_repeat()

        else:
            print("check your value")

    def vis_make_centroides(self):
        cmap = cm.get_cmap(name="tab10", lut=self.K)

        fig, ax = plt.subplots(figsize=(10, 10))
        ax.scatter(self.dataset[..., 0], self.dataset[..., 1], alpha=0.5, s=100)
        ax.scatter(self.dataset[self.centroid_indicies, 0], self.dataset[self.centroid_indicies, 1], color=cmap(np.arange(self.K)),alpha=1, s=200)

        plt.show()
    
    def vis_clustering(self):
        cmap = cm.get_cmap(name="tab10", lut=self.K)

        fig, ax = plt.subplots(figsize=(10, 10))
        ax.scatter(self.dataset[..., 0], self.dataset[..., 1], color=cmap(self.closest_centroid_indices), alpha=0.5, s=100)
        ax.scatter(self.dataset[self.centroid_indicies, 0], self.dataset[self.centroid_indicies, 1], color=cmap(np.arange(self.K)),alpha=1, s=300)

        plt.show()

    def vis_make_new_centroid(self):
        cmap = cm.get_cmap(name="tab10", lut=self.K)

        fig, ax = plt.subplots(figsize=(10, 10))
        ax.scatter(self.dataset[..., 0], self.dataset[..., 1], color=cmap(self.closest_centroid_indices), alpha=0.5, s=100)
        # 기존 centroid
        ax.scatter(self.dataset[self.centroid_indicies, 0], self.dataset[self.centroid_indicies, 1], color=cmap(np.arange(self.K)),alpha=1, s=300)
        # new centroid
        ax.scatter(self.new_centroides[:, 0], self.new_centroides[:, 1], color=cmap(np.arange(self.K)),alpha=1, s=500)
        
        arrowprops = dict(color='black',arrowstyle="-|>")
        for i in range(self.K):
            centroid_idx = self.centroid_indicies[i]
            ax.annotate(" ", 
                xy=(self.new_centroides[i, 0], self.new_centroides[i, 1]), 
                xytext=(self.dataset[centroid_idx, 0], self.dataset[centroid_idx, 1]),
                arrowprops=arrowprops)

        plt.show()
        
    def vis_new_clustering(self):
        cmap = cm.get_cmap(name="tab10", lut=self.K)

        fig, ax = plt.subplots(figsize=(10, 10))
        ax.scatter(self.dataset[..., 0], self.dataset[..., 1], color=cmap(self.closest_centroid_indices), alpha=0.5, s=100)
        ax.scatter(self.new_centroides[..., 0], self.new_centroides[..., 1], color=cmap(np.arange(self.K)),alpha=1, s=300)

        plt.show()

    def vis_repeat(self):
        cmap = cm.get_cmap(name="tab10", lut=self.K)

        fig, ax = plt.subplots(figsize=(10, 10))
        ax.scatter(self.dataset[..., 0], self.dataset[..., 1], color=cmap(self.closest_centroid_indices), alpha=0.5, s=100)
        ax.scatter(self.new_centroides[..., 0], self.new_centroides[..., 1], color=cmap(np.arange(self.K)),alpha=1, s=300)

        plt.show()

 

- 실행 시 결과 시각화

1. kmeans_clustering.make_centroides()

kmeans_clustering.vis_result(value="make_centroides")

 

2. kmeans_clustering.clustering()

 

kmeans_clustering.vis_result(value="clustering")

 

3. kmeans_clustering.make_new_centroid()

kmeans_clustering.vis_result(value="make_new_centroid")

 

 

4. kmeans_clustering.new_clustering()

kmeans_clustering.vis_result(value="new_clustering")

 

5.

repeat_count = 10

kmeans_clustering.repeat(repeat_count)

kmeans_clustering.vis_result()  # value="repeat"

 

이상으로 Classification과 Clustering의 의미와 차이점을 알아보고,

이와 관련된 기초적인 알고리즘 KNN Classification Algorism, K-Means Clustering Algorism에 대해 알아보았다.

뜻깊은 시간이었다. 성장하고 있다는 게 느껴지네라고 말하고 싶은데, 아직 갈길이 멀다. 화이팅

반응형

댓글