1. K-Means(군집화)

5. K-Means

데이터를 K 개의 클러스터(그룹)로 군집화하는 알고리즘,

각 데이터로부터 이들이 속한 클러스터의 중심점(Centroid)까지의 평균 거리를 계산

이론

동작 순서

  1. K 값을 사람이 설정

  2. 지정된 K 개 만큼의 랜덤 좌표 설정

  3. 모든 데이터로부터 가장 가까운 중심점 선택

  4. 데이터들의 평균 중심으로 중심점 이동

  5. 중심점이 더 이상 이동되지 않을 때까지 반복

K-Means++

K-Means의 무작위 선정 문제(Random Initialization Trap) 를 해결한 방식이다.

  • (중심점)무작위 선정 문제란 동작 순서의 2번처럼 무작위로 선정을 하기 때문에 매번 결과가 달라질 가능성이 높다는게 문제이다.

동작 순서

  1. 데이터 중에서 랜덤으로 1개를 중심점으로 선택

  2. 나머지 데이터로부터 중심점까지의 거리 계산

  3. 중심점과 가장 먼 지점의 데이터를 다음 중심점으로 선택

  4. 중심점이 K 개가 될 때까지 반복

  5. K-Means 전통적인 방식으로 진행

즉, 처음부터 제일 멀리 떨어져 있는 중심점을 고른후 K-Means 실행하는 방식!

Elbow Method

최적의 K(Optimal K) 를 찾는 방법

  1. K 변화에 따른 중심점까지의 평균 거리 비교

  2. 경사가 완만해지는 지점의 K 선정

유사도

  • Euclidean Distance(유클리드 거리) : 점과 점사이 직선의 거리 구하는 공식

    • $ d = \sqrt {(x_2-x_1)^2+(y_2-y_1)^2} $
  • Manhattan Distance(맨해튼 거리) : 직선이 아닌 돌아서 가는 공식

    • $ d = x_2-x_1 + y_2-y_1 $
  • Cosine Similarity(코사인 유사도) : 각 직선과 제일 각도가 작은곳이 유사

    • cos(θ) = $ \frac {A\cdot B} {   A   \   B   } $
# 이 부분이 먼저 실행 후에 아래 numpy 가져와야지 numpy가 이부분 체크하고 가져온다.
import os # 경고 대응
os.environ['OMP_NUM_THREADS'] = '1'
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
dataset = pd.read_csv('KMeansData.csv')
dataset[:5]
hour score
0 7.33 73
1 3.71 55
2 3.43 55
3 3.06 89
4 3.33 79
X = dataset.iloc[:, :].values
# X = dataset.values
# X = dataset.to_numpy() # 공식 홈페이지 권장
X[:5]
array([[ 7.33, 73.  ],
       [ 3.71, 55.  ],
       [ 3.43, 55.  ],
       [ 3.06, 89.  ],
       [ 3.33, 79.  ]])

데이터 시각화 (전체 데이터 분포 확인)

plt.scatter(X[:, 0], X[:, 1]) # x축 : hour, y축 : score
plt.title('Score by hours')
plt.xlabel('hours')
plt.ylabel('score')
plt.show()

데이터 시각화 (축 범위 통일)

# 위 전체 데이터를 보면 축 범위가 다르다. 이를 통일하겠다.
plt.scatter(X[:, 0], X[:, 1]) # x축 : hour, y축 : score
plt.title('Score by hours')
plt.xlabel('hours')
plt.xlim(0, 100)
plt.ylabel('score')
plt.ylim(0, 100)
plt.show()

피처 스케일링 (Feature Scaling)

축 범위를 보기 좋게 통일하는 방안이다.

사용도 간편해서 활용하기 좋은 함수이다.

이를 사용하고 안하고의 차이도 상당하므로 왜 사용하는지를 알도록 하자.

from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X = sc.fit_transform(X)
X[:5]
array([[ 0.68729921,  0.73538376],
       [-0.66687438,  0.04198891],
       [-0.77161709,  0.04198891],
       [-0.9100271 ,  1.35173473],
       [-0.8090252 ,  0.96651537]])

데이터 시각화 (스케일링된 데이터)

plt.figure(figsize=(5, 5))
plt.scatter(X[:, 0], X[:, 1])
plt.title('Score by hours')
plt.xlabel('hours')
plt.ylabel('score')
plt.show()

최적의 K 값 찾기 (엘보우 방식 Elbow Method)

from sklearn.cluster import KMeans
inertia_list = []
for i in range(1, 11): # 클러스터 10개까지 결과 구해보기
    kmeans = KMeans(n_clusters=i, init='k-means++', random_state=0)
    kmeans.fit(X) # 학습
    inertia_list.append(kmeans.inertia_) # 각 지점으로부터 클러스터의 중심(centroid) 까지의 거리의 제곱의 합
  
plt.plot(range(1, 11), inertia_list)
plt.title('Elbow Method')
plt.xlabel('n_clusters')
plt.ylabel('inertia')
plt.show()

최적의 K (4) 값으로 KMeans 학습

K = 4 # 최적의 K 값
kmeans = KMeans(n_clusters=K, random_state=0) # default가 k-means++
# kmeans.fit(X)
y_kmeans = kmeans.fit_predict(X) # 각 클러스터의 데이터들을 포함해서 반환
y_kmeans
array([2, 3, 3, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 3, 3, 0, 1, 2, 3, 0, 1, 0,
       3, 1, 2, 2, 3, 3, 3, 3, 1, 1, 3, 0, 2, 2, 3, 0, 0, 0, 3, 1, 2, 3,
       3, 2, 1, 0, 1, 1, 2, 0, 1, 1, 0, 0, 0, 0, 3, 1, 1, 2, 2, 2, 2, 1,
       1, 0, 1, 2, 3, 2, 2, 2, 3, 3, 3, 3, 0, 2, 1, 2, 1, 1, 2, 0, 3, 1,
       2, 3, 0, 1, 0, 2, 3, 2, 2, 0, 1, 3])

데이터 시각화 (최적의 K)

centers = kmeans.cluster_centers_ # 클러스터의 중심점 (centroid) 좌표
centers
array([[-0.57163957,  0.85415973],
       [ 0.8837666 , -1.26929779],
       [ 0.94107583,  0.93569782],
       [-1.22698889, -0.46768593]])
# X[y_kmeans == cluster, 0] 는 y_kmeans에서 cluster인 것들만 의미하고, 뒤에 0은 시간 열을 의미한다.
# s=100은 점의 사이즈를 의미한다.
# centers[cluster, 0] 는 클러스터를 표현하기위해 위에서 구한 클러스터의 중심점 좌표를 활용한 것이다.
for cluster in range(K):
    plt.scatter(X[y_kmeans == cluster, 0], X[y_kmeans == cluster, 1], s=100, edgecolor='black') # 각 데이터
    plt.scatter(centers[cluster, 0], centers[cluster, 1], s=300, edgecolor='black', color='yellow', marker='s') # 중심점 네모
    plt.text(centers[cluster, 0], centers[cluster, 1], cluster, va='center', ha='center') # 클러스터 텍스트 출력
    
plt.title('Score by hours')
plt.xlabel('hours')
plt.ylabel('score')
plt.show()

데이터 시각화 (스케일링 원복)

X_org = sc.inverse_transform(X) # Feature Scaling 된 데이터를 다시 원복
X_org[:5]
array([[ 7.33, 73.  ],
       [ 3.71, 55.  ],
       [ 3.43, 55.  ],
       [ 3.06, 89.  ],
       [ 3.33, 79.  ]])
centers_org = sc.inverse_transform(centers)
centers_org # 위에서 구한 클러스터 좌표를 원복된 실제 데이터로 표현
array([[ 3.96458333, 76.08333333],
       [ 7.8552    , 20.96      ],
       [ 8.0084    , 78.2       ],
       [ 2.21269231, 41.76923077]])
for cluster in range(K):
    plt.scatter(X_org[y_kmeans == cluster, 0], X_org[y_kmeans == cluster, 1], s=100, edgecolor='black') # 각 데이터
    plt.scatter(centers_org[cluster, 0], centers_org[cluster, 1], s=300, edgecolor='black', color='yellow', marker='s') # 중심점 네모
    plt.text(centers_org[cluster, 0], centers_org[cluster, 1], cluster, va='center', ha='center') # 클러스터 텍스트 출력
    
plt.title('Score by hours')
plt.xlabel('hours')
plt.ylabel('score')
plt.show()

참고자료 : 나도코딩-유튜브

추천!

댓글남기기