- K-Means(군집화)
5. K-Means
데이터를 K 개의 클러스터(그룹)로 군집화하는 알고리즘,
각 데이터로부터 이들이 속한 클러스터의 중심점(Centroid)까지의 평균 거리를 계산
이론
동작 순서
-
K 값을 사람이 설정
-
지정된 K 개 만큼의 랜덤 좌표 설정
-
모든 데이터로부터 가장 가까운 중심점 선택
-
데이터들의 평균 중심으로 중심점 이동
-
중심점이 더 이상 이동되지 않을 때까지 반복
K-Means++
K-Means의 무작위 선정 문제(Random Initialization Trap)
를 해결한 방식이다.
-
(중심점)무작위 선정 문제
란 동작 순서의 2번처럼 무작위로 선정을 하기 때문에 매번 결과가 달라질 가능성이 높다는게 문제이다.
동작 순서
-
데이터 중에서 랜덤으로 1개를 중심점으로 선택
-
나머지 데이터로부터 중심점까지의 거리 계산
-
중심점과 가장 먼 지점의 데이터를 다음 중심점으로 선택
-
중심점이 K 개가 될 때까지 반복
-
K-Means 전통적인 방식으로 진행
즉, 처음부터 제일 멀리 떨어져 있는 중심점을 고른후 K-Means 실행하는 방식!
Elbow Method
최적의 K(Optimal K)
를 찾는 방법
-
K 변화에 따른 중심점까지의 평균 거리 비교
-
경사가 완만해지는 지점의 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()
참고자료 : 나도코딩-유튜브
추천!
댓글남기기