Python/ML & DL 공부

[ML] 06-2 k-평균 알고리즘

dori_0 2022. 3. 12. 14:02

혼자 공부하는 머신러닝+딥러닝 책을 바탕으로 공부한 내용입니다.

 

CH6 비지도 학습 ②

과일을 자동으로 분류하기

 


 

k-평균 알고리즘의 작동 방식을 이해하고 타깃값을 모를 때
과일 사진을 자동으로 모으는 비지도 학습 모델을 만들어보자

 

▶ K-평균 알고리즘 소개

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

 

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

 

클러스터 중심

  • k-평균 알고리즘이 만든 클러스터에 속한 샘플의 특성 평균값
  • 센트로이드라고도 부름
  • 가장 가까운 클러스터 중심을 샘플의 또 다른 특성으로 사용하거나 새로운 샘플에 대한 예측으로 활용할 수 있음

 

 

▶ KMeans 클래스

먼저 데이터를 불러와 3차원 배열을 2차원 배열로 변경해주자

# 데이터 불러오기
import wget
url = "https://bit.ly/fruits_300_data" 
wget.download(url)

# 2차원 배열로 변경
import numpy as np
fruits = np.load('fruits_300_data')
fruits_2d = fruits.reshape(-1, 100*100)

 

KMeans 클래스에서 n_clusters로 클러스터 개수를 지정해준다.

# K-평균 알고리즘
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state=42)
km.fit(fruits_2d)

# 군집된 결과 확인
print(km.labels_)
# 레이블 0, 1, 2 개수 확인
print(np.unique(km.labels_, return_counts=True))

  • 비지도 학습이므로 fit() 메서드에서 타깃 데이터를 사용하지 않는다.
  • 각 클러스터가 111, 98, 91개의 샘플을 모았다.
  • 레이블 값과 레이블 순서는 아무 의미도 없다.

 

 

각 클러스터가 어떤 이미지를 나타냈는지 그림으로 출력해주는 draw_fruits() 함수를 만들어보자

# 클러스트가 어떤 이미지를 나타냈는지 그림 출력 함수 만들기
import matplotlib.pyplot as plt

def draw_fruits(arr, ratio=1):
    n = len(arr)  # 샘플 개수
    rows = int(np.ceil(n/10))  # 한 줄에 10개씩
    # 행이 1개면 열 개수는 샘플 개수, 아니면 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: # n개까지 그리기
                axs[i, j].imshow(arr[i*10 + j], cmap='gray_r')
            axs[i, j].axis('off')
    plt.show()
  • draw_fruits() 함수는 (샘플 개수, 너비, 높이)의 3차원 배열을 입력받아 가로로 10개씩 이미지를 출력한다

 

 

이 함수를 사용해 각 레이블에 해당하는 과일 사진을 그려보자

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

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

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

  • k-평균 알고리즘이 완벽하게 구별하진 못했지만 타깃 레이블을 제공하지 않았음에도 스스로 비슷한 샘플들을 잘 모은 것 같다

 

 

▶ 클러스터 중심

KMeans 클래스가 최종적으로 찾은 클러스터 중심은 cluster_centers_ 속성에 저장되어 있다.

이 배열은 fruits_2d 샘플의 클러스터 중심이므로 이미지로 출력하기 위해 2차원 배열로 바꿔주자

# 클러스트 중심 이미지로 출력
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)

  • 앞서 봤던 사과, 바나나, 파인애플의 픽셀 평균값을 출력했던 것과 매우 비슷함을 알 수 있다.

 

 

KMeans 클래스는 훈련 데이터 샘플에서 클러스터 중심까지 거리로 변환해주는 transform() 메서드를 가지고 있다

또, 가장 가까운 클러스터 중심을 예측 클래스로 출력하는 predict() 메서드를 가지고 있다.

 

인덱스가 100인 샘플에 적용해보자

# 인덱스가 100인 샘플에 transform() 적용
print(km.transform(fruits_2d[100:101]))
# 가장 가까운 클러스터 중심을 예측 클래스로
print(km.predict(fruits_2d[100:101]))

  • 0번째 클러스터까지의 거리가 가장 작으므로 0으로 예측하고 있다.

 

위에서 레이블 0은 파인애플이었으므로 샘플도 파인애플 일 것으로 보인다

# 그려보기
draw_fruits(fruits[100:101])

  • 확인해보니 파인애플이 맞다!

 

 

k-평균 알고리즘은 최적의 클러스터를 찾기 위해 반복적으로 클러스터 중심을 옮긴다

알고리즘이 반복한 횟수는 KMeans 클래스의 n_iter_ 속성에 저장된다

# 알고리즘 반복 횟수
print(km.n_iter_)  #4

 

 

▶ 최적의 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()

  • k=3에서 그래프의 기울기가 조금 바뀐 것을 확인할 수 있다.

 

 

▶ 정리

k-평균 알고리즘

  • 비교적 간단하고 속도가 빠르며 이해하기 쉽다
  • 사전에 클러스터 개수를 미리 지정해야 한다.
  • 최적의 클러스터 개수 k를 알아내는 한 가지 방법은 이너셔를 사용하는 것이다.
  • 클러스터 개수를 늘리면서 반복하여 KMeans 알고리즘을 훈련하고 이너셔가 줄어드는 속도가 꺾이는 지점을 최적의 클러스터 개수로 결정한다.

'Python > ML & DL 공부' 카테고리의 다른 글

[DL] 07-1 인공 신경망  (0) 2022.03.20
[ML] 06-3 주성분 분석  (0) 2022.03.12
[ML] 06-1 군집 알고리즘  (0) 2022.03.12
[ML] 05-3 트리의 앙상블  (0) 2022.03.11
[ML] 05-2 교차 검증과 그리드 서치  (0) 2022.03.11