본문 바로가기

KHUDA 활동 아카이브/Machine Learning 기초

Ch 6. 비지도 학습

지금까지는 훈련데이터가 실제로 어떤 결과인지를 알려주는, 즉 지도학습을 공부해봤다.

 

그렇다면, 컴퓨터에게 데이터를 주는데, 그 데이터가 우리도 무엇인지 모르는 상황이라면 어떨까? 

 

컴퓨터에게 데이터 라벨링을 해주지 않아도 자기 스스로 그 특징을 찾아 훈련데이터들을 분류할 수 있을까?

 

이를 ‘비지도 학습’이라고 부른다. 




다음과 같은 상황을 가정하자.



“나는 과일가게에서 일하고 있다. 과일들의 수요를 조사하기 위해, 손님들에게 가장 좋아하는 과일의 사진을 문자로 보내달라고 했다. 

 

문자로 온 과일들의 사진을 분류해서, 어떤 과일을 손님들이 가장 좋아하는지 알고 싶다. 

 

하지만, 어떤 과일 사진들이 올지 우리는 알지 못한다. 대충 예상은 가지만 말이다.

 

손님들이 보내준 과일 사진을 알아서 분류해주는 머신러닝 모델은 없을까?”




바로 이런 상황이다. 



대표적인 비지도 학습 알고리즘으로 ‘k-평균 군집 알고리즘’이 존재한다. 이 알고리즘을 이용해 문제를 해결해보자.

 

우선 k-평균 알고리즘은 다음과 같은 원리로 작동한다.

 





그림으로 살펴보면 다음과 같다.

 




역시 사이킷런에는 이 알고리즘을 구현하는 클래스가 존재한다. 



우선 과일 데이터들의 사진을 불러온다.



!wget https://bit.ly/fruits_300_data -O fruits_300.npy



그리고 다음과 같이 100*100 사진들을 1차원으로 바꿔준다.

import numpy as np

 

fruits = np.load('fruits_300.npy')

fruits_2d = fruits.reshape(-1, 100*100)

 

이제 k-평균 알고리즘을 구현하는 KMeans 클래스를 이용해 모델링을 한다.

 

from sklearn.cluster import KMeans

 

km = KMeans(n_clusters=3, random_state=42)

km.fit(fruits_2d)

 

이 km이라는 모델이 사진을 어떻게 분류했는지 궁금하다. 그 정보는 km의 labels_라는 변수 안에 존재한다.

 

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]




이렇게 분류했다는데, 역시 그림으로 보는게 편할거 같다. 그림을 보여주는 함수를 우선 정의하자.

 

import matplotlib.pyplot as plt

 

def draw_fruits(arr, ratio=1):

    n = len(arr)    # n은 샘플 개수입니다

    # 한 줄에 10개씩 이미지를 그립니다. 샘플 개수를 10으로 나누어 전체 행 개수를 계산합니다.

    rows = int(np.ceil(n/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()



먼저 0으로 분류된 사진을 출력해보면?

 

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

 

 

사과와 바나나가 몇개 섞여있지만, 대부분이 파인애플인 것을 확인할 수 있다. 

 

1과 2도 확인해보면 각각 바나나와 사과가 나타난다.



정확도가 100은 아니지만, 나름 성공적으로 분류를 완료했다!

 

하지만, 위에 우리는 과일이 3가지로 분류된다는 정보를 사용해버렸다. 원래는 알지 못하는데 말이다.

 

즉 몇개의 클래스로 분류되는지를 미리 알 방법이 없는 것이다.k-평균 알고리즘에서의 k의 뜻이 k개의 클래스로 나뉘어진다는 뜻이다.

 

즉 우리는 최적의 k값도 찾아야 한다. 어떻게 찾을 수 있을까?





단순히 생각해보자.

 

보통 우리가 각 데이터를 클러스터로 나누려는 상황일 때, 각 데이터는 같은 클래스끼리 뭉쳐있을 것이다. 

 

처음에 클러스터가 2개라고 가정을 하고, 점차 클러스터의 갯수를 늘려나가는 상황을 생각해보자. 그리고 각 클러스터의 중심과 클러스터에 속한 샘플 간의 거리의 제곱의 합도 함께 고려해보자. 이를 ‘이너셔’라고 부르겠다.

 

클러스터가 늘어날수록  클러스터 내의 샘플의 갯수는 줄어들 것인데, 어느 순간부터 이너셔의 값이 꺾이는 순간이 있을 것이다.



이때가 최적의 클러스터 개수일 것이다. 

 

 

이너셔의 감소 속도가 줄어든다는 의미는, 클러스터 중심으로 부터 먼 샘플들은 다 사라졌고, 가까운 샘플들이 없어지기 시작한다는 징조이기 때문이다.



다시 예제로 돌아오자. 

 

만약 데이터셋의 과일 종류가 3개인지 모르는 상황이라고 가정하고, 클러스터의 값(k값)을 바꿔가며 이너셔의 값을 살펴보자.

 

inertia = []

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



대략 k가 3인 점에서 이너셔의 속도가 꺾이는 것을 알 수 있다. 즉, 과일 종류가 원래 3개인 것을 몰랐어도 이너셔를 이용해 3개임을 알 수 있었을 것이다!







 

  1. k-평균 군집 알고리즘은 군집을 이루고 있는 데이터들을 분류해주는데, 그 원리를 따져보면 군집이 원 형태로 존재할 때만 제 성능을 낼거 같다. 만약 군집의 모양이 곡선과 같은 형태로 이루어져있어도 제대로 성능을 낼 수 있을까? 

 

  1. 최적의 k값을 찾는 방법중 엘보우 방법을 책에서 살펴봤는데, 다른 여러 방법도 장단점이 존재한다고 했다. 엘보우 방법의 장단점은 무엇이고, 다른 방법의 장단점은 무엇일까? 어떤 상황에서 어떤 방법을 사용하는게 좋을까?