[혼공머신] k-평균 알고리즘

강민우·2022년 2월 10일
0
post-thumbnail

[혼자 공부하는 머신러닝+딥러닝] 책에 기반한 정리글입니다.
전체 소스코드는 아래 Github 링크에서 확인할 수 있습니다.

Github 링크

0. 개요

저번 편에서 군집 알고리즘은 타깃값을 미리 알고 있었기 때문에 각 과일 사진의 픽셀의 평균을 구하는 것이 가능했지만, 진짜 비지도 학습에서는 사진에 어떤 과일이 들어있는지 모른다.

타깃값이 없어도 사진을 자동으로 분류하는 k-평균 알고리즘을 알아본다.

1. k-평균 알고리즘

1-1. k-평균 알고리즘의 소개

비지도 학습에서 대상의 타깃을 모를 때 사용할 수 있다.
k-평균 군집 알고리즘이 평균값을 자동으로 찾아주는데, 이 평균값이 클러스터 중심에 위치하기 때문에 클러스터 중심이라고 부른다.

1-2. k-평균 알고리즘의 작동 방식

  1. 무작위로 k개의 클러스터 중심을 정한다.
  2. 각 샘플에서 가장 가까운 클러스터 중심을 찾아 해당 클러스터의 샘플로 지정한다.
  3. 클러스터에 속한 샘플의 평균값으로 클러스터 중심을 변경한다.
  4. 클러스터 중심에 변화가 없을 때까지 2번으로 돌아가 반복한다.

k-평균 알고리즘은 처음에는 랜덤하게 클러스터 중심을 선택하고 점차 가장 가까운 샘플의 중심으로 이동하는 알고리즘이다.

2. k-평균 모델 만들기

2-1. 데이터 준비하기

!wget https://bit.ly/fruits_300_data -O fruits_300.npy
import numpy as np
fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1, 100*100)

준비된 넘파이 배열을 100x10000 크기로 재배열한다.

2-2. k-평균 알고리즘으로 모델 학습하기

사이킷런의 k-평균 알고리즘sklearn.cluster 모듈에 KMeans 클래스가 구현되어 있다.
n_cluster 매개변수로 클러스터 갯수를 지정할 수 있다.
3개로 지정 후 모델을 훈련시킨다.

from sklearn.cluster import KMeans
km = KMeans(n_clusters = 3, random_state=42)
km.fit(fruits_2d)

군집된 결과는 KMeans 객체의 labels_ 속성된 결과에 저장된다.
클러스터 갯수가 3이기 때문에 배열의 값은 0, 1, 2 중 하나이다.
단, 레이블값과 순서에 의미는 없다.

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]

이를 통해 각 클러스터의 샘플의 갯수를 알 수 있다.

print(np.unique(km.labels_, return_counts=True))
출력 (array([0, 1, 2], dtype=int32), array([111,  98,  91]))

2-3. 각 클러스터의 그림 출력하기

각 클러스터가 어떤 이미지를 나타냈는지 그림으로 출력하기 위해 간단한 유틸리티 함수를 만들어본다.

import matplotlib.pyplot as plt
def draw_fruits(arr, ratio=1) :
  n = len(arr)
  rows = int(np.ceil(n/10)) #올림
  cols = n if rows < 2 else 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 :
        axs[i, j].imshow(arr[i*10+j], cmap='gray_r')
      axs[i, j].axis('off')
  plt.show()

draw_fruits()는 (샘플갯수, 너비, 높이)의 3차원 배열을 받아 가로로 10개의 이미지를 출력하는 함수이다.
figsizeratio 매개변수에 비례하여 커진다.

draw_fruits()에 fruits 배열을 불리언 인덱싱을 통해 넣어준다.

draw_fruits(fruits[km.labels_==2])


사과를 완벽하게 분류했다.

draw_fruits(fruits[km.labels_==0])


레이블 0에는 파인애플과 바나나, 사과가 섞여있는 것을 볼 수 있다.

k-평균 알고리즘이 이 샘플들을 완벽하게 분류하진 못했지만, 비슷한 샘플을 잘 모은 것을 볼 수 있다.

2-4. 클러스터의 중심 찾기

KMeans 클래스가 최종적으로 찾은 클러스터 중심은 clustercenters 속성에 저장되어 있다. 이를 그림으로 표현해본다.

draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)


이전에 각 과일의 평균 픽셀값을 출력했던 것과 비슷함을 확인할 수 있다.

훈련 데이터 샘플에서 클러스터 중심까지 거리로 변환해주는 transform() 메서드와, 데이터를 예측하는 predict() 메서드가 있다.
클러스터 중심이 가장 가까운 것이 예측 클래스로 출력된다.

print(km.transform(fruits_2d[100:101]))
print(km.predict(fruits_2d[100:101]))
#출력
[[3393.8136117  8837.37750892 5267.70439881]]
[0]

k-평균 알고리즘은 클러스터 중심을 옮기면서 최적의 클러스터를 찾는 과정을 반복하는데, 알고리즘이 반복한 횟수는 n_iter_에 저장된다.

print(km.n_iter_)
출력 4

3. 최적의 k 찾기 : 엘보우 방법

k-평균 알고리즘의 단점 중 하나는, 클러스터 갯수를 사전에 지정해야 한다는 것이다.
군집 알고리즘에서 적절한 k값을 찾는 완벽한 방법은 없다. 저마다 장단점이 있지만, 가장 대표적인 엘보우 방법을 알아본다.

k-평균 알고리즘은 클러스터 중심과 클러스터에 속한 샘플 사이의 거리를 잴 수 있는데, 이것의 제곱합을 이너셔라고 한다.

이너셔는 클러스터에 속한 샘플이 얼마나 가깝게 모여있는지를 나타내는 값인데, 클러스터 개수가 늘어나면 이너셔도 줄어든다.

엘보우 방법은 클러스터 갯수를 늘려가면서 이너셔의 변화를 관찰하여 최적의 클러스터를 찾는 방법이다.

클러스터 갯수에 대한 이너셔를 그래프로 그리면 꺽이는 지점이 있는데, 그 지점이 바로 적절한 클러스터 갯수이다.

KMeans 클래스는 자동으로 이너셔를 계산해서 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()

그래프를 통해 적절한 클러스터 갯수는 3임을 알 수 있다.

profile
어제보다 성장한 오늘

0개의 댓글