본문 바로가기
Machine Learning

[혼자 공부하는 머신러닝+딥러닝] k-평균

by 가론노미 2023. 5. 26.
[혼자 공부하는 머신러닝+딥러닝] 책의 내용을 정리한 글입니다.

개념

k-평균 알고리즘

: 랜덤 하게 클러스터 중심을 정해 클러스터를 만든 후 클러스터의 중심을 이동하는 과정을 반복하면서 최적의 클러스터를 구성하는 알고리즘

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

 

k-평균 알고리즘의 단점 중 하나는 클러스터 개수를 사전에 지정해야 한다는 것이다.

적절한 k 값을 찾기 위한 완벽한 방법은 없지만, 각각 장단점이 있는 몇 가지 도구들이 있다.

 

엘보우 방법

: 최적의 클러스터 개수를 정하는 방법 중 하나

  • 클러스터 개수를 늘려가면서 이너셔의 변화를 관찰하여 최적의 k 값을 찾는 방법
    • 이너셔 - 클러스터 중심과 샘플 사이 거리의 제곱 합 (클러스터에 속한 샘플이 얼마나 가깝게 모여있는지를 나타냄)
    • 이너셔가 감소하는 속도가 꺾이는 지점을 최적의 클러스터 개수로 판단

코드 작성

k-평균 군집 알고리즘을 통해 타깃 값 없이 과일 데이터를 자동으로 분류해보자.

과일 데이터 준비

!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)

k-평균 알고리즘 클래스인 KMeans 사용

from sklearn.cluster import KMeans

# 우선은 클러스터 개수를 3으로 임의 지정
km = KMeans(n_clusters=3, random_state=42)
km.fit(fruits_2d)

# 각 샘플이 어떤 레이블에 해당되는지 알 수 있는 배열
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]))

각 클러스터가 모은 샘플들을 이미지로 출력

import matplotlib.pyplot as plt

def draw_fruits(arr, ratio=1):
    n = len(arr)    # n은 샘플 개수

    # 한 줄에 10개의 이미지. 샘플 개수를 10으로 나누어 전체 행 개수를 계산.
    rows = int(np.ceil(n/10))
    # rows 1인 경우 샘플 개수가 10보다 작다는 뜻이므로, 열 개수는 rows가 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(fruits[km.labels_==0])
draw_fruits(fruits[km.labels_==1])
draw_fruits(fruits[km.labels_==2])

레이블별로 클러스터링된 이미지 출력

군집 알고리즘이 최종적으로 찾은 클러스터 중심 값 확인

# 100x100 크기의 2차원 배열로 바꾼 후 출력
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)

인덱스가 100인 샘플에 대한 예측 결과 확인

# 각 클러스터 중심까지의 거리를 출력
print(km.transform(fruits_2d[100:101]))
# => [[3393.8136117  8837.37750892 5267.70439881]]

# 가장 가까운 클러스터 중심을 예측 클래스로 출력
print(km.predict(fruits_2d[100:101]))
# => [0]

엘보우 방법을 사용하여 최적의 k 값(클러스터 개수) 찾기

inertia = []
# 클러스터 개수 k를 2~6까지 바꿔가며 5번 훈련
for k in range(2, 7):
    km = KMeans(n_clusters=k, n_init='auto', 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()

클러스터 개수에 따른 이너셔 변화

이 장에서 사용된 핵심 패키지와 함수

scikit-learn

  • KMeans
    • k-평균 알고리즘 클래스
    • n_cluster : 클러스터 개수 지정 (기본값 - 8)
    • n_init : 처음에 랜덤 하게 클러스터 중심을 초기화하기 때문에 여러 번 반복하여 이너셔를 기준으로 가장 좋은 결과를 선택하는데, 이 반복 횟수를 지정 (기본값 - 10)
    • max_iter : k-평균 알고리즘을 한 번 실행하는 동안 최적의 클러스터 중심을 찾기 위해 반복할 수 있는 최대 횟수 (기본값 - 200)