BuNa_
IT Story
BuNa_
전체 방문자
오늘
어제
  • 분류 전체보기 (117)
    • CS (14)
      • 운영체제 (8)
      • 네트워크 (0)
      • Design Pattern (1)
      • OOP (4)
    • 대외활동 (24)
      • 우아한테크코스 (14)
      • DND 동아리 (4)
      • UMC 동아리 (5)
      • 해커톤 (1)
    • Android (29)
      • MVVM (2)
      • 스터디 (11)
      • Compose (3)
      • Unit Test (1)
    • Project (5)
      • 어따세워 (5)
      • DnD 과외 서비스 (0)
    • Programming (11)
      • Kotlin (4)
      • 파이썬 (7)
    • Git (1)
    • 인공지능 (22)
    • 백준 (8)
    • 기타 (3)
      • IntelliJ (1)
      • 일상 (0)

블로그 메뉴

  • 홈

공지사항

인기 글

태그

  • 안드로이드
  • 객체지향 생활체조
  • 우테코 프리코스
  • k-means++
  • 컴공선배
  • 선형회귀
  • K-means
  • 원시값 포장
  • Ai
  • 인공지능 분류
  • 운영체제
  • UMC
  • 우테코 5기
  • Baekjoon
  • MVVM
  • Android
  • RecyclerView
  • ViewModel
  • 딥러닝
  • 우테코
  • 다이나믹 프로그래밍
  • External fragmentation
  • 어따세워
  • 우아한테크코스
  • 파이썬
  • 인공지능
  • 외부 단편화
  • 백준
  • Compose
  • 셀레니움

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
BuNa_

IT Story

[인공지능][실습] K-평균(K-Means) 알고리즘으로 과일(fruits) 사진을 분류해보고 엘보우(Elbow) 방법을 통해 최적의 k값을 찾아보자
인공지능

[인공지능][실습] K-평균(K-Means) 알고리즘으로 과일(fruits) 사진을 분류해보고 엘보우(Elbow) 방법을 통해 최적의 k값을 찾아보자

2021. 5. 23. 19:55

 

 

K-평균(K-Means)에 대한 이론이 필요하신 분들은 아래 링크를 참조해주시기 바랍니다.

[인공지능][개념] K-평균(K-means) 알고리즘과 군집화(Clustering) + 이너셔(Inertia) 이해하기 : https://itstory1592.tistory.com/17

 

 

 

k-means(k-평균)을 통한 과일 분류

 

이번 글에서는 K-평균(K-means) 알고리즘을 사용하여 과일 사진을 비지도 학습으로 분류해보고

엘보우(Elbow) 방법을 통해 최적의 k값을 찾아보자.

 

 

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

 

우선, 과일 사진을 다운로드하기 위해서 위의 코드를 입력해준다.

파이썬에서는 !(느낌표)를 사용하고 리눅스 명령어를 입력하면 해당 명령어를 사용할 수 있다.

여기서는 웹 서버로부터 콘텐츠를 가져오는 wget 명령어를 통해 과일 사진 300장을 가져오도록 하겠다.

 

 

 

그럼 위와 같은 문장들이 출력되면서 과일 사진이 다운로드되는 것을 확인해볼 수 있다.

이 파일은 흑백 과일 사진으로, 사과🍎 바나나🍌 파인애플🍍  사진이 각각 100개씩 총 300장 저장된 fruits_300.npy라는 파일이다.

 

 

import numpy as np

fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1, 100*100)

 

가져온 fruits_300.npy 파일을 넘파이를 통해 변수로 저장한다.

저장을 하였다면, reshape() 메소드를 통해 모든 사진을 1차원으로 펼쳐 주어야 한다.

그 이유는, 다운로드 받은 사진들은 모두 100px * 100px로 이루어져 있는데,

k-means 알고리즘은 각 픽셀 하나하나를 기준으로 분류할 것이기 때문에, 이미지를 1px씩 10000개로 펼쳐주는 작업을 해야 하는 것이다.

 

 

100px * 100px의 이미지를 1px * 10,000px로 펼쳤을 때의 이미지
100px * 100px의 이미지를 1px * 10,000px로 펼쳤을 때의 이미지

 

위 코드를 그림으로 표현하면 이런식으로 나타낼 수 있다.

100px * 100px의 그림 300장이 1px * 10,000px로 펼쳐졌음을 확인할 수 있다.

 

 

from sklearn.cluster import KMeans

km = KMeans(init='random', n_clusters=3, random_state=42)
km.fit(fruits_2d)

 

이제 본격적으로 k-means 알고리즘을 사용하기 위해, 사이킷런의 cluster 패키지에서 KMeans 클래스를 임포트한다.

KMeans 클래스를 생성할 때, init 이라는 매개변수를 설정할 수 있는데,

기본값인 random으로 설정하면 이론에서 설명한 기본적인 K-Means 알고리즘을 사용하게 되고,

random 대신, 'k-means++'로 설정하면 K-Means++ 알고리즘을 사용할 수 있다.

(K-Means++은 다음 글에서 설명하도록 하겠다.)

 

실제 K-Means를 사용할 때는 몇 가지로 분류해야 하는지 알 수 없기 때문에 정확한 클러스터 개수를 알 수 없지만,

해당 예제에서는 이미지가 사과, 바나나, 파인애플 총 3가지로 이루어져 있다는 것을 알기 때문에

n_clusters를 3으로 설정하여 클러스터의 개수를 3개로 지정하였다.

 

 

 

그리고 훈련을 마치면, 우리가 훈련시킨 모델에 대한 정보가 출력될 것이다.

init은 설정한 대로 기본 k-means 알고리즘인 'random'으로, 클러스터의 개수는 3개로 제대로 설정되었다.

최대 반복 횟수 변수인 'max_iter'는 따로 설정하지 않아 300으로 지정되었다.

 

 

print(km.labels_)

 

훈련된 모델에서 데이터가 어떻게 분류되었는지 확인하고 싶다면 labels_ 변수에 접근하면 된다.

labels_에 저장된 숫자들은 총 300개의 데이터들을 결과에 따라 분류해놓은 것이다.

결과를 확인해보면, 어느 정도 잘 분류된듯하나 각 숫자 사이에 다른 숫자들이 껴있는 것을 확인할 수 있다.

각 레이블에 데이터를 몇 개씩 포함하고 있는지 확인해보자.

 

 

print(np.unique(km.labels_, return_counts=True))

 

넘파이의 unique() 메소드에 return_counts를 True로 설정하면,

입력한 배열에 동일한 값의 개수가 몇 개 있는지를 알 수 있다.

 

labels_에는 '0'이 91개,  '1'이 111개,  '2'가 98개 포함되어 있다.

그럼 0, 1, 2라는 숫자는 각각 무슨 과일을 의미하는 것일까?

각 레이블로 분류된 결과를 확인해보도록 하자!

 

 

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

 

각 레이블에 해당하는 결과를 보기 위해서는 draw_fruits()라는 사용자 정의 함수를 만들어야 한다.

 

[코드 설명]

 

배열에 저장되어 있는 값을 10개씩 쪼개어 행(row), 열(column)을 만들고,

맷플롭립(matplotlib) 라이브러리의 pyplot에 있는 subplots() 메소드로 행열에 맞추어 이미지를 출력시킬 공간을 만든다.

그리고 subplots()을 출력함으로써 반환되는 값인 axs의 imshow()를 호출하여, 2중 for문 내에서 배열의 값을 이미지로 출력한다.

 

(결과적으로 위 코드는 배열을 입력받고, 배열에 있는 값을 한 줄에 10개씩 그림으로 출력해주는 함수이다.)

 

이제 우리가 정의한 draw_fruits() 함수를 사용하여 각 label에 들어간 과일의 이미지를 출력해보자! 🍎🍌🍍

 

 

 

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

 

label 0으로 분류된 과일 이미지
label 0으로 분류된 과일 이미지

 

불리언 인덱싱을 통해 label이 0에 해당하는 인덱스들만을 골라 출력해보았다.

예상대로 10개씩 잘 출력되었으며, label 0에 해당하는 이미지는 모두 91개의 사과로 분류되었었다는 것이 확인되었다.

 

 

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

 

label 1로 분류된 과일 이미지
label 1로 분류된 과일 이미지

 

label 1도 총 111개의 이미지로 구성된 과일들이 정상적으로 출력되었다.

label 1에 해당하는 과일 이미지에는 대부분이 파인애플이지만, 일부 사과와 바나나가 섞여 분류되었다.

모델의 정확도가 100%가 아니라는 것을 알 수 있다.

 

 

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

 

label 2로 분류된 과일 이미지
label 2로 분류된 과일 이미지

 

label 2에 해당하는 이미지들을 출력한 결과, 바나나 이미지로 구성된 98개의 과일 이미지가 모두 출력되었다.

제대로 분류되지 않은 바나나 2개는 label 1로 분류되었기 때문에 2개가 비는 것이다.

 

 

매우 놀랍게도 정확도가 100%는 아니지만 모델에게 정답(target)을 알려주지 않아도,

각 픽셀의 특징을 통해 어느 정도 과일들이 분류되었다.

이런 방식의 학습이 바로 비지도 학습(Unsupervised Learning)이다.

 

그렇다면 클러스터의 중심!

센트로이드(Centroid)는 과연 각각의 과일 이미지를 어떤 식으로 평균 짓고 있을까?

 

 

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

 

학습된 모델에서 변수 cluster_centers_ 에 접근하면 센트로이드 값을 얻을 수 있다.

이 값에 접근하여 3개의 클러스터의 평균에 위치한 센트로이드를 다시 100px * 100px의 형태로 변형(reshape)하여 출력해보자!

 

 

cluster_centers_로 확인해본 k-means의 센트로이드 이미지
cluster_centers_로 확인해본 k-means의 센트로이드 이미지

 

🍎 첫 번째 사진은 단숨에 사과임을 알아볼 수 있지만,

🍍🍌 나머지 두 과일들은 우리가 상식적으로 알고 있는 파인애플과 바나나의 형태가 아니다.

하지만 신비롭게도 인공지능은 저 세 가지 이미지를 과일들의 평균적인 형태라고 인식하고 분류해낸 것이다.

 

 

우리는 이번 예제에서 데이터가 사과, 바나나, 파인애플로만 이루어져 있다는 사실을 이미 알고 있었다.

그러나 실제 K-평균 알고리즘을 사용할 때는 무작위 데이터 중에서 분류를 수행해야 하는 일이 비일비재하다.

그렇다면, 총 몇 개의 클러스터로 모델을 구성해야 할까?

다시 말해 몇 가지의 종류로 데이터를 분류해야 하는 것일까?!

 

우리는 이전 이론에서 엘보우(Elbow)라는 최적의 k값을 찾는 방법에 대해 공부하였다.

특정 k값에서 이너셔(inertia)가 급격하게 줄어들어 그래프로 표현했을 때, 사람의 팔꿈치처럼 꺾여 보이는 경우가 있다고 했는데

이때의 k값이 최적의 클러스터 개수가 되는 것이다.

 

직접 이 과일 데이터를 바탕으로 최적의 클러스터 개수를 찾아보도록 하자.

 

 

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

 

먼저 이너셔를 담을 inertia 배열을 만들어준다.

그다음, 특정 k값에서 이너셔가 몇인지 알아낼 수 있도록 클러스터 값을 2부터 6까지 바꿔가면서 훈련을 시킨 후,

훈련을 통해 얻은 이너셔를 이전에 생성한 inertia 배열에 담는다.

 

 

k(클러스터 개수)값과 inertia(이너셔)의 그래프에서 최적의 클러스터 개수 찾기
k(클러스터 개수)값과 inertia(이너셔)의 그래프

 

그래프를 보면 확연하게는 아니지만 k가 3.0에서 꺾인다는 것을 알 수 있다.

데이터가 사과, 바나나, 파인애플로 총 3가지였으므로, 엘보우 방법을 통해 적절한 클러스터 개수를 얻었다고 할 수 있다.

 

 

 

전체 소스 코드 : 

https://colab.research.google.com/drive/1DNGLcW75NYvCET8AwKiM2sXRGXz8UnsM?hl=ko#scrollTo=WjXYJQSd9Txl

 

 

 

(이해가 다소 힘들거나, 틀린 부분이 있다면 댓글 부탁드리겠습니다! 😊)

💖댓글과 공감은 큰 힘이 됩니다!💖

저작자표시 비영리 변경금지 (새창열림)

'인공지능' 카테고리의 다른 글

[인공지능][개념&실습] 차원 축소 알고리즘 - 주성분 분석(PCA)을 알아보고 사진의 크기를 줄여보자  (2) 2021.05.25
[인공지능][개념] K-Means 알고리즘의 문제점과 'K-Means++ 클러스터링'을 통해 개선하기  (0) 2021.05.24
[인공지능][개념] K-평균(K-means) 알고리즘과 군집화(Clustering) + 이너셔(Inertia) 이해하기  (0) 2021.05.21
[인공지능][개념&실습] 트리의 앙상블(Ensemble)[2] - 엑스트라 트리(Random Forest)와 (+ 히스토그램 기반) 그레이디언트 부스팅  (2) 2021.05.20
[인공지능][개념&실습] 트리의 앙상블(Ensemble)[1] - 랜덤 포레스트(Random Forest)  (3) 2021.05.19
    '인공지능' 카테고리의 다른 글
    • [인공지능][개념&실습] 차원 축소 알고리즘 - 주성분 분석(PCA)을 알아보고 사진의 크기를 줄여보자
    • [인공지능][개념] K-Means 알고리즘의 문제점과 'K-Means++ 클러스터링'을 통해 개선하기
    • [인공지능][개념] K-평균(K-means) 알고리즘과 군집화(Clustering) + 이너셔(Inertia) 이해하기
    • [인공지능][개념&실습] 트리의 앙상블(Ensemble)[2] - 엑스트라 트리(Random Forest)와 (+ 히스토그램 기반) 그레이디언트 부스팅
    BuNa_
    BuNa_
    안드로이드 개발자를 향해 달리고 있는 공대생입니다! 🧑 Android, Kotlin, Java, Python 등 학습하고 있는 내용과 프로젝트를 주로 업로드하고 있습니다. 지적과 조언은 언제나 환영입니다!😊 github : https://github.com/tmdgh1592

    티스토리툴바