k-평균
혼공머신 6-2 K-평균
6-1에서 사용한 데이터는 이미 레이블이 되어있는 데이터이기때문에 클러스터링하는 것이 매우 쉬웠다
하지만 실제 비지도 학습에서는 각 이미지들이 레이블되어있지 않을 것이기 때문에 클러스터링 할 수 있는 방법이 필요할 것이다.
클러스터 중심
클러스터 중심 = 센트로이드(centroid)
각 군집(클러스터)의 평균 이미지로서 각 클러스터의 기준이 된다.
k-평균 알고리즘
1.k개(하이퍼 파라미터)의 클러스터 중심을 만들어서 군집의 개수를 정해준다.
2.각 샘플에서 가장 가까운 클러스터 중심을 찾아서 해당 클러스터의 샘플로 지정한다.
3.클러스터에 속한 샘플들의 평균값으로 클러스터 중심을 변경한다.
4.클러스터 중심에 변화가 없을 때까지 2번으로 돌아가 반복한다.
KMeans 클래스
300개의 과일 데이터를 사용해서 KMeans로 훈련해보자
import numpy as np
fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1, 100*100) # 이미지 차원 1차원으로 변경
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state=42) #클러스터 개수 지정
km.fit(fruits_2d)
# train / test를 따로 나누지 않는다.
# 군집에서는 과대적합 괴소적합이라는 개념이 따로 없다.
print(km.labels_)
# [2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
# 2 2 2 2 2 0 2 0 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 0 2
# 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0
# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
# 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
# 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
# 1 1 1 1]
# n_clusters=3으로 지정했기 때문에 0~2의 값이 지정된것을 볼 수 있다.
print(np.unique(km.labels_, return_counts=True))
#(array([0, 1, 2], dtype=int32), array([111, 98, 91]))
#return_counts를 True로 해주면 unique값 0,1,2를 보여준다.
# unique함수를 통해 개수를 알 수 있다. 0 111개 / 1 98개 / 2 91개
KMeans의 n_iter의 기본값은 10이고 이 중에서 가장 좋은 결과를 출력한다.
결과를 보면 각 레이블 0,1,2에 111, 98, 91로 각 클러스터의 개수를 지정했는데
실제 값들은 각각 100개일 것이므로 클러스터 안에 샘플들이 조금 섞여 있다는 것을 알 수 있다.
각 클러스터 확인
import matplotlib.pyplot as plt
def draw_fruits(arr, ratio=1):
n = len(arr) # n은 샘플 개수입니다
# 한 줄에 10개씩 이미지를 그립니다. 샘플 개수를 10으로 나누어 전체 행 개수를 계산합니다.
rows = int(np.ceil(n/10))
# np.ceil 올림 함수를 사용해서 이미지를 출력할 열 개수를 지정한다.
cols = n if rows < 2 else 10
# 행이 1개 이면 열 개수는 샘플 개수입니다. 그렇지 않으면 10개입니다.
fig, axs = plt.subplots(rows, cols,
figsize=(cols*ratio, rows*ratio), squeeze=False)
for i in range(rows):
for j in range(cols):
if i*10 + j < n: # n 개까지만 그립니다.
axs[i, j].imshow(arr[i*10 + j], cmap='gray_r')
# subplot의(i,j)번째 : axs[i,j]
# arrp[i*10 + j] row개수에 n/10해줬으므로 10곱하고 j=cols가 1씩 증가하므로 전부 출력
axs[i, j].axis('off')
# 이미지 선 없애기 위해서 .axis('off')사용
plt.show()
draw_fruits(fruits[km.labels_==0]) # 모든 이미지 중에서 레이블로 선별
draw_fruits(fruits[km.labels_==1])
draw_fruits(fruits[km.labels_==2])
km.labels_==0
km.labels_==1
km.labels_==2
직접 이미지를 확인 해 보면 파인애플에 일부 사과와 바나나가 섞겨있는 것을 볼 수 있다.
클러스터 중심
클러스터 중심값을 통해 어떻게 샘플들을 분류하는지 확인해보자
kmeans로 찾아낸 클러스터 중심을 이미지로 보기 편하게 다시 3차원으로 바꿔서 출력하면
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)
# cluster_centers_ 에 클러스터 중심 값 (센트로이드값)이 들어있다
# 2차원으로 들어있기 때문에 다시 reshape해준다
파인애플 바나나 사과 순으로 클러스터 중심(각 클러스터의 평균) 이미지를 보여준다
print(km.transform(fruits_2d[100:101]))
#[[3393.8136117 8837.37750892 5267.70439881]]
transform을 사용해서 각 클러스터 중심 값과의 거리로 그 이미지를 나타낼 수 있다.
원래 한 이미지는 100x100의 벡터로 나타내는데 이 방법으로 3개의 특성이 있는 벡터로 차원 축소가 가능하다.
모든 군집 메소드가 transform을 제공하지는 않는다 kmeans가 특별히 클러스터 중심을 사용하기 때문에 그 중심과의 거리를 계산할 수 있어서 transform이 존재한다.
print(km.predict(fruits_2d[100:101]))
#[0] 예상 : 파인애플
draw_fruits(fruits[100:101])
print(km.n_iter_)
#4 클러스터 중심을 찾기 위해 반복한 횟수
클러스터 중심으로 예측한 레이블을 확인하기 위해 직접 이미지를 출력해보면 파인애플이 나온 것을 확인할 수 있다.
최적의 k 찾기
kmeans는 클러스터 개수를 지정해주어야 해서 다른 군집 알고지즘보다 까다롭다.
그렇기 때문에 k값을 찾는 알고리즘도 존재한다.
elbow method
k값을 작은 k값부터 큰 k값까지 바꾸어보면서 클러스터의 intertia값을 계산한다.
inertia값 : 클러스터 안에서 클러스터 중심까지의 모든 거리를 평균한 값
이 값이 낮으면 조밀하게 잘 모여있다는 의미가 된다.
k값과 inertia값 사이의 그래프를 확인해 보자
inertia = []
for k in range(2, 7):
km = KMeans(n_clusters=k, random_state=42)
km.fit(fruits_2d)
inertia.append(km.inertia_)
plt.plot(range(2, 7), inertia)
plt.xlabel('k')
plt.ylabel('inertia')
plt.show()
inertia값은 k값이 증가하면 군집이 많아지므로 자연스럽게 낮아질 수 밖에 없다.
하지만 k값과 inertia 사이의 그래프를 보면 특정 k값에서 기울기가 더 완만해지도록 바뀐 구간이 보이는데
이 k값보다 더 큰 k값을 선택해서 inertia값을 낮추더라도 inertia값이 이전 구간보다 더 줄어들지 않았기 때문에 더 효과가 좋다고 말할 수 없다.
그러므로 위 그래프에서는 가장 좋은 k값은 3이라고 할 수 있다.
이 지점이 마치 팔꿈치 모양이라서 엘보우 방법이라고 부른다.
참고
박해선,혼자 공부하는 머신러닝, 한빛미디어, 2021,303~315p