머신러닝

머신 러닝(with Google Colab) - 6일차(2)

aisw7984 2024. 10. 25. 16:46
반응형

 K-평균

앞선 이미지의 분류에서는 300개의 데이터에서 100개 단위로 동일한 종류의 과일이 나열돼있어 분류 시 위치를 알고 선택했었다. 하지만 진정한 비지도 학습이라면 무작위의 데이터에서 이미지를 분류할 수 있어야 할 것이다. 이럴 때 사용하는 방법이 K-평균이다. 앞서 동일한 특징을 가진 데이터의 집합을 군집(클러스터)라고 했었는데 이 군집의 평균을 내어 중심을 찾고 이를 기반으로 이미지를 분류한다.  K-평균 알고리즘의 동작은 아래와 같다.


k-평균 알고리즘 동작방식

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

말로만 설명한다면 당연히 이해가 어려울 테니 코드로 넘어가 보자 데이터는 이전에 사용했던 데이터를 똑같이 쓴다.


KMeans 클래스

 sklearn.cluster 모듈아래 KMeans 클래스는 자동으로 데이터를 지정한 클러스터 수에 맞춰서 분류를 해준다. 

# KMeans 클래스
#정보 가져오기 
!wget https://bit.ly/fruits_300_data -0 fruits_300.npy
import numpy as np
fruits = np.load('fruits_300.npy') # fruits.shape = (300,100,100)

#훈련 데이터
fruits_2d = fruits.reshape(-1,100*100) # fruits_2d.shape = (300,10000)

from sklearn.cluster import KMeans
km =KMeans(n_clusters=3, random_state = 15)
km.fit(fruits_2d) # 비지도 학습이기때문에 fit에는 target을 따로 해주지 않음
# # 각 샘플의 클러스터 번호
print(np.unique(km.labels_,return_counts= True)) # return_counts= True 는 각 샘플별 횟수를 표시해줌

 

[1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 2 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 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 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 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]
(array([0, 1, 2], dtype=int32), array([ 98,  91, 111]))

KMeans 클래스 결과를 확인해 보면 어느 정도 정확하게 동일한 데이터들끼리 묶어둔 것을 볼 수 있다.

우선 간단하게 이미지를 출력해 주는 함수를 하나 만들어 보자.

# 샘플의 개수를 입력받아 이미지를 출력하는 함수
import matplotlib.pyplot as plt

# ratio 은 이미지의 배율

def draw_fruits(arr, ratio=1):
    n =len(arr) # arr 는 입력된 샘플 수
    # row : 행 , col : 열

    # 한행에 10개씩 그림 그리기 위해 행 수 구하기
    rows = int(np.ceil(n/10))
    # np.ceil : 주어진 입력값보다 크거나 같은 최소 정수를 반환한다(올림)
    # 다음을 해주는 이유는 91개의 데이터를 이미지로 출력하려면 10행이 필요하기때문

    #행수가 1이면 n을 반환한하고 아니면 10
    cols = n if rows ==1 else 10

    fig, axs =plt.subplots(rows, cols, figsize=(cols*ratio, rows*ratio),squeeze=False)
    # squeeze  1차원을 제거해주는 기능
    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")
            axs[i,j].axis('off')

    # print("{}개 샘플 {}행 {}열".format(len(arr),rows,cols))
    plt.show()

함수를 간단히 설명하면 데이터의 크기를 파악하여 데이터의 크기에 맞게 열과 행의 개수를 나누고 한열에는 10개가 오도록 한다. 계산된 행과 열의 개수를 바탕으로으로 for문을 통해 이미지를 하나씩 출력한다.라는 함수이다.

draw_fruits(fruits[:15])

함수를 호출한 결과를 보면 앞의 15개의 이미지가 출력됐는데 앞서 앞 15개의 데이터는 '1'을 가리켰고 1은 사과의 데이터임을 알 수 있다.

이제 본격적으로 클러스터의 중심을 찾아보자 KMeans는 알아서 중심을 찾아주는 기능을 제공한다.

이때 주의해야 할 것은 클러스터의 중심의 경우 2차원 데이터 이기 때문에 reshape를 해주어야 한다.

기본적으로 KMeans 또한 거리를 기반으로 분류하기 때문에 군집 중심에 가까운 거리로 해당 군집에 속하게 된다.

# KMeans 클래스가 최종적으로 찾은 클러스터 중심은 cluster_centers 속성에 저장됨
draw_fruits(km.cluster_centers_.reshape(-1,100,100), ratio = 3)
#이때 주의 할점은 클러스트 중심은 2차원이라 reshape 해줘야됨

위에 이미지는 순서대로 바나나 사과 파인애플의 군집의 모양이다. 이제 임의의 100번째 과일을 예측해 보면

#훈련 데이터 샘플에서 클러스터 중심까지 거리로 변환해주는 transform()메서드
# 100번째 과일의 중심까지 거리확인
print(km.transform(fruits_2d[100:101]))
#3번째 군집에 가장 가까움
#100번째 과일 예측
print(km.predict(fruits_2d[100:101]))
# 예측또한 3번쨰 군집으로 예측한것을 알수 있음
->
[[8837.37750892 5267.70439881 3393.8136117 ]]
[2]

각 군집의 거리를 보면 3번째 즉, [2]에 가장 까까운 것을 알 수 있고 이를 이미지 출력함수로 호출해 보면

# 100번째 과일 확인
draw_fruits(fruits[100:101]) # 파인에플이네

파인애플이 나오는 것을 알 수 있다.


최적의 K 찾기

 

사실 앞에서도 완벽한 비지도 학습이라고 할 수 없는 것이 과일의 종류 즉, 군집의 수를 알고서 군집의 수를 넣어 계산하였기 때문에 완벽하지 않다 따라서 군집의 수를 예측하는 방법을 알아보자 대표적인 군집(클러스터)을 찾는 방법은 엘보우 방법이 있다. 해당 방법은 아래와 같다
우선 클러스터의 중심에는 각 항목들이 사방을 떨어져 있는데 이 거리들은 다양하게 섞여있다. 따라서 이 값들을 제곱하여 합해주면 이 값을 이너셔(inertia)라고 하는데 이 이너셔의 변화를 보고 추측할 수 있다.
기본적으로 클러스터의 수를 늘려주면 이너셔의 값은 줄어들게 된다. 클러스터가 무한히 늘어나게 되면 이너셔는 0에 가까워지게 되는데 이는 마치 확률적 경사 하강법과 비슷하게 줄어든다. 클러스터를 늘려주다 보면 어느 순간 경사가 급격히 줄어드는 지점이 있는데 이때가 적정 클러스터 수이다. 코드를 살펴보자

#클러스터(군집) 찾는 방법
#KMeans 클래스에는 inertia_ 속성으로 제공한다.
inertia = []
for k in range(2, 10):
    km = KMeans(n_clusters=k, n_init=10, random_state=15)
    # n_init는 초기화 반복 횟수
    #K값에 따라 훈련
    km.fit(fruits_2d)
    #k일때 이너셔 저장
    inertia.append(km.inertia_)

plt.plot(range(2,10), inertia,marker="o")
plt.xlabel("k")
plt.ylabel("inertia")
plt.show()
#결과를 보면 3d에서 경사가 완만해 지는 것을 볼수 있다

결과를 보면 3일때 그나마 경사가 완만해 지는것을 볼수 있고 이제 하이퍼파라미터인 군집수를 코드에 다시 넣어준다면 좋은 결과를 얻을수 있다. 

반응형